/* La Vuelta a Barca brand: navy/red/blue, matching the itinerary and rider handbook. */
@font-face {
  font-family: "Exo 2";
  src: url("assets/fonts/Exo2-Regular.ttf") format("truetype");
  font-style: normal;
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Exo 2";
  src: url("assets/fonts/Exo2-Medium.ttf") format("truetype");
  font-style: normal;
  font-weight: 500;
  font-display: swap;
}
@font-face {
  font-family: "Exo 2";
  src: url("assets/fonts/Exo2-SemiBold.ttf") format("truetype");
  font-style: normal;
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: "Exo 2";
  src: url("assets/fonts/Exo2-Bold.ttf") format("truetype");
  font-style: normal;
  font-weight: 700;
  font-display: swap;
}
@font-face {
  font-family: "Exo 2";
  src: url("assets/fonts/Exo2-ExtraBold.ttf") format("truetype");
  font-style: normal;
  font-weight: 800;
  font-display: swap;
}

:root {
  --ink: #0C0B3B;
  --ink-deep: #161454;
  --accent-red: #E3001F;
  --accent-blue: #1E2C8F;

  --bg: var(--ink);
  --surface: rgba(16, 15, 70, 0.97);
  --surface-strong: #100f46;
  --surface-soft: rgba(255, 255, 255, 0.06);
  --border: rgba(217, 221, 240, 0.2);
  --fg: #F5F7FF;
  --fg-muted: #A7ABD6;
  --accent: var(--accent-red);
  --accent-bright: #ff2a45;
  --accent-2: var(--accent-blue);
  --accent-2-bright: #3346c2;
  --terrain: #d9a441;
  --success: #48c78e;
  --danger: #ff6b6b;
  --radius-sm: 8px;
  --radius: 14px;
  --radius-lg: 20px;
  --radius-pill: 999px;
  --shadow: 0 18px 50px rgba(0, 0, 0, 0.4);
  --space-xs: 4px;
  --space-sm: 8px;
  --space: 12px;
  --space-md: 16px;
  --space-lg: 24px;
  --header-h: 64px;
  --collapsed-h: 46px;
  /* Desaturated blue for lunch stops, so they read as distinct from the
     red hazard pills at a glance. 5.5:1 against white text. */
  --accent-blue-soft: #5561A6;
  --sidebar-w: 390px;
  --font-sans: "Exo 2", "Helvetica Neue", Arial, sans-serif;
  --font-mono: "SFMono-Regular", Menlo, monospace;
}

* { box-sizing: border-box; }

html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
  overflow: hidden;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-sans);
  /* iOS Safari: stop the document itself from rubber-banding or rescaling
     text when a pinch starts on the map canvas. */
  overscroll-behavior: none;
  -webkit-text-size-adjust: 100%;
}

button,
input { font: inherit; }

button:focus-visible,
input:focus-visible {
  outline: 3px solid rgba(227, 0, 31, 0.5);
  outline-offset: 2px;
}

button { color: inherit; }

/* Permanent header + a relative content area beneath it - the header is part
   of normal flow now, not a floating panel overlapping the map. */
#app {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
}

#content {
  position: relative;
  flex: 1;
  min-height: 0;
  background: var(--ink);
}

/* Full-bleed map - every other panel (stage list, drawer, timeline) floats
   above it as its own framed card. */
#map {
  position: absolute;
  inset: 0;
  /* Claim every touch gesture for MapLibre. Without this, iOS Safari treats a
     two-finger pinch as page zoom at the same time MapLibre treats it as a
     map zoom - Safari wins mid-gesture and snaps the whole document scale. */
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  background:
    radial-gradient(circle at 72% 22%, rgba(30, 44, 143, 0.28), transparent 34%),
    linear-gradient(145deg, var(--ink-deep), var(--ink) 62%);
}

/* ---- Map overlay chrome -------------------------------------------------
   Stage pins and checkpoint labels are HTML markers, not GPU symbols: the
   raster-only style has no glyph endpoint, and DOM gives crisp text at any
   DPI plus a real touch target. */

/* MapLibre owns this element's transform (it writes translate() into it to
   position the marker), so it carries no transform of its own - the scale
   effects go on .map-pin inside it. */
.map-pin-wrap {
  display: block;
  width: 30px;
  height: 30px;
  line-height: 0;
}

.map-pin {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 2px solid rgba(255, 255, 255, 0.92);
  border-radius: 50%;
  background: var(--pin-color, var(--accent-red));
  color: #ffffff;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 800;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
  transition: transform 140ms ease, box-shadow 140ms ease;
}

/* Playback position. An HTML marker, not a GL layer - GL content always
   paints below MapLibre's HTML markers, which put this behind the
   stage-number pins whenever they overlapped. The explicit z-index (stage
   pins have none, so sit at the stacking-context default) guarantees it
   stays on top regardless of marker add/remove order. */
.route-cursor {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #ffffff;
  border: 4px solid rgb(239 85 63);
  box-sizing: border-box;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
  z-index: 10;
  pointer-events: none;
  transition: width 160ms ease, height 160ms ease;
}

/* Swapped in once the branded canvas composite (logo + ring) has decoded -
   the border above is dropped since the ring is baked into that image. */
.route-cursor--badge {
  width: 38px;
  height: 38px;
  border: 0;
  background: center / cover no-repeat;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.5);
}

.map-pin:hover { transform: scale(1.12); }

/* The selected/hovered stage's pin lifts out of the pack. */
.map-pin.is-active {
  transform: scale(1.25);
  border-color: #ffffff;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.55);
}

/* Checkpoint names for the selected stage. Dark plate rather than a text
   outline - readable over both bright imagery and dark topo. */
.map-label {
  padding: 3px 7px;
  border-radius: var(--radius-sm);
  background: rgba(9, 8, 46, 0.88);
  color: #ffffff;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 700;
  line-height: 1.2;
  white-space: nowrap;
  pointer-events: none;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
}

/* Lunch stops carry the same blue as their pill in the stage summary, so the
   two views name the same thing the same way. */
.map-label.is-lunch { background: var(--accent-blue-soft); }

/* MapLibre's popup, restyled to match the app's cards instead of its
   default white bubble. */
.maplibregl-popup-content {
  padding: 10px 12px;
  border-radius: var(--radius);
  background: var(--surface-strong);
  box-shadow: var(--shadow);
}
.maplibregl-popup-tip { border-top-color: var(--surface-strong) !important; }
.maplibregl-popup-anchor-top .maplibregl-popup-tip,
.maplibregl-popup-anchor-top-left .maplibregl-popup-tip,
.maplibregl-popup-anchor-top-right .maplibregl-popup-tip {
  border-bottom-color: var(--surface-strong) !important;
  border-top-color: transparent !important;
}
.maplibregl-popup-close-button {
  padding: 0 6px;
  color: var(--fg-muted);
  font-size: 16px;
}
.popup-title { display: block; color: #ffffff; font-size: 13px; }
.popup-meta { display: block; margin-top: 2px; color: var(--fg-muted); font-size: 11px; }
.popup-desc {
  margin: 8px 0 0;
  padding-top: 8px;
  border-top: 1px solid var(--border);
  color: var(--fg);
  font-size: 12px;
  line-height: 1.45;
}

/* Shown when the map cannot draw at all (no WebGL) or its imagery cannot
   load - otherwise both failures look identical to a blank screen. */
.map-error {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 5;
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: min(320px, calc(100% - 32px));
  padding: 16px 18px;
  transform: translate(-50%, -50%);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-strong);
  box-shadow: var(--shadow);
  text-align: center;
}

.map-error strong { font-size: 14px; }
.map-error span { color: var(--fg-muted); font-size: 12px; line-height: 1.45; }

.panel {
  border: 1px solid var(--border);
  background: var(--surface);
  box-shadow: var(--shadow);
  backdrop-filter: blur(16px) saturate(120%);
  -webkit-backdrop-filter: blur(16px) saturate(120%);
}

/* Logo and stats columns are both 1fr - equal width regardless of their
   actual content - so the title in between sits in the true visual centre
   of the bar, not just centred within whatever space the logo left over.
   The third column is empty: .header-hud is absolutely positioned into it
   from #content, but the column must stay to keep the title centred. */
.topbar {
  z-index: 20;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space);
  flex: none;
  height: var(--header-h);
  padding: 0 var(--space-md);
  border-bottom: 1px solid rgba(12, 11, 59, 0.12);
  background: #ffffff;
  color: var(--ink);
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
  --fg: var(--ink);
  --fg-muted: #5b5e94;
  --border: rgba(12, 11, 59, 0.14);
}

.topbar-title { justify-self: center; min-width: 0; }

/* Tour-wide stats as a small HUD readout, where the "N stages ready" status
   pill used to sit - dark digital-display tiles against the white header.
   The element sits in #content (so it can float over the map on mobile), so
   on desktop it is positioned back into the header's right-hand slot. */
.header-hud {
  position: absolute;
  top: calc(0px - var(--header-h));
  right: var(--space-md);
  height: var(--header-h);
  display: flex;
  align-items: center;
  flex: none;
  gap: 6px;
  z-index: 21;
}

.hud-box {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  min-width: 74px;
  padding: 6px 12px;
  border-radius: var(--radius-sm);
  background: var(--ink);
}

.hud-box span {
  color: #8890d6;
  font-size: 8px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  white-space: nowrap;
}

.hud-box strong {
  color: #ffffff;
  font-size: 14px;
  font-family: var(--font-mono);
  white-space: nowrap;
}

.brand-mark {
  height: 28px;
  width: auto;
  object-fit: contain;
}

.eyebrow {
  margin: 0 0 2px;
  color: var(--fg-muted);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.topbar h1,
/* Shared by every card's heading row (Stages, Map) so both look identical
   rather than drifting whenever only one of them sat inside .sidebar. */
.list-heading h3 { margin: 0; font-size: 14px; }

.topbar h1 {
  overflow: hidden;
  font-size: 17px;
  font-weight: 800;
  font-style: italic;
  letter-spacing: -0.01em;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.topbar h1 span { color: var(--accent-red); }

/* Sized to its content (up to a cap that leaves room for the timeline), not
   stretched to fill the sidebar's whole vertical allowance - nine stages
   don't fill that space, and stretching left a slab of blank white card
   below the list. The list itself scrolls internally if it ever needs to. */
.sidebar {
  position: absolute;
  z-index: 20;
  top: var(--space-md);
  left: var(--space-md);
  width: var(--sidebar-w);
  max-height: calc(100% - var(--space-md) - 108px - var(--space-md));
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transition: max-height 200ms ease;
}

.sidebar.detail-open {
  max-height: calc(100% - var(--space-md) - var(--space-md) - 92px);
}

/* Collapses by shrinking height (bottom edge rises toward the header row),
   not width - the panel stays legible and the same width whether the list
   is open or not.
   Collapsed height is a single exact value shared with the row inside it.
   It used to be 54px while the row's intrinsic height was ~56px (36px icon
   button + 20px padding), so overflow:hidden cropped the bottom couple of
   pixels and left the text looking vertically off-centre. */
.sidebar.collapsed,
.map-panel.collapsed {
  max-height: var(--collapsed-h);
  /* .sidebar clips with overflow:hidden but carried no radius of its own, so
     the pill-shaped child was being squared off at the corners. */
  border-radius: var(--radius-pill);
}

.sidebar.collapsed .stage-list-section { flex: none; border-radius: var(--radius-pill); }
.sidebar.collapsed .stage-list { display: none; }
.sidebar.collapsed .stage-detail { display: none; }

.sidebar.collapsed .list-heading,
.map-panel.collapsed .list-heading {
  border-bottom: none;
  min-height: var(--collapsed-h);
  padding: 0 6px 0 var(--space-md);
}

/* A 36px button inside a 46px pill leaves too little breathing room. */
.sidebar.collapsed .icon-button,
.map-panel.collapsed .icon-button {
  width: 32px;
  height: 32px;
}

.stage-list-section {
  border-radius: var(--radius-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  /* White card, matching the itinerary site's cards - local overrides so
     every child (list heading, stage cards, text) reads dark-on-white
     without duplicating rules. */
  background: #ffffff;
  color: var(--ink);
  --fg: var(--ink);
  --fg-muted: #5b5e94;
  --border: rgba(12, 11, 59, 0.12);
  --surface-soft: rgba(12, 11, 59, 0.05);
}

.section-heading,
.detail-heading,
.list-heading,
.control-label,
.timeline-labels {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space);
}

.icon-button,
.text-button,
.secondary-button,
.segmented button,
.play-button {
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface-soft);
  cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease, transform 120ms ease;
}

.icon-button:hover,
.text-button:hover,
.secondary-button:hover,
.segmented button:hover,
.play-button:hover {
  border-color: rgba(255, 255, 255, 0.34);
  transform: translateY(-1px);
}

.icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 36px;
  height: 36px;
  padding: 0;
  font-weight: 800;
  line-height: 1;
}

.text-button {
  padding: 6px 10px;
  color: var(--fg-muted);
  font-size: 11px;
}

.detail-metrics span {
  display: block;
  color: var(--fg-muted);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.list-heading {
  flex: none;
  padding: 10px var(--space-md);
  border-bottom: 1px solid var(--border);
}

.list-heading span {
  overflow: hidden;
  max-width: 60%;
  color: var(--fg-muted);
  font-size: 10px;
  text-align: right;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.stage-list {
  display: grid;
  gap: 6px;
  align-content: start;
  flex: 1;
  overflow-y: auto;
  padding: 8px 10px 12px;
  scrollbar-width: thin;
  scrollbar-color: rgba(12, 11, 59, 0.22) transparent;
}

.stage-list[hidden] { display: none; }

.stage-card {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 10px;
  align-items: center;
  width: 100%;
  padding: 10px;
  border: 1px solid transparent;
  border-radius: 12px;
  background: transparent;
  color: var(--fg);
  text-align: left;
  cursor: pointer;
}

.stage-copy {
  display: grid;
  gap: 3px;
  min-width: 0;
}

.stage-card:hover,
.stage-card.active {
  border-color: var(--border);
  background: var(--surface-soft);
}

.stage-card.active { box-shadow: inset 3px 0 var(--stage-color); }

.stage-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--stage-color);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--stage-color) 24%, transparent);
}

.stage-name {
  display: block;
  font-size: 12px;
  font-weight: 750;
  line-height: 1.25;
  white-space: normal;
  overflow-wrap: anywhere;
}

.stage-meta,
.stage-distance {
  color: var(--fg-muted);
  font-size: 10px;
}

.stage-distance {
  align-self: start;
  padding-top: 2px;
  font-family: var(--font-mono);
  white-space: nowrap;
}

/* Stage detail replaces the stage list in place, in the same card, instead
   of opening as a popup - a popup needed a full-screen invisible click
   layer to close on outside-click, which also silently blocked panning or
   zooming the map while a stage was selected. */
.stage-detail {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--space-md);
  scrollbar-width: thin;
  scrollbar-color: rgba(12, 11, 59, 0.22) transparent;
  animation: detail-in 160ms ease;
}

.stage-detail[hidden] { display: none; }

.detail-topbar {
  display: grid;
  gap: 7px;
  margin-bottom: 12px;
}

.detail-heading-copy {
  min-width: 0;
}

.detail-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}

.detail-close {
  width: 32px;
  height: 32px;
  background: rgba(227, 0, 31, 0.08);
  color: var(--accent-red);
}

.detail-nav {
  width: 32px;
  height: 32px;
}

.detail-close svg,
.detail-nav svg { display: block; }

@keyframes detail-in {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: translateY(0); }
}

#detail-title {
  max-width: 100%;
  margin: 0;
  font-size: 23px;
  line-height: 1.06;
  letter-spacing: -0.02em;
}

.detail-metrics {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 8px;
  margin: 16px 0 14px;
}

.detail-metrics div {
  min-width: 0;
  min-height: 46px;
  padding: 7px 8px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: rgba(12, 11, 59, 0.035);
}

.detail-metrics div:nth-child(1),
.detail-metrics div:nth-child(2),
.detail-metrics div:nth-child(3) { grid-column: span 2; }

.detail-metrics div:nth-child(4) { grid-column: 2 / span 2; }
.detail-metrics div:nth-child(5) { grid-column: 4 / span 2; }

.detail-metrics strong {
  display: block;
  margin-top: 3px;
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.elevation-profile {
  display: block;
  width: 100%;
  height: 100px;
  overflow: visible;
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.16);
}

.stage-notes {
  display: grid;
  gap: 14px;
  margin-top: 14px;
  padding-top: 0;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border);
}

.notes-variant {
  margin: 0;
  color: #b7c0ff;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.notes-body {
  margin: 0;
  color: var(--fg);
  font-size: 13px;
  line-height: 1.6;
}

.notes-checkpoints > span,
.notes-hazards > span {
  display: block;
  margin-bottom: 6px;
  color: var(--fg-muted);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.notes-checkpoints ul,
.notes-hazards ul {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.notes-checkpoints li {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  height: 30px;
  min-height: 30px;
  max-width: 100%;
  padding: 4px 9px;
  border: 1px solid var(--ink);
  border-radius: var(--radius-pill);
  background: var(--ink);
  color: #ffffff;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
}

.notes-checkpoints li > span:not(.checkpoint-dot):not(.checkpoint-tag) {
  display: inline-flex;
  align-items: center;
  min-width: 0;
  height: auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.notes-checkpoints li.is-lunch {
  border-color: var(--accent-blue-soft);
  background: var(--accent-blue-soft);
  color: #ffffff;
}

/* Hazards read as red at a glance rather than sitting in the navy pack -
   they are the chips a rider most needs to spot in this list. */
.notes-checkpoints li.is-hazard {
  border-color: var(--accent-red);
  background: var(--accent-red);
  color: #ffffff;
}

/* The dot inside a red/blue pill needs inverting - a red dot on red (or on
   blue) vanishes. */
.notes-checkpoints li.is-hazard .checkpoint-dot,
.notes-checkpoints li.is-lunch .checkpoint-dot {
  background: #ffffff;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.25);
}

.checkpoint-dot {
  width: 7px;
  height: 7px;
  flex: none;
  border-radius: 50%;
  background: var(--accent-red);
  /* The halo was tuned for a light pill background; on the now-solid dark
     pill it needs its own white ring to stay visible. */
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.18);
}

.checkpoint-tag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 20px;
  min-height: 20px;
  padding: 3px 7px;
  border-radius: var(--radius-pill);
  /* Red badge on the blue lunch pill - the two colours now carry different
     meanings, so the badge stays red while the pill behind it does not. */
  background: var(--accent-red);
  color: #ffffff;
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.04em;
  line-height: 1;
  text-transform: uppercase;
}

.notes-hazards ul {
  display: grid;
  gap: 8px;
}

.notes-hazards li {
  display: grid;
  gap: 6px;
  padding: 11px 12px;
  border: 1px solid rgba(227, 0, 31, 0.24);
  border-radius: 14px;
  /* Pale pink card - text must be dark, not the white this was set to
     (presumably left over from a dark-card version of this design). */
  background:
    linear-gradient(180deg, rgba(227, 0, 31, 0.08), rgba(227, 0, 31, 0.02)),
    #fff6f7;
  color: var(--ink);
}

.hazard-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.hazard-title {
  font-size: 12px;
  font-weight: 800;
  color: var(--ink);
}

.hazard-tag {
  display: inline-flex;
  align-items: center;
  padding: 4px 7px;
  border-radius: var(--radius-pill);
  background: var(--accent-red);
  color: white;
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.hazard-desc {
  margin: 0;
  color: #5b1219;
  font-size: 11px;
  line-height: 1.45;
}

.notes-official {
  margin: 0;
  color: var(--fg-muted);
  font-size: 10px;
  font-family: var(--font-mono);
}

/* Basemap style, place-name layers, and Fit route - a white card top-right,
   matching the Stages card's look (including its collapse-upward behaviour)
   rather than the dark floating panel style. */
.map-panel {
  position: absolute;
  z-index: 20;
  top: var(--space-md);
  right: var(--space-md);
  width: 250px;
  max-height: 400px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: #ffffff;
  color: var(--ink);
  transition: max-height 200ms ease;
  --fg: var(--ink);
  --fg-muted: #5b5e94;
  --border: rgba(12, 11, 59, 0.12);
  --surface-soft: rgba(12, 11, 59, 0.05);
}

/* Height and radius come from the shared collapsed rule above - this later
   duplicate was re-asserting the old 54px and overriding it. */
.map-panel.collapsed .map-panel-body { display: none; }

.map-panel .list-heading { border-bottom: 1px solid var(--border); }

.map-panel-body {
  display: grid;
  gap: 8px;
  padding: var(--space-md);
}

.map-attribution {
  margin: 0;
  padding-top: 8px;
  border-top: 1px solid var(--border);
  color: var(--fg-muted);
  font-size: 9px;
  line-height: 1.4;
}

.private-camera-controls {
  display: grid;
  gap: 8px;
  margin-top: 4px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}

.private-camera-controls[hidden] { display: none; }

.private-camera-header {
  display: grid;
  gap: 2px;
}

.private-camera-kicker {
  color: var(--fg-muted);
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.private-camera-summary {
  color: var(--ink);
  font-size: 11px;
  font-weight: 700;
}

.secondary-button--tight {
  padding-block: 8px;
}

.camera-list {
  display: grid;
  gap: 6px;
  overflow: auto;
  max-height: 220px;
  padding-right: 4px;
  scrollbar-width: thin;
  scrollbar-color: rgba(12, 11, 59, 0.24) transparent;
}

.camera-item {
  display: grid;
  gap: 4px;
  padding: 8px 9px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: rgba(12, 11, 59, 0.04);
  color: var(--ink);
  text-align: left;
  cursor: pointer;
  box-shadow: inset 3px 0 var(--camera-color, var(--accent));
}

.camera-item strong {
  display: flex;
  gap: 6px;
  align-items: center;
  font-size: 11px;
}

.camera-type,
.camera-vma {
  color: var(--fg-muted);
  font-size: 10px;
  font-weight: 700;
}

.control-label {
  color: var(--fg-muted);
  font-size: 11px;
  font-weight: 700;
}

.control-label output {
  color: var(--fg);
  font-family: var(--font-mono);
}

input[type="range"] {
  accent-color: var(--accent);
  cursor: pointer;
}


.segmented {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
  padding: 4px;
  border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.18);
}

.segmented.triple { grid-template-columns: repeat(3, 1fr); }

.segmented button,
.secondary-button,
.play-button {
  min-height: 34px;
  padding: 7px 12px;
  font-size: 11px;
  font-weight: 750;
}

.segmented button.active,
.play-button {
  border-color: transparent;
  background: var(--accent);
  color: white;
}

.play-button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  /* "Play" and "Pause" are different widths, so swapping the label resized
     the button and shoved the whole timeline row sideways. Reserving the
     wider of the two keeps everything to its right perfectly still. */
  min-width: 84px;
}

.play-label {
  /* Centre within the reserved width rather than letting the shorter word
     sit off to one side of it. */
  text-align: center;
}

.play-icon {
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 5px 0 5px 8px;
  border-color: transparent transparent transparent currentColor;
}

.play-button[aria-pressed="true"] {
  background: var(--accent-blue);
  box-shadow: 0 0 0 3px rgba(30, 44, 143, 0.35);
}

.play-button[aria-pressed="true"] .play-icon {
  width: 8px;
  height: 10px;
  border-width: 0;
  border-left: 3px solid currentColor;
  border-right: 3px solid currentColor;
}

.secondary-button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Toggle rows: a static label plus an on/off switch, instead of text that
   changes between two phrasings ("Checkpoint names on/off") which read as
   two different settings at a glance rather than one thing being toggled. */
.toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space);
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface-soft);
  color: inherit;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease;
}

.toggle-row:hover { border-color: rgba(255, 255, 255, 0.34); }

.toggle-row--compact {
  width: auto;
  padding: 7px 10px 7px 12px;
}

.toggle-row--follow {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  padding: 0;
}

.toggle-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  min-width: 0;
  text-align: left;
}

.toggle-row--follow .toggle-label {
  line-height: 1.1;
}

.follow-controls {
  display: flex;
  align-items: stretch;
  gap: 10px;
  min-width: 220px;
  padding: 7px 10px 7px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface-soft);
}

.timeline .follow-controls:hover {
  border-color: rgba(12, 11, 59, 0.3);
}

.follow-controls .toggle-row:hover {
  border-color: transparent;
}

/* No longer .icon-button - it holds the word "Controls" now, not a glyph, so
   it needs its own display/padding/type rules rather than icon-button's
   fixed 36x36 square. */
.follow-controls__more {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  align-self: stretch;
  height: auto;
  padding: 0 12px;
  border: 0;
  border-radius: 999px;
  background: rgba(12, 11, 59, 0.08);
  color: inherit;
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
  transition: background 120ms ease;
}

.follow-controls__more:hover {
  border-color: transparent;
  background: rgba(12, 11, 59, 0.14);
}

.toggle-switch {
  position: relative;
  flex: none;
  width: 32px;
  height: 18px;
  border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.2);
  transition: background 150ms ease;
}

.toggle-switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #ffffff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
  transition: transform 150ms ease;
}

.toggle-row[aria-pressed="true"] .toggle-switch { background: var(--accent-blue); }
.toggle-row[aria-pressed="true"] .toggle-switch::after { transform: translateX(14px); }

.map-panel .toggle-row:hover,
.timeline .toggle-row:hover { border-color: rgba(12, 11, 59, 0.3); }
.map-panel .toggle-switch,
.timeline .toggle-switch { background: rgba(12, 11, 59, 0.18); }

.control-help {
  margin: 0;
  color: var(--fg-muted);
  font-size: 10px;
  line-height: 1.45;
}

/* Play, Follow and the scrubber share one row - the whole bar stays a fixed
   height. Speed/follow-zoom live in a small popover, not a full-width strip,
   anchored above the far-right toggle tab and stacked vertically within it. */
.timeline {
  position: absolute;
  z-index: 30;
  right: var(--space-md);
  bottom: var(--space-md);
  left: var(--space-md);
  padding: 14px 16px;
  border-radius: var(--radius-lg);
  /* White card, matching Stages and Map instead of the dark glass panel. */
  background: #ffffff;
  color: var(--ink);
  --fg: var(--ink);
  --fg-muted: #5b5e94;
  --border: rgba(12, 11, 59, 0.14);
  --surface-soft: rgba(12, 11, 59, 0.05);
}

.timeline-row {
  display: flex;
  align-items: center;
  gap: var(--space);
}

/* Prev / Play / Next as one transport cluster, so the stage-skip buttons read
   as belonging to playback rather than floating beside it. */
.transport {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
}

.transport__step {
  width: 34px;
  height: 34px;
  color: var(--fg-muted);
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
}

.transport__step svg { display: block; }
.transport__step:hover { color: var(--fg); }

.route-scrubber { display: grid; flex: 1; gap: 7px; min-width: 0; }

/* Stage divisions draw OVER the range input, not under it - the native track
   is opaque, so ticks behind it were completely hidden. pointer-events:none
   keeps every pointer event on the input underneath. */
.scrubber-track { position: relative; display: grid; }
.scrubber-track input[type="range"] { position: relative; z-index: 1; width: 100%; margin: 0; }

.stage-ticks {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
}

.stage-ticks span {
  position: absolute;
  top: 50%;
  width: 2px;
  height: 14px;
  margin-left: -1px;
  transform: translateY(-50%);
  border-radius: 1px;
  background: var(--ink);
  /* Light halo so a division stays legible against both the filled (red) and
     unfilled (grey) halves of the track. */
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.7);
}

.timeline-labels {
  color: var(--fg-muted);
  font-size: 10px;
  font-weight: 700;
}

#route-progress { font-family: var(--font-mono); }

.playback-extra {
  position: absolute;
  z-index: 31;
  right: 16px;
  bottom: calc(100% + 8px);
  display: flex;
  flex-direction: column;
  gap: var(--space);
  width: 190px;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: #ffffff;
  box-shadow: var(--shadow);
}

.playback-extra[hidden] { display: none; }

.speed-control {
  display: grid;
  grid-template-columns: 1fr;
  gap: 4px;
  align-items: center;
  width: 100%;
  transition: opacity 160ms ease;
}

/* Follow-zoom only acts on the camera while auto-follow is on - greyed
   rather than hidden so it stays clear it exists. Speed is never greyed:
   playback advances the cursor regardless of whether the camera follows. */
.speed-control.is-disabled {
  opacity: 0.4;
  pointer-events: none;
}

.speed-control label {
  grid-column: 1 / -1;
  color: var(--fg-muted);
  font-size: 10px;
  font-weight: 700;
}

.speed-control input[type="range"] {
  grid-column: 1 / -1;
}

.speed-control output {
  display: block;
  color: var(--fg);
  font-family: var(--font-mono);
}

.range-scale {
  grid-column: 1 / -1;
  display: flex;
  justify-content: space-between;
  margin-top: -2px;
  color: var(--fg-muted);
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.drive-panel {
  position: absolute;
  z-index: 35;
  right: var(--space-md);
  bottom: var(--space-md);
  left: var(--space-md);
  display: none;
  gap: 12px;
  padding: 14px;
  border-radius: var(--radius-lg);
  background: rgba(9, 8, 46, 0.96);
  color: #f8faff;
  box-shadow: var(--shadow);
  backdrop-filter: blur(18px) saturate(120%);
  -webkit-backdrop-filter: blur(18px) saturate(120%);
}

.drive-panel__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.drive-panel__eyebrow {
  margin: 0 0 4px;
  color: #94a0ff;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.drive-panel__top h2 {
  margin: 0;
  font-size: 20px;
  line-height: 1.05;
}

.drive-panel__status {
  padding: 6px 10px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.08);
  color: #d7dbff;
  font-size: 10px;
  font-weight: 700;
  white-space: nowrap;
}

.drive-panel__metrics {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}

.drive-metric {
  padding: 10px 11px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.05);
}

.drive-metric span {
  display: block;
  color: #aab3f0;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.drive-metric strong {
  display: block;
  margin-top: 4px;
  font-size: 13px;
  font-family: var(--font-mono);
}

.drive-panel__actions {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
}

.drive-panel__actions .play-button,
.drive-panel__actions .secondary-button,
.drive-panel__actions .toggle-row {
  min-width: 0;
  justify-content: center;
}

.drive-panel__list {
  display: grid;
  gap: 8px;
  max-height: 33vh;
  overflow: auto;
  padding-top: 10px;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}

.drive-panel__list-heading {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  color: #b9c0ff;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.drive-camera-list {
  display: grid;
  gap: 8px;
}

.drive-camera-item {
  display: grid;
  gap: 4px;
  padding: 10px 11px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.05);
}

.drive-camera-item strong {
  font-size: 12px;
}

.drive-camera-item span {
  color: #aab3f0;
  font-size: 10px;
}

.drive-camera-item--next {
  border-color: rgba(255, 88, 70, 0.55);
  box-shadow: inset 3px 0 rgba(255, 88, 70, 0.95);
}

#tooltip {
  position: absolute;
  z-index: 50;
  max-width: 260px;
  padding: 10px 12px;
  pointer-events: none;
  opacity: 0;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: rgba(9, 8, 46, 0.96);
  box-shadow: var(--shadow);
  transition: opacity 80ms linear;
}

#tooltip strong,
#tooltip span { display: block; }
#tooltip span { margin-top: 3px; color: var(--fg-muted); font-size: 11px; }

.state-overlay {
  position: absolute;
  z-index: 100;
  inset: 0;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 8px;
  background: radial-gradient(circle at 50% 40%, var(--ink-deep), var(--ink) 62%);
  text-align: center;
}

.state-overlay span,
.state-overlay p {
  max-width: 460px;
  margin: 0;
  color: var(--fg-muted);
  font-size: 13px;
}

.state-overlay[hidden] { display: none; }

.loader {
  width: 38px;
  height: 38px;
  margin-bottom: 8px;
  border: 3px solid rgba(255, 255, 255, 0.16);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 800ms linear infinite;
}

.error-state strong { color: var(--danger); }

@keyframes spin { to { transform: rotate(360deg); } }

@media (min-width: 761px) and (max-width: 1180px) {
  /* Set on a shared ancestor, not .sidebar itself - custom properties don't
     cross to sibling elements like the stage drawer, which also reads this. */
  #app { --sidebar-w: 270px; }


  .stage-card {
    padding: 9px 8px;
    gap: 8px;
  }

  .stage-distance { display: none; }

  .topbar .eyebrow { display: none; }
  .header-hud { gap: 4px; }
  .hud-box { min-width: 60px; padding: 5px 8px; }
  .hud-box strong { font-size: 12px; }

  .map-panel { width: 210px; }

  .segmented.triple button { padding: 8px 4px; font-size: 10px; }
}

@media (max-width: 760px) {
  :root { --header-h: 56px; }

  .topbar { padding: 0 12px; gap: 8px; }
  .topbar .eyebrow { display: none; }
  .topbar h1 { font-size: 14px; }
  .brand-mark { height: 22px; }

  /* The tour stats were display:none on mobile - the header has no room for
     them beside the logo and title. Re-home them as a floating strip over the
     map's top-left, opposite the map-controls button, collapsible via the
     header title so they can be dismissed when reading the map. */
  .header-hud {
    position: absolute;
    top: var(--space-sm);
    right: var(--space-sm);
    left: auto;
    z-index: 23;
    display: flex;
    gap: 4px;
    padding: 4px;
    border-radius: var(--radius);
    background: rgba(12, 11, 59, 0.82);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    box-shadow: var(--shadow);
    transition: opacity 200ms ease, transform 200ms ease;
  }
  .header-hud .hud-box {
    min-width: 0;
    padding: 4px 9px;
    background: none;
  }
  .header-hud .hud-box span { font-size: 7px; }
  .header-hud .hud-box strong { font-size: 12px; }
  /* Thin rules between tiles - they share one container now, so the tiles'
     own backgrounds would read as three separate chips. */
  .header-hud .hud-box + .hud-box {
    border-left: 1px solid rgba(255, 255, 255, 0.16);
  }

  /* Tap to collapse to a single chip - the map is the point of the screen,
     so the stats must be dismissable without hunting for a control. */
  .header-hud { cursor: pointer; }
  .header-hud.stats-collapsed .hud-box + .hud-box { display: none; }
  .header-hud.stats-collapsed .hud-box { padding: 4px 10px; }
  /* Chevron leads the chip, pointing back toward the stats that are hidden
     off to the left. */
  .header-hud.stats-collapsed::before {
    content: "\2039";
    order: -1;
    align-self: center;
    padding-left: 6px;
    color: rgba(255, 255, 255, 0.5);
    font-size: 13px;
  }

  /* Auto-hidden while the sheet is raised past peek - at that point the map
     is mostly covered and the strip is just clutter. */
  .sidebar[data-sheet="half"] ~ .header-hud,
  .sidebar[data-sheet="full"] ~ .header-hud {
    opacity: 0;
    transform: translateY(-6px);
    pointer-events: none;
  }

  /* ---- Mobile bottom sheet ------------------------------------------------
     Route playback is hidden on mobile for now (see .timeline below). It cost
     ~110px of permanent chrome - a fifth of a small phone's screen - for a
     secondary "watch the route animate" feature, while the map and stage
     detail are what a rider actually opens this for. The controls stay in the
     DOM (JS still drives the cursor and progress line), so this is a display
     decision only, and the intent is to bring playback back as its own
     mobile screen later. --timeline-h drops to 0 so the stage sheet sits
     flush to the bottom. */
  :root { --timeline-h: 0px; }

  .sidebar {
    --stages-peek: 46px;
    --sheet-peek: var(--stages-peek);
    --sheet-h: var(--sheet-peek);
    top: auto;
    right: 0;
    bottom: var(--timeline-h);
    left: 0;
    width: auto;
    max-height: none;
    height: var(--sheet-h);
    z-index: 30;
    display: flex;
    flex-direction: column;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    /* Opaque white, matching .stage-list-section's own light surface. The
       sheet sits over live map imagery, so it must not inherit .panel's
       translucent --surface or the route shows through the stage list. */
    background: #ffffff;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow: 0 -8px 32px rgba(12, 11, 59, 0.18);
    padding-bottom: env(safe-area-inset-bottom, 0px);
    transition: height 260ms cubic-bezier(0.32, 0.72, 0, 1);
    overflow: hidden;
  }
  /* No transition while a finger is down - the height must track the drag. */
  .sidebar.dragging { transition: none; }

  /* The desktop collapse class must not fight the sheet's height if it was
     left set before a resize/rotate into mobile. */
  .sidebar.collapsed { max-height: none; height: var(--sheet-h); }
  .sidebar.collapsed .stage-list,
  .sidebar.collapsed .stage-detail { display: block; }
  .sidebar.detail-open { max-height: none; }

  .timeline { display: none; }

  .sidebar .stage-list-section {
    flex: 1;
    min-height: 0;
    border-radius: inherit;
    border: none;
  }

  /* Grab handle - the affordance that says "this pulls up". */
  .sidebar .list-heading {
    position: relative;
    padding-top: 18px;
    cursor: grab;
    touch-action: none;
  }
  .sidebar .list-heading::before {
    content: "";
    position: absolute;
    top: 7px;
    left: 50%;
    width: 36px;
    height: 4px;
    margin-left: -18px;
    border-radius: 2px;
    background: rgba(12, 11, 59, 0.22);
  }
  .list-heading-count {
    color: var(--fg-muted);
    font-weight: 600;
    font-size: 12px;
  }

  /* Peek: just "Stages (N)" + a +/- button, no detail text and no visible
     list - the collapsed strip the user asked for, matching the Map panel's
     own collapsed treatment. */
  .sidebar[data-sheet="peek"] .stage-list-section { border-top: none; }
  .sidebar[data-sheet="peek"] .list-heading { padding-top: 12px; }
  .sidebar[data-sheet="peek"] .list-heading::before { display: none; }
  .sidebar[data-sheet="peek"] #selection-label { display: none; }
  .sidebar[data-sheet="peek"] .stage-list,
  .sidebar[data-sheet="peek"] .stage-detail { display: none; }

  /* Only the sheet's own scroller may consume vertical drags; anywhere else
     the gesture belongs to the sheet itself. */
  .sidebar .stage-list,
  .sidebar .stage-detail { overscroll-behavior: contain; }

  /* Map style/layers lives top-right, clear of the timeline and sheet at the
     bottom of the screen. Drops down from under its own button. */
  .map-panel {
    top: calc(var(--space-sm) + var(--stats-h, 44px) + var(--space-xs) + 44px + var(--space-xs));
    right: var(--space-sm);
    bottom: auto;
    left: auto;
    width: min(280px, calc(100vw - var(--space-sm) * 2));
    max-height: 52vh;
    overflow-y: auto;
    z-index: 25;
  }
  .map-panel .list-heading span { display: none; }
  .map-panel[hidden] { display: none; }

  /* Shared circular button recipe for the two FABs stacked top-right.
     display:grid on a class beats the UA [hidden] rule on specificity-tie/
     source-order grounds, so the hidden attribute alone would not hide these
     - the explicit [hidden] override below restores it. */
  .fab-button {
    position: absolute;
    right: var(--space-sm);
    z-index: 26;
    width: 44px;
    height: 44px;
    display: grid;
    place-items: center;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 50%;
    background: #ffffff;
    color: var(--ink);
    box-shadow: var(--shadow);
    font-size: 18px;
    transition: top 200ms ease, opacity 180ms ease;
  }
  .fab-button[hidden] { display: none; }

  /* Directly below the stats strip, sharing its right edge. --stats-h is
     written by app.js from the strip's measured height, so the button follows
     it when the stats collapse to a chip. */
  #map-panel-fab {
    top: calc(var(--space-sm) + var(--stats-h, 44px) + var(--space-xs));
  }

  /* Stacked below the map-style FAB, at a fixed 52px offset from it - both
     are a constant 44px, so no measurement needed here the way --stats-h is. */
  #compass-reset {
    top: calc(var(--space-sm) + var(--stats-h, 44px) + var(--space-xs) + 52px);
    color: var(--ink);
  }
  #compass-needle {
    transform-origin: 12px 12px;
    transition: transform 300ms ease;
  }

  /* ---- Dormant: mobile playback layout -----------------------------------
     Everything from here to .playback-extra styles the route-playback bar,
     which is display:none on mobile (see .timeline above). Kept rather than
     deleted because the plan is to bring playback back as its own mobile
     screen - this is the layout it will want. Inert until then. */
  .timeline { padding: 8px 12px 4px; }

  /* Labels are the most numerous thing over the map, so they carry the most
     visual weight per pixel saved. */
  .map-label {
    padding: 2px 6px;
    font-size: 10px;
  }

  /* Play + scrubber + toggle + chevron cannot share one ~370px row at any
     size - something always collapses. Stack instead: a row of controls on
     top, the scrubber full-bleed underneath where it gets the whole width. */
  .timeline-row {
    display: grid;
    grid-template-areas:
      "play follow more"
      "scrub scrub scrub";
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 8px;
  }

  /* The transport cluster (prev/play/next) is the grid child now, not the
     play button itself, which sits inside it. */
  .timeline .transport { grid-area: play; }

  .route-scrubber {
    grid-area: scrub;
    width: 100%;
  }
  /* Labels above the track, track full width beneath it. */
  .route-scrubber .timeline-labels {
    font-size: 11px;
    margin-bottom: 2px;
  }
  /* Now the primary control - give it a finger-sized hit area rather than the
     native thumb's default. */
  #route-slider {
    width: 100%;
    height: 28px;
    margin: 0;
    touch-action: pan-x;
  }
  /* "Stage 1" was wrapping to two lines and shoving the track down. */
  #playback-stage,
  #route-progress { white-space: nowrap; }

  /* Dissolve the desktop wrapper pill so its two buttons become direct grid
     children and can take their own areas. Desktop keeps the pill intact. */
  .follow-controls {
    display: contents;
    min-width: 0;
    padding: 0;
    border: none;
    background: none;
  }

  /* Auto-follow keeps its label - it is the one control whose purpose is not
     guessable from an icon - but drops the explanatory note. The base
     .toggle-row is justify-content:space-between, which on this wide 1fr
     grid column stretched the label to the far edge and the switch to the
     opposite edge, reading as unrelated controls. Overridden to a tight,
     left-aligned cluster instead - label directly against its own switch. */
  .timeline .toggle-row--follow { grid-area: follow; }
  .timeline .toggle-row--follow {
    justify-content: flex-start;
    width: 100%;
    height: 38px;
    padding: 0 10px;
    gap: 10px;
  }
  .timeline .toggle-row--follow .toggle-label {
    flex: none;
    font-size: 12px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* "Controls" text, so width fits content rather than the old fixed 38px
     icon circle - a faint border matches the follow toggle it sits beside. */
  .follow-controls__more {
    grid-area: more;
    width: auto;
    height: 38px;
    padding: 0 12px;
    align-self: center;
    flex: none;
    font-size: 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    background: var(--surface-soft);
  }

  /* Anchor to the viewport (not .timeline's own box via bottom:100%) and
     z-index far above every other mobile layer - was reported invisible on
     open despite the hidden attribute toggling correctly, and this sidesteps
     whatever containing-block/stacking subtlety was swallowing it rather than
     depend on being positioned exactly flush against the timeline bar. */
  .playback-extra {
    position: fixed;
    right: 10px;
    left: 10px;
    bottom: calc(var(--timeline-h) + var(--space-sm));
    width: auto;
    z-index: 60;
  }
  .drive-panel {
    right: var(--space-sm);
    bottom: var(--space-sm);
    left: var(--space-sm);
    padding: 12px;
  }

  .drive-panel__metrics,
  .drive-panel__actions {
    grid-template-columns: 1fr 1fr;
  }

  .drive-panel__top h2 {
    font-size: 17px;
  }

  #detail-title { font-size: 20px; }
  .detail-actions { gap: 6px; }
  .detail-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; }
  .detail-metrics div:nth-child(1),
  .detail-metrics div:nth-child(2),
  .detail-metrics div:nth-child(3),
  .detail-metrics div:nth-child(4),
  .detail-metrics div:nth-child(5) { grid-column: auto; }
  .notes-checkpoints li { font-size: 10px; }
}

.drive-mode .sidebar,
.drive-mode .map-panel,
.drive-mode .timeline {
  display: none !important;
}

.drive-mode .drive-panel {
  display: grid;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
