/* ══════════════════════════════════════════════════════════════════════
   Nobody's Road

   Mobile-first, down to 360px. Light and dark via prefers-color-scheme.

   Colour note (deliberate, not stylistic): the band ramp is SINGLE-HUE
   sequential navy-blue, matching the brand's deep-navy-on-cream identity.
   Red-to-green is prohibited here — it reads as a danger/safety scale,
   which this map does not make, and it is the exact ramp that fails
   red-green colour vision deficiency. Lightness, saturation AND line
   weight all carry the signal alongside hue, so the map still ranks
   correctly in greyscale or with any form of CVD. The four band colours
   are identical in light and dark mode because they sit on the same
   light basemap tiles either way; the ramp's dark end is pulled inward
   from pure near-black so it still reads against a dark-mode panel, and
   its pale end is pulled inward from pure pastel so it still reads
   against a cream/white panel — see app.js BANDS for the exact values
   and the contrast reasoning.
   ═══════════════════════════════════════════════════════════════════ */

:root {
  /* Makes the browser paint its OWN chrome — scrollbars above all — to
     match the active theme. Without it the page renders dark while the
     panel's scrollbar stays light grey, which was the last non-navy
     surface left in dark mode. No custom ::-webkit-scrollbar rules are
     used on purpose: the native dark scrollbar already tracks the theme
     and respects platform accessibility settings that a hand-rolled one
     would override. */
  color-scheme: light dark;

  --band-solitude: #25498b;
  --band-quiet:    #3f6cb4;
  --band-easy:     #7e9bc5;
  --band-steady:   #a3b7cd;

  /* Light theme. Same rule as the dark block below: BRAND NAVY family
     (hue ~217) only, nothing in the 245-325 violet range. This block was
     the last violet left in the project — the dark theme was re-pitched
     first and the light one was missed, so a light-mode visitor still got
     plum-tinted paper (#f6f4fb h257) and plum ink (#1d1a2b h251) under a
     navy band ramp.

     The re-pitch changed HUE ONLY. Every lightness AND saturation below is
     carried over from the violet value it replaced, to the decimal — the
     measured lightness delta on all six tokens is 0.00%, so the depth
     ladder (bg -> surface-2 -> line) and every text contrast ratio are
     preserved exactly rather than redesigned. --surface (#ffffff) and
     --accent (#25498b, already hue 219) were untouched.

     ONE deliberate exception, exactly mirroring the dark block's: --ink-3
     was darkened L51.8 -> L44.0. At its carried-over lightness it scored
     3.64 / 3.91 / 4.27 on the three surfaces — a PRE-EXISTING AA failure
     inherited from the violet build (#7d7791 scored 3.64 / 3.91 / 4.27 too),
     not caused by the re-pitch. Every --ink-3 use is small text (10 -
     12.5px: .card .val span, .band .ct, .citation .fine, .src, .facts dt,
     .hint, .legend h3), so 4.5:1 applies with no large-text exemption
     available. L44.0 clears it on all three surfaces (4.86 / 5.22 / 4.56).

     Verified after re-pitch, light mode: 14 pairs at WCAG AA, 0 failures. */
  --bg:        #f4f7fb;
  --surface:   #ffffff;
  --surface-2: #ebf0f8;
  --ink:       #1a212b;
  --ink-2:     #505a6b;
  --ink-3:     #646d7c;
  --line:      #d8e1ef;
  --accent:    #25498b;
  --accent-ink:#ffffff;
  --shadow:    0 1px 3px rgba(26, 33, 43, .10), 0 6px 20px rgba(26, 33, 43, .07);
  --topbar-h:  56px;
  /* Same serif stack as the hero's --nr-serif (styles.css §HERO), for the
     one place the map app quotes it: the felt-time headline (swarm-ops
     #103). Not theme-dependent, so it is not repeated in the dark block. */
  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua",
           Georgia, "Times New Roman", serif;
}

/* Dark theme. Every surface, ink and line here sits in the BRAND NAVY
   family (hue ~217, the hue of #25498b / #16233C / #0F1C33). It is not
   allowed to drift violet: an earlier build ran the whole dark theme at
   hue ~250-257, so once the band ramp was re-pitched to navy the panel,
   cards and filter rows read plum-purple against a navy legend. Nothing
   in this block may carry a hue in the 250-320 range.

   The re-pitch changed HUE ONLY — every lightness below is carried over
   from the violet values it replaced, to the decimal, so the depth ladder
   (bg darkest -> surface -> surface-2 lightest) and every text contrast
   ratio are preserved rather than redesigned. Saturation moved toward the
   brand navies on the surfaces and away from tint on the inks.

   ONE deliberate exception: --ink-3 was lifted L58.2 -> L60.0. At the
   original lightness it scored 4.32:1 on --surface-2 — a PRE-EXISTING AA
   failure for `.card .val span` ("cars/day", 10.5px), inherited from the
   violet build and not caused by this change. L60.0 clears 4.5:1 on all
   three surfaces (4.68 / 5.45 / 6.06).

   Verified after re-pitch, dark mode: 12 text-over-surface pairs plus
   accent and accent-ink all pass WCAG AA; nine of the twelve improved
   against the violet values and none regressed below its threshold. */
@media (prefers-color-scheme: dark) {
  :root {
    --bg:        #0e1622;
    --surface:   #152033;
    --surface-2: #1e2c43;
    --ink:       #edf0f5;
    --ink-2:     #b1bccd;
    --ink-3:     #8a96a8;
    --line:      #29384f;
    --accent:    #a3b7cd;
    --accent-ink:#16233c;
    --shadow:    0 1px 3px rgba(0, 0, 0, .5), 0 6px 20px rgba(0, 0, 0, .38);
  }
}

* { box-sizing: border-box; }

/* The document scrolls (hero first, map below). The map application is
   its own full-viewport flex column — see .app — so everything inside it
   sizes exactly as it did before the hero existed. */
html, body {
  margin: 0;
  padding: 0;
}

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-text-size-adjust: 100%;
  overscroll-behavior-y: none;
}

h1, h2, h3, p, ol, ul { margin: 0; }
ol, ul { padding: 0; list-style: none; }
em { font-style: normal; font-weight: 600; }

/* ══════════════════════════════════════════════════════════════════════
   HERO / LANDING SECTION

   Generated by scripts/hero/ — do not hand-edit; rebuild and re-apply.

   EVERY rule below is scoped under .nr-root and every class is nr-*, so
   the hero cannot reach the map UI (which owns the unprefixed .band,
   .card, .legend, .topbar, .mark and .foot names) and the map cannot
   reach the hero. The hero's custom properties are all --nr-*, so its
   --nr-accent / --nr-ink / --nr-band-* never collide with the app's
   --accent, --accent-ink or --band-* tokens above.

   The legend illustration in the "rarest tier" section uses the app's
   real band ramp (--nr-band-solitude … --nr-band-steady are the same hex
   as app.js BANDS), so the landing page and the map agree on colour.
   ═══════════════════════════════════════════════════════════════════ */

/* ==========================================================================
   Nobody's Road — landing page
   Self-contained: system fonts only, inline SVG only, zero runtime requests.
   ========================================================================== */ /* ---- tokens: light ------------------------------------------------------ */
.nr-root{--nr-paper-bg:#F5F1E7;   --nr-paper-ink:#16233C;  --nr-paper-mut:#4A5A75;
  --nr-paper-rule:rgba(22,35,60,.20); --nr-paper-hair:rgba(22,35,60,.12);
  --nr-navy-bg:#16233C;    --nr-navy-ink:#F2EDE3;   --nr-navy-mut:#AABCD2;
  --nr-navy-rule:rgba(242,237,227,.26); --nr-navy-hair:rgba(242,237,227,.14);
  --nr-foot-bg:#0F1C33;
  --nr-accent:#3F6488;          /* muted slate blue, on paper */
  --nr-accent-on-navy:#9FBFDC;  /* muted slate blue, on navy  */
  --nr-shield-bg:#E7D9B0;  --nr-shield-ink:#16233C; --nr-shield-line:#A8801F;
  --nr-btn-bg:#16233C;     --nr-btn-ink:#F5F1E7;    --nr-btn-bg-h:#233458;
  /* the map's own band ramp — identical hex to app.js BANDS */
  --nr-band-solitude:#25498b; --nr-band-quiet:#3f6cb4;
  --nr-band-easy:#7e9bc5;     --nr-band-steady:#a3b7cd;
  --nr-legend-surface:#ffffff; --nr-legend-edge:rgba(22,35,60,.22);
  --nr-contour:rgba(22,35,60,.115);
  --nr-map-fill:rgba(22,35,60,.032);
  --nr-map-terrain:rgba(22,35,60,.085);
  --nr-map-outline:rgba(22,35,60,.55);
  --nr-map-road:#3F6488;
  --nr-map-road-hi:#16233C;
  --nr-sans:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --nr-serif:"Iowan Old Style","Palatino Linotype",Palatino,"Book Antiqua",Georgia,"Times New Roman",serif;
  --nr-cond:"Avenir Next Condensed","Roboto Condensed","Arial Narrow",var(--nr-sans);
  --nr-mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
  --nr-pad:clamp(1.25rem,4.4vw,4rem);}
/* ---- tokens: dark (a considered inversion, not a filter) ----------------- */
@media (prefers-color-scheme:dark){
  .nr-root{/* the "paper" bands become a warm-tinted deep slate; the "navy" bands go
     deeper still, so the light/dark alternation reads in the same direction */
  --nr-paper-bg:#151E2C;   --nr-paper-ink:#EDE6D8;  --nr-paper-mut:#9FADC2;
  --nr-paper-rule:rgba(237,230,216,.24); --nr-paper-hair:rgba(237,230,216,.13);
  --nr-navy-bg:#0A1220;    --nr-navy-ink:#EDE6D8;   --nr-navy-mut:#93A5BC;
  --nr-navy-rule:rgba(237,230,216,.22); --nr-navy-hair:rgba(237,230,216,.11);
  --nr-foot-bg:#060C16;
  --nr-accent:#89AFD4;
  --nr-accent-on-navy:#89AFD4;
  --nr-shield-bg:#3A331F;  --nr-shield-ink:#E8D9A6; --nr-shield-line:#B99B49;
  --nr-btn-bg:#E8DFCD;     --nr-btn-ink:#0F1826;    --nr-btn-bg-h:#F5EEE0;
  /* band ramp is fixed across themes — it sits on the same basemap either way */
  /* Was a hue-250 violet — the lone violet token in the hero, carried
     over from the app's old dark surface. Re-pitched to the same
     lightness in brand navy so the hero's legend illustration matches the
     rest of the hero (#060C16 / #0F1826) and the app panel behind it. */
  --nr-legend-surface:#152033; --nr-legend-edge:rgba(237,230,216,.20);
  --nr-contour:rgba(237,230,216,.085);
  --nr-map-fill:rgba(237,230,216,.035);
  --nr-map-terrain:rgba(237,230,216,.07);
  --nr-map-outline:rgba(237,230,216,.45);
  --nr-map-road:#7FA6CC;
  --nr-map-road-hi:#D9E6F3;}
}
/* ---- base --------------------------------------------------------------- */
.nr-root *,
.nr-root *::before,
.nr-root *::after{box-sizing:border-box}
.nr-root{-webkit-text-size-adjust:100%}
.nr-root{margin:0; overflow-x:hidden;
  font-family:var(--nr-sans); font-size:16px; line-height:1.55;
  background:var(--nr-paper-bg); color:var(--nr-paper-ink);
  -webkit-font-smoothing:antialiased;}
.nr-root img,
.nr-root svg{max-width:100%}
.nr-root h1,
.nr-root h2,
.nr-root h3,
.nr-root p,
.nr-root ul,
.nr-root figure,
.nr-root blockquote{margin:0}
.nr-root ul{padding:0;list-style:none}
.nr-root a{color:inherit}
.nr-root :focus-visible{outline:2px solid var(--nr-accent);outline-offset:3px;border-radius:2px}
.nr-root .nr-band{position:relative;padding:clamp(3rem,7vw,6rem) var(--nr-pad)}
.nr-root .nr-wrap{max-width:1180px;margin:0 auto;position:relative;z-index:1}
.nr-root .nr-band--paper{background:var(--nr-paper-bg);color:var(--nr-paper-ink);--nr-bandbg:var(--nr-paper-bg);
  --nr-ink:var(--nr-paper-ink); --nr-mut:var(--nr-paper-mut); --nr-rule:var(--nr-paper-rule);
  --nr-hair:var(--nr-paper-hair); --nr-acc:var(--nr-accent);}
.nr-root .nr-band--navy{background:var(--nr-navy-bg);color:var(--nr-navy-ink);--nr-bandbg:var(--nr-navy-bg);
  --nr-ink:var(--nr-navy-ink); --nr-mut:var(--nr-navy-mut); --nr-rule:var(--nr-navy-rule);
  --nr-hair:var(--nr-navy-hair); --nr-acc:var(--nr-accent-on-navy);}
/* shared type helpers */
.nr-root .nr-eyebrow{font-size:.6875rem;letter-spacing:.19em;text-transform:uppercase;
  font-weight:650;color:var(--nr-mut);}
.nr-root .nr-sec-label{text-align:center;font-size:.75rem;letter-spacing:.24em;text-transform:uppercase;
  font-weight:650;color:var(--nr-mut);margin-bottom:2.75rem;}
.nr-root .nr-sec-label::after{content:"";display:block;width:34px;height:1px;background:var(--nr-rule);
  margin:.9rem auto 0;}
/* ==========================================================================
   1 — HERO
   ========================================================================== */
.nr-root .nr-hero{padding-top:clamp(1.1rem,2.6vw,1.75rem);overflow:hidden}
.nr-root .nr-hero-bg{position:absolute;inset:-8% -4%;width:108%;height:118%;
  z-index:0;pointer-events:none;}
.nr-root .nr-hero-bg .nr-ct{fill:none;stroke:var(--nr-contour);stroke-width:1.1;vector-effect:non-scaling-stroke}
.nr-root .nr-topbar{display:flex;align-items:center;justify-content:space-between;gap:1rem;
  flex-wrap:wrap;padding-bottom:clamp(2rem,5vw,3.75rem);}
.nr-root .nr-mark{display:flex;align-items:center;gap:.6rem;min-width:0}
.nr-root .nr-mark-glyph{width:30px;height:30px;flex:none}
.nr-root .nr-mark-glyph .nr-g-line{fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round}
.nr-root .nr-mark-glyph .nr-g-road{fill:none;stroke:var(--nr-accent);stroke-width:1.6;
  stroke-linecap:round;stroke-dasharray:3 2.6}
.nr-root .nr-mark-txt{font-family:var(--nr-cond);font-weight:800;font-size:clamp(.95rem,2.4vw,1.075rem);
  letter-spacing:.115em;text-transform:uppercase;white-space:nowrap;}
.nr-root .nr-coverage{display:flex;align-items:center;gap:.55rem;
  border:1px solid var(--nr-rule);border-radius:999px;padding:.4rem .85rem .4rem .7rem;}
.nr-root .nr-coverage svg{width:12px;height:16px;flex:none;fill:none;stroke:var(--nr-accent);
  stroke-width:1.5;stroke-linejoin:round}
.nr-root .nr-coverage b,
.nr-root .nr-coverage span{display:block;font-size:.625rem;letter-spacing:.16em;
  text-transform:uppercase;line-height:1.35}
.nr-root .nr-coverage b{font-weight:700}
.nr-root .nr-coverage span{color:var(--nr-mut);font-weight:500}
.nr-root .nr-hero-grid{display:grid;grid-template-columns:minmax(0,1.02fr) minmax(0,1fr);
  gap:clamp(2rem,4.5vw,3.75rem);align-items:center;}
.nr-root h1{font-family:var(--nr-serif);font-weight:600;
  /* sized so both lines stay unbroken from 360px up to the 1180px cap */
  font-size:clamp(2rem,4.15vw,3.3rem);line-height:1.06;letter-spacing:-.018em;}
@media (max-width:900px){
  .nr-root h1{font-size:clamp(2rem,8.4vw,3.6rem)}
}
.nr-root h1 .nr-l{display:block}
.nr-root .nr-h-rule{width:62px;height:2px;background:var(--nr-ink);opacity:.85;margin:1.5rem 0 1.25rem;border:0}
.nr-root .nr-subhead{font-size:clamp(1rem,1.55vw,1.145rem);color:var(--nr-mut);max-width:44ch;line-height:1.55}
.nr-root .nr-pullquote{margin:1.75rem 0 0;padding:.15rem 0 .15rem 1.05rem;
  border-left:2px solid var(--nr-acc);
  font-family:var(--nr-serif);font-style:italic;font-size:clamp(1.02rem,1.7vw,1.19rem);
  line-height:1.45;max-width:30ch;}
.nr-root .nr-cta-row{margin-top:2.1rem}
.nr-root .nr-cta{display:inline-flex;align-items:center;gap:.65rem;
  background:var(--nr-btn-bg);color:var(--nr-btn-ink);
  text-decoration:none;border:1px solid var(--nr-btn-bg);
  padding:.92rem 1.6rem;border-radius:3px;
  font-family:var(--nr-cond);font-weight:750;font-size:.9375rem;
  letter-spacing:.13em;text-transform:uppercase;
  transition:background-color .16s ease,transform .16s ease;}
.nr-root .nr-cta:hover{background:var(--nr-btn-bg-h);border-color:var(--nr-btn-bg-h)}
.nr-root .nr-cta:active{transform:translateY(1px)}
.nr-root .nr-cta svg{width:19px;height:19px;flex:none;fill:none;stroke:currentColor;
  stroke-width:1.6;stroke-linejoin:round;stroke-linecap:round}
.nr-root .nr-cta-note{margin-top:.85rem;font-size:.8125rem;color:var(--nr-mut);letter-spacing:.01em}
.nr-root .nr-hero-map{margin:0;min-width:0}
.nr-root .nr-hero-map svg{display:block;width:100%;height:auto}
.nr-root .nr-map-fill{fill:var(--nr-map-fill)}
.nr-root .nr-tr{fill:none;stroke:var(--nr-map-terrain);stroke-width:.85;vector-effect:non-scaling-stroke}
.nr-root .nr-net{fill:none;stroke:var(--nr-map-road);stroke-width:1.9;stroke-linecap:round;
  stroke-linejoin:round}
.nr-root .nr-featline{fill:none;stroke:var(--nr-map-road-hi);stroke-width:3.4;stroke-linecap:round;
  stroke-linejoin:round}
.nr-root .nr-st-outline{fill:none;stroke:var(--nr-map-outline);stroke-width:1.5;stroke-linejoin:round}
/* State codes on the two map illustrations. 26px/38px were right when the
   maps held TWO states about 250 viewBox units wide each; across eleven the
   states are 90-130 units wide and those sizes ran the labels straight over
   the borders and into each other. Sized to roughly a tenth of a typical
   state's width, as the originals were. The halo stroke shrinks with the
   type so it stays a hairline rather than a slab. */
.nr-root .nr-st-label{fill:var(--nr-mut);font-family:var(--nr-cond);font-weight:750;font-size:13px;
  letter-spacing:.1em;text-anchor:middle;opacity:.62;
  stroke:var(--nr-bandbg);stroke-width:3px;paint-order:stroke fill}
.nr-root .nr-map-cap{margin-top:.9rem;font-size:.6875rem;letter-spacing:.15em;
  text-transform:uppercase;color:var(--nr-mut);text-align:right}
/* ==========================================================================
   2 — THE TRANSLATION
   ========================================================================== */
.nr-root .nr-trans-grid{display:grid;grid-template-columns:minmax(0,1.5fr) minmax(0,.85fr);
  gap:clamp(2rem,4.5vw,4rem);align-items:center;}
.nr-root .nr-trans-pair{display:grid;grid-template-columns:1fr auto 1fr;gap:clamp(.75rem,2vw,1.35rem);
  align-items:stretch}
.nr-root .nr-tbox{border:1px solid var(--nr-rule);padding:clamp(1.15rem,2.6vw,1.85rem) 1rem;
  text-align:center;display:flex;flex-direction:column;justify-content:center;
  gap:.6rem;min-width:0;}
.nr-root .nr-tbox .nr-eyebrow{font-size:.625rem;letter-spacing:.17em}
.nr-root .nr-tbox-num{font-family:var(--nr-sans);font-weight:250;font-size:clamp(1.8rem,5.2vw,2.85rem);
  line-height:1;letter-spacing:-.01em;}
.nr-root .nr-tbox-num b{font-weight:400}
.nr-root .nr-tbox-sub{font-size:.6875rem;letter-spacing:.17em;text-transform:uppercase;color:var(--nr-mut)}
.nr-root .nr-tbox-felt{font-family:var(--nr-serif);font-size:clamp(1.25rem,3.4vw,1.85rem);line-height:1.18;
  font-weight:500;}
.nr-root .nr-arrow{align-self:center;flex:none}
.nr-root .nr-arrow svg{display:block;width:clamp(26px,4.5vw,44px);height:auto;fill:none;
  stroke:var(--nr-acc);stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
.nr-root .nr-trans-copy p{font-size:clamp(1rem,1.5vw,1.09rem);color:var(--nr-mut);max-width:34ch}
.nr-root .nr-trans-copy .nr-kicker{margin-top:1.15rem;font-family:var(--nr-serif);font-style:italic;
  color:var(--nr-ink);font-size:clamp(1.1rem,2vw,1.32rem);line-height:1.35;}
/* ==========================================================================
   3 — PROOF IS ON THE MAP
   ========================================================================== */
.nr-root .nr-cards{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:1px;
  background:var(--nr-rule);border:1px solid var(--nr-rule)}
.nr-root .nr-card{background:var(--nr-paper-bg);padding:1.35rem 1.15rem 1.5rem;display:flex;
  flex-direction:column;gap:.85rem;min-width:0}
.nr-root .nr-card-head{display:flex;align-items:flex-start;justify-content:space-between;gap:.6rem}
.nr-root .nr-card-id{min-width:0}
.nr-root .nr-card h3{font-family:var(--nr-cond);font-weight:750;font-size:1.02rem;line-height:1.2;
  letter-spacing:.005em}
.nr-root .nr-card-sub{margin-top:.3rem;font-size:.6875rem;letter-spacing:.06em;color:var(--nr-mut);
  text-transform:uppercase}
.nr-root .nr-shield{width:38px;height:40px;flex:none}
.nr-root .nr-shield-bg{fill:var(--nr-shield-bg)}
.nr-root .nr-shield-line{fill:none;stroke:var(--nr-shield-line);stroke-width:1.5}
.nr-root .nr-shield-num{fill:var(--nr-shield-ink);font-family:var(--nr-cond);font-weight:800;
  letter-spacing:.01em}
.nr-root .nr-card-fig{margin:0}
/* each sketch keeps its true north-up aspect; the box reserves equal height */
.nr-root .nr-figbox{height:104px;display:flex;align-items:center;justify-content:center}
.nr-root .nr-figbox svg{display:block;max-width:100%;max-height:104px;height:auto}
.nr-root .nr-road-shadow{fill:none;stroke:var(--nr-hair);stroke-width:5.5;stroke-linecap:round;
  stroke-linejoin:round;vector-effect:non-scaling-stroke}
.nr-root .nr-road{fill:none;stroke:var(--nr-accent);stroke-width:2;stroke-linecap:round;
  stroke-linejoin:round;vector-effect:non-scaling-stroke}
.nr-root .nr-card-fig figcaption{margin-top:.55rem;font-size:.72rem;color:var(--nr-mut);line-height:1.4}
.nr-root .nr-card-aadt{margin-top:auto;padding-top:.7rem;border-top:1px solid var(--nr-hair);
  display:flex;align-items:baseline;gap:.45rem;flex-wrap:wrap}
.nr-root .nr-card-aadt .nr-num{font-family:var(--nr-cond);font-weight:250;font-size:2.05rem;line-height:1;
  letter-spacing:-.015em}
.nr-root .nr-card-aadt .nr-unit{font-size:.6875rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--nr-mut)}
.nr-root .nr-card-felt{font-family:var(--nr-serif);font-style:italic;font-size:.98rem;color:var(--nr-ink)}
.nr-root .nr-cards-note{margin-top:1.5rem;text-align:center;font-size:.75rem;color:var(--nr-mut);
  letter-spacing:.03em}
/* ==========================================================================
   3b — DESERT BAND (full-bleed engraving)
   ========================================================================== */
.nr-root .nr-desert-band{position:relative;background:var(--nr-paper-bg);color:var(--nr-paper-ink);
  --nr-ink:var(--nr-paper-ink);--nr-mut:var(--nr-paper-mut);--nr-rule:var(--nr-paper-rule);
  border-top:1px solid var(--nr-paper-hair);border-bottom:1px solid var(--nr-paper-hair);
  overflow:hidden;}
.nr-root .nr-desert-band svg{display:block;width:100%;height:clamp(230px,30vw,430px);}
.nr-root .nr-dz{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;
  vector-effect:non-scaling-stroke}
.nr-root .nr-d-sky{stroke-width:.9;opacity:.16}
.nr-root .nr-d-far{stroke-width:.9;opacity:.3}
.nr-root .nr-d-hz{stroke-width:1.1;opacity:.5}
.nr-root .nr-d-mesa{stroke-width:1.35;opacity:.78}
.nr-root .nr-d-hatch{stroke-width:.75;opacity:.3}
.nr-root .nr-d-ground{stroke-width:.85;opacity:.26}
.nr-root .nr-d-scrub{stroke-width:.95;opacity:.5}
.nr-root .nr-d-road{stroke-width:1.5;opacity:.85}
.nr-root .nr-d-centre{stroke-width:1.6;opacity:.62;stroke:var(--nr-centre-line);
  stroke-dasharray:16 20}
.nr-root{--nr-centre-line:#B7902E}
@media (prefers-color-scheme:dark){
  .nr-root{--nr-centre-line:#C9A64F}
}
.nr-root .nr-desert-quote{position:absolute;top:clamp(1.4rem,4.2vw,3rem);left:50%;transform:translateX(-50%);
  width:min(92%,42ch);margin:0;text-align:center;z-index:1;
  font-family:var(--nr-serif);font-style:italic;line-height:1.3;
  font-size:clamp(1.15rem,3vw,2rem);letter-spacing:-.005em;}
@media (max-width:620px){
  .nr-root .nr-desert-band svg{height:250px}
  .nr-root .nr-desert-quote{top:1.15rem}
}
/* ==========================================================================
   4 — RAREST TIER
   ========================================================================== */
.nr-root .nr-rare-grid{display:grid;grid-template-columns:minmax(0,.9fr) minmax(0,1.5fr) minmax(0,.62fr);
  gap:clamp(1.75rem,4vw,3.5rem);align-items:center;}
.nr-root .nr-rare-states svg{display:block;width:100%;height:auto}
.nr-root .nr-rare-states .nr-st-outline{stroke:var(--nr-acc);stroke-width:1.6;opacity:.9}
.nr-root .nr-rare-states .nr-st-fill{fill:var(--nr-acc);opacity:.07}
.nr-root .nr-rare-states .nr-st-label{fill:var(--nr-ink);opacity:.75;font-size:17px}
.nr-root .nr-rare-states .nr-net{stroke:var(--nr-acc);stroke-width:.8;opacity:.34}
.nr-root .nr-rare-copy{text-align:center}
.nr-root .nr-rare-copy .nr-eyebrow{display:block;margin-bottom:1.1rem}
.nr-root .nr-rare-big{font-family:var(--nr-cond);font-weight:700;line-height:1.02;letter-spacing:-.012em;
  font-size:clamp(1.85rem,5.6vw,3.35rem);text-transform:uppercase;}
.nr-root .nr-rare-sub{margin-top:1.15rem;font-size:.78rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--nr-mut);line-height:1.75;max-width:34ch;margin-inline:auto;}
/* The legend illustration uses the map's OWN four-band ramp and the map's
   own line weights — same hex values as app.js BANDS — so the landing page
   and the map read as one thing. Single-hue navy; explicitly not a
   red-to-green scale. The bars sit on the map-panel surface colour rather
   than the navy section background, because that is the surface they sit
   on in the app; in dark mode they carry the same faint ring the app's
   legend swatches carry, for the darkest band. */
.nr-root .nr-legend{display:flex;flex-direction:column;gap:.55rem}
.nr-root .nr-legend-cap{font-size:.625rem;letter-spacing:.18em;text-transform:uppercase;color:var(--nr-mut)}
.nr-root .nr-legend-bars{display:flex;flex-direction:column;gap:.65rem;
  padding:.8rem .85rem;background:var(--nr-legend-surface);
  border:1px solid var(--nr-legend-edge);border-radius:6px}
.nr-root .nr-legend-bars i{display:block;width:100%;border-radius:1px}
.nr-root .nr-legend-bars .nr-lb-solitude{height:18px;background:var(--nr-band-solitude)}
.nr-root .nr-legend-bars .nr-lb-quiet{height:13px;background:var(--nr-band-quiet)}
.nr-root .nr-legend-bars .nr-lb-easy{height:9px; background:var(--nr-band-easy)}
.nr-root .nr-legend-bars .nr-lb-steady{height:6px; background:var(--nr-band-steady)}
.nr-root .nr-legend-note{font-size:.6875rem;line-height:1.5;color:var(--nr-mut)}
/* A 4-step single-hue ramp cannot clear 3:1 (WCAG 1.4.11, non-text) at BOTH
   ends against one surface: on white the pale end lands at 2.8:1 / 2.1:1, on
   the dark panel the deep end lands at 2.0:1. The app backstops the dark case
   with a faint ring on its legend swatches; this does the same in both
   directions, so every bar has a defined edge whichever theme you are in.
   Ring alphas chosen so the ring itself clears 3:1 on its own surface
   (light 0.55 -> 3.7:1 on #fff; dark 0.32 -> the app's existing value). */
.nr-root .nr-legend-bars i{box-shadow:0 0 0 1px rgba(22,35,60,.55)}
@media (prefers-color-scheme:dark){
  .nr-root .nr-legend-bars i{box-shadow:0 0 0 1px rgba(255,255,255,.32)}
}
/* ==========================================================================
   5 — HOW IT WORKS
   ========================================================================== */
.nr-root .nr-how{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:clamp(1.5rem,3.4vw,2.75rem)}
.nr-root .nr-how-item{min-width:0}
.nr-root .nr-ico{width:56px;height:56px;display:block;margin-bottom:1.05rem}
.nr-root .nr-ico-ring{fill:none;stroke:var(--nr-rule);stroke-width:1.1}
.nr-root .nr-ico-art{fill:none;stroke:var(--nr-acc);stroke-width:1.5;stroke-linecap:round;
  stroke-linejoin:round}
.nr-root .nr-how-n{font-family:var(--nr-mono);font-size:.6875rem;letter-spacing:.16em;color:var(--nr-mut);
  margin-bottom:.5rem}
.nr-root .nr-how-t{font-size:.97rem;line-height:1.55;max-width:30ch}
/* ==========================================================================
   6 — FOOTER
   ========================================================================== */
.nr-root .nr-foot{background:var(--nr-foot-bg);color:var(--nr-navy-ink);
  --nr-ink:var(--nr-navy-ink);--nr-mut:var(--nr-navy-mut);--nr-rule:var(--nr-navy-rule);--nr-acc:var(--nr-accent-on-navy);
  padding:clamp(2.5rem,5vw,3.75rem) var(--nr-pad)}
.nr-root .nr-foot-grid{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(0,1fr);
  gap:clamp(1.75rem,4vw,3rem);align-items:start}
.nr-root .nr-foot h2{font-size:.6875rem;letter-spacing:.22em;text-transform:uppercase;
  color:var(--nr-mut);font-weight:650;margin-bottom:.85rem}
.nr-root .nr-foot p{font-size:.9rem;line-height:1.6;max-width:46ch}
.nr-root .nr-foot .nr-src-url{display:inline-block;margin-top:.6rem;font-family:var(--nr-mono);
  font-size:.78rem;color:var(--nr-acc);word-break:break-all}
.nr-root .nr-foot-right{display:flex;gap:1.25rem;align-items:flex-start;justify-content:flex-end}
.nr-root .nr-foot-right p{font-size:.82rem;color:var(--nr-mut);max-width:32ch;text-align:right}
.nr-root .nr-compass{width:52px;height:58px;flex:none;order:2}
.nr-root .nr-compass .nr-c-n{fill:var(--nr-mut);font-family:var(--nr-cond);font-weight:700;font-size:11px;
  letter-spacing:.12em}
.nr-root .nr-compass .nr-c-ring{fill:none;stroke:var(--nr-rule);stroke-width:1.1}
.nr-root .nr-compass .nr-c-tick{stroke:var(--nr-mut);stroke-width:1.1;stroke-linecap:round}
.nr-root .nr-compass .nr-c-needle{fill:var(--nr-acc);opacity:.9}
.nr-root .nr-compass .nr-c-needle-b{fill:var(--nr-mut);opacity:.5}
/* Compact footer info row (swarm-ops #103): methodology/privacy/support
 * plus the data vintage, above the legal line it already sits beside. */
.nr-root .nr-foot .nr-foot-links{margin-top:2.25rem;display:flex;gap:1.1rem;flex-wrap:wrap;
  font-size:.78rem;color:var(--nr-mut)}
.nr-root .nr-foot .nr-foot-links a{color:var(--nr-acc);text-decoration:none}
.nr-root .nr-foot .nr-foot-links a:hover,
.nr-root .nr-foot .nr-foot-links a:focus-visible{text-decoration:underline}
.nr-root .nr-foot .nr-foot-legal{max-width:none;margin-top:.85rem;padding-top:1.25rem;border-top:1px solid var(--nr-rule);
  font-size:.75rem;color:var(--nr-mut);display:flex;justify-content:space-between;
  gap:1rem;flex-wrap:wrap}
/* ==========================================================================
   7 — STATIC INFO PAGES (methodology.html / privacy.html / support.html)
   swarm-ops #103. Same visual family as the rest of the site — paper/navy
   bands, the same --nr-* tokens, both themes — but these three are read
   top to bottom rather than laid out as marketing sections, so they get
   one prose scope (.nr-doc) instead of a bespoke class per element.
   ========================================================================== */
.nr-root .nr-doc-head{padding-top:1.5rem;padding-bottom:1.5rem}
.nr-root .nr-doc-head-row{display:flex;align-items:center;justify-content:space-between;
  gap:1rem;flex-wrap:wrap}
.nr-root .nr-doc-back{font-size:.8rem;font-weight:650;color:var(--nr-acc);text-decoration:none}
.nr-root .nr-doc-back:hover,
.nr-root .nr-doc-back:focus-visible{text-decoration:underline}
.nr-root .nr-doc{padding-top:1rem}
.nr-root .nr-doc-wrap{max-width:720px}
.nr-root .nr-doc h1{font-family:var(--nr-serif);font-weight:600;font-size:clamp(1.9rem,4.5vw,2.6rem);
  line-height:1.12;margin-bottom:.5rem}
.nr-root .nr-doc .nr-doc-lede{font-size:1.02rem;color:var(--nr-mut);margin-bottom:2.5rem;max-width:56ch}
.nr-root .nr-doc h2{font-family:var(--nr-cond);font-weight:750;font-size:1.05rem;letter-spacing:.01em;
  margin:2.5rem 0 .85rem}
.nr-root .nr-doc h2:first-of-type{margin-top:0}
.nr-root .nr-doc p{font-size:.94rem;line-height:1.65;margin-bottom:1rem;max-width:64ch}
.nr-root .nr-doc ul{margin:0 0 1rem;padding:0}
.nr-root .nr-doc ul li{padding-left:1.1rem;position:relative;font-size:.94rem;line-height:1.6;
  margin-bottom:.5rem;max-width:60ch}
.nr-root .nr-doc ul li::before{content:"";position:absolute;left:0;top:.6em;width:5px;height:5px;
  border-radius:50%;background:var(--nr-acc)}
.nr-root .nr-doc table{border-collapse:collapse;width:100%;max-width:64ch;margin:0 0 1.5rem;font-size:.86rem}
.nr-root .nr-doc th,
.nr-root .nr-doc td{text-align:left;padding:.5rem .75rem .5rem 0;border-bottom:1px solid var(--nr-hair)}
.nr-root .nr-doc th{color:var(--nr-mut);font-weight:650;font-size:.72rem;letter-spacing:.06em;
  text-transform:uppercase}
.nr-root .nr-doc .nr-doc-note{font-size:.82rem;color:var(--nr-mut);border-left:2px solid var(--nr-rule);
  padding-left:.9rem;margin:1.25rem 0;max-width:60ch}
.nr-root .nr-doc .nr-doc-placeholder{border:1px dashed var(--nr-rule);border-radius:10px;
  padding:1rem 1.1rem;font-size:.88rem;color:var(--nr-mut);max-width:60ch}
/* ==========================================================================
   responsive
   ========================================================================== */
@media (max-width:1080px){
  .nr-root .nr-cards{grid-template-columns:repeat(3,minmax(0,1fr))}
  .nr-root .nr-rare-grid{grid-template-columns:minmax(0,.85fr) minmax(0,1.6fr);row-gap:2.25rem}
  .nr-root .nr-legend{grid-column:1 / -1;max-width:320px;margin-inline:auto;width:100%}
}
@media (max-width:900px){
  .nr-root .nr-hero-grid{grid-template-columns:1fr;gap:2.5rem}
  .nr-root .nr-hero-map{order:2}
  .nr-root .nr-map-cap{text-align:left}
  .nr-root .nr-trans-grid{grid-template-columns:1fr;gap:2.25rem}
  .nr-root .nr-trans-copy p{max-width:none}
  .nr-root .nr-how{grid-template-columns:repeat(2,minmax(0,1fr));row-gap:2.5rem}
  .nr-root .nr-foot-grid{grid-template-columns:1fr}
  .nr-root .nr-foot-right{justify-content:flex-start;flex-direction:row-reverse}
  .nr-root .nr-foot-right p{text-align:left}
}
@media (max-width:760px){
  .nr-root .nr-cards{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:620px){
  .nr-root .nr-trans-pair{grid-template-columns:1fr;gap:0}
  .nr-root .nr-arrow{justify-self:center;padding:.7rem 0}
  .nr-root .nr-arrow svg{transform:rotate(90deg);width:26px}
  .nr-root .nr-rare-grid{grid-template-columns:1fr}
  .nr-root .nr-rare-states{max-width:300px;margin-inline:auto;width:100%}
}
@media (max-width:520px){
  .nr-root .nr-cards{grid-template-columns:1fr}
  .nr-root .nr-how{grid-template-columns:1fr;row-gap:2rem}
  .nr-root .nr-how-t{max-width:none}
  .nr-root .nr-coverage{padding:.35rem .7rem .35rem .6rem}
}
@media (prefers-reduced-motion:reduce){
  .nr-root *{transition:none!important;animation:none!important}
}
@media print{
  .nr-root .nr-hero-bg{display:none}
  .nr-root .nr-band{padding:1.5rem 0}
}

/* ══════════════════════════════════════════════════════════════════════
   END HERO
   ═══════════════════════════════════════════════════════════════════ */

/* ── top bar ──────────────────────────────────────────────────────── */

.topbar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  height: var(--topbar-h);
  padding: 0 12px;
  padding-left: max(12px, env(safe-area-inset-left));
  padding-right: max(12px, env(safe-area-inset-right));
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  z-index: 1200;
}

.brand {
  display: flex; align-items: center; gap: 10px; min-width: 0;
  color: inherit; text-decoration: none;
}
.brand:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.mark {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  display: block;
}
.mark svg { display: block; width: 100%; height: 100%; }

.brandtext { min-width: 0; }

.brandtext .bn {
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -.01em;
  line-height: 1.15;
  white-space: nowrap;
}

.brandtext p {
  font-size: 11.5px;
  color: var(--ink-3);
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.panel-toggle {
  flex: 0 0 auto;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--accent-ink);
  background: var(--accent);
  border: 0;
  border-radius: 999px;
  padding: 8px 14px;
  cursor: pointer;
  min-height: 36px;
}
.panel-toggle:active { transform: translateY(1px); }

/* ── layout ───────────────────────────────────────────────────────── */

/* One viewport tall, and the anchor target for the hero's "Open the map"
   button. dvh first-class where supported so the mobile URL bar does not
   crop the footer; vh is the fallback. */
.app {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
}

.layout {
  flex: 1 1 auto;
  position: relative;
  display: flex;
  min-height: 0;
}

.mapwrap {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
}

#map {
  position: absolute;
  inset: 0;
  background: var(--surface-2);
}

/* Leaflet chrome, themed */
.leaflet-container { font: inherit; background: var(--surface-2); }
.leaflet-control-attribution {
  background: rgba(255, 255, 255, .82) !important;
  color: #333 !important;
  font-size: 10.5px !important;
}
/* Hue-only re-pitch of #2d2a55 (h244, indigo) to the brand navy family;
   saturation and lightness carried across unchanged, so the 10.7:1 it
   scores on the attribution bar's rgba(255,255,255,.82) is unchanged. */
.leaflet-control-attribution a { color: #2a3a55 !important; }
.leaflet-bar a {
  background: var(--surface) !important;
  color: var(--ink) !important;
  border-bottom-color: var(--line) !important;
}
.leaflet-bar a:hover { background: var(--surface-2) !important; }

.loading {
  position: absolute;
  left: 50%;
  top: 16px;
  transform: translateX(-50%);
  z-index: 900;
  background: var(--surface);
  color: var(--ink-2);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 7px 15px;
  font-size: 13px;
  box-shadow: var(--shadow);
}
.loading[hidden] { display: none; }

/* ── legend (over the map) ────────────────────────────────────────── */

.legend {
  position: absolute;
  left: 10px;
  bottom: 10px;
  z-index: 800;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: var(--shadow);
  padding: 8px 10px;
  font-size: 11.5px;
  color: var(--ink-2);
  max-width: 58vw;
}
.legend h3 {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--ink-3);
  margin-bottom: 5px;
}
.legend .row { display: flex; align-items: center; gap: 7px; margin-top: 3px; }
.legend .swatch { flex: 0 0 auto; width: 22px; border-radius: 2px; }
.legend .lbl { color: var(--ink); font-weight: 600; }

/* ── panel ────────────────────────────────────────────────────────── */

.panel {
  flex: 0 0 auto;
  background: var(--surface);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

/* mobile: full-bleed sheet over the map, toggled from the top bar */
@media (max-width: 799px) {
  .panel {
    position: absolute;
    inset: 0;
    /* ONE STACKING CONTEXT HOLDS ALL OF THIS, SO THE NUMBER IS THE WHOLE
       ARGUMENT. #mapwrap and #map are positioned but z-index:auto, so they
       establish no context of their own: the panel, Leaflet's control panes,
       the planner sheet, the toast and the roll chart are all siblings in
       main.layout's context and compete on z-index alone.

       At 1000 the panel TIED with .leaflet-top and .leaflet-bottom and lost
       on document order, because the map markup follows the panel. The zoom
       control landed on the panel's own heading and the attribution line ran
       across its band chips, in both themes, at every width the sheet exists
       at (swarm-ops #98). The planner sheet at 1100 covered its bottom 152px
       for the same reason.

       1150 puts the open panel above the entire map layer — tiles, controls,
       legend, the planner sheet — and below the two things that must still
       win: the toast at 1200, which is a one-line notice and cannot eat a tap
       (pointer-events:none), and the roll chart at 1300, which is its own
       full-screen takeover. Anything added to this context later belongs
       either side of that line, not on it.

       The panel is display:none until .open, so this changes nothing about
       the closed state or about desktop, where the panel is a column in the
       flow rather than a sheet. */
    z-index: 1150;
    display: none;
    padding-bottom: max(16px, env(safe-area-inset-bottom));
  }
  .panel.open { display: block; }
}

/* desktop: permanent left column */
@media (min-width: 800px) {
  .panel {
    width: 380px;
    border-right: 1px solid var(--line);
  }
  .panel-toggle { display: none; }
}

.view { padding: 14px 14px 20px; }

.block { margin-bottom: 22px; }
.block:last-child { margin-bottom: 0; }

.block h2 {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--ink-3);
  margin-bottom: 8px;
}

.hint { font-size: 12.5px; color: var(--ink-3); margin-bottom: 10px; }

.fine {
  font-size: 12.5px;
  color: var(--ink-2);
  line-height: 1.55;
  margin-bottom: 9px;
}
.fine:last-child { margin-bottom: 0; }

/* The ⓘ affordance into /methodology.html (swarm-ops #103) — a plain
 * inline link, not a new panel element, so it reads as part of the
 * existing "About the numbers" copy rather than a new control. */
.info-link {
  color: var(--accent);
  text-decoration: none;
  font-weight: 600;
  white-space: nowrap;
}
.info-link:hover, .info-link:focus-visible { text-decoration: underline; }

/* ── state chips ──────────────────────────────────────────────────────
   The state picker. Wraps rather than scrolls horizontally: twelve short
   chips fit in three rows on a 360px phone, and a horizontal scroller
   would hide the last states behind an edge with no affordance.

   A chip carries three states at once — pressed (this state is what the
   map and the leaderboard are showing), ready (its data is downloaded, so
   it works offline from here on), and loading. Ready is a left rule rather
   than a colour change so it reads without relying on hue, and the
   pressed state is carried by BOTH fill and weight, never by colour
   alone.                                                                */

.chips { display: flex; flex-wrap: wrap; gap: 6px; }

.chip {
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--ink-2);
  background: var(--surface-2);
  border: 1.5px solid transparent;
  border-radius: 8px;
  padding: 8px 10px;
  min-height: 36px;
  cursor: pointer;
  position: relative;
}
.chip[aria-pressed="true"] {
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 700;
}
.chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Downloaded and therefore available offline. */
.chip.ready::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 50%;
  transform: translateY(-50%);
  width: 2px;
  height: 12px;
  border-radius: 1px;
  background: currentColor;
  opacity: .5;
}
.chip.ready { padding-left: 12px; }

.chip.loading { opacity: .55; }

/* ── band filter chips ────────────────────────────────────────────── */

.bands { display: grid; gap: 7px; }

.band {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  font: inherit;
  color: var(--ink);
  background: var(--surface-2);
  border: 1.5px solid transparent;
  border-radius: 10px;
  padding: 9px 11px;
  cursor: pointer;
  min-height: 48px;
}
.band[aria-pressed="false"] { opacity: .42; }
.band:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.band .dot {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  border-radius: 4px;
}
.band .txt { min-width: 0; flex: 1 1 auto; }
.band .nm { font-weight: 700; font-size: 13.5px; display: block; }
.band .sub { font-size: 11.5px; color: var(--ink-2); display: block; }
.band .ct { font-size: 11px; color: var(--ink-3); font-variant-numeric: tabular-nums; }

/* ── leaderboard ──────────────────────────────────────────────────── */

.leaderboard { display: grid; gap: 7px; }

/* State heading inside the all-states leaderboard. Only rendered when no
   single state is selected — with one state chosen the block's own <h2>
   already names it and repeating it would be noise. */
.lb-state {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--ink-3);
  padding: 8px 2px 1px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 1px;
}
.lb-state:first-child { padding-top: 0; }

.card.loading { opacity: .55; }

.card {
  display: grid;
  grid-template-columns: 26px 1fr auto;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  font: inherit;
  color: var(--ink);
  background: var(--surface-2);
  border: 1.5px solid transparent;
  border-left-width: 4px;
  border-radius: 10px;
  padding: 9px 11px;
  cursor: pointer;
  min-height: 52px;
}
.card:hover { border-color: var(--line); }
.card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.card .rank {
  font-size: 12px;
  font-weight: 700;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.card .nm { font-weight: 700; font-size: 14px; }
.card .meta { font-size: 11.5px; color: var(--ink-2); }

/* Truck-share tag. Only rendered above TRUCK_TAG_PCT (app.js), so it is
   an exception marker, not a per-card stat. Neutral outline on purpose:
   a filled or tinted chip would read as a warning badge, and truck mix
   is an experience note here, never a hazard rating. No red/green. */
.card .tk {
  display: inline-block;
  margin-left: 6px;
  padding: 0 5px;
  border: 1px solid var(--line);
  border-radius: 5px;
  font-size: 10.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: var(--ink-2);
}
/* Felt-time-first (swarm-ops #103): the sentence a rider reads leads, the
 * measured AADT citation trails it at roughly half the visual weight — same
 * inversion as the detail view's .felt block below. */
.card .val { text-align: right; max-width: 108px; }
.card .val .val-felt {
  display: block;
  font-family: var(--serif);
  font-size: 13px;
  line-height: 1.25;
  font-weight: 600;
}
.card .val .val-aadt { font-size: 10px; color: var(--ink-3); }

/* ── detail ───────────────────────────────────────────────────────── */

.back {
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--accent);
  background: none;
  border: 0;
  padding: 4px 0 12px;
  cursor: pointer;
}

.detail-head { margin-bottom: 14px; }
.detail-head .nm {
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -.015em;
  line-height: 1.15;
}
.detail-head .where { font-size: 13px; color: var(--ink-2); margin-top: 2px; }

.pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 10px;
  padding: 5px 11px 5px 8px;
  border-radius: 999px;
  font-size: 12.5px;
  font-weight: 700;
  /* text colour is set inline per-band in app.js (band.ink) — the ramp
     spans dark-navy-on-white to pale-blue-on-white and a single fixed
     text colour cannot hit 4.5:1 against both ends, so the darker two
     bands get white text and the paler two get dark ink text. */
}
.pill .dot { width: 9px; height: 9px; border-radius: 50%; background: currentColor; opacity: .85; }

.felt {
  background: var(--surface-2);
  border-radius: 12px;
  padding: 14px;
  margin-bottom: 14px;
}
/* Felt-time-first (swarm-ops #103): the headline is the sentence a rider
 * asks for, set in the same serif display stack the hero uses for its own
 * felt-time line (--nr-tbox-felt / --nr-card-felt); the measured AADT count
 * becomes the citation underneath it, at about half the visual weight. */
.felt .big {
  font-family: var(--serif);
  font-size: 25px;
  font-weight: 600;
  letter-spacing: -.01em;
  line-height: 1.15;
}
.felt .cap { font-size: 12.5px; color: var(--ink-2); margin-top: 4px; }

.facts { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--line);
         border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
.facts div { background: var(--surface); padding: 10px 12px; }
.facts dt { font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em;
            color: var(--ink-3); margin-bottom: 2px; }
.facts dd { margin: 0; font-size: 14px; font-weight: 600; font-variant-numeric: tabular-nums; }

.src { font-size: 11.5px; color: var(--ink-3); margin-top: 12px; line-height: 1.5; }

/* ── the scenic line ──────────────────────────────────────────────
   Three attributed facts on one line, weighted like a citation rather than
   a verdict: the same surface as the felt-time block, no accent fill, no
   badge, no colour that could read as a grade. A road with nothing to cite
   shows no block at all. */
.scenic {
  background: var(--surface-2);
  border-radius: 12px;
  padding: 12px 14px;
  margin-bottom: 14px;
}
.scenic .sc-line {
  font-size: 14px;
  font-weight: 700;
  line-height: 1.4;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.scenic .sc-name {
  display: block;
  font-size: 12px;
  color: var(--ink-2);
  margin-top: 2px;
}
.scenic .cap { font-size: 12.5px; color: var(--ink-2); margin-top: 5px; line-height: 1.45; }
.scenic .sc-src {
  font-size: 11px;
  color: var(--ink-3);
  margin-top: 8px;
  line-height: 1.45;
}

/* ── when to ride ─────────────────────────────────────────────────
   The same surface as the felt-time block and the scenic line, deliberately:
   this is a third attributed fact about the road, not a verdict and not a
   feature with its own brand. No accent fill, no badge, no colour that could
   read as a grade — and in particular no red-to-green, because "quieter" is
   an experience and a hue would turn it into advice.

   Hidden entirely at :empty. A road whose state has no curve, or a rider with
   no signal and nothing cached, sees no block — never "no data", never a
   zero, never a dash. */
.det-tod:empty { display: none; }

.tod {
  background: var(--surface-2);
  border-radius: 12px;
  padding: 12px 14px;
  margin-bottom: 14px;
}
.tod-h {
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--ink-3);
  margin-bottom: 6px;
}
.tod-lead {
  font-size: 14px;
  line-height: 1.4;
  color: var(--ink);
}
.tod-lead b { font-weight: 700; }

/* Three rows, label left and felt time right. Two columns rather than a
   sentence each, because the whole point is that they are compared. */
.tod-rows { margin: 9px 0 0; }
.tod-rows > div {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  padding: 4px 0;
  border-top: 1px solid var(--line);
}
.tod-rows dt {
  font-size: 12.5px;
  color: var(--ink-2);
  white-space: nowrap;
}
.tod-rows dd {
  margin: 0;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ink);
  text-align: right;
}
.tod-cap {
  font-size: 12.5px;
  color: var(--ink-2);
  margin-top: 8px;
  line-height: 1.45;
}
.tod-src {
  font-size: 11px;
  color: var(--ink-3);
  margin-top: 7px;
  line-height: 1.45;
}

.rc-tod:empty { display: none; }

/* ── footer ───────────────────────────────────────────────────────── */

.citation {
  flex: 0 0 auto;
  background: var(--surface);
  border-top: 1px solid var(--line);
  padding: 8px 12px;
  padding-bottom: max(8px, env(safe-area-inset-bottom));
  font-size: 11px;
  line-height: 1.45;
  color: var(--ink-3);
  text-align: center;
}
.citation .fine { font-size: 11px; color: var(--ink-3); margin: 2px 0 0; }

/* Compact footer info row (swarm-ops #103): the three static pages plus
 * the data vintage, in one line, same size as the citation it sits under. */
.citation-links {
  display: flex;
  justify-content: center;
  gap: 12px;
  flex-wrap: wrap;
}
.citation-links a { color: var(--accent); text-decoration: none; }
.citation-links a:hover, .citation-links a:focus-visible { text-decoration: underline; }

@media (max-width: 380px) {
  .brandtext p { display: none; }
  .legend { max-width: 50vw; font-size: 11px; }
}

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

/* Dark-end legibility: the deepest band colour (Solitude) is dark navy by
   design (see band ramp note up top), which pulls its contrast against
   the dark-mode panel surfaces low even after pulling the ramp's floor
   inward. A faint fixed ring on the small band-colour shapes (filter dot,
   legend swatch) guarantees they stay perceptible against ANY band
   colour, independent of the ramp maths — belt-and-suspenders, not a
   substitute for the ramp adjustment itself. */
@media (prefers-color-scheme: dark) {
  .band .dot,
  .legend .swatch {
    box-shadow: 0 0 0 1px rgba(255, 255, 255, .32);
  }

  /* Same treatment, extended to the leaderboard card's 4px band stripe.
     Unifying the dark surfaces onto the band ramp's own hue costs the
     darkest bands a little separation from the card they sit on
     (Solitude 1.76 -> 1.61 against --surface-2), because stripe and
     surface now share a hue. That is the price of a single-hue system
     and the ramp is not being distorted to hide it; instead the stripe
     gets the defined edge the dots and swatches already have. `inset`
     paints at the padding edge, i.e. exactly the seam between the
     border stripe and the card surface. */
  .card {
    box-shadow: inset 1px 0 0 0 rgba(255, 255, 255, .32);
  }
}

/* ══════════════════════════════════════════════════════════════════════
   ROUTE PLANNER
   ══════════════════════════════════════════════════════════════════════

   The card sits OVER the map, not in the panel. On a phone the panel is a
   full-bleed sheet, so a slider living in it would cover the line it is
   moving — and watching the line move is the entire product of that
   control. Desktop puts it top-right, away from the panel (left), the
   zoom control (top-left), the legend (bottom-left) and the Esri
   attribution (bottom-right). Mobile docks it to the bottom edge as a
   sheet and app.js publishes its measured height as --route-h, which the
   rule below uses to lift Leaflet's bottom controls clear rather than
   hiding the attribution.

   Colours are the existing tokens only. NOTHING here introduces red or
   green: the route reads as one deep-navy line against the band ramp's
   mid and pale blues, and every quantity is stated as a number rather
   than coded by hue.                                                    */

.routebtn {
  font: inherit;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--accent-ink);
  background: var(--accent);
  border: 0;
  border-radius: 10px;
  padding: 11px 16px;
  min-height: 44px;
  width: 100%;
  cursor: pointer;
}
.routebtn:active { transform: translateY(1px); }
.routebtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* THREE DETENTS, AND WHY THE PEEK CARRIES THE ANSWER.
   The v2 card was one 46vh height with the controls at the top and the
   answer below its own scroll fold, so on a 800px phone it covered
   y=347-715 and left 291px of tappable map. A first-timer's second tap
   landed on the card and did nothing, silently. So: peek (152px) until a
   route solves, half (46vh) once one does, full (88vh) on demand — and
   #rc-peek sits above the scroller at every one of them.

   The card is a flex COLUMN: handle, peek, scrolling body, action footer.
   That footer used to be position:sticky inside the scroller, which sliced
   the content passing under it (the slider thumb at 360px, a stats row on
   desktop). A real flex footer cannot clip anything. */
.routecard {
  --rc-inset: env(safe-area-inset-bottom);
  position: absolute;
  z-index: 1100;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: var(--shadow);
  overflow: hidden;
  /* 14px against a 16px landing page: the planner used to read at 13px and
     12.5px, which made it look like a different product bolted on. */
  font-size: 14px;
  line-height: 1.45;
  color: var(--ink);
}
.routecard[hidden] { display: none; }

.rc-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  padding: 0 13px 14px;
}
/* Scroll affordance where the body meets the footer, so a body that
   continues under the bar says so. */
.rc-body::after {
  content: "";
  display: block;
  height: 2px;
}

/* mobile: bottom sheet, map stays visible above it */
@media (max-width: 799px) {
  .routecard {
    left: 0; right: 0; bottom: 0;
    border-radius: 14px 14px 0 0;
    border-left: 0; border-right: 0; border-bottom: 0;
    height: calc(152px + var(--rc-inset, 0px));
    padding-bottom: var(--rc-inset, 0px);
    transition: height 180ms cubic-bezier(.32, .72, 0, 1);
  }
  .routecard.is-half { height: calc(46vh + var(--rc-inset, 0px)); }
  .routecard.is-full { height: calc(88vh + var(--rc-inset, 0px)); }
  /* At peek the sheet is the handle, the line and one button. Everything
     else is a scroll away and the map has the screen. */
  .routecard.is-peek .rc-body,
  .routecard.is-peek .rc-actions { display: none; }
  /* A finger on the handle beats any animation. */
  .routecard.is-dragging { transition: none; }

  /* env(safe-area-inset-*) is the iOS notch/home-indicator API. It is the
     one permitted use of that word anywhere in this project. */

  /* EVERY PIXEL OF MAP IS THE POINT WHILE THE PLANNER IS OPEN.
     .app is a 100dvh column — bar, map, citation footer — so on a 800px
     phone the map region is only 659px before the sheet takes any of it.
     The footer's 85px are a page-level DUPLICATE of the attribution that
     Leaflet's own control carries inside the map, and that control stays
     on screen throughout (it is lifted clear of the sheet by --route-h
     immediately below). So the duplicate stands down while the planner is
     open on a phone, and the map gets the 85px. It comes straight back on
     close. Desktop is untouched: it has the room. */
  .app.planning .citation { display: none; }

  .mapwrap.routing .leaflet-bottom { bottom: var(--route-h, 0px); }

  /* THE LEGEND SITS ABOVE THE ATTRIBUTION LINE, MEASURED, NOT GUESSED.
     Both are bottom-left on a phone, and the attribution is the one thing
     on this page that is REQUIRED to stay legible — "Fuel & lodging ©
     OpenStreetMap contributors, ODbL" pushed it to two wrapped lines and
     they landed on top of the legend's last row. app.js publishes the
     control's measured height as --attrib-h whenever it changes, exactly
     as it publishes --route-h for the card, because the line wraps to a
     different number of lines depending on which layers are on. */
  .legend { bottom: calc(var(--attrib-h, 0px) + 14px); }
  /* …and while the planner is open the attribution has itself been lifted
     clear of the sheet, so the legend has to clear both. This never showed
     before because the 46vh card covered the legend entirely; a 152px peek
     does not, and the attribution landed on top of it. */
  .mapwrap.routing .legend {
    bottom: calc(var(--route-h, 0px) + var(--attrib-h, 0px) + 14px);
  }
}

/* THE LEGEND MUST NOT EAT A TAP. It is aria-hidden, has no controls in it,
   and while the planner is open it sits over ~180×160 of live map in the
   bottom-left corner — which is a dead zone for the one gesture the whole
   surface is for. It never showed as one before because the old 46vh card
   covered the legend entirely. Taps pass straight through it to the road
   underneath; the legend still reads exactly the same. */
.mapwrap.routing .legend { pointer-events: none; }

/* desktop: top-right column, unchanged in shape — no detents, no handle */
@media (min-width: 800px) {
  .routecard {
    top: 10px; right: 10px;
    width: 344px;
    max-height: calc(100% - 20px);
  }
  /* Two-class selectors: the .rc-grab / .rc-peek blocks below are later in
     source order, so a single-class override here would lose to them. */
  .routecard .rc-grab { display: none; }
  .routecard .rc-peek { padding-top: 12px; }
}

/* ── the grab handle ───────────────────────────────────────────────── */

.rc-grab {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 26px;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: grab;
  touch-action: none;
  /* 44px of hit area around a 4px bar, without a 44px gap in the layout */
  position: relative;
}
.rc-grab::before {
  content: "";
  position: absolute;
  left: 0; right: 0; top: -9px; bottom: -9px;
}
.rc-grab:active { cursor: grabbing; }
.rc-grab-bar {
  width: 36px; height: 4px;
  border-radius: 2px;
  background: var(--line);
}
.rc-grab:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; }

/* ── the peek line: what to do, or what happened ───────────────────── */

.rc-peek {
  flex: 0 0 auto;
  padding: 0 56px 12px 13px;   /* room for the pinned close button */
  touch-action: none;
}
.rc-peekline {
  font-size: 15px;
  line-height: 1.35;
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  margin: 0;
}
.rc-peekline b { font-weight: 700; }
.rc-peekline .sub {
  display: block;
  margin-top: 2px;
  font-size: 12.5px;
  font-weight: 400;
  color: var(--ink-2);
}
.rc-peekbtn {
  font: inherit;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--accent-ink);
  background: var(--accent);
  border: 0;
  border-radius: 10px;
  padding: 11px 16px;
  min-height: 44px;
  width: 100%;
  margin-top: 9px;
  cursor: pointer;
  touch-action: manipulation;
}
.rc-peekbtn[hidden] { display: none; }
.rc-peekbtn:active { transform: translateY(1px); }
.rc-peekbtn:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* ── the summary block, above the slider ───────────────────────────── */

.rc-summary:empty { display: none; }
.rc-summary { margin-bottom: 4px; }

.rc-head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.rc-head h2 {
  flex: 1 1 auto;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--ink-3);
}
/* 44×44, pinned to the card so it survives the peek detent. Was 32×32
   inside the scrolling head. */
.rc-close {
  position: absolute;
  top: 4px; right: 5px;
  z-index: 3;
  font: inherit;
  font-size: 21px;
  line-height: 1;
  color: var(--ink-2);
  background: var(--surface-2);
  border: 0;
  border-radius: 10px;
  width: 44px; height: 44px;
  cursor: pointer;
  touch-action: manipulation;
}
.rc-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.rc-modes { display: flex; gap: 6px; }
.rc-modes button {
  flex: 1 1 0;
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-2);
  background: var(--surface-2);
  border: 1.5px solid transparent;
  border-radius: 9px;
  padding: 9px 8px;
  min-height: 40px;
  cursor: pointer;
}
.rc-modes button[aria-pressed="true"] {
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 700;
}
.rc-modes button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.rc-loopwrap {
  display: flex; align-items: center; gap: 7px;
  margin-top: 9px;
  font-size: 12.5px;
  color: var(--ink-2);
}
.rc-loopwrap[hidden] { display: none; }
.rc-loopwrap input {
  font: inherit;
  font-size: 13px;
  width: 76px;
  min-height: 38px;
  padding: 6px 8px;
  color: var(--ink);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 8px;
}

/* The slider. Touch targets: a 28px-high track region and a 26px thumb,
   both above the 24px minimum, so it is draggable with a thumb on a
   360px phone without zooming. */
.rc-slider { margin: 13px 0 4px; }
.rc-slider input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  /* 44px of grabbable height around a 6px track and a 26px thumb. The
     thumb's negative margin is relative to the TRACK, not the input, so
     raising the input height leaves the visual rhythm alone. */
  height: 44px;
  background: transparent;
  cursor: pointer;
  display: block;
  margin: 0;
}
.rc-slider input[type="range"]::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--band-steady), var(--band-solitude));
}
.rc-slider input[type="range"]::-moz-range-track {
  height: 6px;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--band-steady), var(--band-solitude));
}
.rc-slider input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 26px; height: 26px;
  margin-top: -10px;
  border-radius: 50%;
  background: var(--surface);
  border: 3px solid var(--accent);
  box-shadow: var(--shadow);
}
.rc-slider input[type="range"]::-moz-range-thumb {
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--surface);
  border: 3px solid var(--accent);
  box-shadow: var(--shadow);
}
.rc-slider input[type="range"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.rc-ends {
  display: flex; justify-content: space-between;
  font-size: 11.5px;
  color: var(--ink-3);
  font-weight: 600;
}

/* ── the scenic control ──────────────────────────────────────────
   Deliberately quieter than the slider above it: a checkbox and a sentence,
   not a second dial. The two axes are not equally measured and the control
   should not pretend otherwise. */
.rc-scenic { margin: 10px 0 2px; }
/* The label wraps the input, so the whole 44px row is the target — the box
   itself was 18×18, well under the floor every major map app holds. */
.rc-check { min-height: 44px; }
.rc-check {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
}
.rc-check input[type="checkbox"] {
  width: 18px; height: 18px;
  accent-color: var(--accent);
  cursor: pointer;
  flex: 0 0 auto;
}
.rc-check input[type="checkbox"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.rc-scenic-note {
  margin: 4px 0 0 27px;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--ink-3);
}
.rc-scenic-out {
  margin-top: 9px;
  background: var(--surface-2);
  border-radius: 9px;
  padding: 9px 11px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-2);
}
.rc-scenic-out b { color: var(--ink); font-weight: 700; }

.rc-out { margin-top: 11px; }

.rc-msg {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 10px 11px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-2);
}
.rc-msg b { color: var(--ink); font-weight: 700; }

/* The headline: what this slider position bought and what it cost. Stated
   as two plain numbers with their signs, never as a colour. */
.rc-verdict {
  font-size: 13.5px;
  line-height: 1.45;
  color: var(--ink);
  background: var(--surface-2);
  border-radius: 9px;
  padding: 9px 11px;
  margin-bottom: 9px;
}
.rc-verdict b { font-weight: 700; font-variant-numeric: tabular-nums; }
.rc-verdict .sub {
  display: block;
  margin-top: 3px;
  font-size: 11.5px;
  color: var(--ink-3);
}

.rc-cmp { width: 100%; border-collapse: collapse; margin-bottom: 9px; }
.rc-cmp th, .rc-cmp td {
  font-size: 12px;
  padding: 4px 0;
  text-align: right;
  font-variant-numeric: tabular-nums;
  border-bottom: 1px solid var(--line);
}
.rc-cmp th { color: var(--ink-3); font-weight: 600; }
.rc-cmp th:first-child, .rc-cmp td:first-child { text-align: left; }
.rc-cmp td:first-child { color: var(--ink-2); }
.rc-cmp tr.now td { color: var(--ink); font-weight: 700; }

.rc-stat {
  display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
  padding: 6px 0;
  border-bottom: 1px solid var(--line);
  font-size: 13px;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.rc-stat b { color: var(--ink); font-variant-numeric: tabular-nums; }
/* The headline four, above the slider: the same row, one step louder, so
   the figures a rider asks for first are not the same weight as the
   twelfth fact down the card. */
.rc-summary .rc-stat { font-size: 13.5px; }
.rc-summary .rc-stat b { font-size: 15px; font-weight: 700; }
.rc-summary .rc-stat span { color: var(--ink-3); }

/* What the route is riding on, by band. A stacked rule rather than a pie:
   it is one dimension (miles) and it reads at 360px. */
.rc-mix { display: flex; height: 8px; border-radius: 4px; overflow: hidden; margin: 10px 0 6px; }
.rc-mix span { display: block; height: 100%; }
.rc-mixkey { display: flex; flex-wrap: wrap; gap: 3px 11px; font-size: 11px; color: var(--ink-3); }
.rc-mixkey span { display: flex; align-items: center; gap: 5px; }
.rc-mixkey i { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 auto; }

.rc-steps { list-style: none; margin: 8px 0 0; padding: 0; }
.rc-steps li {
  display: flex; align-items: baseline; gap: 8px;
  padding: 4px 0;
  border-bottom: 1px solid var(--line);
  font-size: 12.5px;
}
.rc-steps .sw { flex: 0 0 auto; width: 4px; align-self: stretch; border-radius: 2px; }
.rc-steps .nm { flex: 1 1 auto; min-width: 0; color: var(--ink); }
.rc-steps .mi { flex: 0 0 auto; color: var(--ink-3); font-variant-numeric: tabular-nums; }

.rc-more { margin-top: 8px; }
.rc-more summary {
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-2);
  cursor: pointer;
  min-height: 32px;
  display: flex;
  align-items: center;
}
.rc-more summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.rc-note {
  margin-top: 11px;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--ink-3);
}

/* The A and B pins. Deliberately NOT band-ramp colours — they are markers,
   not measurements. The wrapper is 30px so the armed ring has room to sit
   outside the pin without the icon being clipped. */
.rc-pin {
  width: 22px; height: 22px;
  margin: 4px;
  border-radius: 50%;
  background: #16233C;
  border: 3px solid #ffffff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, .4);
  color: #ffffff;
  font: 700 11px/16px var(--nr-sans, system-ui);
  text-align: center;
}
/* The end the next tap will move. A ring, not a colour: the pins are
   already the darkest thing on the map and there is no second hue to
   spend here. */
.rc-pin.is-armed {
  box-shadow: 0 0 0 3px rgba(22, 35, 60, .35), 0 1px 4px rgba(0, 0, 0, .4);
  animation: nr-armed 1.6s ease-in-out infinite;
}
@keyframes nr-armed {
  0%, 100% { box-shadow: 0 0 0 3px rgba(22, 35, 60, .35), 0 1px 4px rgba(0, 0, 0, .4); }
  50%      { box-shadow: 0 0 0 7px rgba(22, 35, 60, .12), 0 1px 4px rgba(0, 0, 0, .4); }
}
@media (prefers-reduced-motion: reduce) {
  .rc-pin.is-armed { animation: none; }
}
@media (prefers-color-scheme: dark) {
  .rc-pin { border-color: #EDE6D8; color: #F2EDE3; }
  .rc-pin.is-armed {
    box-shadow: 0 0 0 3px rgba(237, 230, 216, .45), 0 1px 4px rgba(0, 0, 0, .5);
  }
  @keyframes nr-armed {
    0%, 100% { box-shadow: 0 0 0 3px rgba(237, 230, 216, .45), 0 1px 4px rgba(0, 0, 0, .5); }
    50%      { box-shadow: 0 0 0 7px rgba(237, 230, 216, .16), 0 1px 4px rgba(0, 0, 0, .5); }
  }
}

/* ── the snap, made visible ────────────────────────────────────────────
   The point is dropped where the ROAD is, not where the finger was. The
   v2 build drew the pin at the raw tap and silently reported a road up to
   25 miles away, so two pins sat in open desert claiming "I-80" and
   "NV-233" and a start on a UT-21 vertex came back labelled "UT-159"
   (swarm-ops #99 audit, m10/m14). Now: a hollow ghost marks the tap, the
   lettered pin travels to the snapped vertex, a dashed leader joins them
   when the move is far enough to be worth explaining, and a chip under
   the pin names the road it landed on. */

.rc-ghost {
  width: 12px; height: 12px;
  border-radius: 50%;
  border: 2px solid #16233C;
  background: transparent;
  opacity: .75;
  animation: nr-ghost-out 1.4s ease-out forwards;
}
@keyframes nr-ghost-out {
  0%   { opacity: .75; transform: scale(1); }
  28%  { opacity: .75; transform: scale(1); }
  100% { opacity: 0;   transform: scale(1.5); }
}
@media (prefers-color-scheme: dark) {
  .rc-ghost { border-color: #EDE6D8; }
}
@media (prefers-reduced-motion: reduce) {
  .rc-ghost { animation: none; opacity: .5; }
}

/* The road the pin landed on, under the pin. 11px, surface, hairline —
   the one part of a dropped point a rider can check against a map. */
.rc-pinlabel {
  position: absolute;
  top: 27px; left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  font: 700 11px/1 var(--nr-sans, system-ui);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 3px 5px;
  pointer-events: none;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .18);
}

/* ── the five transitions this page is allowed ─────────────────────────
   Sheet detent (above), pin snap (JS lerp), route line draw-on, stats
   crossfade, peek-line crossfade. Nothing else, nothing over 240ms except
   the 400ms line sweep, and all of it off under prefers-reduced-motion. */

.nr-fade-in { animation: nr-fade-in 160ms ease-out both; }
@keyframes nr-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* One pulse of the road network the first time the planner opens, so that
   "tap a blue road" has a referent on screen. */
.mapwrap.nr-pulse .leaflet-overlay-pane {
  animation: nr-netpulse 900ms ease-in-out;
}
@keyframes nr-netpulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .45; }
}

@media (prefers-reduced-motion: reduce) {
  .routecard { transition: none !important; }
  .routecard.has-route .rc-slider,
  .routecard.has-route .rc-scenic { animation: none; }
  .nr-fade-in { animation: none; }
  .mapwrap.nr-pulse .leaflet-overlay-pane { animation: none; }
}

/* Leaflet's own zoom buttons ship at 30×30. */
.leaflet-control-zoom a {
  width: 44px !important;
  height: 44px !important;
  line-height: 44px !important;
  font-size: 20px !important;
}

@media (max-width: 380px) {
  .routecard { font-size: 13.5px; }
  .rc-body { padding-left: 11px; padding-right: 11px; }
  .rc-peek { padding-left: 11px; }
  .rc-modes button { font-size: 12px; padding: 9px 5px; }
}

/* ══════════════════════════════════════════════════════════════════════
   ROUTE UX v2 — the way in, the roll chart, live position

   Same rules as everything above it: brand navy and cream only, no red,
   no green, nothing coded by hue that is not also stated in words. Touch
   targets are 44px where they are the primary action and never below 36.
   Everything here reads at 360px.
   ═══════════════════════════════════════════════════════════════════ */

/* ── the two front doors ──────────────────────────────────────────── */

.block-route .hint { margin-bottom: 10px; }

.routebtn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
}
.routebtn-gl { width: 20px; height: 20px; flex: 0 0 auto; }

/* The pill over the map. On a phone the panel starts closed, so without
   this the planner has no visible door at all. */
.route-fab {
  position: absolute;
  top: 10px; right: 10px;
  z-index: 900;
  display: flex;
  align-items: center;
  gap: 8px;
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  color: var(--accent-ink);
  background: var(--accent);
  border: 0;
  border-radius: 999px;
  padding: 0 16px 0 13px;
  min-height: 44px;
  box-shadow: var(--shadow);
  cursor: pointer;
}
.route-fab[hidden] { display: none; }
.route-fab svg { width: 19px; height: 19px; flex: 0 0 auto; }
.route-fab:active { transform: translateY(1px); }
.route-fab:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* Shown once, ever. */
.route-coach {
  position: absolute;
  top: 62px; right: 10px;
  z-index: 901;
  max-width: min(290px, calc(100% - 20px));
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 11px;
  box-shadow: var(--shadow);
  padding: 11px 12px 10px;
}
.route-coach[hidden] { display: none; }
.route-coach::before {
  content: "";
  position: absolute;
  top: -7px; right: 26px;
  width: 12px; height: 12px;
  background: var(--surface);
  border-left: 1px solid var(--line);
  border-top: 1px solid var(--line);
  transform: rotate(45deg);
}
.route-coach p {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-2);
  margin-bottom: 8px;
}
.route-coach b { color: var(--ink); }
.route-coach button {
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  color: var(--accent-ink);
  background: var(--accent);
  border: 0;
  border-radius: 8px;
  padding: 7px 13px;
  min-height: 36px;
  cursor: pointer;
}

/* While an end is armed, the map is a thing you are about to tap. */
.mapwrap.arming #map { cursor: crosshair; }

/* ── the two ends ──────────────────────────────────────────────────── */

.rc-points { display: flex; gap: 6px; margin-top: 9px; }
.rc-pt {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  font: inherit;
  text-align: left;
  color: var(--ink-2);
  background: var(--surface-2);
  border: 1.5px solid transparent;
  border-radius: 9px;
  padding: 7px 9px;
  min-height: 46px;
  cursor: pointer;
}
.rc-pt[aria-pressed="true"] { border-color: var(--accent); }
.rc-pt:disabled { opacity: .45; cursor: default; }
.rc-pt:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.rc-pt-badge {
  flex: 0 0 auto;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--line);
  color: var(--ink-3);
  font: 700 11px/22px var(--nr-sans, system-ui);
  text-align: center;
}
.rc-pt.is-set .rc-pt-badge { background: #16233C; color: #fff; }
@media (prefers-color-scheme: dark) {
  .rc-pt.is-set .rc-pt-badge { background: var(--accent); color: var(--accent-ink); }
}
.rc-pt-tx { min-width: 0; display: block; }
.rc-pt-tx b {
  display: block;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--ink-3);
}
.rc-pt-tx > span {
  display: block;
  font-size: 12px;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.dim { color: var(--ink-3); }

.rc-ptbar { display: flex; gap: 6px; margin-top: 6px; }
.rc-ptbar button {
  flex: 1 1 0;
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-2);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 7px 6px;
  min-height: 44px;
  cursor: pointer;
}
.rc-ptbar button:disabled { opacity: .4; cursor: default; }
.rc-ptbar button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── the slider, promoted ──────────────────────────────────────────── */

/* IT DOES NOT EXIST UNTIL THERE IS A LINE FOR IT TO MOVE.
   A live 0-100 dial reading "Halfway" was the visual centre of the empty
   state — a control for a thing not yet created, and the first thing a
   stranger had to decode. It arrives with the route it governs. */
.rc-slider, .rc-scenic, .rc-tradehint { display: none; }
.routecard.has-route .rc-slider,
.routecard.has-route .rc-scenic { display: block; }
.routecard.has-route .rc-tradehint:not([hidden]) { display: block; }
.routecard.has-route .rc-slider,
.routecard.has-route .rc-scenic {
  animation: nr-reveal 200ms ease-out both;
}
@keyframes nr-reveal {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

/* Shown once ever, under the control it explains. */
.rc-tradehint {
  margin: 9px 0 2px;
  padding: 9px 11px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-2);
  background: var(--surface-2);
  border-radius: 9px;
}
.rc-tradehint b { color: var(--ink); font-weight: 700; }

.rc-slider { margin: 14px 0 4px; }
.rc-sliderhead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 7px;
}
.rc-sliderhead span {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--ink-3);
}
.rc-sliderhead b {
  font-size: 14px;
  font-weight: 700;
  color: var(--ink);
}

/* ── what you can do with a solved route ───────────────────────────── */

/* A real footer of the flex column, not a sticky bar inside the scroller.
   The card is a sheet on a phone and a solved route fills it — put these
   in document order and "Roll chart" lands below the fold, which is how
   an earlier build hid its own best feature. But sticky-over-the-body cut
   whatever passed under it: the slider thumb was bisected at 360px and a
   stats row was sliced mid-line on desktop (swarm-ops #99 audit, m10/d02).
   Outside the scrolling body it cannot clip anything, and the 16px scrim
   above it says the body continues. */
.rc-actions {
  flex: 0 0 auto;
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 6px;
  padding: 9px 13px 11px;
  background: var(--surface);
  border-top: 1px solid var(--line);
}
.rc-actions::before {
  content: "";
  position: absolute;
  left: 0; right: 0; top: -17px;
  height: 16px;
  pointer-events: none;
  background: linear-gradient(to top, var(--surface), transparent);
}
.rc-actions[hidden] { display: none; }
.rc-actions button {
  font: inherit;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ink);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 10px 4px;
  min-height: 42px;
  cursor: pointer;
  white-space: nowrap;
}
.rc-actions #rc-roll {
  grid-column: 1 / -1;
  color: var(--accent-ink);
  background: var(--accent);
  border-color: var(--accent);
}
.rc-actions button[aria-pressed="true"] {
  color: var(--accent-ink);
  background: var(--accent);
  border-color: var(--accent);
}
.rc-actions button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── live position, inside the card ────────────────────────────────── */

.rc-live { margin-top: 11px; }
.rc-live[hidden] { display: none; }

.rc-next {
  display: flex;
  align-items: center;
  gap: 11px;
  background: var(--surface-2);
  border-radius: 9px;
  padding: 10px 11px;
}
.rc-next-mi {
  flex: 0 0 auto;
  display: flex;
  align-items: baseline;
  gap: 3px;
  font-variant-numeric: tabular-nums;
}
.rc-next-mi b { font-size: 24px; font-weight: 700; color: var(--ink); line-height: 1; }
.rc-next-mi i { font-style: normal; font-size: 11px; color: var(--ink-3); }
.rc-next-tx {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13px;
  font-weight: 700;
  color: var(--ink);
  line-height: 1.3;
}
.rc-next-tx .sub {
  display: block;
  font-size: 11.5px;
  font-weight: 400;
  color: var(--ink-3);
  margin-top: 2px;
}

.rc-prog {
  height: 5px;
  border-radius: 3px;
  background: var(--line);
  overflow: hidden;
  margin: 7px 0 4px;
}
.rc-prog span { display: block; height: 100%; background: var(--accent); }

.rc-livebar { display: flex; gap: 6px; margin-top: 8px; }
.rc-livebar button {
  flex: 1 1 0;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-2);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 8px 6px;
  min-height: 38px;
  cursor: pointer;
}
.rc-livebar button[aria-pressed="true"] {
  color: var(--accent-ink);
  background: var(--accent);
  border-color: var(--accent);
}
.rc-livebar button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── the position marker ───────────────────────────────────────────── */

.nrt-dot {
  position: relative;
  display: block;
  width: 64px; height: 64px;
}
.nrt-core {
  position: absolute;
  left: 50%; top: 50%;
  width: 16px; height: 16px;
  margin: -8px 0 0 -8px;
  border-radius: 50%;
  background: #16233C;
  border: 3px solid #ffffff;
  box-shadow: 0 1px 5px rgba(0, 0, 0, .45);
}
/* The cone is a direction, not an uncertainty — it is a fixed wedge and it
   never widens to imply a confidence it does not have.

   Geometry: a border triangle with `border-top` points DOWN, so its apex
   sits at the bottom-centre of its 30x30 border box. The negative margins
   put that apex exactly on the dot's centre, and transform-origin 50%/100%
   is that same apex — so rotate(heading) swings the cone around the dot
   rather than around a corner 30px away from it, and heading 0 points
   north. */
.nrt-cone {
  position: absolute;
  left: 50%; top: 50%;
  width: 0; height: 0;
  margin: -30px 0 0 -15px;
  border-left: 15px solid transparent;
  border-right: 15px solid transparent;
  border-top: 30px solid rgba(22, 35, 60, .34);
  transform-origin: 50% 100%;
}
.nrt-dot.is-stale .nrt-core { opacity: .45; }
.nrt-dot.is-stale .nrt-cone { opacity: .35; }

/* NO DARK-MODE OVERRIDE HERE, DELIBERATELY. The dot and its cone sit on the
   BASEMAP, which is the same light topo raster in both themes — the same
   reason ROUTE_CORE / ROUTE_CASING in app.js are one fixed pair rather than
   tokens. A cream dot is the right colour against a dark panel and very
   nearly invisible against pale desert, which is where it would actually be
   read; this was caught in the browser, not reasoned about. Navy core,
   white ring, navy cone, both themes. */

/* ── one-line notice ───────────────────────────────────────────────── */

.nr-toast {
  position: absolute;
  left: 50%;
  bottom: calc(var(--route-h, 0px) + 14px);
  transform: translateX(-50%);
  z-index: 1200;
  max-width: min(420px, calc(100% - 24px));
  background: #16233C;
  color: #F2EDE3;
  border-radius: 10px;
  padding: 10px 14px;
  font-size: 12.5px;
  line-height: 1.45;
  box-shadow: var(--shadow);
  pointer-events: none;
}
.nr-toast[hidden] { display: none; }
@media (prefers-color-scheme: dark) {
  .nr-toast { background: #EDE6D8; color: #16233C; }
}

/* ══════════════════════════════════════════════════════════════════════
   THE ROLL CHART

   Big type, high contrast, one column, mileage-anchored. It is read at
   arm's length on a moving bike or printed and taped down, so nothing in
   here is smaller than 12px and the instruction line is 17.
   ═══════════════════════════════════════════════════════════════════ */

.rollchart {
  position: absolute;
  inset: 0;
  z-index: 1300;
  display: flex;
  flex-direction: column;
  background: var(--surface);
}
.rollchart[hidden] { display: none; }

.rcx-head {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 11px 12px;
  border-bottom: 1px solid var(--line);
}
.rcx-head h2 {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--ink-3);
}
/* Full width under the buttons rather than beside them: at 360px a
   five-clause summary sharing a row with three controls wraps to four
   lines and squeezes them to nothing. */
.rcx-sum {
  flex: 1 1 100%;
  font-size: 12px;
  line-height: 1.45;
  color: var(--ink-2);
  margin-top: 5px;
}
.rcx-sum b { color: var(--ink); font-variant-numeric: tabular-nums; }
.rcx-btn {
  flex: 0 0 auto;
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  color: var(--ink-2);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 0 11px;
  min-height: 38px;
  cursor: pointer;
}
.rcx-close {
  flex: 0 0 auto;
  font: inherit;
  font-size: 20px;
  line-height: 1;
  color: var(--ink-2);
  background: var(--surface-2);
  border: 0;
  border-radius: 8px;
  width: 38px; height: 38px;
  cursor: pointer;
}
.rcx-btn:focus-visible,
.rcx-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.rcx-body {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 0 12px;
}
.rcx-foot {
  flex: 0 0 auto;
  padding: 9px 12px max(11px, env(safe-area-inset-bottom));
  border-top: 1px solid var(--line);
  font-size: 11px;
  line-height: 1.5;
  color: var(--ink-3);
}

.rcx-rows { list-style: none; margin: 0; padding: 0; }
.rcx-row {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  padding: 13px 0;
  border-bottom: 1px solid var(--line);
}
.rcx-mi {
  flex: 0 0 62px;
  display: flex;
  align-items: baseline;
  gap: 3px;
  font-variant-numeric: tabular-nums;
}
.rcx-mi b { font-size: 20px; font-weight: 700; color: var(--ink); line-height: 1.1; }
.rcx-mi i { font-style: normal; font-size: 10.5px; color: var(--ink-3); }
.rcx-gl { flex: 0 0 30px; }
.rcx-arrow { width: 30px; height: 30px; display: block; }
.rcx-tx { flex: 1 1 auto; min-width: 0; }
.rcx-tx > b {
  display: block;
  font-size: 17px;
  font-weight: 700;
  line-height: 1.25;
  color: var(--ink);
}
.rcx-sub {
  display: block;
  margin-top: 3px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--ink-3);
}
.rcx-sub b { color: var(--ink-2); font-variant-numeric: tabular-nums; }

/* Where the rider is, when the position watch is running. A rule down the
   left and a slightly heavier surface — no colour, because the arrow
   glyphs already carry the band ramp. */
.rcx-row.is-now {
  background: var(--surface-2);
  box-shadow: inset 3px 0 0 var(--accent);
  padding-left: 9px;
  margin: 0 -12px;
  padding-right: 12px;
  margin-left: -12px;
}
.rcx-row.is-done .rcx-mi b,
.rcx-row.is-done .rcx-tx > b { opacity: .45; }
.rcx-row.is-end .rcx-tx > b { letter-spacing: .04em; }

@media (min-width: 800px) {
  .rcx-body { padding: 0 max(12px, calc(50% - 330px)); }
  .rcx-head { padding: 13px max(12px, calc(50% - 330px)); }
  .rcx-foot { padding: 11px max(12px, calc(50% - 330px)); }
}

@media (max-width: 380px) {
  .rcx-mi { flex-basis: 54px; }
  .rcx-mi b { font-size: 18px; }
  .rcx-tx > b { font-size: 15.5px; }
  .rcx-gl { flex-basis: 26px; }
  .rcx-arrow { width: 26px; height: 26px; }
}

/* ══════════════════════════════════════════════════════════════════════
   LOCAL SERVICES — fuel, camping, dispersed, lodging

   FOUR SHAPES, ONE COLOUR PAIR. Every marker on the map is brand navy
   (#16233C) and cream (#FBF7EE), and the layers are told apart by shape:
   square = fuel, solid triangle = a campground someone maintains, hollow
   triangle = a spot on an agency list, circle = a bed you pay for. No red,
   no green, no per-layer hue at all — the same rule the band ramp follows,
   for the same reasons (greyscale, colour vision deficiency, and the fact
   that a red pin among green ones reads as a judgement none of these
   layers makes).

   The marker colours are FIXED across both themes, exactly like the band
   ramp: they sit on the same light topo basemap in light and dark mode, so
   a theme-reactive marker would be wrong in one of them.
   ═══════════════════════════════════════════════════════════════════ */

.svc-toggles { display: flex; flex-wrap: wrap; gap: 6px; }

/* Same affordance as the state chips — pressed is carried by fill AND
   weight, never by colour alone — with the layer's own glyph in front of
   the label so the button and the map marker are recognisably one thing. */
.svc-t {
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-2);
  background: var(--surface-2);
  border: 1.5px solid transparent;
  border-radius: 8px;
  padding: 7px 10px 7px 8px;
  min-height: 36px;
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
}
.svc-t[aria-pressed="true"] {
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 700;
}
.svc-t:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.svc-t.loading { opacity: .55; }
.svc-gl { width: 17px; height: 17px; flex: 0 0 auto; display: block; }
/* The glyph is a two-tone mark on a panel surface that is itself dark in
   dark mode, so it gets a cream disc behind it rather than being re-tinted:
   the marker on the map must look identical to the marker on the button. */
.svc-t .svc-gl { border-radius: 4px; }

.svc-note { margin-top: 9px; margin-bottom: 6px; font-size: 12px; color: var(--ink-3); }
.svc-note:empty { display: none; }
.svc-src { font-size: 11.5px; color: var(--ink-3); }

/* ── the markers ──────────────────────────────────────────────────────
   divIcon wrappers. `pointer-events` stays on the svg so the tap target is
   the shape rather than the padded box, which matters when four layers are
   on at once in a town. */
.svc-pin {
  display: block;
  line-height: 0;
  filter: drop-shadow(0 1px 1.2px rgba(0, 0, 0, .3));
}
.svc-pin svg { display: block; }

/* The legend's services strip. One wrapped line, not a row per layer: the
   band ramp rows each carry a sentence and earn their height, these carry a
   word, and four more full rows run the legend off the bottom of a 360px
   map. */
.legend-svc {
  display: flex;
  flex-wrap: wrap;
  gap: 3px 10px;
  margin-top: 7px;
  padding-top: 6px;
  border-top: 1px solid var(--line);
}
.legend-svc > span {
  display: flex;
  align-items: center;
  gap: 5px;
  color: var(--ink);
  font-weight: 600;
}
.legend-svc i { line-height: 0; display: block; }
.legend-svc svg { display: block; }

/* ── the popover ──────────────────────────────────────────────────── */
.svc-pop { font-size: 13px; line-height: 1.5; min-width: 168px; max-width: 250px; }
.svc-pop h4 {
  margin: 0 0 2px;
  font-size: 14.5px;
  font-weight: 700;
  color: var(--ink);
  line-height: 1.3;
}
.svc-pop .kind {
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--ink-3);
  font-weight: 700;
}
.svc-pop dl {
  margin: 7px 0 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2px 10px;
  font-size: 12.5px;
}
.svc-pop dt { color: var(--ink-3); }
.svc-pop dd { margin: 0; color: var(--ink); }
.svc-pop .src {
  margin-top: 8px;
  padding-top: 7px;
  border-top: 1px solid var(--line);
  font-size: 11px;
  line-height: 1.45;
  color: var(--ink-3);
}
.svc-pop .caveat { margin-top: 4px; font-size: 11px; color: var(--ink-3); }
.svc-pop a { color: var(--accent); }

/* Leaflet's popup chrome is white by default, which is a hole in dark
   mode. It is the only third-party surface on the page. */
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow);
}
.leaflet-popup-content { margin: 11px 13px; }
.leaflet-container a.leaflet-popup-close-button { color: var(--ink-3); }

/* ── fuel on the route card ───────────────────────────────────────────
   The block the whole layer exists for. It sits with the other route
   statistics and is written in the same register: a distance, a name, and
   where the number came from. Never a warning. "121 mi" is the fact. */
.rc-fuel { margin-top: 10px; }
.rc-fuel:empty { display: none; }
.rc-fuel h3 {
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--ink-3);
  font-weight: 700;
  margin: 0 0 2px;
}
.rc-fuel .rc-gap {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 6px 0 4px;
}
.rc-fuel .rc-gap b {
  font-size: 21px;
  font-weight: 700;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.rc-fuel .rc-gap span { font-size: 12.5px; color: var(--ink-2); }
.rc-fuel .rc-fuelnote {
  margin-top: 7px;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--ink-3);
}

/* ── fuel on the roll chart ───────────────────────────────────────────
   A rider plans fuel on the roll chart, so the stops are interleaved into
   the strip at their own mileage rather than listed somewhere else. The
   row is set apart by a rule down the left and the marker glyph in the
   arrow column — no colour, because the arrow column already carries the
   band ramp and a fifth hue there would read as a fifth band. */
.rcx-row.rcx-fuel {
  box-shadow: inset 2px 0 0 var(--line);
  padding-left: 9px;
  margin-left: -11px;
}
.rcx-row.rcx-fuel .rcx-tx > b { font-size: 15px; }
.rcx-fuel .rcx-gl svg { width: 24px; height: 24px; display: block; margin: 3px; }

/* ── nearest fuel in the road detail ──────────────────────────────── */
.det-fuel { margin-top: 12px; }
.det-fuel:empty { display: none; }
.det-fuel .row {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  align-items: baseline;
  padding: 8px 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  font-size: 12.5px;
  color: var(--ink-2);
}
.det-fuel .row b { color: var(--ink); font-variant-numeric: tabular-nums; }
.det-fuel .cap { margin-top: 6px; font-size: 11.5px; line-height: 1.5; color: var(--ink-3); }

/* ── printing ──────────────────────────────────────────────────────
   A rider may genuinely print this and tape it to a tank bag, so the
   printed sheet is the roll chart and nothing else: no map, no panel, no
   navigation, no page furniture. Colour is dropped to ink — the arrows
   carry direction by shape and the traffic figure is a sentence, so
   nothing is lost on a monochrome printer.                            */
@media print {
  /* Peel the page back one container at a time rather than hiding
     everything and un-hiding the chart: `display: revert` on the chart's
     descendants would throw away the flex layout the rows depend on. */
  body.charting { background: #fff !important; }
  body.charting > *:not(.app) { display: none !important; }
  body.charting .app > *:not(.layout) { display: none !important; }
  body.charting .layout > *:not(.mapwrap) { display: none !important; }
  body.charting .mapwrap > *:not(.rollchart) { display: none !important; }

  body.charting .app,
  body.charting .layout,
  body.charting .mapwrap {
    display: block !important;
    position: static !important;
    height: auto !important;
    min-height: 0 !important;
    overflow: visible !important;
  }

  .rollchart {
    position: static !important;
    inset: auto !important;
    display: block !important;
    background: #fff !important;
    color: #000 !important;
  }
  .rcx-head { border-bottom: 2px solid #000; padding: 0 0 8pt; }
  .rcx-btn, .rcx-close { display: none !important; }
  .rcx-head h2 { color: #000; font-size: 11pt; }
  /* the bold spans inside the summary carry --ink, which is near-white in
     dark mode and prints as nothing */
  .rcx-sum, .rcx-sum b { color: #000; font-size: 9.5pt; }
  .rcx-body { overflow: visible !important; padding: 0 !important; height: auto !important; }
  .rcx-foot { border-top: 1px solid #000; color: #000; font-size: 8pt; padding: 6pt 0 0; }
  .rcx-row {
    border-bottom: 1px solid #000;
    padding: 7pt 0;
    page-break-inside: avoid;
    break-inside: avoid;
  }
  .rcx-row.is-now, .rcx-row.is-done {
    background: none !important;
    box-shadow: none !important;
    margin: 0 !important;
    padding-left: 0 !important;
  }
  .rcx-row.is-done .rcx-mi b, .rcx-row.is-done .rcx-tx > b { opacity: 1; }
  /* A fuel stop keeps its place in the strip on paper too — it is the one
     line a rider plans the day around. The screen rule is a shadow, which
     most printers drop, so it becomes a real border here. */
  .rcx-row.rcx-fuel {
    box-shadow: none !important;
    margin-left: 0 !important;
    border-left: 3pt solid #000;
    padding-left: 6pt !important;
  }
  .rcx-mi b { font-size: 13pt; color: #000; }
  .rcx-mi i, .rcx-sub, .rcx-sub b { color: #000; }
  .rcx-tx > b { font-size: 12pt; color: #000; }
  .rcx-gl { color: #000 !important; }
  .rcx-sub { font-size: 8.5pt; }
  @page { margin: 12mm 14mm; }
}
