/* No Road Block — Swiss/Dutch minimalism, light + dark.
   Paper, ink, and the nav blue carry everything: cards are card-ground with
   blue border and blue type (the report status card is the template), and
   hierarchy is size and case. No yellow accent — it was removed on purpose
   and stays gone. The maps are monochrome + blue so the markers own the
   colour. Mobile-first: read on a phone, outdoors. */

/* Body face. Suisse Int'l Mono (Swiss Typefaces, licensed) — single weight,
   so hierarchy is carried by size and case, never faux bold. */
@font-face {
  font-family: 'Suisse Intl Mono';
  src: url('../fonts/SuisseIntlMono-Regular.woff') format('woff');
  font-weight: 400;
  font-display: swap;
}

/* The wordmark is vector art (media/wordmark.svg) — Greg's hand-set outlines
   of Pilowlava Atome (Velvetyne, SIL OFL), exported from Figma. No display
   font is loaded. */

:root {
  color-scheme: light;

  /* @tokens:light — GENERATED from data/tokens.js (the docs live there too).
     Edit tokens.js, then `npm run tokens`. Never hand-edit between markers. */
  --paper: #f7f6f2;
  --ink: #1807ff;
  --muted: #655ec4;
  --hair: #1807ff;
  --ink-1: #1807ff;
  --clear: #007a33;
  --foot: #c96a00;
  --blocked: #d0231c;
  --unknown: #8b8985;
  --card: #ffffff;
  --on-ink: #ffffff;
  --map-ground: #f2f2f7;
  --wash: rgba(247, 246, 242, 0.85);
  --scrim-dot: rgba(12, 4, 120, 0.85);
  --scrim-tint: rgba(12, 4, 120, 0.3);
  --disc: #8b8985;
  --disc-ring: #ffffff;
  --disc-glyph: #ffffff;
  --card-ink: #000000;
  --map-img-filter: grayscale(1);
  --mark-filter: invert(4%) sepia(100%) saturate(6800%) hue-rotate(250deg) brightness(1.1) contrast(1.5);
  --mark-blue-filter: none;
  /* @tokens:end */

  --bnav-h: 64px;

  /* spacing — 4px grid */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px; --s5: 20px;
  --s6: 24px; --s8: 32px; --s10: 40px; --s12: 48px; --s16: 64px;

  /* THE type variable — every piece of text on the site (and the map's
     canvas labels) reads from this one line. Change the family here,
     change it everywhere. */
  --font: 'Suisse Intl Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* motion — one voice everywhere. Everything eases out on the same long
     decelerating curve; the spring is reserved for the report check's pop. */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --t-fast: 0.18s;  /* press feedback, exits */
  --t-med: 0.4s;    /* entrances */
  --t-slow: 0.55s;  /* colour cross-fades, released fills */
}

/* Cross-page navigation cross-fades (progressive: browsers without
   cross-document view transitions just load the page as before). The nav
   pill is named so it holds still while the page fades around it. */
@view-transition { navigation: auto; }

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.3s;
  animation-timing-function: var(--ease-out);
}

/* shared entrance verbs — a quiet rise and a plain fade */
@keyframes rise-in {
  from { opacity: 0; transform: translateY(10px); }
}

@keyframes fade-in {
  from { opacity: 0; }
}

@keyframes fade-out {
  to { opacity: 0; }
}

/* ---------- dark theme ----------
   data-theme is set on <html> by the inline pre-paint script (system
   preference, or the visitor's saved toggle choice). Every component reads
   tokens, so this block IS the dark theme — no component overrides. The
   map's own dark paint lives in data/tokens.js (MAP_THEME_DARK). */
:root[data-theme='dark'] {
  color-scheme: dark;

  /* @tokens:dark — GENERATED from data/tokens.js (the docs live there too).
     Edit tokens.js, then `npm run tokens`. Never hand-edit between markers. */
  --paper: #131316;
  --ink: #cecaff;
  --muted: #918ad2;
  --hair: #cecaff;
  --ink-1: #cecaff;
  --clear: #3dc873;
  --foot: #e89042;
  --blocked: #ff6a61;
  --unknown: #7c7a76;
  --card: #1c1c22;
  --on-ink: #131316;
  --map-ground: #101013;
  --wash: rgba(19, 19, 22, 0.85);
  --scrim-dot: rgba(2, 1, 24, 0.85);
  --scrim-tint: rgba(2, 1, 24, 0.35);
  --disc: #7c7a76;
  --disc-ring: #131316;
  --disc-glyph: #131316;
  --card-ink: #ffffff;
  --map-img-filter: grayscale(1) invert(0.92);
  --mark-filter: invert(17%) sepia(100%) saturate(2000%) hue-rotate(235deg) brightness(1.6);
  --mark-blue-filter: invert(0.12) brightness(1.9);
  /* @tokens:end */
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* Brand-moment palette, shared by the home hero and the map's report
   confirmation. Light: blue ground, white ink. Dark: flipped — the dark
   ground with the pale ink doing the talking (a pale-lavender slab reads
   as glare there). Hand-written dark overrides: this pairing is its own
   thing, not a site token. */
:root {
  --hero-bg: var(--ink-1);
  --hero-ink: var(--on-ink);
  --hero-pill-line: var(--on-ink);
}

:root[data-theme='dark'] {
  --hero-bg: var(--paper);
  --hero-ink: var(--ink-1);
  --hero-pill-line: var(--ink-1);
}

/* even a text selection wears the brand ink */
::selection { background: var(--ink); color: var(--paper); }

/* No visible scrollbar anywhere (scrolling itself still works): a painted
   scrollbar narrows the viewport on the tabs that scroll, which nudges the
   fixed nav pill off centre — the pill must sit identically on every tab. */
html {
  -webkit-text-size-adjust: 100%;
  scrollbar-width: none; /* Firefox, Chrome 121+ */
  scroll-behavior: smooth;
}

html::-webkit-scrollbar { display: none; } /* Safari + older Blink */

body {
  overflow-x: clip; /* the full-bleed hero uses 100vw */
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font);
  font-weight: 400;
  font-size: 16px;
  line-height: 1.5;
  max-width: 560px;
  margin: 0 auto;
  padding: 0 var(--s5) var(--s16);
}

/* ---------- header ---------- */

/* Location pages are map-first: no brand header there at all. */
body[data-view="location"] .site-head { display: none; }

body[data-view="location"] .banner {
  position: fixed;
  top: var(--s3);
  left: var(--s3);
  right: var(--s3);
  z-index: 1100; /* above Leaflet's panes and controls */
  margin: 0;
}

.site-head { padding-top: var(--s10); }

/* page-load entrance: wordmark, tagline, legend arrive as one breath,
   each a half-step behind the last */
.site-head .site-title,
.site-head .tagline,
.site-head .legend {
  animation: rise-in var(--t-slow) var(--ease-out) both;
}

.site-head .tagline { animation-delay: 60ms; }
.site-head .legend { animation-delay: 120ms; }

.site-title { line-height: 0; }

/* Fills the content column at any viewport; aspect ratio locked by the
   SVG's intrinsic 350x400 box. */
.wordmark {
  display: block;
  width: 100%;
  height: auto;
  filter: var(--mark-filter);
}

.tagline {
  margin-top: var(--s6);
  font-size: 17px;
  line-height: 1.49;
  letter-spacing: 0.02em;
  max-width: 36ch;
}

.tagline strong { font-weight: 400; }

/* No caps anywhere — labels read quiet, in sentence case. */
.legend {
  margin-top: var(--s8);
  padding-top: var(--s3);
  border-top: 1px solid var(--hair);
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  font-size: 12px;
  letter-spacing: 0.02em;
  color: var(--muted);
}

.legend li {
  display: flex;
  align-items: center;
  gap: 6px;
}

.lgi {
  width: 24px;
  height: 24px;
  flex: none;
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* the icon's circle IS the disc — same tokens as the map markers, and the
   same status colour-coding */
.lgi circle { fill: var(--disc); stroke: none; }
.lgi path { stroke: var(--disc-glyph); }

/* the glyphs trace themselves in on load, one row after the next — the
   dasharray outruns every glyph's length, so each draws start to finish */
.lgi path {
  stroke-dasharray: 32;
  stroke-dashoffset: 32;
  animation: legend-draw 0.7s ease-out forwards;
  animation-delay: 0.4s; /* after the legend's own rise-in has mostly landed */
}

.legend li:nth-child(2) .lgi path { animation-delay: 0.5s; }
.legend li:nth-child(3) .lgi path { animation-delay: 0.6s; }
.legend li:nth-child(4) .lgi path { animation-delay: 0.7s; }

@keyframes legend-draw {
  to { stroke-dashoffset: 0; }
}
.lgi--clear circle { fill: var(--clear); }
.lgi--foot circle { fill: var(--foot); }
.lgi--blocked circle { fill: var(--blocked); }
.lgi--unknown circle { fill: var(--unknown); }

/* ---------- banner (offline / cache notice) ---------- */

/* display:flex would otherwise override the hidden attribute — never let
   this element paint while hidden */
.banner[hidden] { display: none !important; }

.banner {
  display: flex;
  /* arrives softly whenever it's unhidden; dismissal fades via .is-closing */
  animation: banner-in var(--t-med) var(--ease-out);
  align-items: flex-start;
  gap: var(--s2);
  margin-top: var(--s6);
  padding: var(--s3) var(--s2) var(--s3) var(--s4);
  background: var(--card);
  border: 1px solid var(--ink-1);
  color: var(--ink-1);
  font-size: 13px;
}

.banner-msg { flex: 1; }

.banner-x {
  flex: none;
  margin: calc(-1 * var(--s3)) 0;
  padding: 0 var(--s3);
  min-height: 44px; /* thumb-sized */
  border: none;
  background: none;
  font-family: var(--font);
  font-size: 20px;
  line-height: 1;
  color: var(--ink-1);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.banner-x:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* every dismissal X gives slightly under the thumb, like the buttons do */
.banner-x,
.report-x,
.nrb-locate-note-x {
  transition: transform var(--t-fast) var(--ease-out);
}

.banner-x:active,
.report-x:active,
.nrb-locate-note-x:active { transform: scale(0.85); }

.banner.is-closing {
  animation: fade-out var(--t-fast) ease-out forwards;
  pointer-events: none;
}

@keyframes banner-in {
  from { opacity: 0; transform: translateY(-8px); }
}

/* ---------- section labels ---------- */

.list-label {
  margin-top: var(--s12);
  padding-top: var(--s3);
  border-top: 1px solid var(--hair);
  font-size: 12px;
  letter-spacing: 0.02em;
  color: var(--muted);
}

/* ---------- focused location ---------- */

.card { margin-top: var(--s8); }

.card--hero { margin-top: 0; }

.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  margin-top: var(--s4);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.02em;
}

.pill::before {
  content: '';
  width: 11px; height: 11px;
  background: var(--unknown);
  /* live data lands after first paint — the swatch glides to its colour */
  transition: background-color var(--t-slow) var(--ease-out);
}

.pill--clear::before { background: var(--clear); }
.pill--foot::before { background: var(--foot); }
.pill--blocked::before { background: var(--blocked); }
.pill--unknown::before { background: var(--unknown); }

/* live data moved this status — the swatch swells for a beat, same
   language as the map discs (class set by the status painters) */
.pill.is-changed::before { animation: disc-pulse 0.7s var(--ease-spring); }

/* ---------- view swaps (hash routing) ---------- */

/* each render replaces #view's children, so fresh nodes replay these:
   content rises in; the full-bleed map only fades (a 100dvh surface
   sliding reads as jank, not polish) */
#view > * { animation: rise-in var(--t-med) var(--ease-out) both; }
#view > .card--hero { animation-name: fade-in; }
/* the stack's rows carry their own staggered entrance below — animating the
   container too would move everything twice */
#view > .stack { animation: none; }

/* the all-points list deals its rows one at a time */
.stack .mini { animation: rise-in var(--t-slow) var(--ease-out) both; }
.stack .mini:nth-child(2) { animation-delay: 45ms; }
.stack .mini:nth-child(3) { animation-delay: 90ms; }
.stack .mini:nth-child(4) { animation-delay: 135ms; }
.stack .mini:nth-child(5) { animation-delay: 180ms; }
.stack .mini:nth-child(6) { animation-delay: 225ms; }

/* ---------- full-viewport hero map + live markers ---------- */

/* escape the page column to true full bleed */
.hero-bleed { margin: 0 calc(50% - 50vw); }

.hero-map {
  position: relative;
  overflow: hidden;
  /* the map surface colour (mirrors MAP_THEME.background in
     data/tokens.js) so tiles paint onto their own ground, no flash */
  background: var(--map-ground);
  /* the map owns the whole viewport at every size; the nav pill and rail
     float on top. In the static-image fallback, object-fit: cover crops the
     portrait render to fit — markers outside the crop simply don't paint. */
  height: 100vh;
  height: 100dvh;
  width: 100vw;
}

.hero-map-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: var(--map-img-filter);
  transition: opacity var(--t-slow) var(--ease-out);
}

/* map imagery holds at zero until its pixels have landed (class managed
   by showStaticMap / buildMini), then fades up — no half-decoded pop-in */
.hero-map-img.is-loading,
.mini-map.is-loading { opacity: 0; }

.hero-markers {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.hm {
  position: absolute;
  transform: translate(-50%, -50%);
  pointer-events: auto;
}

.hm-inner {
  display: block;
  color: inherit;
  text-decoration: none;
}

.hm-inner:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* Leaflet mode: Leaflet positions the marker box itself; the box passes
   drags through to the map, the chip/link stays tappable. */
.hm--lf { transform: none; pointer-events: none; }
.hm--lf .hm-inner { pointer-events: auto; }

/* status icon — the disc, ring, and knocked-out glyph each have their own
   token (--disc / --disc-ring / --disc-glyph in data/tokens.js). The ring is
   what keeps the disc legible where it sits on the trail line. */
.hm-ic {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  background: var(--disc);
  border: 2px solid var(--disc-ring);
  border-radius: 50%;
  /* statuses arrive after first paint — discs glide to their colour */
  transition: background-color var(--t-slow) var(--ease-out), transform var(--t-fast) var(--ease-out);
  /* discs deal onto the map in trail order (--mki set by the marker
     builders), each springing up in place */
  animation: disc-in 0.4s var(--ease-spring) both;
  animation-delay: calc(var(--mki, 0) * 45ms);
}

@keyframes disc-in {
  from { opacity: 0; transform: scale(0.4); }
}

/* live data moved this stop's status — the disc swells for a beat as the
   news lands (class set by updateStatusUI, only on a real change) */
.hm-ic.is-changed {
  animation: disc-pulse 0.7s var(--ease-spring);
}

@keyframes disc-pulse {
  30% { transform: scale(1.2); }
}

/* the current stop's disc is a real button — it shows/hides the card */
.hm--here .hm-ic {
  width: 48px;
  height: 48px;
  padding: 0;
  font: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.hm--here .hm-ic:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

.hm--here .hm-ic:active { transform: scale(0.92); }

/* tap off the card → just the map; the disc stays as the way back in.
   The card scales toward its disc as it fades (the independent `scale`
   property — placeHereCard owns `transform`); visibility flips after the
   fade so it stays untappable and unread while hidden, but keeps its
   layout box for placeHereCard's measurements. */
.hm--here .hm-card {
  transition:
    opacity var(--t-med) var(--ease-out),
    scale var(--t-med) var(--ease-out),
    visibility 0s;
}

.hm--here .hm-lead {
  transition: opacity var(--t-med) var(--ease-out), visibility 0s;
}

.hero-map.is-card-hidden .hm--here .hm-card,
.hero-map.is-card-hidden .hm--here .hm-lead {
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--t-fast) var(--ease-out),
    scale var(--t-fast) var(--ease-out),
    visibility 0s var(--t-fast);
}

.hero-map.is-card-hidden .hm--here .hm-card { scale: 0.94; }

.hm-svg {
  width: 80%;
  height: 80%;
  fill: none;
  stroke: var(--disc-glyph);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* the disc itself is the circle now — don't draw it twice */
.hm-svg circle { display: none; }

/* the disc colour IS the status — same vars as the pills and the legend.
   data-status is set by updateStatusUI alongside the glyph. */
.hm-ic[data-status='clear'] { background: var(--clear); }
.hm-ic[data-status='foot'] { background: var(--foot); }
.hm-ic[data-status='blocked'] { background: var(--blocked); }
.hm-ic[data-status='unknown'] { background: var(--unknown); }

/* Current stop: the "you are here card" (Figma template) hangs under the
   disc — name, report tallies, and the report CTA in one bordered card. */
.hm-card {
  position: absolute;
  left: 50%;
  top: calc(50% + 32px);
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: var(--s5);
  width: 200px;
  padding: 18px 0 var(--s2); /* 18px top per Figma — the name hangs off the disc */
  background: var(--card);
  border: 1px solid var(--ink-1);
  color: var(--card-ink);
  text-align: center;
}

.hm-card-name {
  display: block;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.5px;
}

.hm-card-meta {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  font-size: 10px;
  line-height: 10px;
  letter-spacing: 0.5px;
}

.hm-card-actions {
  display: flex;
  flex-direction: column;
  padding-inline: var(--s2);
}

/* leader line — drawn (by placeHereCard) when the card has had to move away
   from its disc to clear other markers; painted under both ends */
.hm-lead {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 0;
  height: 1px;
  background: var(--ink-1);
  transform-origin: 0 50%;
  pointer-events: none;
}

/* ---------- report scrim ---------- */

/* Error prevention: reporting is pick-then-confirm inside a scrim over the
   map — no accidental one-tap reports. The wash is a halftone screen, not a
   flat grey: two offset paper-dot grids (a 45° screen) over a light tint,
   so the map dithers away behind the sheet like everything else here. */
.report-scrim[hidden] { display: none !important; }

.report-scrim {
  position: fixed;
  inset: 0;
  z-index: 1200; /* over every piece of map chrome, nav pill included */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s5);
  /* dark-blue dots, not paper: a deep shade of the nav ink (--ink-1
     #1807ff at ~45% lightness) screens the map back so the white sheet
     reads as unmistakable figure against a receding ground */
  background:
    radial-gradient(circle, var(--scrim-dot) 1.1px, transparent 1.3px) 0 0 / 4px 4px,
    radial-gradient(circle, var(--scrim-dot) 1.1px, transparent 1.3px) 2px 2px / 4px 4px,
    var(--scrim-tint);
  /* replays on every open — unhiding restarts the animation */
  animation: fade-in var(--t-med) var(--ease-out);
}

/* the close() in app.js adds this, waits out the fade, then hides */
.report-scrim.is-closing {
  animation: fade-out var(--t-fast) ease-out forwards;
  pointer-events: none;
}

.report-sheet {
  position: relative;
  /* the sheet rises a beat behind the wash */
  animation: sheet-in 0.45s var(--ease-out) backwards;
  display: flex;
  flex-direction: column;
  gap: var(--s4);
  width: min(320px, 100%);
  padding: var(--s5) var(--s4) var(--s4);
  background: var(--card);
  border: 1px solid var(--ink-1);
  color: var(--ink-1);
  text-align: center;
}

.report-x {
  position: absolute;
  top: 0;
  right: 0;
  width: 44px; /* thumb-sized */
  height: 44px;
  border: 0;
  background: none;
  font-family: var(--font);
  font-size: 20px;
  line-height: 1;
  color: var(--ink-1);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.report-x:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }

.report-head {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}

.report-name {
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.5px;
}

.report-q {
  font-size: 10px;
  line-height: 14px;
  letter-spacing: 0.5px;
}

/* the sheet's status input is the shared segmented control (.rp-options,
   further down) — .report-options remains in the markup only so the
   is-done state can hide it */

/* one button voice across card and sheet: solid = act, outline = choose */
.hm-card-report,
.report-confirm {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 40px;
  padding: 0;
  border: 0;
  background: var(--ink-1);
  color: var(--on-ink);
  font-family: var(--font);
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.5px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  /* fills fade on the slow beat; the press-scale returns on the fast one */
  transition:
    background-color var(--t-slow) var(--ease-out),
    color var(--t-slow) var(--ease-out),
    border-color var(--t-slow) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}

@keyframes pick-pop {
  40% { transform: scale(0.96); }
}

/* confirm wakes up only once a status is picked */
.report-confirm:disabled {
  background: var(--card);
  color: var(--muted);
  border: 1px solid var(--muted);
  cursor: default;
}

.hm-card-report:focus-visible,
.report-confirm:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }

/* same tap feedback as the nav pill — fill on press, linger, fade — plus a
   slight give under the thumb that springs back on release */
.hm-card-report:active,
.report-confirm:active:not(:disabled) {
  background: var(--ink-1);
  color: var(--on-ink);
  transform: scale(0.97);
  transition: none;
}

.report-note {
  min-height: 14px;
  font-size: 10px;
  line-height: 14px;
  letter-spacing: 0.5px;
}

/* ---------- status rows + segmented status input (shared) ----------
   The site's ONE status input: a connected three-segment control (same
   shape language as the nav pill) used by the home list, the report page,
   and the map's report sheet. The segment filled with its status colour IS
   the live status readout; a tap previews a pick in the same voice, and
   the awake confirm underneath is what says "not saved yet". */

.rp-list {
  list-style: none;
  border-top: 1px solid var(--ink-1);
}

.rp-list li {
  padding: var(--s4) 0;
  border-bottom: 1px solid var(--ink-1);
}

.rp-name {
  display: inline-block;
  font-size: 15px;
  color: inherit;
}

/* when the name is a link (home list) it keeps the underline voice */
a.rp-name {
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: text-underline-offset var(--t-fast) var(--ease-out);
}

/* pointer devices: the underline drops away a touch on hover */
@media (hover: hover) {
  a.rp-name:hover { text-underline-offset: 6px; }
}

.rp-options {
  display: flex;
  align-items: stretch;
  margin-top: var(--s3);
  height: 40px;
  background: var(--card);
  border: 1px solid var(--ink-1);
  border-radius: 20px;
  overflow: hidden; /* clips segment fills to the rounded ends */
}

/* inside the sheet the card's own gap spaces it */
.report-sheet .rp-options { margin-top: 0; }

.rp-opt {
  flex: 1 1 33.333%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--s1);
  border: 0;
  background: none;
  color: var(--ink-1);
  font-family: var(--font);
  font-size: 11px;
  line-height: 16px;
  letter-spacing: 0.5px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  /* fills fade on the slow beat (same voice as the nav tabs) */
  transition:
    background-color var(--t-slow) var(--ease-out),
    color var(--t-slow) var(--ease-out);
}

.rp-opt + .rp-opt { border-left: 1px solid var(--ink-1); }

/* the fill: the live status — or the pick previewing itself — in that
   status's own colour, text flipped to the on-ink white */
.rp-opt.is-on[data-status='clear'] { background: var(--clear); }
.rp-opt.is-on[data-status='foot'] { background: var(--foot); }
.rp-opt.is-on[data-status='blocked'] { background: var(--blocked); }

.rp-opt.is-on {
  color: var(--on-ink);
  animation: pick-pop 0.25s var(--ease-spring);
}

.rp-opt:disabled {
  color: var(--muted);
  cursor: default;
}

.rp-opt.is-on:disabled {
  background: var(--muted);
  color: var(--on-ink);
}

/* confirm appears only once a status is picked — solid, full width, same
   pill radius as the control above so the pair reads as one component */
.rp-confirm {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 40px;
  margin-top: var(--s2);
  padding: 0 var(--s1);
  background: var(--ink-1);
  color: var(--on-ink);
  border: 1px solid var(--ink-1);
  border-radius: 20px;
  font-family: var(--font);
  font-size: 11px;
  line-height: 16px;
  letter-spacing: 0.5px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  animation: rise-in var(--t-med) var(--ease-out);
  transition: transform var(--t-fast) var(--ease-out);
}

.rp-confirm[hidden] { display: none; }

.rp-confirm:disabled {
  background: var(--card);
  color: var(--muted);
  border-color: var(--muted);
  cursor: default;
}

.rp-opt:focus-visible,
.rp-confirm:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }

/* same tap feedback as everywhere: a slight give under the thumb that
   springs back on release (the segments' fills belong to the statuses,
   so the press cue is the give, not an ink flood) */
.rp-opt:active:not(:disabled),
.rp-confirm:active:not(:disabled) {
  transform: scale(0.97);
  transition: none;
}

/* sending / logged / no-signal line — holds its space so rows don't jump */
.rp-note {
  min-height: 14px;
  margin-top: var(--s2);
  font-size: 10px;
  line-height: 14px;
  letter-spacing: 0.5px;
}

/* sent: the controls give way to a check that draws itself in — ring first,
   then the tick — with a small pop. The sheet closes itself afterwards. */
.report-done { display: none; }

.report-scrim.is-done .report-options,
.report-scrim.is-done .report-confirm,
.report-scrim.is-done .report-q { display: none; }

.report-scrim.is-done .report-done {
  position: relative;
  display: flex;
  justify-content: center;
  padding: var(--s2) 0;
  animation: report-pop 0.7s var(--ease-spring);
}

.report-check { width: 88px; height: 88px; }

/* full-screen confirmation: the check takes more of the stage */
.report-scrim.is-done .report-check { width: 108px; height: 108px; }

/* the dot burst — the site's halftone language as celebration: twelve ink
   dots fling outward and fade as the tick lands. Box-shadows ride the
   element's transform, so one scaling dot carries the whole ring. */
.report-scrim.is-done .report-done::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 6px;
  height: 6px;
  margin: -3px 0 0 -3px;
  border-radius: 50%;
  background: transparent;
  box-shadow:
    44px 0 0 var(--hero-ink),
    38px 22px 0 -1px var(--hero-ink),
    22px 38px 0 var(--hero-ink),
    0 44px 0 -1px var(--hero-ink),
    -22px 38px 0 var(--hero-ink),
    -38px 22px 0 -1px var(--hero-ink),
    -44px 0 0 var(--hero-ink),
    -38px -22px 0 -1px var(--hero-ink),
    -22px -38px 0 var(--hero-ink),
    0 -44px 0 -1px var(--hero-ink),
    22px -38px 0 var(--hero-ink),
    38px -22px 0 -1px var(--hero-ink);
  opacity: 0;
  animation: report-dots 0.9s var(--ease-out) 0.35s;
}

@keyframes report-dots {
  0% { transform: scale(0.25) rotate(0deg); opacity: 0; }
  20% { opacity: 1; }
  100% { transform: scale(2.1) rotate(28deg); opacity: 0; }
}

.report-check circle,
.report-check path {
  fill: none;
  stroke: var(--ink-1);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.report-check circle {
  stroke-dasharray: 139; /* 2π · r22 */
  stroke-dashoffset: 139;
  animation: report-draw 0.45s ease-out forwards;
}

.report-check path {
  stroke-dasharray: 30;
  stroke-dashoffset: 30;
  animation: report-draw 0.3s ease-out 0.3s forwards;
}

/* Sent: the whole screen becomes the confirmation — the halftone gives way
   to a full-bleed hero ground (blue/white in light, flipped in dark), the
   sheet dissolves into it, and the check draws itself in its centre. */
.report-scrim.is-done {
  background: var(--hero-bg);
}

.report-scrim.is-done .report-sheet {
  background: none;
  border-color: transparent;
  color: var(--hero-ink);
}

.report-scrim.is-done .report-head { display: none; }
.report-scrim.is-done .report-note { display: none; }
/* nothing left to cancel — the moment lasts two seconds and closes itself */
.report-scrim.is-done .report-x { display: none; }

.report-scrim.is-done .report-check circle,
.report-scrim.is-done .report-check path { stroke: var(--hero-ink); }

/* the sign-off line under the check — arrives once the tick has landed */
.report-done-msg { display: none; }

.report-scrim.is-done .report-done-msg {
  display: block;
  color: var(--hero-ink);
  font-size: 13px;
  letter-spacing: 0.5px;
  animation: fade-in var(--t-slow) var(--ease-out) 0.6s both;
}

/* the burst: a ring blooms out of the check as the tick lands */
.report-scrim.is-done .report-done::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 108px;
  height: 108px;
  margin: -54px 0 0 -54px;
  border: 2px solid var(--hero-ink);
  border-radius: 50%;
  opacity: 0;
  animation: report-burst 0.6s ease-out 0.4s;
}

@keyframes sheet-in {
  from { opacity: 0; transform: translateY(14px) scale(0.98); }
}

@keyframes report-draw {
  to { stroke-dashoffset: 0; }
}

@keyframes report-pop {
  0% { transform: scale(0.3); }
  55% { transform: scale(1.12); }
  100% { transform: scale(1); }
}

@keyframes report-burst {
  from { transform: scale(0.55); opacity: 0.9; }
  to { transform: scale(1.6); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .report-scrim.is-done .report-done,
  .report-scrim.is-done .report-done::after,
  .report-check circle,
  .report-check path {
    animation: none;
    stroke-dashoffset: 0;
  }
}

/* ---------- interactive (Leaflet) layer ---------- */

.hero-leaflet {
  position: absolute;
  inset: 0;
}

.leaflet-container {
  background: transparent;
  font-family: var(--font);
}

/* beat Leaflet's own `.leaflet-container a` link-blue inside the markers */
.leaflet-container a.hm-inner { color: inherit; }

/* the pre-rendered image is a fallback only — hidden unless the live map
   bows out (is-static: no Leaflet, or tiles unreachable) */
.hero-map:not(.is-static) .hero-map-img,
.hero-map:not(.is-static) .hero-markers,
.hero-map:not(.is-static) > .map-attr { display: none; }

.map-attr {
  position: absolute;
  right: 0;
  bottom: 0;
  padding: 2px 6px;
  font-size: 10px;
  color: var(--muted);
  background: var(--wash);
}

/* the brand mark (badge off the branding page) — the only branding on the
   map view (the wordmark header is hidden there); links back to the home page */
.map-logo {
  display: block;
  position: absolute;
  left: var(--s5);
  top: var(--s5);
  width: 40px;
  aspect-ratio: 1744 / 1939; /* the mark's own ratio */
  color: var(--ink-1);
  z-index: 500; /* over the trail line (400), under the markers (600) */
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--t-fast) var(--ease-out);
}

.map-logo:active { transform: scale(0.92); }

.map-logo svg {
  display: block;
  width: 100%;
  height: auto;
}

.map-logo:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* ---------- map rail (zoom + locate) ---------- */

/* Map tools per the Figma "map tools v2" frame: the locate button broken out
   as its own circle, the zoom pair grouped in one pill below it. The rail
   rides the right edge at every size — thumb territory on phones, and where
   Google Maps keeps it on desktop. On phones it's held clear of the
   full-width nav pill below it. */
.map-rail {
  position: absolute;
  z-index: 1000; /* Leaflet's control tier — above map panes, below the nav pill */
  right: var(--s5);
  bottom: calc(var(--s5) + var(--bnav-h) + var(--s10) + env(safe-area-inset-bottom, 0px));
  width: 40px;
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}

/* the zoom pair share one pill */
.map-rail-zoom {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--ink-1);
  border-radius: 20px;
  overflow: hidden; /* clips pressed fills to the rounded ends */
}

.map-rail-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 50px;
  padding: 0;
  border: 0;
  background: var(--card);
  color: var(--ink-1);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition:
    background-color var(--t-slow) var(--ease-out),
    color var(--t-slow) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}

.map-rail-btn + .map-rail-btn { border-top: 1px solid var(--ink-1); }

.map-rail-btn:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }

/* same tap feedback as the nav pill — fill on press, linger, fade */
.map-rail-btn:active {
  background: var(--ink-1);
  color: var(--on-ink);
  transform: scale(0.94);
  transition: none;
}

.map-rail-btn svg {
  width: 20px;
  height: 20px;
  fill: currentColor;
}

/* zoom limit reached — quiet the button without removing it */
.map-rail-btn.is-off { color: var(--muted); cursor: default; }

/* the full-trail, tour, and theme buttons are each their own full circle —
   full-trail tops the right rail; tour above theme on the left */
.map-rail .map-rail-trail,
.map-rail .map-rail-tour,
.map-rail .map-rail-theme {
  height: 40px;
  border: 1px solid var(--ink-1);
  border-radius: 50%;
}

/* the theme toggle's one-button rail mirrors the tools on the far left */
.map-rail--left {
  left: var(--s5);
  right: auto;
}

/* floating theme toggle for pages without the map rail; the location view
   hides it — there the rail carries the toggle */
.theme-btn {
  position: fixed;
  top: var(--s3);
  right: var(--s3);
  z-index: 1150;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--ink-1);
  border-radius: 50%;
  background: var(--card);
  color: var(--ink-1);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.theme-btn { transition: transform var(--t-fast) var(--ease-out); }
.theme-btn:active { transform: scale(0.92); }

.theme-btn svg { width: 20px; height: 20px; fill: currentColor; }

/* the sun/moon is swapped in fresh by paintButtons() on every flip, so the
   new icon turns into place rather than teleporting */
.theme-btn svg,
.map-rail-theme svg {
  animation: icon-turn 0.5s var(--ease-out);
}

@keyframes icon-turn {
  from { transform: rotate(-90deg); opacity: 0; }
}

.theme-btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

body[data-view='location'] .theme-btn { display: none; }

/* ---------- locate yourself (opt-in) ---------- */

/* the locate button rides above the zoom pill as its own full circle */
.map-rail .nrb-locate-btn {
  height: 40px;
  border: 1px solid var(--ink-1);
  border-radius: 50%;
}

.nrb-locate-btn.is-on { background: var(--ink-1); color: var(--on-ink); }
.nrb-locate-btn.is-denied { color: var(--muted); }
.nrb-locate-btn.is-locating .nrb-locate-ic { animation: nrb-locate-pulse 1s ease-in-out infinite; }

@keyframes nrb-locate-pulse { 50% { opacity: 0.25; } }

/* the you-dot: ink with a paper-white ring — stays legible over the
   brand-blue trail line (a blue dot would vanish on it) */
.nrb-you { pointer-events: none; }

.nrb-you-dot {
  position: relative;
  display: block;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--ink);
  border: 3px solid var(--on-ink);
}

/* a slow radar ping off the dot — the one thing on the map that says
   "live" without a word (reduced motion caps it to a single beat) */
.nrb-you-dot::after {
  content: '';
  position: absolute;
  inset: -5px;
  border-radius: 50%;
  border: 2px solid var(--ink);
  opacity: 0;
  animation: you-ping 2.4s var(--ease-out) infinite;
}

@keyframes you-ping {
  0% { transform: scale(0.6); opacity: 0.7; }
  70%, 100% { transform: scale(2.1); opacity: 0; }
}

/* action feedback ("you're 6 km away", "blocked in settings") — auto-hides
   after 8 s and has a real X; it never comes back unprompted */
.nrb-locate-note[hidden] { display: none !important; }

.nrb-locate-note {
  position: absolute;
  animation: rise-in var(--t-med) var(--ease-out);
  z-index: 1050; /* above Leaflet's controls (1000), below banner + nav (1100) */
  left: var(--s3);
  /* clears the left rail's stack (tour + theme circles, 88px) bottom-left */
  bottom: calc(var(--s5) + var(--bnav-h) + var(--s10) + 88px + var(--s2) + env(safe-area-inset-bottom, 0px));
  max-width: min(36ch, 75%);
  display: flex;
  align-items: flex-start;
  gap: var(--s2);
  padding: var(--s3) var(--s2) var(--s3) var(--s4);
  border: 1px solid var(--hair);
  border-left: 8px solid var(--ink-1);
  background: var(--paper);
  font-size: 13px;
}

.nrb-locate-note-msg { flex: 1; }

.nrb-locate-note-x {
  flex: none;
  margin: calc(-1 * var(--s3)) 0;
  padding: 0 var(--s3);
  min-height: 44px; /* thumb-sized */
  border: none;
  background: none;
  font-family: var(--font);
  font-size: 20px;
  line-height: 1;
  color: var(--ink);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.nrb-locate-note-x:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

.status-line {
  margin-top: var(--s5);
  padding-top: var(--s3);
  border-top: 1px solid var(--hair);
  font-size: 13px;
}

.status-line--empty { color: var(--muted); }

/* ---------- all-access-points stack ---------- */

.stack {
  margin-top: var(--s4);
  border-top: 1px solid var(--hair);
}

.mini {
  color: var(--ink);
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}

.stack .mini {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--s4);
  padding: var(--s4) 0 var(--s5);
  border-bottom: 1px solid var(--hair);
}

.stack .mini .mini-map {
  order: 2;
  width: 112px;
  height: auto;
  flex: none;
  border: 1px solid var(--hair);
  filter: var(--map-img-filter);
  transition: transform var(--t-med) var(--ease-out), opacity var(--t-slow) var(--ease-out);
}

/* pointer devices: the row's thumbnail leans in a touch on hover */
@media (hover: hover) {
  .stack .mini:hover .mini-map { transform: scale(1.04); }
}

/* and everywhere: it dips under the thumb while the row is pressed */
.stack .mini:active .mini-map { transform: scale(0.97); }

.stack .mini .mini-body { flex: 1; }

.mini-index {
  display: block;
  font-size: 10px;
  letter-spacing: 0.14em;
  color: var(--muted);
}

.mini-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s2);
  flex-wrap: wrap;
}

.mini-name {
  font-size: 17px;
  font-weight: 400;
  letter-spacing: -0.01em;
  text-transform: lowercase;
}

.mini .pill { margin-top: 0; font-size: 11px; }
.mini .pill::before { width: 8px; height: 8px; }

.mini .status-line {
  display: block;
  margin-top: var(--s1);
  padding: 0;
  border: none;
  font-size: 12px;
  color: var(--muted);
}

/* ---------- footer ---------- */

.site-foot {
  margin-top: var(--s16);
  border-top: 1px solid var(--hair);
  padding-top: var(--s5);
}

.disclaimer {
  font-size: 12px;
  line-height: 1.6;
  color: var(--muted);
}

.colophon {
  margin-top: var(--s4);
  font-size: 11px;
  color: var(--muted);
}

.noscript { margin-top: var(--s6); color: var(--blocked); }

/* ---------- bottom nav ---------- */

/* Floating pill, present on every view — never docked, the same shape on a
   phone as on a desktop (per the Figma "UI layout mobile" frame). */

/* clear the floating pill (+ device safe area) so content never hides under it */
body {
  padding-bottom: calc(var(--s16) + var(--s5) + var(--bnav-h) + env(safe-area-inset-bottom, 0px));
}

/* the location view is exactly one viewport — the map, with the pill
   floating on top; nothing below the fold to scroll to */
body[data-view="location"] { padding-bottom: 0; }

.bnav {
  position: fixed;
  z-index: 1100; /* above Leaflet's panes and controls, same tier as the banner */
  left: var(--s5);
  right: var(--s5);
  bottom: calc(var(--s5) + env(safe-area-inset-bottom, 0px));
  max-width: 560px; /* match the content column */
  margin-inline: auto;
  display: flex;
  align-items: stretch;
  height: var(--bnav-h);
  background: var(--card); /* segments are white per Figma — deliberately brighter than --paper */
  border: 1px solid var(--ink-1);
  border-radius: calc(var(--bnav-h) / 2);
  overflow: hidden; /* clips the selected segment's fill to the rounded ends */
  /* named for cross-document view transitions: the pill holds still while
     the page cross-fades around it */
  view-transition-name: bnav;
  transition: opacity var(--t-med) var(--ease-out);
}

/* reporting owns the screen — the pill fades out for the whole flow */
body.is-reporting .bnav {
  opacity: 0;
  pointer-events: none;
}

.bnav-item {
  position: relative;
  /* an explicit one-third basis, not 0: with basis 0 the end tabs' cap
     padding joins the flex distribution and inflates their segments —
     the tabs must stay equal thirds with the padding absorbed inside */
  flex: 1 1 33.333%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  color: var(--ink-1);
  text-decoration: none;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.5px;
  -webkit-tap-highlight-color: transparent;
  transition: background-color var(--t-slow) var(--ease-out), color var(--t-slow) var(--ease-out);
}

.bnav-item:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }

/* selected tab — segment fills nav blue, contents flip to white */
.bnav-item[aria-current="page"] {
  background: var(--ink-1);
  color: var(--on-ink);
}

/* tap feedback — fills instantly while pressed, then the fill lingers and
   fades out over the transition above once the finger lifts */
.bnav-item:active {
  background: var(--ink-1);
  color: var(--on-ink);
  transition: none;
}

/* end segments: nudge contents inward, clear of the rounded caps */
.bnav-item:first-child { padding-left: var(--s4); }
.bnav-item:last-child { padding-right: var(--s4); }

.bnav-ic {
  width: 20px;
  height: 20px;
  flex: none;
  fill: currentColor;
}

@media (min-width: 480px) {
  body { padding-left: var(--s6); padding-right: var(--s6); }
  .stripes { margin-left: calc(-1 * var(--s6)); margin-right: calc(-1 * var(--s6)); }
}

/* ---------- desktop ---------- */

/* The map owns the whole window; the chrome floats on top of it. */
@media (min-width: 900px) {
  /* same pill, but it stops stretching and hugs its three tabs instead —
     the 20px chrome inset carries over from mobile unchanged */
  .bnav {
    max-width: none;
    width: fit-content;
    bottom: var(--s5);
  }

  .bnav-item {
    flex: none;
    min-width: 102px;
  }

  /* wide screens keep the tools bottom-right, where Google Maps has them —
     the centred nav pill leaves that corner free, so they drop to its base */
  .map-rail {
    bottom: var(--s5);
  }

  /* the offline banner floats like the rest of the chrome instead of
     spanning the whole window */
  body[data-view="location"] .banner {
    left: 50%;
    right: auto;
    transform: translateX(-50%);
    width: min(560px, calc(100vw - var(--s12)));
  }
}

/* ---------- first-visit tour ---------- */

/* One ring, six stops: it glides between targets rather than blinking,
   so the eye follows the tour instead of hunting for it. */
.tour-ring {
  position: fixed;
  z-index: 1150; /* above the nav pill (1100), below the report scrim (1200) */
  pointer-events: none;
  border: 2px solid var(--ink-1);
  border-radius: 999px;
  animation: fade-in var(--t-med) var(--ease-out);
  transition:
    left 0.45s var(--ease-out),
    top 0.45s var(--ease-out),
    width 0.45s var(--ease-out),
    height 0.45s var(--ease-out);
}

.tour-tip {
  position: fixed;
  z-index: 1150;
  width: min(270px, calc(100vw - 32px));
  padding: var(--s4) var(--s4) var(--s3);
  background: var(--card);
  border: 1px solid var(--ink-1);
  color: var(--ink-1);
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.5px;
  animation: rise-in var(--t-med) var(--ease-out);
}

.tour-msg { padding-right: var(--s6); /* clear of the X */ }

/* the opening ask — anchored to the left rail's tour button (the same
   corner the replay button lives in), just above its two circles */
.tour-offer {
  left: var(--s5);
  bottom: calc(var(--s5) + var(--bnav-h) + var(--s10) + 88px + var(--s2) + env(safe-area-inset-bottom, 0px));
}

@media (min-width: 900px) {
  /* the rail drops to the window base on desktop — the offer follows */
  .tour-offer {
    bottom: calc(var(--s5) + 88px + var(--s2));
  }
}

.tour-x {
  position: absolute;
  top: 0;
  right: 0;
  width: 44px; /* thumb-sized */
  height: 44px;
  border: 0;
  background: none;
  font-family: var(--font);
  font-size: 20px;
  line-height: 1;
  color: var(--ink-1);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--t-fast) var(--ease-out);
}

.tour-x:active { transform: scale(0.85); }
.tour-x:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }

.tour-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  margin-top: var(--s3);
}

.tour-count { font-size: 10px; color: var(--muted); }

/* same solid button voice as the report sheet */
.tour-next {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 32px;
  padding: 0 var(--s5);
  border: 0;
  background: var(--ink-1);
  color: var(--on-ink);
  font-family: var(--font);
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.5px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition:
    background-color var(--t-slow) var(--ease-out),
    color var(--t-slow) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}

.tour-next:active { transform: scale(0.97); transition: none; }
.tour-next:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }

/* ---------- reduced motion ---------- */

/* One switch kills all of it: entrances, exits, colour glides, press
   springs, and the page cross-fades. The near-zero duration (not `none`)
   still fires animationend, so the JS that waits on exits never hangs. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
  }

  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}
