/* Layout and components (DESIGN §2, B1-B9). Plain CSS on the tokens (owner decision Q1). */
* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  min-height: 100%;
}

body {
  background:
    radial-gradient(900px 420px at 0% 0%, var(--tint-you), transparent 70%),
    radial-gradient(900px 420px at 100% 0%, var(--tint-ref), transparent 70%),
    var(--bg);
  color: var(--text);
  font-family: "Inter", system-ui, sans-serif;
  font-size: var(--fs-13);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
  -webkit-font-smoothing: antialiased;
}

button,
.btn {
  font: inherit;
  color: inherit;
  transition: background-color 120ms, color 120ms;
}

button:focus-visible,
.btn:focus-within {
  outline: 2px solid var(--you);
  outline-offset: 2px;
}

.app {
  max-width: 1600px;
  margin: 0 auto;
  padding: var(--sp-16) var(--sp-24) var(--sp-24);
}

.eyebrow {
  margin: 0 0 var(--sp-8);
  font-size: var(--fs-11);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Header */
.top {
  display: flex;
  align-items: center;
  gap: var(--sp-16);
  margin-bottom: var(--sp-16);
  min-width: 0;
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--sp-12);
  flex: none;
}

.logo {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: var(--r-button);
  background: var(--you);
  color: var(--on-you);
  font-weight: 800;
  font-size: var(--fs-15);
}

.word {
  font-weight: 800;
  letter-spacing: 0.24em;
  font-size: var(--fs-13);
}

.facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4) var(--sp-12);
  color: var(--muted);
  flex: 1;
  min-width: 0;
}

.fact:first-child {
  color: var(--text);
  font-weight: 600;
}

/* Separators are text at the end of the previous fact ("a ·"), inside its nowrap box: a
   wrapped line never starts with "·". */
.fact {
  white-space: nowrap;
}

.fact .sep {
  margin-left: var(--sp-12);
  color: var(--faint);
  font-weight: 400;
}

.toggle-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-8);
  flex: none;
}

.toggles {
  display: flex;
  padding: 3px;
  border-radius: var(--r-button);
  background: var(--surface-top);
  border: 1px solid var(--border);
  flex: none;
}

.seg {
  border: 0;
  background: transparent;
  color: var(--muted);
  padding: 4px 10px;
  border-radius: 5px;
  cursor: pointer;
  font-size: var(--fs-12);
}

.seg.on {
  background: var(--seg-on);
  color: var(--text);
}

/* Buttons */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 10px 18px;
  border-radius: var(--r-button);
  border: 1px solid var(--glass-border);
  background: var(--surface-top);
  cursor: pointer;
  font-weight: 600;
}

.btn.primary {
  background: var(--you);
  border-color: var(--you);
  color: var(--on-you);
}

.btn.small {
  padding: 5px 12px;
  font-size: var(--fs-12);
  flex: none;
}

.btn input[type="file"] {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
  width: 100%;
}

/* Empty / loading / error / pick */
.drop {
  display: grid;
  place-items: center;
  min-height: 70vh;
  border: 1.5px dashed var(--dashed);
  border-radius: var(--r-card);
  background: linear-gradient(var(--surface-top), var(--surface-bottom));
}

.drop-inner {
  display: grid;
  justify-items: center;
  gap: var(--sp-16);
  padding: var(--sp-24);
  text-align: center;
  max-width: 560px;
}

.open-buttons {
  display: flex;
  gap: var(--sp-12);
  flex-wrap: wrap;
  justify-content: center;
}

.hint,
.loading {
  color: var(--muted);
  margin: 0;
}

.error {
  border: 1px solid rgba(255, 166, 43, 0.35);
  background: rgba(255, 166, 43, 0.06);
  border-radius: var(--r-button);
  padding: var(--sp-12) var(--sp-16);
}

.error h2 {
  margin: 0 0 var(--sp-4);
  font-size: var(--fs-15);
}

.error p {
  margin: 0;
  color: var(--muted);
}

.pick {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--sp-8);
}

/* Session screen */
/* Desktop: map | video, then lanes | laps (DESIGN §2 keeps the right column for the lists), so
   the lanes card ends above the fold at 1600x1000 (scripts/viewer.py asserts it). */
.viewer {
  display: grid;
  grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
  /* Piece 4: the summary (chips + gap) and the where-bar float over the map as glass panels
     (B4), the top-3 cards sit above the laps next to the lanes. */
  /* Review m5: the where-bar leaves the map (the track is the hero, DESIGN B2) and heads the
     right column next to the lanes, as in dashboard-v2. */
  grid-template-areas:
    "map side"
    "lanes where"
    "lanes laps"
    "lanes top3"
    "lanes coach";
  gap: var(--sp-16);
  align-items: start;
}

.card {
  position: relative;
  border-radius: var(--r-card);
  border: 1px solid var(--border);
  background: linear-gradient(var(--surface-top), var(--surface-bottom));
  box-shadow: inset 0 1px 0 var(--highlight);
  padding: var(--sp-16);
  min-width: 0;
}

/* The map canvas fills its card (absolute: it must not size the card by its own backing store);
   the card is as tall as the video card next to it. Engineer mode's extra lanes scroll on
   purpose. */
.map-card {
  grid-area: map;
  align-self: stretch;
  padding: 0;
  overflow: hidden;
  /* web-polish-3 KM2: at 330 px the summary's glass box and the nudge GPS panel met (1 px
     overlap); 340 px leaves them --sp-8 apart (scripts/gps.py SUMMARY_BOX_HEADROOM_PX). */
  min-height: 340px;
}

canvas.map {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

.laps-card {
  grid-area: laps;
}

.glass {
  background: var(--glass);
  border: 1px solid var(--glass-border);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-radius: 12px;
}

.you-panel {
  position: absolute;
  top: var(--sp-16);
  left: var(--sp-16);
  padding: var(--sp-12) var(--sp-16);
}

.chip {
  display: grid;
  grid-template-columns: auto auto 1fr;
  align-items: baseline;
  column-gap: var(--sp-8);
  min-width: 220px;
}

.dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--you);
  box-shadow: 0 0 8px var(--you);
  display: inline-block;
}

.who {
  font-weight: 600;
}

.which {
  color: var(--muted);
}

.time.big {
  grid-column: 1 / -1;
  margin-top: var(--sp-4);
  font-size: var(--fs-28);
  font-weight: 700;
  letter-spacing: -0.03em;
}

.side {
  grid-area: side;
  display: grid;
  gap: var(--sp-16);
  align-content: start;
  min-width: 0;
}

.player {
  position: relative;
  border-radius: var(--r-video);
  overflow: hidden;
  background: #000;
  aspect-ratio: 16 / 9;
}

.player video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.tag {
  position: absolute;
  left: var(--sp-8);
  top: var(--sp-8);
  display: flex;
  align-items: center;
  gap: var(--sp-8);
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--glass);
  border: 1px solid var(--glass-border);
  font-size: var(--fs-12);
}

.banner {
  position: absolute;
  inset: auto var(--sp-8) auto var(--sp-8);
  top: var(--sp-8);
  padding: var(--sp-8) var(--sp-12);
  border-radius: var(--r-button);
  background: var(--glass);
  border: 1px solid rgba(255, 166, 43, 0.45);
}

.banner p {
  margin: 0 0 var(--sp-4);
}

.banner small {
  color: var(--muted);
  overflow-wrap: anywhere;
}

.outside {
  position: absolute;
  left: var(--sp-8);
  bottom: 46px;
  padding: 4px 10px;
  border-radius: var(--r-button);
  background: var(--glass);
  border: 1px solid var(--glass-border);
  color: var(--muted);
  font-size: var(--fs-12);
}

.video-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-8);
  margin-bottom: var(--sp-8);
}

.video-head .eyebrow {
  margin: 0;
}

.speeds {
  display: inline-flex;
  padding: 3px;
  border-radius: var(--r-button);
  background: var(--surface-top);
  border: 1px solid var(--border);
}

.video-name {
  margin: var(--sp-8) 0 0;
  color: var(--faint);
  font-size: var(--fs-11);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.note {
  margin: 0 0 var(--sp-8);
  color: var(--muted);
}

.laps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(92px, 1fr));
  gap: var(--sp-8);
}

.lap {
  width: 100%;
  display: grid;
  gap: 2px;
  text-align: left;
  padding: 8px 10px;
  border-radius: var(--r-button);
  border: 1px solid var(--border);
  background: var(--tile);
  cursor: pointer;
}

.lap .label {
  font-size: var(--fs-11);
  color: var(--muted);
  white-space: nowrap;
}

.lap .label em {
  font-style: normal;
  color: var(--you-text);
}

.lap .time {
  font-weight: 600;
  font-size: var(--fs-13);
}

.lap .gap-note {
  font-size: var(--fs-11);
  color: var(--faint);
}

.lap .bar {
  height: 2px;
  background: var(--grid);
  border-radius: 1px;
  overflow: hidden;
}

.lap .bar i {
  display: block;
  height: 100%;
  background: var(--muted);
}

.lap.dim {
  opacity: 0.55;
}

.lap.best .bar i {
  background: var(--you);
}

.lap.on {
  border-color: var(--you);
  background: rgba(255, 166, 43, 0.1);
}

/* Review m5: the REFERENCE lap carries the blue of the reference (dashboard-v2 V7). */
.lap[data-ref] {
  border-color: var(--ref);
  background: color-mix(in srgb, var(--ref) 10%, transparent);
}

/* P3-2: the optimal lap (answer first, DESIGN rule 1) and its per-sector source laps. */
.optimal {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px var(--sp-8);
  margin: 0 0 var(--sp-8);
}

.optimal .label {
  font-size: var(--fs-11);
  color: var(--muted);
}

.optimal .time {
  font-weight: 600;
  font-size: var(--fs-13);
  font-variant-numeric: tabular-nums;
}

.optimal .vs {
  font-size: var(--fs-11);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.optimal .sources {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.optimal .source {
  font-size: var(--fs-11);
  color: var(--muted);
  padding: 1px 6px;
  border: 1px solid var(--border);
  border-radius: var(--r-button);
  white-space: nowrap;
}

.laps li {
  display: grid;
  gap: 2px;
}

.laps .exclude {
  font-size: var(--fs-11);
  color: var(--faint);
  background: none;
  border: 0;
  padding: 0 2px;
  text-align: left;
  cursor: pointer;
}

.laps .exclude.on {
  color: var(--muted);
  text-decoration: underline;
}

/* An excluded lap is "left out", not wrong: dashed and dimmer, never another color (rule 5). */
.laps li.excluded .lap {
  border-style: dashed;
  opacity: 0.55;
}

.readout .line-gap {
  margin-left: 6px;
  color: var(--muted);
}

.lanes-card {
  grid-area: lanes;
}

.readout {
  display: flex;
  gap: var(--sp-24);
  padding: var(--sp-8) var(--sp-12);
  margin-bottom: var(--sp-4);
  border-radius: var(--r-button);
  background: var(--tile);
  border: 1px solid var(--border);
}

.readout .cell {
  display: grid;
  gap: 2px;
}

.readout .eyebrow {
  margin: 0;
}

.readout .value {
  font-size: var(--fs-15);
  font-weight: 700;
}

.value.you {
  color: var(--you-text);
}

.readout {
  flex-wrap: wrap;
  row-gap: var(--sp-8);
}

.zoombar {
  display: flex;
  align-items: center;
  gap: var(--sp-8) var(--sp-12);
  margin-bottom: var(--sp-4);
  min-height: 28px;
}

.crumb,
.crumb-window {
  white-space: nowrap;
}

.crumb {
  border: 0;
  background: none;
  padding: 0;
  color: var(--muted);
  cursor: pointer;
}

.crumb-sep {
  color: var(--faint);
}

.crumb-window {
  padding: 3px 10px;
  border-radius: var(--r-button);
  border: 1px solid var(--select-edge);
  background: var(--select);
  font-weight: 700;
}

.zoombar .hint {
  flex: 1;
  color: var(--faint);
  font-size: var(--fs-12);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.zoombar .btn[disabled],
.osm-refresh .btn[disabled] {
  opacity: 0.45;
  cursor: default;
}

.overview-row,
.lanes-body {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr);
  align-items: center;
}

.overview-row .eyebrow {
  margin: 0;
  white-space: nowrap;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: none;
  font-size: var(--fs-12);
  color: var(--faint);
}

canvas.overview {
  display: block;
  width: 100%;
  height: 32px;
  cursor: grab;
  touch-action: none;
}

.lanes-body {
  align-items: start;
  /* Room for the corner names T1…Tn and the zoom markers above the lanes. */
  margin-top: 16px;
}

.lane-labels {
  position: relative;
}

.lane-label {
  position: absolute;
  left: 0;
  right: 0;
  display: grid;
  font-weight: 600;
  line-height: 1.25;
}

.lane-label small {
  color: var(--faint);
  font-weight: 400;
}

.lane-stack {
  position: relative;
}

.lane-stack canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

canvas.lanes {
  cursor: crosshair;
  touch-action: pan-y;
}

@media (max-width: 900px) {
  .app {
    padding: var(--sp-12);
  }

  .top {
    flex-wrap: wrap;
    gap: var(--sp-8) var(--sp-12);
  }

  .facts {
    order: 3;
    flex-basis: 100%;
    font-size: var(--fs-12);
  }

  /* DESIGN phone order: summary -> top 3 -> map -> video -> lanes (then the laps list). */
  .viewer {
    display: flex;
    flex-direction: column;
    /* Review F10: every card takes the full column (the desktop grid's `start` shrank them). */
    align-items: stretch;
  }

  .summary,
  .where-card {
    position: static;
    margin: 0;
    width: auto;
  }

  .summary {
    order: 1;
  }

  .where-card {
    order: 2;
  }

  .top3-card {
    order: 3;
  }

  .map-card {
    order: 4;
  }

  .side {
    order: 5;
  }

  .lanes-card {
    order: 6;
  }

  .laps-card {
    order: 7;
  }

  .map-card {
    height: auto;
  }

  .chip {
    min-width: 0;
  }

  .map-card {
    display: flex;
    flex-direction: column-reverse;
  }

  canvas.map {
    position: static;
    height: 280px;
  }

  .you-panel {
    position: static;
    margin: var(--sp-12) var(--sp-12) 0;
  }

  .time.big {
    font-size: var(--fs-20);
  }

  .overview-row,
  .lanes-body {
    grid-template-columns: 76px minmax(0, 1fr);
  }

  .lane-label {
    font-size: var(--fs-11);
  }

  .zoombar .hint {
    display: none;
  }

  .toggle-row {
    order: 4;
    flex-basis: 100%;
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
    animation: none !important;
  }
}

/* Piece 4: compare screen */
.summary {
  grid-area: map;
  position: relative;
  top: auto;
  left: auto;
  align-self: start;
  justify-self: start;
  z-index: 2;
  margin: var(--sp-16);
  padding: var(--sp-12) var(--sp-16);
  display: grid;
  gap: var(--sp-4);
  min-width: 250px;
}

.where-card {
  grid-area: where;
  padding: var(--sp-12);
}

.dot.ref {
  background: var(--ref);
  box-shadow: 0 0 8px var(--ref);
}

.chip select.pick-ref {
  font: inherit;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 0 var(--sp-4);
}

.chip.ref .time {
  color: var(--ref-text);
}

/* OD-SIMS-INVALID-BEST: a chip's not-counted note on its own full-width row. */
.chip .not-counted {
  grid-column: 1 / -1;
  font-size: var(--fs-11);
  color: var(--faint);
}

.gap {
  display: flex;
  align-items: baseline;
  gap: var(--sp-12);
}

.gap-value {
  font-size: var(--fs-40);
  font-weight: 800;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

.gap-value.ref {
  color: var(--ref-text);
  text-shadow: 0 0 18px var(--ref);
}

.gap-value.you {
  color: var(--you-text);
  text-shadow: 0 0 18px var(--you);
}

/* Review F12: an 18 px glow smears on white; 6 px there. */
[data-theme="light"] .gap-value.ref {
  text-shadow: 0 0 6px color-mix(in srgb, var(--ref) 45%, transparent);
}

[data-theme="light"] .gap-value.you {
  text-shadow: 0 0 6px color-mix(in srgb, var(--you) 45%, transparent);
}

.gap-caption {
  color: var(--muted);
  font-size: var(--fs-12);
  max-width: 14ch;
}

.where-head {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-8);
}

.where-head .eyebrow {
  margin: 0;
}

.where-bar {
  display: flex;
  gap: 2px;
  margin-top: var(--sp-4);
  height: 40px;
}

.where-bar .block {
  /* Dimmed by a fainter tint, never by opacity on the text (DESIGN rule 12, review F12). */
  --tint: 12%;
  /* Border-box, no padding and no side border: the flex basis is the whole width, so widths
     stay proportional to |gain| even for a tiny block (compare.py reads them back). */
  box-sizing: border-box;
  min-width: 0;
  overflow: hidden;
  border: 0;
  border-top: 2px solid currentColor;
  border-radius: 6px;
  padding: 0;
  font: inherit;
  font-size: var(--fs-11);
  text-align: left;
  display: grid;
  align-content: start;
  cursor: pointer;
  font-variant-numeric: tabular-nums;
}

/* `lead`, not `top`: `.top` is the header's class. */
.where-bar .block.lead {
  --tint: 30%;
}

.where-bar .block.ref {
  color: var(--ref-text);
  background: color-mix(in srgb, var(--ref) var(--tint), transparent);
}

.where-bar .block.you {
  color: var(--you-text);
  background: color-mix(in srgb, var(--you) var(--tint), transparent);
}

.where-bar .block.even {
  color: var(--muted);
  background: var(--tile);
}

.where-bar .block.unknown {
  color: var(--muted);
  background: repeating-linear-gradient(45deg, var(--tile) 0 4px, var(--dashed) 4px 6px);
}

/* coach-r0-floor N3: a block whose loss is within the noise floor (data-noise: N0, or a headline
   within noise) reads as neither lap's win: the muted ink on the neutral tile with a dashed top,
   never the winner's hue. After the winner rules: both selectors are (0,3,0), so it wins by source
   order (keep it below them). */
.where-bar .block[data-noise] {
  color: var(--muted);
  background: var(--tile);
  border-top-style: dashed;
}

.block-name,
.block-gain {
  display: block;
  white-space: nowrap;
  padding: 1px 0 0 4px;
  line-height: 1.3;
}

.block-gain {
  font-weight: 700;
}

.map-legend {
  position: absolute;
  top: var(--sp-12);
  right: var(--sp-12);
  z-index: 2;
  display: flex;
  gap: var(--sp-12);
  padding: var(--sp-4) var(--sp-12);
  font-size: var(--fs-11);
  color: var(--muted);
}

.map-legend .item {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  white-space: nowrap;
}

.map-legend .sw {
  display: inline-block;
  width: 14px;
  height: 3px;
  border-radius: 2px;
}

.sw.ref {
  background: var(--ref);
}

.sw.you {
  background: var(--you);
}

.sw.even {
  background: var(--muted);
}

.top3-card {
  grid-area: top3;
}

.top3 {
  display: grid;
  gap: var(--sp-8);
  margin-top: var(--sp-8);
}

.top-card {
  padding: var(--sp-12);
}

.top-card p {
  margin: var(--sp-4) 0 var(--sp-8);
  font-size: var(--fs-12);
}

.top-row {
  display: flex;
  align-items: baseline;
  gap: var(--sp-8);
}

.top-row .rank {
  color: var(--muted);
  font-size: var(--fs-12);
}

.top-row .corner {
  font-weight: 700;
  font-size: var(--fs-20);
}

.top-gain {
  margin-left: auto;
  font-weight: 700;
  font-size: var(--fs-20);
  color: var(--ref-text);
  font-variant-numeric: tabular-nums;
}

.top-actions {
  display: flex;
  gap: var(--sp-8);
}

.lane-stack {
  position: relative;
}

.guides {
  position: absolute;
  top: -16px;
  left: 0;
  right: 0;
  height: 16px;
  pointer-events: none;
}

.guide {
  position: absolute;
  /* Left, width and top from `corners::guide_layout` (review 3 M1/M2: each name over its own
     zone, a second row 13 px above when one row cannot hold them). */
  height: 13px;
  line-height: 13px;
  box-sizing: border-box;
  overflow: hidden;
  text-align: center;
  white-space: nowrap;
  pointer-events: auto;
  font: inherit;
  font-size: var(--fs-11);
  color: var(--muted);
  background: none;
  border: 0;
  padding: 0 2px;
  cursor: pointer;
}

.marker {
  position: absolute;
  line-height: 13px;
  text-align: center;
  font-size: var(--fs-11);
  font-weight: 600;
  white-space: nowrap;
  pointer-events: none;
}

.marker.you {
  color: var(--you-text);
}

.marker.ref {
  color: var(--ref-text);
}

/* P1: in a zoom the marker names get their own row above the corner names. */
.lanes-body.zoomed {
  margin-top: 30px;
}

.bracket {
  position: absolute;
  right: 64px;
  font-weight: 700;
  font-size: var(--fs-13);
  pointer-events: none;
  font-variant-numeric: tabular-nums;
}

.bracket.ref {
  color: var(--ref-text);
}

.bracket.you {
  color: var(--you-text);
}

.readout .pair {
  display: flex;
  gap: var(--sp-4);
  align-items: baseline;
}

.readout .value.ref {
  color: var(--ref-text);
}

.cursor-note {
  color: var(--muted);
  font-size: var(--fs-11);
}

/* Phone: the glass panels leave the map and take the full column (after the rules above). */
@media (max-width: 900px) {
  .summary,
  .where-card {
    align-self: stretch;
    justify-self: stretch;
    margin: 0;
    min-width: 0;
  }
}

/* A guide off its apex keeps a vertical 1 px tick at the apex, from its bottom to the first
   lane (review 3 M2: top and height from `Guides`). */
.guide-leader {
  position: absolute;
  width: 1px;
  background: var(--muted);
  pointer-events: none;
}

/* Phone zoom: the four marker names do not fit one row; YOU's row sits above the reference's. */
.lanes-body.zoomed.two-rows {
  margin-top: 43px;
}

/* Two rows of corner guides (a phone): one more 13 px row above everything. */
.lanes-body.guides-2 {
  margin-top: 29px;
}

.lanes-body.zoomed.guides-2 {
  margin-top: 43px;
}

.lanes-body.zoomed.two-rows.guides-2 {
  margin-top: 56px;
}

/* Piece 5: split video (docs/plans §6.4). YOUR video left (or on top on a phone, where the
   component stacks them below 900 px), the reference right, the delta pill above the seam. */
.split-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-8);
  margin-bottom: var(--sp-8);
}

.split-controls .loop {
  border: 1px solid var(--border);
}

/* The pill sits in its own row above the seam, never on a picture or a tag (review 1 m7). */
.split-players {
  position: relative;
  display: grid;
  gap: var(--sp-8);
  align-items: start;
  padding-top: 28px;
}

.split-players .tag {
  font-size: var(--fs-11);
  padding: 2px 8px;
}

.pill {
  position: absolute;
  left: 50%;
  top: 0;
  z-index: 3;
  transform: translateX(-50%);
  padding: 2px 10px;
  border-radius: 999px;
  border: 1px solid var(--muted);
  background: var(--glass);
  color: var(--muted);
  font-size: var(--fs-12);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.pill.you {
  color: var(--you-text);
  border-color: var(--you);
}

.pill.ref {
  color: var(--ref-text);
  border-color: var(--ref);
}

/* Piece 5: satellite tiles (§5.2) in the map canvas's own box, painted under it. */
canvas.map-tiles {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.satellite {
  position: absolute;
  right: var(--sp-12);
  bottom: var(--sp-12);
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--sp-4);
  max-width: min(420px, 70%);
  padding: var(--sp-4) var(--sp-8);
  font-size: var(--fs-11);
  color: var(--muted);
  text-align: right;
}

.satellite .attribution {
  color: var(--text);
}

@media (max-width: 900px) {
  /* The map canvas is in the flow here: the tiles take its 280 px at the card's bottom, and the
     line canvas is positioned so it paints over them. */
  canvas.map {
    position: relative;
  }

  canvas.map-tiles {
    inset: auto 0 0 0;
    height: 280px;
  }

  .satellite {
    position: static;
    align-items: flex-start;
    max-width: none;
    margin: var(--sp-8) var(--sp-12) 0;
    text-align: left;
  }
}

/* P2-9 track editor: a full-bleed canvas on the satellite tiles, the toolbar above it. Handles
   are drawn on the canvas (model/editor/screen.rs sizes them); buttons here are 36 px tall
   (44 px on a phone) so every control is easy to hit. */
/* Review r2 MAJOR-1: the GPS panel and the editor button share one bottom-left column over the
   map, so neither can cover the other. */
/* P2-11 review r2 R2-2: from 1024 px the tools column (left) and the satellite/OSM
   corner (right) share one bottom row, so they split its width and can never overlap. The tools
   keep one width, idle or nudging (a narrower GPS panel wraps into more rows and grows up into the
   summary: web-polish-3 item 1; a width that follows the nudge toggle would reshape the corner, a
   glass panel the track fit avoids, and move the map); the corner takes what is left and wraps its
   words. 8 px from the card's bottom and a 4 px gap in the column keep the nudge panel >= 8 px under
   the summary's text (scripts/gps.py SUMMARY_HEADROOM_PX) on every desktop width. */
.map-bottom {
  position: absolute;
  left: var(--sp-12);
  right: var(--sp-12);
  bottom: var(--sp-8);
  z-index: 2;
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--sp-8);
  pointer-events: none;
}

:root {
  /* The desktop tools column (.map-tools); the GPS panel's hidden nudge-row measure is laid out at
     its width (.gps-fit-reserve). */
  --tools-w: 460px;
}

.map-tools {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-4);
  flex: none;
  /* web-polish-2 item 1: 460 px, or the row's width when that is less (never a share of it): on a
     narrow desktop a 60 % column wrapped into so many rows that it grew up over the summary's gap
     (scripts/gps.py summary_clear); wider, it would sit over the corner badges. */
  width: min(var(--tools-w), 100%);
  pointer-events: none;
}

.map-tools > * {
  pointer-events: auto;
}

.edit-track {
  position: static;
  color: var(--text);
}

.editor {
  display: flex;
  flex-direction: column;
  gap: var(--sp-8);
  padding: var(--sp-12) var(--sp-16);
}

.editor-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-8);
  padding: var(--sp-8);
}

.editor-toolbar .seg,
.editor .seg {
  min-height: 36px;
  padding: 0 var(--sp-12);
  font-size: var(--fs-13);
  color: var(--text);
}

.editor-toolbar .seg.primary {
  border: 1px solid var(--text);
  font-weight: 600;
}

.editor-gap {
  flex: 1;
}

.editor-stage {
  position: relative;
  height: calc(100vh - 190px);
  min-height: 360px;
  overflow: hidden;
  border-radius: var(--r-card);
  background: var(--track);
}

.editor-stage canvas.map-tiles {
  inset: 0;
  height: 100%;
}

canvas.editor-canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  cursor: crosshair;
  touch-action: none;
}

.editor-banner {
  position: absolute;
  top: var(--sp-12);
  left: 50%;
  z-index: 2;
  max-width: calc(100% - 2 * var(--sp-12));
  margin: 0;
  padding: var(--sp-8) var(--sp-12);
  transform: translateX(-50%);
  font-size: var(--fs-13);
  color: var(--text);
  pointer-events: none;
}

.editor-draft {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-8);
  margin: 0;
  padding: var(--sp-8) var(--sp-12);
  font-size: var(--fs-13);
  color: var(--text);
}

.editor-hint,
.editor-attribution {
  position: absolute;
  bottom: var(--sp-12);
  z-index: 2;
  margin: 0;
  padding: var(--sp-4) var(--sp-8);
  font-size: var(--fs-11);
  color: var(--muted);
  pointer-events: none;
}

.editor-hint {
  left: var(--sp-12);
}

.editor-attribution {
  right: var(--sp-12);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  max-width: min(420px, 60%);
  color: var(--text);
}

.editor-help {
  margin: 0;
  font-size: var(--fs-12);
  color: var(--muted);
}

@media (max-width: 900px) {
  .editor {
    padding: var(--sp-8);
  }

  .editor-toolbar .seg,
  .editor .seg {
    min-height: 44px;
  }

  .editor-gap {
    display: none;
  }

  .editor-stage {
    height: calc(100vh - 300px);
    min-height: 320px;
  }

  /* Phone: the tools above the corner, both in the card's flow as before the shared row. */
  .map-bottom {
    position: static;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
  }

  /* web-polish-3 KM3: the desktop's 4 px gap is for the summary's headroom; a phone has the tools
     in the flow, under no summary, so its parts keep the design's 8 px between separate boxes. */
  .map-tools {
    margin: var(--sp-8) var(--sp-12) 0;
    width: auto;
    max-width: none;
    gap: var(--sp-8);
  }
}

/* U1 (docs/plans/coach.md §6.1): the top-3 card's rule title, sentence (numbers bold), micro
   delta chart and a third button; long corner names and sentences wrap, never overflow. */
.top-card .top-title {
  margin: var(--sp-4) 0 0;
  font-size: var(--fs-13);
  font-weight: 600;
  overflow-wrap: anywhere;
}

.top-card .top-sentence {
  margin: var(--sp-4) 0 var(--sp-8);
  font-size: var(--fs-12);
  color: var(--muted);
  overflow-wrap: anywhere;
}

.top-sentence b.num,
.row-said b.num {
  color: var(--text);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.top-row .corner {
  min-width: 0;
  overflow-wrap: anywhere;
}

canvas.spark {
  display: block;
  width: 100%;
  margin-bottom: var(--sp-8);
}

.top-actions {
  flex-wrap: wrap;
}

.full-analysis {
  margin-top: var(--sp-8);
}

/* "Análise completa": over the lanes on desktop (the lanes card is its box). */
.analysis {
  position: absolute;
  inset: 0;
  z-index: 20;
  overflow: auto;
  padding: var(--sp-16);
  border-radius: var(--r-card);
  background: var(--bg);
}

.analysis-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-8);
}

.analysis-rows {
  list-style: none;
  margin: var(--sp-8) 0 0;
  padding: 0;
  display: grid;
  gap: var(--sp-8);
}

.analysis-row {
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: var(--sp-12);
  min-width: 0;
}

.analysis-row:hover,
.analysis-row:focus-within {
  border-color: var(--select-edge);
}

.row-head .row-zoom {
  background: none;
  border: 0;
  padding: 0;
  color: var(--text);
  font-family: inherit;
  cursor: pointer;
  text-align: left;
  text-decoration: underline dotted;
}

.phase-times {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4) var(--sp-12);
  margin: 0 0 var(--sp-8);
  font-size: var(--fs-11);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.row-head {
  display: flex;
  align-items: baseline;
  gap: var(--sp-8);
}

.row-head .corner {
  font-weight: 700;
  font-size: var(--fs-15);
  min-width: 0;
  overflow-wrap: anywhere;
}

.row-loss {
  margin-left: auto;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.row-loss.ref {
  color: var(--ref-text);
}

.row-loss.you {
  color: var(--you-text);
}

.phase-bar {
  display: flex;
  gap: 2px;
  height: 8px;
  margin: var(--sp-8) 0;
}

.phase-bar .phase {
  min-width: 0;
  border-radius: 2px;
  background: var(--seg-on);
}

.phase-bar .phase.ref {
  background: var(--ref);
}

.phase-bar .phase.you {
  background: var(--you);
}

.row-said {
  margin: 0 0 var(--sp-8);
  font-size: var(--fs-12);
  color: var(--muted);
  overflow-wrap: anywhere;
}

.row-said .row-title {
  color: var(--text);
}

table.facts {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-12);
  font-variant-numeric: tabular-nums;
}

table.facts th,
table.facts td {
  padding: 2px var(--sp-4);
  text-align: left;
  font-weight: 400;
}

table.facts th {
  color: var(--muted);
}

@media (max-width: 900px) {
  /* Phone: a full-screen sheet. */
  .analysis {
    position: fixed;
    z-index: 100;
    border-radius: 0;
  }
}

/* P2-8 GPS panel (gps-track.md §7, B4 glass over the map): raw/fixed, the shift, the nudge. The
   track fit avoids its nudge-mode box (ui/map.rs nudge_box), the corner badges its visible one.
   Never a color for "unsure": words only. */
.gps-panel {
  position: static;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-8);
  padding: var(--sp-4) var(--sp-8);
  font-size: var(--fs-11);
  color: var(--muted);
}

.gps-panel .gps-shift,
.gps-panel .nudge-text {
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.gps-panel .nudge-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4);
}

.gps-panel .nudge-toggle,
.gps-panel .nudge-row .seg {
  border: 1px solid var(--border);
}

/* web-polish-5 item 1: the invisible copy of the nudge row (ui/gps_panel.rs fit_reserve): out of
   the flow, never seen, never hit; the map reads its height while idle (ui/map.rs nudge_box).
   r1 MAJOR-1: its containing block is the IDLE panel (.glass has backdrop-filter), whose width
   changes with the language, the shift text and the nudge; so it gets the nudge-mode panel's
   content width (the tools column less the panel's --sp-8 padding and 1 px border each side) and
   wraps where the real row wraps. On a desktop the column is always --tools-w (901 px leaves a
   518 px card). */
.gps-fit-reserve {
  position: absolute;
  left: 0;
  top: 0;
  width: calc(var(--tools-w) - 2 * var(--sp-8) - 2px);
  visibility: hidden;
  pointer-events: none;
}

@media (max-width: 900px) {
  .gps-panel {
    max-width: none;
  }

  /* Phone: the column is the card's width, not --tools-w, and the panel is off the canvas (the fit
     never reads it): the reserve keeps the panel's width, so it never widens the page. */
  .gps-fit-reserve {
    width: auto;
  }
}

/* U2: the coach card (docs/plans/coach.md §6.3, dashboard-v2): the --you hue at 7 % with an 18 %
   border, radius 16, avatar "C", the focus in bold. It sits under the top 3 in the right column
   (the `coach` row of the .viewer grid);
   on the phone after the top 3 (DESIGN §2 phone order: summary -> top 3 -> coach -> map). */
.coach-card {
  grid-area: coach;
  border-radius: 16px;
  background: color-mix(in srgb, var(--you) 7%, transparent);
  border-color: color-mix(in srgb, var(--you) 18%, transparent);
}

.coach-head {
  display: flex;
  align-items: center;
  gap: var(--sp-12);
}

.coach-avatar {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--you);
  color: var(--on-you);
  font-weight: 700;
  flex: none;
}

.coach-title {
  margin: 0;
  font-size: 14px;
}

.coach-sub {
  margin: 0;
  color: var(--muted);
  font-size: var(--fs-12);
}

.coach-bubble {
  margin: var(--sp-8) 0 var(--sp-4);
  padding: var(--sp-8) var(--sp-12);
  border-radius: 12px;
  background: color-mix(in srgb, var(--you) 7%, transparent);
  font-size: var(--fs-12);
  line-height: 1.45;
}

.coach-status,
.coach-note {
  margin: var(--sp-8) 0;
  font-size: var(--fs-12);
  color: var(--muted);
}

.coach-error {
  color: var(--text);
}

.coach-chips,
.coach-charts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4) var(--sp-8);
}

.coach-chip {
  color: var(--you-text);
  border-color: color-mix(in srgb, var(--you) 40%, transparent);
}

canvas.coach-chart {
  display: block;
  border-radius: 6px;
  background: var(--track);
}

.coach-frames {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
  margin-top: var(--sp-8);
  font-size: var(--fs-12);
  color: var(--muted);
}

.coach-chat {
  margin-top: var(--sp-12);
}

.coach-turns {
  list-style: none;
  margin: 0;
  padding: 0;
}

.coach-question {
  margin: var(--sp-8) 0 0;
  text-align: right;
  font-size: var(--fs-12);
}

.coach-ask {
  display: flex;
  gap: var(--sp-8);
  margin-top: var(--sp-8);
}

.coach-ask input {
  flex: 1;
  min-width: 0;
  padding: var(--sp-8) var(--sp-12);
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--track);
  color: var(--text);
  font: inherit;
  font-size: var(--fs-12);
}

/* coach-u3: the 👍/👎 of a top-3 card and of a coach claim, the 👎 note and the grade's status. */
.grade {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4) var(--sp-8);
  margin-top: var(--sp-4);
  font-size: var(--fs-12);
}

.grade-btn[aria-pressed="true"] {
  border-color: var(--you);
  background: color-mix(in srgb, var(--you) 14%, transparent);
}

.grade-note {
  display: flex;
  flex: 1 0 100%;
  gap: var(--sp-8);
}

.grade-note input {
  flex: 1;
  min-width: 0;
  padding: var(--sp-4) var(--sp-8);
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--track);
  color: var(--text);
  font: inherit;
}

/* The phone's sheet controls: the desktop shows the chat in the card, never these. */
.coach-chat-open,
.coach-chat-head {
  display: none;
}

@media (max-width: 900px) {
  .coach-card {
    order: 3;
  }

  /* coach-u3: on the phone the chat is a full-screen sheet over the page, opened from the
     card; its question box sits on the sheet's bottom above the home-bar safe area, and the
     dynamic viewport height keeps it above an open keyboard. */
  .coach-chat-open {
    display: block;
    width: 100%;
    margin-top: var(--sp-12);
    text-align: left;
  }

  .coach-chat {
    display: none;
  }

  .coach-chat[data-chat-open="true"] {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    flex-direction: column;
    height: 100dvh;
    margin: 0;
    padding: var(--sp-12) var(--sp-12) calc(var(--sp-12) + env(safe-area-inset-bottom));
    background: var(--bg);
  }

  .coach-chat[data-chat-open="true"] .coach-chat-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  .coach-chat[data-chat-open="true"] .coach-turns {
    flex: 1;
    min-height: 0;
    overflow: auto;
  }
}

/* P2-11: the OSM failed/refused line and the "Dar nome à pista" prompt in the header. */
.osm-notes:empty {
  display: none;
}

/* Its own full-width row under the header line: the facts keep their width. */
.top:has(> .osm-notes:not(:empty)) {
  flex-wrap: wrap;
}

.osm-notes {
  order: 10;
  flex: 1 0 100%;
  min-width: 0;
  font-size: var(--fs-12);
}

.osm-notes .gps-note {
  margin: 0;
}

.name-prompt {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4) var(--sp-8);
  font-size: var(--fs-12);
  flex: 0 1 auto;
  min-width: 0;
}

/* P2-11 review r1 F4: the satellite controls and the OSM attribution share one bottom-right
   column, so neither can cover the other; on a phone it is in the flow like .satellite. */
.map-corner {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--sp-4);
  flex: 0 1 auto;
  min-width: 0;
  max-width: 420px;
  /* Squeezed by the tools on a narrow desktop: its words wrap inside it, never over the tools. */
  overflow-wrap: anywhere;
  pointer-events: none;
}

/* P2-11 review r2 N-c: nothing to show (no tile source, no OSM line) takes no room, not even the
   phone's top margin. */
.map-corner:empty {
  display: none;
}

/* 901-1023 px: the row would leave the corner too narrow for its toggle; both stay in their own
   corner of the card as before the row (a satellite box can still meet the tools there: the
   pre-existing part of R2-2, a follow-up). */
@media (min-width: 901px) and (max-width: 1023px) {
  .map-bottom {
    display: contents;
  }

  .map-tools {
    position: absolute;
    left: var(--sp-12);
    bottom: var(--sp-8);
    z-index: 2;
    /* web-polish-5: the column's nudge-mode width in both modes, as on a wider desktop, so the
       map reads the nudge-mode panel's width while idle (ui/map.rs nudge_box; the column is
       transparent and takes no clicks: nothing visible changes). */
    width: min(var(--tools-w), calc(100% - 2 * var(--sp-12)));
  }

  .map-corner {
    position: absolute;
    right: var(--sp-12);
    bottom: var(--sp-8);
    z-index: 2;
    max-width: min(420px, 70%);
  }
}

.map-corner > * {
  pointer-events: auto;
}

.map-corner > .satellite {
  position: static;
  max-width: none;
  margin: 0;
}

.osm-attribution {
  padding: 2px 6px;
  font-size: var(--fs-11);
}

@media (max-width: 900px) {
  .map-corner {
    align-items: flex-start;
    max-width: none;
    margin: var(--sp-8) var(--sp-12) 0;
  }
}

/* gps-p3-3: the second file's notes; the right video panel without his mp4. */
.coach-file { display: grid; gap: var(--sp-8); justify-items: start; }
.coach-file .note { margin: 0; }
.player-ref[data-ref-video="none"] { aspect-ratio: 16 / 9; }
.player-ref [data-video="no-coach"] {
  position: absolute; inset: 0; margin: 0; display: grid; place-items: center; text-align: center;
}
