/* Tour PWA — a field instrument, not an app screen.
 *
 * The walk is the product: radar full-bleed as the primary surface, a large
 * readout you can parse at arm's length in sunlight, everything else pulled up
 * from the bottom edge only when asked for.
 *
 * Palette is warm paper / ink rather than system grey — this is a walking
 * instrument, closer to a printed map than a settings screen. Type is a
 * deliberate scale, not the system defaults.
 *
 * Guidelines kept: 44px targets, focus-visible, safe areas, reduced motion,
 * reduced transparency.
 *
 * **Light only, deliberately.** This screen is read outdoors, at arm's length,
 * usually in daylight — and a dark UI in sunlight is the one combination that
 * cannot be rescued by turning the brightness up, because the contrast the
 * walker fights is the sky reflecting off the glass. Dark ink on a bright
 * ground beats it; light ink on a dark ground does not. The theme is therefore
 * pinned rather than following the system, so a phone set to dark for the
 * evening does not hand its owner an unreadable instrument at noon.
 *
 * `color-scheme: light` is part of that, not decoration: without it the UA
 * still paints <select> and scrollbars from the OS theme, which is how you get
 * a dark dropdown on a paper-white sheet.
 */

:root {
  color-scheme: light;
  /* Paper */
  --paper: #FAF9F7;
  --paper-2: #FFFFFF;
  --paper-sunk: #F1EFEA;
  --ink: #16181D;
  --ink-2: #5B6069;
  --ink-3: #8E939C;
  --rule: rgba(22, 24, 29, 0.10);
  --rule-strong: rgba(22, 24, 29, 0.18);

  /* Instrument accents. Amber = you, because nothing else on the disc is warm. */
  --signal: #E4572E;
  --signal-soft: rgba(228, 87, 46, 0.16);
  --near: #1F7A5C;
  --near-soft: rgba(31, 122, 92, 0.14);
  --approach: #C8791A;
  --approach-soft: rgba(200, 121, 26, 0.14);
  --action: #1B4D8F;
  --action-press: #16407A;
  --warn: #B5541C;
  --err: #B3261E;

  --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, "SF Mono", "Roboto Mono", Menlo, monospace;

  /* Type scale — 1.25 minor third off 16px, plus a display step for the readout */
  --t-micro: 11px;
  --t-fine: 12.5px;
  --t-body: 16px;
  --t-lead: 20px;
  --t-title: 25px;
  --t-display: clamp(44px, 15vw, 68px);

  --radius-sheet: 22px;
  --radius-lg: 16px;
  --radius-md: 12px;
  --radius-pill: 999px;

  --dock-h: 76px;
  --handle-h: 46px;
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);

  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 20px; --s6: 24px; --s8: 32px;

  --shadow-sheet: 0 -10px 50px rgba(16, 18, 22, 0.16);
  --shadow-lift: 0 2px 10px rgba(16, 18, 22, 0.08);
  --focus: var(--action);
}

* { box-sizing: border-box; }
html, body { height: 100%; }
/* The component root fills its container — body standalone, #tour-card embedded. */
.tour-root { height: 100%; }

body {
  margin: 0;
  background: var(--paper);
}
/* The component root carries the font, overscroll, and color — so it works
   as body (standalone) or as #tour-card (embedded in map.html, which has its
   own font). The card must not inherit map.html's font. */
.tour-root {
  background: var(--paper);
  color: var(--ink);
  font: var(--t-body)/1.4 var(--font);
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
  overscroll-behavior: none;
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 4px;
}

button, select, a { font: inherit; color: inherit; }
button { -webkit-tap-highlight-color: transparent; }

.kicker {
  margin: 0;
  font-size: var(--t-micro);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* ── Top bar ─────────────────────────────────────────────────────────────── */

#topbar {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 20;
  display: grid;
  grid-template-columns: 40px 1fr 40px;
  align-items: center;
  gap: var(--s2);
  padding: calc(var(--s2) + var(--safe-top)) var(--s3) var(--s2);
  background: linear-gradient(to bottom, var(--paper) 55%, transparent);
}

.icon-btn {
  display: grid;
  place-items: center;
  width: 40px; height: 40px;
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--ink-2);
  font-size: 19px;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
}
#back-btn { font-size: 26px; font-weight: 300; }
.icon-btn:active { background: var(--rule); }
.icon-btn:disabled { color: var(--near); opacity: 1; }

.tour-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  min-width: 0;
  min-height: 40px;
  padding: 0 var(--s2);
  border: none;
  background: none;
  cursor: pointer;
}
.tour-btn-name {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--t-fine);
  font-weight: 650;
  letter-spacing: -0.005em;
}
.tour-btn-chip {
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* ── Stage: the instrument ───────────────────────────────────────────────── */

.stage {
  position: absolute;
  inset: 0;
  padding: calc(56px + var(--safe-top)) var(--s4)
           calc(var(--dock-h) + var(--handle-h) + var(--safe-bottom));
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  align-items: center;
  justify-items: center;
  gap: var(--s3);
}

.radar-layer {
  position: relative;
  width: 100%;
  height: 100%;
  min-height: 0;
  display: grid;
  place-items: center;
}

.radar {
  width: min(100%, 62vh, 420px);
  aspect-ratio: 1;
  display: block;
  overflow: visible;
}

/* Both modes get the disc. A route walk has more to show, not less — it can
 * draw the planned path, which open-world has no equivalent of. */
.radar-path {
  fill: none;
  stroke: var(--ink-3);
  stroke-width: 1.25;
  stroke-linejoin: round;
  stroke-linecap: round;
  stroke-dasharray: 3 3;
  opacity: 0.55;
}

.radar-empty {
  position: absolute;
  margin: 0;
  font-size: var(--t-fine);
  color: var(--ink-3);
}
.radar-empty b { color: var(--ink-2); font-weight: 650; }

/* Zoom + orientation, down the right edge of the disc and inside thumb reach. */
.radar-controls {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s2);
}

.radar-btn {
  width: 38px;
  height: 38px;
  border: 1px solid var(--rule);
  border-radius: var(--radius-pill);
  background: var(--paper-2);
  color: var(--ink-2);
  font-size: 17px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--shadow-lift);
}
.radar-btn:active { background: var(--paper-sunk); }
.radar-btn.wide { font-size: 13px; letter-spacing: -0.02em; }
.radar-btn[aria-pressed="true"] {
  background: var(--action);
  border-color: var(--action);
  color: #fff;
}

.radar-range {
  font-size: var(--t-micro);
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  color: var(--ink-3);
  letter-spacing: 0.02em;
  cursor: pointer;
  padding: 2px 4px;
  min-width: 44px;
  text-align: center;
}

/* Course-up: north drifts, so mark it. */
.radar-north { fill: var(--signal); }
.radar-heading { fill: var(--signal); }

.radar-disc { fill: var(--paper-2); stroke: var(--rule); stroke-width: 1; }
.radar-ring { fill: none; stroke: var(--rule); stroke-width: 0.75; }
.radar-axis { stroke: var(--rule); stroke-width: 0.75; stroke-dasharray: 2 4; }
.radar-cardinal {
  fill: var(--ink-3);
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.08em;
  font-family: var(--font);
}
.radar-ringlabel {
  fill: var(--ink-3);
  font-size: 7px;
  font-family: var(--mono);
  opacity: 0.8;
}

.radar-you { fill: var(--signal); }
.radar-you-ring { fill: none; stroke: var(--signal); stroke-width: 1.5; opacity: 0.4; }

.radar-stop { stroke: var(--paper-2); stroke-width: 1.5; fill: var(--ink); opacity: 0.85; }
.radar-stop-pending { opacity: 0.4; }
.radar-stop-approaching { fill: var(--approach); opacity: 1; }
.radar-stop-arrived { fill: var(--near); opacity: 1; }
.radar-stop-departed { fill: var(--ink-3); opacity: 0.45; }
.radar-stop-far { opacity: 0.22; }
.radar-hit { fill: transparent; cursor: pointer; }

/* Tier weight: a cathedral is not a plaque. */
.radar-stop-t5 { stroke-width: 2; }
.radar-stop-t1, .radar-stop-t2 { stroke-width: 1; }

.radar-halo {
  fill: none;
  stroke: var(--near);
  stroke-width: 1.5;
  opacity: 0;
  transform-origin: center;
}
.radar-halo.on { animation: halo 1.4s ease-out; }
@keyframes halo {
  from { opacity: 0.9; r: 6px; }
  to   { opacity: 0; r: 22px; }
}

.radar-label {
  fill: var(--ink-2);
  font-size: 7.5px;
  font-weight: 600;
  font-family: var(--font);
  paint-order: stroke;
  stroke: var(--paper);
  stroke-width: 2.5px;
  stroke-linejoin: round;
}
/* Far stops live beyond the range, so their labels sit outside the rim and
   recede a little — near stops are what you are steering by and must read
   first. The stroke halo is kept so they stay legible over the page edge. */
.radar-label-rim { fill: var(--ink-3); }
/* Leader tying a displaced far-rim label back to its dot when the dot's own
   slots are all blocked. Thin and faint: it is a pointer, not the content. */
.radar-leader { stroke: var(--ink-3); stroke-width: 0.5; opacity: 0.5; }

/* ── Readout ─────────────────────────────────────────────────────────────── */

.readout {
  width: 100%;
  max-width: 26rem;
  text-align: center;
}

.readout-label {
  margin: 0 0 2px;
  font-size: var(--t-micro);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.readout-target {
  margin: 0;
  font-size: var(--t-lead);
  font-weight: 650;
  letter-spacing: -0.02em;
  line-height: 1.15;
  color: var(--ink);
  min-height: 1.15em;
  overflow-wrap: break-word;
}

.readout-figure {
  margin: var(--s1) 0 0;
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 0.12em;
  line-height: 0.95;
}
.readout-value {
  font-size: var(--t-display);
  font-weight: 300;
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.readout-unit {
  font-size: var(--t-lead);
  font-weight: 500;
  color: var(--ink-3);
  letter-spacing: -0.01em;
}

.readout-sub {
  margin: var(--s1) 0 0;
  font-size: var(--t-fine);
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--ink-2);
  min-height: 1.2em;
}

/* The depth offer. Quiet on purpose: it is an invitation, not an instruction,
   and it appears while the walker is standing still looking at something — it
   must not compete with the thing they came to look at. */
.readout-more {
  margin: var(--s1) 0 0;
  font-size: var(--t-fine);
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--accent, var(--ink-3));
  opacity: 0.9;
}
.readout-more[hidden] { display: none; }

/* Sized for a thumb at arm's length in sunlight, not for a mouse. One control
   here rather than a play glyph per blip — the disc's job is "what am I near
   and how far", and scattering buttons across it destroys that. */
.readout-listen {
  display: inline-block;
  margin: var(--s2) 0 0;
  padding: 0.7em 1.6em;
  min-height: 44px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface-2, #fff);
  color: var(--ink-1);
  font: inherit;
  font-weight: 600;
  font-size: var(--t-fine);
  letter-spacing: 0.02em;
  cursor: pointer;
}
.readout-listen:active { transform: scale(0.97); }
/* A class setting `display` beats the attribute, so the toggle needs this or
   the button never actually hides. */
.readout-listen[hidden] { display: none; }

/* State tints the figure, so a glance is enough. */
.tour-root.near .readout-value { color: var(--near); }
.tour-root.approaching .readout-value { color: var(--approach); }

.status {
  margin: var(--s3) 0 0;
  font-size: var(--t-fine);
  color: var(--ink-3);
  min-height: 1.2em;
}
.status:empty { display: none; }
.status.warn { color: var(--warn); }
.status.err { color: var(--err); }
.status.sim { color: var(--action); }

/* ── Places handle ───────────────────────────────────────────────────────── */

.places-handle {
  position: absolute;
  left: 0; right: 0;
  bottom: calc(var(--dock-h) + var(--safe-bottom));
  z-index: 25;
  height: var(--handle-h);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  border: none;
  border-top: 1px solid var(--rule);
  background: var(--paper);
  color: var(--ink-2);
  font-size: var(--t-fine);
  font-weight: 600;
  letter-spacing: 0.02em;
  cursor: pointer;
  max-width: 560px;
  margin: 0 auto;
  /* The handle is swipe-up-to-open, so the browser must not claim the vertical
     gesture for scrolling or pull-to-refresh — without this, pointermove stops
     arriving mid-drag and the sheet sticks to the finger's first few pixels. */
  touch-action: none;
}
.places-handle:active { background: var(--paper-sunk); }
.places-handle-grip {
  width: 28px; height: 4px;
  border-radius: 2px;
  background: var(--rule-strong);
}

/* ── Dock ────────────────────────────────────────────────────────────────── */

.dock {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 30;
  max-width: 560px;
  margin: 0 auto;
  background: var(--paper-2);
  border-top: 1px solid var(--rule);
  padding-bottom: var(--safe-bottom);
}

/* ── Scrubber ────────────────────────────────────────────────────────────────
 *
 * A visible fill under a transparent <input type="range">. The native input
 * carries the interaction — drag, keyboard, and the accessibility tree come
 * free — while the fill draws the line the walker actually reads, which the
 * native track cannot be styled into across browsers.
 *
 * The dock's hit area is taller than the 2px it appears to be, because a 2px
 * target is unhittable with a thumb while walking.
 */
.scrub { position: relative; height: 2px; background: var(--rule); }
.scrub-fill {
  height: 100%; width: 0;
  background: var(--signal);
  transition: width 0.25s linear;
}
.scrub-input {
  position: absolute;
  left: 0; right: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 100%;
  height: 30px;
  margin: 0;
  padding: 0;
  background: none;
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
  /* Explicit, not inherited. The sheet is touch-action:none and its scroller
     pan-y; relying on the intersection of those to leave a horizontal drag
     for the slider is exactly the kind of assumption that works in one
     browser. The control claims its own gestures. */
  touch-action: none;
}
.scrub-input:disabled { cursor: default; }
/* Track is drawn by .scrub-fill; the input contributes only its thumb. */
.scrub-input::-webkit-slider-runnable-track { background: none; height: 30px; }
.scrub-input::-moz-range-track { background: none; height: 30px; }
.scrub-input::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 14px; height: 14px;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--signal);
  box-shadow: var(--shadow-lift);
}
.scrub-input::-moz-range-thumb {
  width: 14px; height: 14px;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--signal);
  box-shadow: var(--shadow-lift);
}
/* No thumb when there is nothing to scrub — a draggable handle on a dead
   control is a promise the UI cannot keep. */
.scrub-input:disabled::-webkit-slider-thumb { opacity: 0; }
.scrub-input:disabled::-moz-range-thumb { opacity: 0; }

/* In the sheet the scrubber is a control in its own right, not a hairline. */
.sheet-transport { margin: 0 0 var(--s4); }
.sheet-transport[hidden] { display: none; }
.sheet-transport .scrub {
  height: 4px;
  border-radius: var(--radius-pill);
  background: var(--paper-sunk);
  overflow: visible;
}
.sheet-transport .scrub-fill { border-radius: var(--radius-pill); }

.scrub-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  margin-top: var(--s3);
}
.scrub-time {
  font-size: var(--t-fine);
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}
.round-btn.small { width: 38px; height: 38px; font-size: 16px; }

.dock-body {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: var(--dock-h);
  padding: 0 var(--s4);
}

.dock-now { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
/* Tappable only while a stop is playing — opens that stop's sheet. The idle
   dock ("Ready when you are") must not read as a button, so the affordance is
   gated on .is-current rather than always on. */
.dock-now.is-current { cursor: pointer; }
.dock-now.is-current:active { opacity: 0.5; }
.dock-segment {
  font-size: var(--t-micro);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.dock-segment:empty { display: none; }
.dock-title {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.01em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dock-cta {
  flex: 0 0 auto;
  min-width: 92px;
  min-height: 44px;
  padding: 0 var(--s5);
  border: none;
  border-radius: var(--radius-pill);
  background: var(--action);
  color: #fff;
  font-size: 15px;
  font-weight: 650;
  letter-spacing: 0.01em;
  cursor: pointer;
  box-shadow: var(--shadow-lift);
}
.dock-cta:active { background: var(--action-press); }
.dock-cta[hidden] { display: none; }

.dock-transport { display: flex; gap: var(--s1); flex: 0 0 auto; }
.dock-transport[hidden] { display: none; }

.round-btn {
  width: 44px; height: 44px;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--paper-sunk);
  color: var(--ink);
  font-size: 13px;
  cursor: pointer;
}
.round-btn:active { background: var(--rule-strong); }
.round-btn.end { color: var(--signal); font-size: 11px; }
.round-btn.end[hidden] { display: none; }

/* Finished: the disc is history, so let it recede behind the tally. */
.tour-root.finished .radar { opacity: 0.4; }
.tour-root.finished .readout-value { color: var(--near); }
.tour-root.finished .readout-unit { color: var(--ink-3); }

/* ── Sheets ──────────────────────────────────────────────────────────────── */

.scrim {
  position: absolute;
  inset: 0;
  z-index: 40;
  background: rgba(10, 11, 13, 0.42);
  animation: fade-in 0.2s ease;
}
.scrim[hidden] { display: none; }
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }

.sheet {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 50;
  display: flex;
  flex-direction: column;
  max-height: min(88dvh, 780px);
  max-width: 560px;
  margin: 0 auto;
  background: var(--paper-2);
  border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
  box-shadow: var(--shadow-sheet);
  padding-bottom: var(--safe-bottom);
  animation: sheet-up 0.32s cubic-bezier(0.32, 0.72, 0, 1);
  touch-action: none;
}
.sheet[hidden] { display: none; }

/* A sheet under the finger must not also be playing its entrance animation —
   the two fight and the sheet jumps to full height on the first pixel. */
.sheet.sheet-dragging { animation: none; }
.scrim.scrim-dragging { animation: none; }
/* A sheet that arrived by drag-open is already on screen, so its entrance
   animation must not replay once the drag settles. sheet-dragged holds
   `animation: none` after the drag ends open; it is cleared on close so a
   later tap-open (a fresh display:none→shown) animates normally. Without this
   the drag placed the sheet, then sheet-up fired again from translateY(100%):
   a visible snap-down followed by a second slide-up. */
.sheet.sheet-dragged { animation: none; }
.scrim.scrim-dragged { animation: none; }

@keyframes sheet-up {
  from { transform: translateY(100%); }
  to { transform: translateY(0); }
}

.sheet-grab {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  height: 26px;
  cursor: grab;
  touch-action: none;
}
.sheet-grab span {
  width: 38px; height: 5px;
  border-radius: 3px;
  background: var(--rule-strong);
}

.sheet-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: 0 var(--s5) var(--s3);
  border-bottom: 1px solid var(--rule);
}
.sheet-head-title {
  margin: 0;
  font-size: var(--t-lead);
  font-weight: 700;
  letter-spacing: -0.02em;
}
.sheet-done {
  min-height: 36px;
  padding: 0 var(--s3);
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--action);
  font-size: 15px;
  font-weight: 650;
  cursor: pointer;
}
.sheet-done:active { background: var(--paper-sunk); }

.sheet-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  touch-action: pan-y;
  padding-bottom: var(--s6);
}

.section-kicker { padding: var(--s5) var(--s5) var(--s2); }

/* ── Places list ─────────────────────────────────────────────────────────── */

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

.stop {
  display: flex;
  border-bottom: 1px solid var(--rule);
}
.stop:last-child { border-bottom: none; }
.stop.departed { opacity: 0.5; }

.stop-open {
  flex: 1;
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-width: 0;
  min-height: 60px;
  padding: var(--s3) var(--s5);
  border: none;
  background: none;
  text-align: left;
  cursor: pointer;
}
.stop-open:active { background: var(--paper-sunk); }

/* One tap to hear a stop. Sits inside the row so the row still opens the
   detail — two targets, one line, and the play never moves as the list
   re-sorts by distance. */
.stop-play {
  flex: 0 0 auto;
  align-self: center;
  width: 44px; height: 44px;
  margin-right: var(--s3);
  border: 1px solid var(--rule);
  border-radius: var(--radius-pill);
  background: var(--paper-2);
  color: var(--action);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}
.stop-play:active { background: var(--paper-sunk); }
.stop.departed .stop-play { color: var(--ink-3); }

.stop-mark {
  flex: 0 0 auto;
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--ink-3);
  opacity: 0.45;
}
.stop.approaching .stop-mark { background: var(--approach); opacity: 1; }
.stop.arrived .stop-mark { background: var(--near); opacity: 1; }
.stop.departed .stop-mark { background: var(--ink-3); opacity: 0.5; }

.stop-main { flex: 1; min-width: 0; }

.stop-name {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  letter-spacing: -0.01em;
}
/* Importance earns visual weight — tier 5 reads first in a glance. */
.stop-t5 .stop-name { font-size: var(--t-lead); font-weight: 650; }
.stop-t4 .stop-name { font-size: 17px; font-weight: 600; }
.stop-t3 .stop-name { font-size: var(--t-body); font-weight: 550; }
.stop-t2 .stop-name,
.stop-t1 .stop-name { font-size: 15px; font-weight: 450; color: var(--ink-2); }

.stop-meta {
  display: block;
  margin-top: 2px;
  font-size: var(--t-fine);
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.stop.arrived .stop-meta { color: var(--near); font-weight: 600; }
.stop.approaching .stop-meta { color: var(--approach); font-weight: 600; }

/* Shown only on the row the walker is standing at, and only while that stop
   still owes dwell beats — see renderStops. Its own element rather than text
   appended to .stop-meta, so a badge can never fuse onto a distance. */
.stop-more {
  display: block;
  margin-top: 2px;
  font-size: var(--t-micro);
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--near);
}
.stop-more[hidden] { display: none; }

.badge {
  display: inline-block;
  margin-left: var(--s2);
  padding: 1px 7px;
  border-radius: var(--radius-pill);
  background: var(--paper-sunk);
  color: var(--ink-3);
  font-size: var(--t-micro);
  font-weight: 650;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  vertical-align: 2px;
}

.stop-chevron { flex: 0 0 auto; color: var(--ink-3); font-size: 17px; font-weight: 300; }

/* The row button no longer owns the full width, so its own padding has to
   stop short of the play control rather than run underneath it. */
.stop-open { padding-right: var(--s2); }

/* ── Attribution (visible, as CC BY-SA requires) ─────────────────────────── */

.attribution {
  padding: var(--s5);
  border-top: 1px solid var(--rule);
  font-size: var(--t-fine);
  color: var(--ink-3);
}
.attribution:empty { display: none; }
.attribution ul { list-style: none; margin: var(--s2) 0 0; padding: 0; }
.attribution li { padding: 3px 0; }
.attribution a { color: var(--action); text-decoration: none; }
.attribution a:active { text-decoration: underline; }

/* ── Stop sheet ──────────────────────────────────────────────────────────── */

.sheet-hero {
  width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--paper-sunk) center/cover no-repeat;
}
.sheet-hero[hidden] { display: none; }
.sheet-hero.placeholder {
  display: grid;
  place-items: center;
  font-size: 46px;
  font-weight: 300;
  letter-spacing: -0.03em;
  color: var(--ink-3);
}

.sheet-float-close {
  position: absolute;
  top: 34px; right: var(--s4);
  z-index: 2;
  width: 34px; height: 34px;
  border: none;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--paper-2) 82%, transparent);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--ink);
  font-size: 14px;
  cursor: pointer;
}

.sheet-body { padding: var(--s5); }

.sheet-title {
  margin: var(--s1) 0 var(--s1);
  font-size: var(--t-title);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.12;
}
.sheet-meta {
  margin: 0 0 var(--s4);
  font-size: var(--t-fine);
  font-weight: 550;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}

.sheet-actions {
  display: flex;
  gap: var(--s2);
  align-items: center;
  margin-bottom: var(--s4);
}

.primary {
  min-height: 46px;
  padding: 0 var(--s6);
  border: none;
  border-radius: var(--radius-pill);
  background: var(--action);
  color: #fff;
  font-size: 16px;
  font-weight: 650;
  cursor: pointer;
}
.primary:active { background: var(--action-press); }

.secondary {
  display: block;
  width: calc(100% - var(--s5) * 2);
  margin: var(--s2) var(--s5) 0;
  min-height: 46px;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--action);
  font-size: 15px;
  font-weight: 650;
  cursor: pointer;
}
.secondary:active { background: var(--paper-sunk); }

.seg-wrap { position: relative; flex: 1; display: flex; }
#sheet-segment {
  flex: 1;
  min-height: 46px;
  appearance: none;
  background: var(--paper-sunk);
  border: none;
  border-radius: var(--radius-md);
  padding: 0 var(--s8) 0 var(--s4);
  font-size: 15px;
  font-weight: 550;
  color: var(--ink);
  cursor: pointer;
}
.seg-caret {
  position: absolute;
  right: var(--s4); top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
  color: var(--ink-3);
  font-size: 11px;
}

.sheet-script {
  font-size: 17px;
  line-height: 1.55;
  color: var(--ink);
  white-space: pre-wrap;
  margin-bottom: var(--s4);
  padding-top: var(--s4);
  border-top: 1px solid var(--rule);
}
.sheet-script:empty { display: none; }

.sheet-sources {
  font-size: var(--t-fine);
  color: var(--ink-3);
  padding-top: var(--s3);
  border-top: 1px solid var(--rule);
}
.sheet-sources:empty { display: none; }
.sheet-sources .src-label { margin-bottom: var(--s1); }
.sheet-sources a { color: var(--action); text-decoration: none; display: inline-block; padding: 2px 0; }

/* ── Option lists (tour / language pickers) ──────────────────────────────── */

.options { list-style: none; margin: 0; padding: 0; }
.options li { border-bottom: 1px solid var(--rule); }
.options li:last-child { border-bottom: none; }

.option {
  width: 100%;
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: 56px;
  padding: var(--s3) var(--s5);
  border: none;
  background: none;
  text-align: left;
  cursor: pointer;
}
.option:active { background: var(--paper-sunk); }
.option-main { flex: 1; min-width: 0; }
.option-name { display: block; font-weight: 600; letter-spacing: -0.01em; }
.option-sub {
  display: block;
  margin-top: 1px;
  font-size: var(--t-fine);
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.option-check { flex: 0 0 auto; color: var(--action); font-size: 17px; font-weight: 700; opacity: 0; }
.option[aria-selected="true"] .option-check { opacity: 1; }
.option[aria-selected="true"] .option-name { color: var(--action); }

/* ── Diagnostics ─────────────────────────────────────────────────────────── */

.debug-fab {
  position: absolute;
  right: var(--s3);
  bottom: calc(var(--dock-h) + var(--handle-h) + var(--safe-bottom) + var(--s3));
  z-index: 26;
  width: 40px; height: 40px;
  border: 1px solid var(--rule);
  border-radius: var(--radius-pill);
  background: var(--paper-2);
  color: var(--ink-3);
  font-size: 15px;
  cursor: pointer;
  box-shadow: var(--shadow-lift);
}
.debug-fab[hidden] { display: none; }

/* A small, always-visible build-version chip — the mobile way to confirm "am I
   running the new code?" without typing ?debug=1. Sits bottom-left, opposite
   the debug FAB (bottom-right, hidden) and clear of the radar zoom controls
   (top-right), so it never crowds the walking UI. One tap opens Diagnostics. */
.version-chip {
  position: absolute;
  left: var(--s3);
  bottom: calc(var(--dock-h) + var(--handle-h) + var(--safe-bottom) + var(--s3));
  z-index: 26;
  min-width: 40px; height: 28px;
  padding: 0 var(--s2);
  border: 1px solid var(--rule);
  border-radius: var(--radius-pill);
  background: var(--paper-2);
  color: var(--ink-3);
  font-size: var(--t-fine);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  cursor: pointer;
  box-shadow: var(--shadow-lift);
}

/* Version panel at the top of the Diagnostics sheet. Two lines — shell and
   service worker — that should read the same when a deploy is settled. */
.version-panel { padding: var(--s3) var(--s5); border-bottom: 1px solid var(--rule); }
.version-line {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--s2) 0;
  font-size: var(--t-fine); color: var(--ink-2);
}
.version-line b { color: var(--ink); font-variant-numeric: tabular-nums; }
.version-hint {
  margin: var(--s2) 0 0;
  font-size: var(--t-fine); color: var(--ink-2); line-height: 1.4;
}
.version-refresh {
  margin-top: var(--s3);
  width: 100%;
  min-height: 36px;
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
  background: var(--paper-sunk);
  color: var(--ink-2);
  font-size: var(--t-fine);
  cursor: pointer;
}
.version-refresh:disabled { opacity: 0.6; cursor: default; }

.debug-grid { padding: 0 var(--s5); }
.debug-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: var(--s3) 0;
  border-bottom: 1px solid var(--rule);
  font-size: var(--t-fine);
  color: var(--ink-2);
}
.debug-row select {
  min-height: 36px;
  appearance: none;
  background: var(--paper-sunk);
  border: none;
  border-radius: var(--radius-md);
  padding: 0 var(--s3);
  font-size: var(--t-fine);
  color: var(--ink);
}

.event-log {
  list-style: none;
  margin: 0;
  padding: 0 var(--s5);
  font-family: var(--mono);
  font-size: var(--t-micro);
  line-height: 1.5;
  color: var(--ink-3);
}
.event-log li { padding: 3px 0; border-bottom: 1px solid var(--rule); }
.event-log span { margin-right: var(--s2); opacity: 0.7; }
.event-log .log-warn { color: var(--warn); }
.event-log .log-play { color: var(--ink); }

/* ── Preferences ─────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  .sheet, .scrim { animation: none; }
  #progress-bar { transition: none; }
  .radar-halo.on { animation: none; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

@media (prefers-reduced-transparency: reduce) {
  .sheet-float-close { backdrop-filter: none; -webkit-backdrop-filter: none; background: var(--paper-2); }
  #topbar { background: var(--paper); }
}

@media (prefers-contrast: more) {
  :root { --rule: rgba(22, 24, 29, 0.3); --rule-strong: rgba(22, 24, 29, 0.5); --ink-3: #5B6069; }
}

/* Landscape / short screens: the disc must not push the readout off. */
@media (max-height: 620px) {
  :root { --t-display: clamp(34px, 11vw, 48px); }
  .radar { width: min(100%, 48vh, 300px); }
}

/* ── Card height tiers: fluid scaling when the tour mounts as a slide-up card ──
   .tour-card is a CSS size container (container-type: size; container-name:
   tour), set in map.css. So these @container rules fire on the CARD's own
   height — which is what changes when you drag the handle — not the viewport.
   That is the difference between a fluid reorganization and a full-size layout
   that overflows its box: a @media (max-height) query watches the viewport,
   which never changes during a drag, so nothing reflowed and the viewport-
   sized radar slid over the readout text.

   Standalone tour.html has no container ancestor, so none of these @container
   rules match and the layout above is unchanged — the field instrument keeps
   its full-viewport behaviour.

   The base sizes (radar width, --t-display) use viewport units (vh/vw) so
   standalone works. The unconditional @container tour block below retargets
   them to container-query units (cqh) so they track the card at every height,
   then the two conditional tiers reorganize the layout as the card gets short.

   Three tiers, radar-first throughout:
     full    (≥ 560px): today's layout, radar/readout scaled to the card
     medium  (380–560px): radar shrinks, secondary readout + places handle hide
     peek    (< 380px): small disc + now-playing bar; a compact "what's near"
*/

/* Retarget viewport-relative sizes to the card. Without this the radar — sized
   in vh — stays viewport-large when the card is dragged short and overflows its
   box, sliding over the readout and decoupling the "Press Start" caption from
   the disc (the caption is centred in the layer while an oversized SVG reflows
   past it). cqh resolves against the nearest size container — .tour-card — so
   the disc shrinks with the card. Standalone has no container, so cqh falls
   back to the small viewport and the base vh rule above governs there. */
@container tour {
  .tour-root .radar { width: min(100%, 62cqh, 420px); }
  .tour-root { --t-display: clamp(34px, 18cqh, 68px); }
  /* A sheet opening inside a short card must not exceed the card. */
  .tour-root .sheet { max-height: min(88cqh, 780px); }
}

/* MEDIUM: ~40-70vh on a phone. The radar stays the focus but cedes room; the
   map showing through below carries the stop list, so the places handle is
   redundant here. Readout keeps target + distance, drops the sub-line and the
   "more here" nudge. Topbar compacts. */
@container tour (max-height: 560px) {
  .tour-root .stage {
    padding: calc(44px + var(--safe-top)) var(--s3) calc(var(--dock-h) + var(--safe-bottom));
    gap: var(--s2);
  }
  .tour-root #topbar { grid-template-columns: 36px 1fr 36px; }
  .tour-root .places-handle { display: none; }
  .tour-root .readout-sub,
  .tour-root .readout-more,
  .tour-root .readout-listen { display: none; }
  .tour-root .readout-target { font-size: clamp(22px, 7cqi, 30px); }
  .tour-root .radar-controls { transform: scale(0.88); transform-origin: center; }
}

/* PEEK: < ~40vh. A "now playing" bar with a compass. The radar becomes a small
   disc, the readout reduces to target + distance, the dock's scrubber hides
   (transport buttons stay so pause/skip are reachable). The stage stops being
   a grid and becomes a compact row: disc left, readout right. */
@container tour (max-height: 380px) {
  .tour-root { --dock-h: 60px; }
  .tour-root .stage {
    padding: var(--s2) var(--s3) calc(var(--dock-h) + var(--safe-bottom));
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: var(--s3);
  }
  .tour-root .radar-layer {
    width: auto;
    height: 100%;
    flex: 0 0 auto;
    aspect-ratio: 1;
  }
  .tour-root .readout {
    flex: 1 1 auto;
    text-align: left;
    max-width: none;
  }
  .tour-root .readout-label { display: none; }
  .tour-root .readout-target { font-size: 20px; min-height: 0; }
  .tour-root .readout-figure { font-size: 15px; }
  .tour-root .radar-controls { display: none; }
  .tour-root .scrub { display: none; }
  .tour-root #topbar { display: none; }
  .tour-root .places-handle { display: none; }
}
