/* ===========================================================================
   carrysafe.app — THE WEB EXTENSION LAYER.

   This file adds ONLY what a browser at desktop width needs and the app has no
   opinion about: sizes above the app's 34pt ceiling, a page grid, breakpoints,
   section rhythm, and CSS-transition equivalents of the app's springs.

   IT DEFINES NO COLOUR. Not one hex, not one rgb(). Every colour on
   carrysafe.app comes from carrysafe-tokens.css, which is a verified mirror of
   design-kit/tokens.css, which is GENERATED from lib/theme.ts. Where this file
   needs the accent at an alpha the kit does not already name, it derives it with
   color-mix() FROM the token — so changing COLORS.accent in the app changes the
   map here, and typing a hex here is impossible by construction.

   Load order, exactly:
       carrysafe-tokens.css   // verified mirror of design-kit/tokens.css
       web-tokens.css         // this file
       site.css               // components
       map.css                // generated per-state rules, home page only

   NAMING: every custom property added here is prefixed --w-. Anything WITHOUT
   that prefix came from the app. That is the whole audit: grep for `--w-` and
   you have the complete list of things the website invented.

   CLASS NAMING: website classes are prefixed w-, never cs-. cs-* belongs to
   design-kit/kit.css, whose classes carry @rn import contracts for RN screens.
   Never point `node design-kit/generate.mjs --check` at website HTML: its linter
   bans `position: sticky/fixed` (correct for RN, wrong for a web page).
   =========================================================================== */

:root {
  color-scheme: dark;          /* dark only, deliberately — see site.css § dark */

  /* ── TYPE ────────────────────────────────────────────────────────────────
     Font: inherited from --font (tokens.css). No webfont file. No download.

     TRACKING ABOVE 34px. The app's tracking(size) is a step function that
     plateaus at -0.9px for every size over 28. Freezing that ABSOLUTE value at
     64px would read loose, because SF Pro's real tracking is proportional. The
     honest continuation holds the plateau in EM, taken from its own last
     defined point: -0.9px / 34px = -0.0265em. That reproduces tracking(34) to
     within 0.001px and keeps behaving like the function above it.

         trackingWeb(px) = px > 34 ? -0.0265 * px : tracking(px)

     Sizes at or below 34px use the app's own --track-* variables verbatim.
     ALL-CAPS labels never take tracking() at any size — they take
     --track-caps-*, per the rule in tokens.css and lib/theme.ts. */
  --w-track-em: -0.0265em;

  /* NEW-FOR-WEB sizes. All three are above the app's ceiling; there is no size
     below 34px on this site that does not already exist in the app.
     THERE IS NO SIZE ABOVE 64. An 80px hero on a 1120 stage out-shouts the map
     it sits over, and the app's whole visual argument is that the map is the
     subject. Stated here so nobody adds one. */
  --w-t-hero: 64px;      --w-t-hero-lh: 68px;   /* 1.063 — one per site */
  --w-t-h2: 44px;        --w-t-h2-lh: 49px;     /* 1.114 — section openers */
  --w-t-num: 40px;       --w-t-num-lh: 44px;    /* 1.100 — tabular figures */

  /* APP SIZES, app roles. */
  --w-t-h3b: 34px;       --w-t-h3b-lh: 40px;    /* showcase head — 34 IS the app's tracking ceiling */
  --w-t-h3: 29px;        --w-t-h3-lh: 35px;     /* Onboarding.tsx st.title, exact */
  --w-t-h4: 21px;        --w-t-h4-lh: 27px;
  --w-t-body-sm: 15.5px; --w-t-body-sm-lh: 22.5px; /* st.body, exact */
  --w-t-note: 13.5px;    --w-t-note-lh: 19px;      /* st.note, exact */
  --w-t-fine: 11.5px;    --w-t-fine-lh: 16px;      /* st.fine — the legal slot, exact */
  --w-t-caps: 10px;                                /* uppercase kicker, exact */
  --w-t-caps-lg: 12px;                             /* larger caps eyebrow — inside the app's Caption band (12·11.5·11) */

  /* APP SIZES, NEW ROLE on web. 17 and 19 exist in the app as title sizes; a
     scrolling document read at arm's length needs them as body and lead. Both
     ratios (1.41, 1.42) sit inside the app's 1.38–1.42 body band. */
  --w-t-body: 17px;      --w-t-body-lh: 24px;
  --w-t-lead: 19px;      --w-t-lead-lh: 27px;

  /* CTA. WCAG: white on --color-accent measures 3.24:1. That clears AA only as
     LARGE text, which starts at 18.66px bold. The app's CTA is 16.5/800 and
     relies on iOS's own rendering; on the web it must be >= 18.66px bold or it
     fails. 19/800 is the smallest app-existing size that passes.
     (This site does not use white-on-accent — see site.css .btn, which puts
     --color-bg on accent at 5.82:1. --w-t-cta is the size floor either way.) */
  --w-t-cta: 19px;

  --w-w-body: 400;   /* RN <Text> default. The app's body copy IS 400. */
  --w-w-quiet: 600;
  --w-w-label: 700;
  --w-w-display: 800;

  /* ── LAYOUT ──────────────────────────────────────────────────────────────
     Gutters and gaps are app values only: 12 (card blocks), 14 (sheet gutter),
     18 (paywall gutter), 20 (onboarding gutter). Nothing new. */
  --w-page-max: 1120px;      /* NEW: 12 × 68.33 + 11 × 20 */
  --w-measure: 680px;        /* NEW: ~66 characters at 17px — a web-only problem */
  --w-page-pad: 20px;        /* = --gutter-onboarding */
  --w-grid-cols: 12;
  --w-grid-gap: 20px;
  --w-card-pad: 20px;
  --w-block-gap: 12px;       /* the app's card-block separator */

  /* Section rhythm. WEB-ONLY: an 874pt screen never needs this. Each value is a
     multiple of the app's dominant gap (8). */
  --w-sec-y: 128px;

  /* ── THE STAGE AND THE FLOATING CARD ─────────────────────────────────────
     The app's one signature composition: a quiet hairline map with a single
     elevated surface floating on it. Every length below is read out of
     components/UpgradeSheet.tsx, not chosen.

       st.card       marginHorizontal: 16, overflow: "hidden"
       <Glass …      radius={28} elevation="lg" solid   → --surface-solid + --shadow-lg
       st.scrollPad  paddingHorizontal: 20, paddingTop: 20
       HERO_BLEED    30

     THE CARD HAS NO BORDER. components/Glass.tsx:101-114 is explicit — the white
     rim was rejected twice and separation comes from the scrim plus the shadow.
     That is the single biggest fidelity defect a web port of this composition
     makes, so it is written here as well as in the rule. */
  --w-r-float: 28px;         /* UpgradeSheet.tsx:1385 radius={28} — NEVER shrinks with the viewport */
  --w-float-pad: 20px;       /* st.scrollPad */
  --w-float-inset: 16px;     /* st.card marginHorizontal */
  --w-float-max: 720px;      /* web-only: the card's desktop measure */
  /* THE CARD'S PAGE HEIGHT. Fixed, because ONBOARDING-HANDOFF §1 makes "nothing
     scrolls" a hard rule and because a card that grows past the map stops
     annotating it and starts competing with it. 300 is the tallest page's
     content (heading + one sentence + the county plate) at the card's measure. */
  --w-page-h: 300px;
  --w-stage-bleed: 30px;     /* HERO_BLEED */

  /* The backdrop is sized in ABSOLUTE height, not aspect-ratio, because the card
     overlaps it by a fraction of that height — and a percentage margin resolves
     against the container's WIDTH, which silently breaks the overlap the moment
     a max-height clamps the box. One number, used twice, cannot disagree. */
  --w-backdrop-h: 600px;
  --w-stage-overlap: 0.34;   /* HERO_VISIBLE 0.44 → the card covers the lower 34% */

  /* ── DEVICE FRAMES ───────────────────────────────────────────────────────
     Captures are 1206×2622 = iPhone 16 Pro, 402×874pt @3×. Screen aspect
     reduces to 201:437. The display corner is 62pt on a 402pt width = 15.4%.
     Dynamic Island measured off the capture, not quoted: 376×110px at (415, 42)
     → 31.18% × 4.195% at (34.41%, 1.602%). */
  --w-dev-w: 380px;
  --w-dev-corner: 0.154;     /* × width — the real 16 Pro display corner */
  --w-dev-shot-ar: 201 / 437;
  --w-dev-island-w: 31.18%;
  --w-dev-island-h: 4.195%;
  --w-dev-island-x: 34.41%;
  --w-dev-island-y: 1.602%;

  /* ── MOTION ──────────────────────────────────────────────────────────────
     Six animations, no more. Each duration is an app timing or the CSS
     equivalent of an app spring; see site.css for triggers and reduced-motion
     end states. Per lib/motion.ts, Reduce Motion removes TRAVEL, never the
     state change. */
  --w-dur-press: 90ms;     /* <Tap> spring(speed 45, bounciness 0) settles ~90ms */
  --w-dur-raise: 140ms;    /* state hover / focus */
  --w-dur-chrome: 170ms;   /* CornerSheet close — a TIMING, never a spring */
  --w-dur-rise: 320ms;     /* paywall entrance — also cardLift */
  --w-dur-device: 420ms;   /* paywall hero cross-fade — the app's own ceiling, and deviceRise is
                              the only thing on this site allowed to travel that long */
  --w-dur-panel: 260ms;    /* the floating card swapping to a jurisdiction — under
                              --w-dur-device, because this is a content change
                              inside a card that is already on screen, not an
                              entrance */
  --w-dur-plate: 380ms;    /* the county outline settling in behind it, one beat
                              later so the eye reads the words first */
  --w-dur-camera: 900ms;   /* the map flying to a jurisdiction. The app's own state
                              fly-to is 1200ms (ONBOARDING-HANDOFF §5); 900 keeps
                              the same read on a page where the map is smaller and
                              nobody is waiting on it */
  --w-dur-draw: 1500ms;    /* UsAtlas SWEEP_MS */
  --w-dur-pulse: 2400ms;   /* UsAtlas PULSE_MS */

  --w-ease-out: cubic-bezier(0.22, 0.9, 0.3, 1);   /* UsAtlas sweep — the app's only named bezier */
  --w-ease-sine: cubic-bezier(0.37, 0, 0.63, 1);   /* the cosine bump, (1 − cos 2πp)/2 */
  --w-ease-camera: cubic-bezier(0.22, 0.61, 0.24, 1);
  --w-ease-standard: cubic-bezier(0.2, 0, 0, 1);
  --w-ease-wow: cubic-bezier(0.16, 1, 0.3, 1);
  --w-dur-wow: 520ms;
  --w-float-dur: 6000ms;

  /* ── THE HERO MAP ────────────────────────────────────────────────────────
     Values traced to components/UsAtlas.tsx paint(treatment: "outline").
     Strokes are declared in CSS px and drawn with vector-effect:
     non-scaling-stroke, which is exactly the app's `* unit` scaling expressed
     without arithmetic: a weight in RENDERED points, immune to render size.

     THE SITE'S MAP CARRIES ONE HUE, --color-accent, AND NO OTHER. Status
     red/amber/green is a reviewed legal finding and may not appear on a map
     that has not made one.
     (docs/WEBSITE-CLAIM-RECONCILIATION-2026-08-01.md)

     Every alpha below is derived from the accent token with color-mix() rather
     than retyped as rgba(). --accent-emph is the kit's own 0.55 and is used
     as-is where 0.55 is wanted. */
  --w-map-fill:   color-mix(in srgb, var(--color-accent) 4.5%, transparent);   /* withAlpha(accent, 0.045) — resting */
  --w-map-stroke: color-mix(in srgb, var(--color-accent) 26%,  transparent);
  --w-map-width: 0.55px;

  --w-map-fill-hover:   color-mix(in srgb, var(--color-accent) 10%, transparent);
  --w-map-stroke-hover: color-mix(in srgb, var(--color-accent) 45%, transparent);
  --w-map-width-hover: 0.8px;

  /* The reader's own state, picked BY HAND. Never geolocated, never guessed
     (lib/onboarding.ts founding constraint 3). Deliberately short of the app's
     `lit` fill of 0.50: in the app 0.50 is the end of the sweep, the single
     moment that says a plan opens this ground. Nothing on a marketing page has
     earned that. */
  --w-map-fill-on:   color-mix(in srgb, var(--color-accent) 20%, transparent);
  --w-map-stroke-on: var(--color-accent);
  --w-map-width-on: 1.2px;

  --w-map-marker-r: 11px;     /* HomeMarker r = max(4, renderedWidth × 0.010); 11.2 at a 1120 hero */
  --w-map-hit-pad: 8px;       /* transparent fat stroke on the hit layer */

  /* The app's four-stop hero scrim (UpgradeSheet.tsx:1330-1336), glued to the
     MAP rather than to the card — gradient job 3, "image scrim for legibility".
     Derived from --color-bg with color-mix so no rgba() literal appears. */
  --w-gradient-hero: linear-gradient(180deg,
    color-mix(in srgb, var(--color-bg) 62%, transparent)  0%,
    color-mix(in srgb, var(--color-bg) 12%, transparent) 34%,
    color-mix(in srgb, var(--color-bg) 55%, transparent) 80%,
    var(--color-bg)                                     100%);
}

/* ── BREAKPOINTS ───────────────────────────────────────────────────────────
   Three, and they are the only three. The app has none (useWindowDimensions),
   so these are web-only; each is pinned to something real.
     1024  desktop — 12 columns fit at --w-page-max
      720  tablet  — the hero map stops being interactive below this
      380  the floor — just under --device-375x667-w, the app's practical floor */

@media (max-width: 1023.98px) {
  :root {
    --w-t-hero: 44px;  --w-t-hero-lh: 49px;
    --w-t-h2: 34px;    --w-t-h2-lh: 40px;
    --w-t-num: 34px;   --w-t-num-lh: 40px;
    --w-t-h3b: 29px;   --w-t-h3b-lh: 35px;
    --w-grid-cols: 6;
    --w-grid-gap: 14px;      /* = --sheet-gutter */
    --w-card-pad: 14px;      /* app card padding */
    --w-sec-y: 96px;
    --w-float-max: 640px;
    --w-backdrop-h: 440px;
    --w-stage-overlap: 0.36;
    --w-dev-w: 260px;
  }
}

@media (max-width: 719.98px) {
  :root {
    --w-t-hero: 34px;  --w-t-hero-lh: 40px;
    --w-t-h2: 29px;    --w-t-h2-lh: 35px;
    --w-t-h3b: 24px;   --w-t-h3b-lh: 30px;
    --w-t-lead: 17px;  --w-t-lead-lh: 24px;
    --w-grid-cols: 4;
    --w-grid-gap: 12px;
    --w-sec-y: 72px;
    /* The card's own inset becomes the app's literal 16, because below 720 the
       composition IS the app's composition rather than a desktop echo of it. */
    --w-float-pad: 14px;     /* = --w-card-pad */
    --w-stage-bleed: 0px;    /* nothing to bleed into at the page gutter */
    --w-backdrop-h: 300px;
    --w-stage-overlap: 0.40;
    --w-dev-w: 240px;
  }
}

@media (max-width: 379.98px) {
  :root {
    --w-t-hero: 29px;   --w-t-hero-lh: 35px;
    --w-t-body: 15.5px; --w-t-body-lh: 22.5px;
    --w-page-pad: 14px;      /* = --sheet-gutter, the app's tightest */
    --w-backdrop-h: 240px;
    --w-stage-overlap: 0.42;
    --w-dev-w: 200px;
  }
}

/* ── HAIRLINES ARE ONE DEVICE PIXEL ────────────────────────────────────────
   StyleSheet.hairlineWidth is 1/scale — 0.5 at @2x, 0.33 at @3x — i.e. always
   exactly one device pixel. On a 1× monitor the browser antialiases 0.55px into
   a smear and the country nearly disappears, so the same INTENT needs a
   different number, exactly as it does on the device. */
@media (max-resolution: 1.5dppx) {
  :root {
    --w-map-width: 0.8px;
    --w-map-width-hover: 1px;
    --hairline: 1px;
  }
}

/* ── PREFERS-CONTRAST ──────────────────────────────────────────────────────
   --color-text-faint measures 4.42:1 on --color-surface-alt, below 4.5. Under
   `more` the map stroke is promoted to the kit's own 0.55 alpha and white-alpha
   hairlines are promoted to --grabber, which IS white at 0.24 in tokens.css.
   No new colour is introduced; two existing tokens are re-pointed. */
@media (prefers-contrast: more) {
  :root {
    --w-map-stroke: var(--accent-emph);
    --w-map-width: 1px;
    --border-on-glass: var(--grabber);
    --border-on-glass-soft: var(--grabber);
  }
}

/* Reduced motion: durations are zeroed here, and every animation names its own
   end state in site.css. The STATE CHANGE still happens; only the travel goes. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --w-dur-press: 0ms;
    --w-dur-raise: 0ms;
    --w-dur-chrome: 0ms;
    --w-dur-rise: 0ms;
    --w-dur-device: 0ms;
    --w-dur-panel: 0ms;
    --w-dur-plate: 0ms;
    --w-dur-camera: 0ms;
    --w-dur-draw: 0ms;
  }
}

/* ===========================================================================
   carrysafe.app — components and layout.

   EVERY colour, radius, shadow, tracking and status value below is var(--…)
   from carrysafe-tokens.css, the verified mirror of design-kit/tokens.css,
   which is GENERATED from lib/theme.ts. Nothing here re-types a hex, an rgb()
   or a shadow. Where an alpha the kit does not name is needed, it is derived
   with color-mix() FROM a token. That is the whole point: the website cannot
   drift away from the app, because it does not hold its own copy of anything.

   TYPE. tokens.css says 1 CSS px == 1 React Native point. Every size at or
   below 34px on this site exists in the app and takes the app's own --track-N.
   The three sizes above it (--w-t-hero, --w-t-h2, --w-t-num) are declared in
   web-tokens.css with the em-continuation of tracking(), because a 64px hero
   cannot be built from a 29px ceiling and freezing the app's -0.9px plateau at
   64px reads loose.

   ALL-CAPS labels never take --track-N. They take --track-caps-*, per the rule
   in tokens.css and lib/theme.ts: tracking() is for sentence case only.

   WHAT IS WEB-ONLY, deliberately: media queries, hover states, focus rings,
   CSS grid, backdrop-filter, and scroll-driven animation. React Native has none
   of them and the app must not grow them; a browser has them and refusing to
   use them would just be a worse website.
   =========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html {
  color-scheme: dark;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

/* ── Rhythm ──────────────────────────────────────────────────────────────
   No spacing scale exists in the app (per-component integers). The web needs
   one, so it is declared here and derived from the app's observed clusters:
   card padding 12–14, gutters 14/18/20, block gap 12. Page-level measurements
   (--w-page-max, --w-measure, --w-sec-y) live in web-tokens.css with the rest
   of the web-only vocabulary. */
:root {
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 14px;
  --sp-5: 20px;
  --sp-6: 26px;
  --sp-7: 32px;
  --sp-8: 44px;
  --sp-9: 64px;
}

body {
  margin: 0;
  position: relative;      /* the screen wash positions against the page, not the column */
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font);
  font-size: var(--w-t-body);
  line-height: var(--w-t-body-lh);
  font-weight: var(--w-w-body);
  letter-spacing: var(--track-17);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}

/* ── Futuristic aurora — calm, not cute: 2 blurred orbs behind wash ──────── */
body::after {
  content: "";
  position: absolute;
  inset: 80px 0 auto 0;
  height: 740px;
  pointer-events: none;
  z-index: -1;
  background:
    radial-gradient(ellipse 680px 520px at 16% 10%, color-mix(in srgb, var(--color-accent) 14%, transparent) 0%, transparent 68%),
    radial-gradient(ellipse 720px 520px at 86% 12%, color-mix(in srgb, var(--color-ok) 9%, transparent) 0%, transparent 70%),
    radial-gradient(ellipse 420px 320px at 50% 34%, color-mix(in srgb, var(--color-warn) 5%, transparent) 0%, transparent 72%);
  filter: blur(28px);
  opacity: 0.50;
  animation: orbFloat var(--w-float-dur) var(--w-ease-sine) infinite alternate;
}
@keyframes orbFloat { from { transform: translateY(0) scale(1); } to { transform: translateY(-14px) scale(1.02); } }
/* Cute sprinkles — tiny drifting dots around hero (no emoji, just color-mix dots) */
.w-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: 0.42;
  background:
    radial-gradient(circle 1.2px at 14% 28%, color-mix(in srgb, var(--color-accent) 55%, transparent) 0 1.2px, transparent 1.3px),
    radial-gradient(circle 1px at 78% 18%, color-mix(in srgb, var(--color-ok) 45%, transparent) 0 1px, transparent 1.1px),
    radial-gradient(circle 1px at 62% 82%, color-mix(in srgb, var(--color-warn) 40%, transparent) 0 1px, transparent 1.1px),
    radial-gradient(circle 0.9px at 88% 64%, color-mix(in srgb, var(--color-text-faint) 38%, transparent) 0 0.9px, transparent 1px);
  animation: sprinkleDrift 18s linear infinite alternate;
}
@keyframes sprinkleDrift { from { transform: translateY(0); } to { transform: translateY(-8px); } }

@media (max-width: 379.98px) {
  body {
    font-size: var(--w-t-body-sm);
    line-height: var(--w-t-body-sm-lh);
    letter-spacing: var(--track-15-5);
  }
}

img, svg { max-width: 100%; height: auto; }

a {
  color: var(--color-accent);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--accent-border);
}
a:hover { text-decoration-color: var(--color-accent); }

:focus-visible {
  outline: var(--border-featured) solid var(--color-accent);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}

code {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  font-size: 0.92em;
  background: var(--fill-card);
  border: var(--hairline) solid var(--border-on-glass-soft);
  border-radius: var(--radius-xs);
  padding: 1px 5px;
}

.skip {
  position: absolute;
  left: -9999px;
  top: var(--sp-2);
  background: var(--color-surface);
  color: var(--color-text);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius-sm);
  z-index: var(--z-review-desk);
}
.skip:focus { left: var(--sp-2); }

/* Available to the accessible tree, absent from the page. Used where a control
   shows a two-letter code and a screen reader must hear the whole name — a
   `title` attribute is not an accessible name, is unreachable by keyboard and
   never appears on touch. `clip-path` rather than `clip`, and 1px rather than 0,
   because a zero-size box is skipped by some screen readers outright. */
.w-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.wrap {
  width: 100%;
  max-width: var(--w-page-max);
  margin-inline: auto;
  padding-inline: var(--w-page-pad);
}

/* ── CONTINUOUS CORNERS ──────────────────────────────────────────────────────
   Every radius in the app ships with borderCurve:'continuous' — iOS corners are
   squircles, and a circular arc kinks where it meets the straight edge. The kit
   says the browser cannot draw one; it can now, behind a guard. The base rule is
   always a plain radius, so the design must read correctly with circular corners
   and only gets closer to the app where the browser can oblige. */
@supports (corner-shape: squircle) {
  .callout, .card, .plan, .cta, .status, .faq details, .tablewrap, .ledger,
  .w-panel, .w-chip, .w-pick, .w-float, .w-dev, .w-dev-screen, .h-mark img,
  .w-show-cap { corner-shape: squircle; }
}

/* ── Header ─────────────────────────────────────────────────────────────────
   The web NavigationGlass: --surface-nav plus a real blur. NOTHING that animates
   opacity or transform may be an ancestor of a backdrop-filter element — the
   app's blur trap is a browser bug too (Safari drops the blur, Chrome re-roots
   the containing block). The header is a direct child of <body> for that reason.
   NavigationGlass is also the one surface whose hairline rim is WANTED.

   THE SHAPE IS hyda.studio's: a DETACHED PILL that floats over the page rather
   than a full-bleed bar welded to the top edge. Measured off the live site —
   sticky at 10px, width min(1120px, 100% − 20px), 12px of breathing room above,
   a 100px radius, a hairline rim and a deep 44px drop shadow with a 1px inset
   highlight along the top edge. Those numbers are transcribed; the PALETTE is
   not. hyda's rim is its yellow on near-black, and reproducing that here would
   put a second brand's colour on CarrySafe's chrome. Every colour below is a
   CarrySafe token, so the nav reads as the app's NavigationGlass in hyda's
   geometry — which is the ask, and the only version of it that is honest.

   Three zones, space-between, exactly as hyda lays them out: the lockup, the
   links, and the actions. --w-page-max is already 1120px, so the pill lines up
   with the reading column instead of being a second, competing measure. */

.h {
  position: sticky;
  top: calc(10px + env(safe-area-inset-top, 0px));
  z-index: var(--z-top-bar);
  width: min(var(--w-page-max), 100% - 20px);
  margin: 12px auto 0;
  padding: 9px 10px 9px 22px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  min-height: 58px;
  box-sizing: border-box;
  background: var(--surface-nav);
  border: var(--hairline) solid var(--border-on-glass);
  border-radius: var(--radius-pill);
  -webkit-backdrop-filter: blur(20px) saturate(140%);
  backdrop-filter: blur(20px) saturate(140%);
  /* --shadow-lg is the app's deepest, and the inset hairline is the top-edge
     highlight that stops a translucent pill from looking like a flat sticker. */
  box-shadow: var(--shadow-lg), inset 0 1px 0 var(--border-on-glass-soft);
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .h { background: var(--surface-solid); }
}

/* A floating pill has page behind it on all four sides, so the wash and the
   first heading must not start underneath it. */
.h + main .doc { padding-block-start: var(--sp-6); }

/* ── The lockup ─────────────────────────────────────────────────────────────
   Copied from the app's own brand row (UpgradeSheet.tsx:2214-2222): gap 10, a
   30×30 mark at radius 8, and the wordmark as TEXT at 15/700/--track-15. There
   is no wordmark artwork in this repo and none is to be drawn.

   THE MARK IS icon.png, NOT mark.png. The app uses require("../assets/icon.png")
   for exactly this slot: its black ground reads as a deliberate badge against
   #0B1120, where mark.png at 30px puts the map's α≈0.75 coastline straight onto
   the nav glass and turns to mush. The 44px responder is the <a>; the image is
   decorative inside it. */
.h-mark {
  display: inline-flex;
  align-items: center;
  gap: 10px;                       /* st.brandRow */
  color: var(--color-text);
  text-decoration: none;
  min-height: var(--min-tap);
  flex: none;
}

.h-mark img {
  width: 30px;                     /* st.brandMark */
  height: 30px;
  border-radius: var(--radius-xs);
  flex: none;
}

.h-word {
  font-size: 15px;                 /* st.wordmark */
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-15);
}

/* The centre zone. hyda runs its links as plain text at a 26px gap with no
   resting chrome — the pill IS the chrome — so the tab-indicator fill this site
   used to paint behind every item is gone. The current page is still marked, by
   colour AND by a 2px underline, because colour is never the only signal. */
.h-nav {
  display: flex;
  align-items: center;
  gap: var(--sp-6);                /* 26px, hyda's .links gap */
  flex-wrap: wrap;
  justify-content: center;
  min-width: 0;
}

.h-nav a {
  display: inline-flex;
  align-items: center;
  min-height: var(--min-tap);
  color: var(--color-text-muted);
  font-size: var(--w-t-note);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-13-5);
  text-decoration: none;
  white-space: nowrap;
  transition: color var(--w-dur-press) var(--w-ease-standard);
}
.h-nav a:hover { color: var(--color-text); }
.h-nav a[aria-current="page"] {
  color: var(--color-accent);
  box-shadow: inset 0 -2px 0 var(--color-accent);
}

/* ── The action zone ─────────────────────────────────────────────────────── */

.h-act {
  display: flex;
  align-items: center;
  gap: var(--sp-3);                /* 12px, hyda's .navRight gap */
  flex: none;
}

/* hyda's .cta.small: a filled pill at 10/20 padding with a coloured glow under
   it. The glow is --color-accent at low alpha rather than a black drop shadow,
   which is what makes the button read as lit rather than raised.

   CONTRAST: --color-bg on --color-accent is 5.82:1 and passes AA at any size —
   the same pairing .btn already uses, and the reason the label is not white. */
.h-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 38px;
  padding: 0 var(--sp-5);
  border-radius: var(--radius-pill);
  background: var(--color-accent);
  color: var(--color-bg);
  font-size: var(--w-t-note);
  font-weight: var(--w-w-display);
  letter-spacing: var(--track-13-5);
  text-decoration: none;
  white-space: nowrap;
  border: var(--hairline) solid transparent;
  box-shadow: 0 12px 34px color-mix(in srgb, var(--color-accent) 28%, transparent);
  transition: opacity var(--w-dur-press) var(--w-ease-standard),
              transform var(--w-dur-press) var(--w-ease-standard);
}
.h-cta:hover { opacity: 0.92; }
.h-cta:active { opacity: var(--press-dim); transform: scale(0.97); }

/* ── The compact menu ────────────────────────────────────────────────────── */
/* A <details>, so it opens with no JavaScript. Hidden above 720px, which is
   where hyda drops its own links, and where five items stop fitting here. */

.h-more { display: none; position: relative; }

.h-burger {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border: 1.5px solid var(--border-on-glass);
  border-radius: 50%;
  cursor: pointer;
  list-style: none;
}
.h-burger::-webkit-details-marker { display: none; }
.h-burger::marker { content: ""; }

/* Three rules from one element: the middle bar is the box, the outer two are
   its pseudo-elements. Nothing here animates a transform on an ancestor of the
   header's backdrop-filter — the bars are inside the pill, not around it. */
.h-bar, .h-bar::before, .h-bar::after {
  display: block;
  width: 16px;
  height: 2px;
  border-radius: 2px;
  background: var(--color-accent);
  content: "";
}
.h-bar { position: relative; }
.h-bar::before { position: absolute; top: -5px; }
.h-bar::after { position: absolute; top: 5px; }

.h-drop {
  position: absolute;
  top: calc(100% + var(--sp-3));
  inset-inline-end: 0;
  min-width: 220px;
  display: flex;
  flex-direction: column;
  padding: var(--sp-2);
  /* THE PANEL IS OPAQUE, AND IT HAS TO BE STATED IN TWO LAYERS. --surface-solid
     is ALPHA.solid = 0.98, and 2% ought to be invisible — measured in Chrome
     over this header it is not, because the panel escapes the bounds of an
     ancestor that carries backdrop-filter and gets composited against the
     blurred backdrop rather than the page. That is the same blur trap the
     header comment above describes, seen from the other side.

     So: --color-bg as an opaque base, and the nav tint painted over it as a
     one-stop gradient. The token stays the only source of the colour, nothing
     here is a literal, and body copy cannot show through a menu again. */
  background-color: var(--color-bg);
  background-image: linear-gradient(var(--surface-solid), var(--surface-solid));
  border: var(--hairline) solid var(--border-on-glass);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}

.h-drop a {
  display: flex;
  align-items: center;
  min-height: var(--min-tap);
  padding: 0 var(--sp-4);
  border-radius: var(--radius-sm);
  color: var(--color-text);
  font-size: var(--w-t-note);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-13-5);
  text-decoration: none;
}
.h-drop a:hover { background: var(--fill-card); }
.h-drop a[aria-current="page"] {
  color: var(--color-accent);
  background: var(--accent-fill);
}

@media (max-width: 720px) {
  .h { padding-left: 16px; }
  .h-nav { display: none; }
  .h-more { display: block; }
  .h-cta { padding: 0 var(--sp-4); }
}

/* Under ~420px the wordmark and a full CTA label stop coexisting. The mark
   alone is still the brand, and the CTA is the thing an ad click came for. */
@media (max-width: 419.98px) {
  .h { padding-left: 12px; }
  .h-word { display: none; }
}

/* ── The consent banner — MODERN bottom sheet ───────────────────────────────
   Drawn by /tag.js, styled here (CSP style-src 'self'). Modern glass sheet:
   centered pill, rounded 24px, blur 20px, slide-up, not a full-width bar. */

.consent {
  position: fixed;
  inset-inline: 0;
  bottom: max(16px, env(safe-area-inset-bottom, 0px));
  z-index: var(--z-corner-sheet);
  display: flex;
  justify-content: center;
  padding: 0 var(--w-page-pad);
  pointer-events: none;
}
.consent-card {
  pointer-events: auto;
  width: min(640px, 100%);
  display: grid;
  gap: var(--sp-3);
  padding: var(--sp-5) var(--sp-5) var(--sp-4);
  background: var(--surface-solid);
  border: var(--hairline) solid var(--border-on-glass);
  border-radius: 24px;
  box-shadow: var(--shadow-lg), 0 12px 40px color-mix(in srgb, var(--color-bg) 38%, transparent);
  -webkit-backdrop-filter: blur(20px) saturate(140%);
  backdrop-filter: blur(20px) saturate(140%);
  animation: consentIn 420ms var(--w-ease-out) both;
}
@keyframes consentIn { from { opacity: 0; transform: translateY(12px) scale(0.98); } to { opacity: 1; transform: none; } }
.consent-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  font-size: var(--w-t-body-sm);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-15-5);
  color: var(--color-text);
}
.consent-head::before {
  content: "";
  width: 28px;
  height: 28px;
  border-radius: 999px;
  flex: none;
  background: color-mix(in srgb, var(--color-accent) 14%, transparent);
  border: var(--hairline) solid color-mix(in srgb, var(--color-accent) 22%, transparent);
  box-shadow: inset 0 0 0 4px color-mix(in srgb, var(--color-accent) 10%, transparent);
}
.consent-t {
  margin: 0;
  font-size: var(--w-t-note);
  line-height: var(--w-t-note-lh);
  letter-spacing: var(--track-13-5);
  color: var(--color-text-muted);
}
.consent-t a { color: var(--color-accent); }
.consent-a {
  display: flex;
  gap: var(--sp-3);
  flex-wrap: wrap;
  align-items: center;
  margin-top: 2px;
}
.consent-b {
  min-height: 42px;
  padding: 0 var(--sp-4);
  font-size: var(--w-t-note);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-13-5);
  cursor: pointer;
  font-family: inherit;
  border-radius: var(--radius-pill);
}
.consent-link {
  margin-left: auto;
  font-size: var(--w-t-note);
  color: var(--color-text-muted);
  background: none;
  border: 0;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.consent-link:hover { color: var(--color-text); }
@media (max-width: 559.98px) {
  .consent-a { gap: var(--sp-2); }
  .consent-b { flex: 1 1 0; }
  .consent-link { flex: 1 1 100%; text-align: left; margin-left: 0; }
}
/* Preferences sheet — second modal, same glass */
.consent-sheet {
  position: fixed;
  inset: 0;
  z-index: calc(var(--z-corner-sheet) + 1);
  display: grid;
  place-items: center;
  padding: var(--sp-4);
  background: color-mix(in srgb, var(--color-bg) 62%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
.consent-sheet-card {
  width: min(560px, 100%);
  padding: var(--sp-6);
  background: var(--surface-solid);
  border: var(--hairline) solid var(--border-on-glass);
  border-radius: 20px;
  box-shadow: var(--shadow-lg);
  display: grid;
  gap: var(--sp-4);
}
.consent-sheet h3 { margin: 0; font-size: var(--w-t-h4); letter-spacing: var(--track-21); }
.consent-sheet p { margin: 0; color: var(--color-text-muted); font-size: var(--w-t-note); line-height: var(--w-t-note-lh); }
.consent-toggle { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); padding: var(--sp-3) var(--sp-4); background: var(--fill-card); border: var(--hairline) solid var(--border-on-glass-soft); border-radius: var(--radius-lg); }
.consent-toggle strong { font-size: var(--w-t-body-sm); }
.consent-toggle span { font-size: var(--w-t-note); color: var(--color-text-muted); }

/* ── Document column ────────────────────────────────────────────────────── */

.doc { padding-block: var(--sp-7) var(--w-sec-y); }

/* The screen wash — job 2 of the sanctioned gradients, applied once per page.
   It hangs off <body>, not off the reading column: anchored to .doc it inherits
   the column's left gutter and its max-width and stops being a wash, becoming a
   tinted rectangle with three hard edges. No vw units — 100vw includes the
   scrollbar and would put a horizontal scrollbar on every classic-scrollbar
   platform, which is the one thing the layout promises never to do. */
body::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 520px;
  background: var(--gradient-wash);
  pointer-events: none;
  z-index: -1;
}

/* --w-measure is ~66 characters at 17px. It is a web-only constraint and is
   labelled as one: no phone needs it. Blocks that are grids opt out below. */
.doc > * {
  max-width: var(--w-measure);
  margin-inline: 0;
}

.doc h1 {
  font-size: var(--w-t-hero);
  line-height: var(--w-t-hero-lh);
  font-weight: var(--w-w-display);
  letter-spacing: var(--w-track-em);
  margin: 0 0 var(--sp-5);
  max-width: 18ch;
  text-wrap: balance;
}

.doc h2 {
  font-size: var(--w-t-h2);
  line-height: var(--w-t-h2-lh);
  font-weight: var(--w-w-display);
  letter-spacing: var(--w-track-em);
  margin: var(--w-sec-y) 0 var(--sp-5);
  scroll-margin-top: 80px;
  text-wrap: balance;
}

/* At and below 34px the app's own tracking applies, not the em continuation. */
@media (max-width: 1023.98px) {
  .doc h1, .doc h2 { letter-spacing: var(--track-34); }
}
@media (max-width: 719.98px) {
  .doc h1 { letter-spacing: var(--track-34); }
  .doc h2 { letter-spacing: var(--track-29); }
}
@media (max-width: 379.98px) {
  .doc h1 { letter-spacing: var(--track-29); }
}

.doc h3 {
  font-size: var(--w-t-h4);
  line-height: var(--w-t-h4-lh);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-21);
  margin: var(--sp-6) 0 var(--sp-2);
}

.doc p { margin: 0 0 var(--sp-5); }

.lede {
  font-size: var(--w-t-lead);
  line-height: var(--w-t-lead-lh);
  letter-spacing: var(--track-19);
  color: var(--color-text);
  text-wrap: pretty;
}

.kicker {
  font-size: var(--w-t-caps);
  font-weight: var(--w-w-display);
  letter-spacing: var(--track-caps-lg);
  text-transform: uppercase;
  color: var(--color-text-faint);
  margin: 0 0 var(--sp-2);
}

.fine {
  font-size: var(--w-t-note);
  line-height: var(--w-t-note-lh);
  letter-spacing: var(--track-13-5);
  color: var(--color-text-muted);
}

.rule {
  border: 0;
  border-top: var(--hairline) solid var(--color-border);
  margin: var(--w-sec-y) 0;
  max-width: var(--w-measure);
}

/* ── Lists ──────────────────────────────────────────────────────────────── */

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

.list li {
  position: relative;
  padding-inline-start: 20px;
  color: var(--color-text-muted);
}
.list li strong { color: var(--color-text); }

.list li::before {
  content: "";
  position: absolute;
  left: 3px;
  top: 0.62em;
  width: 5px;
  height: 5px;
  border-radius: var(--radius-pill);
  background: var(--color-text-faint);
}

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

.steps li {
  display: flex;
  gap: var(--sp-4);
  align-items: flex-start;
  padding: var(--sp-2);
  border-radius: var(--radius-md);
  border: var(--hairline) solid transparent;
  transition: background var(--w-dur-raise) var(--w-ease-standard), border-color var(--w-dur-raise) var(--w-ease-standard), transform var(--w-dur-wow) var(--w-ease-wow);
}
.steps li:hover { background: color-mix(in srgb, var(--color-accent) 4%, transparent); border-color: color-mix(in srgb, var(--color-accent) 10%, transparent); transform: translateY(-1px); }
.steps li:focus-within { border-color: var(--accent-border); }

.steps-n {
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-pill);
  background: var(--accent-fill);
  border: var(--hairline) solid var(--accent-border);
  color: var(--color-accent);
  font-size: var(--w-t-note);
  font-weight: var(--w-w-display);
  display: grid;
  place-items: center;
  font-variant-numeric: tabular-nums;
}

.steps-b { min-width: 0; }
.steps-t { margin: 0 0 var(--sp-1) !important; font-size: var(--w-t-body) !important; }
.steps-b p { margin: 0; color: var(--color-text-muted); }

/* Review gate filter tabs — injected via CSS fallback, real filter is JS search above */
.w-ledger-filter input::placeholder { color: var(--color-text-faint); }

/* Futuristic status + feature hover */
.status, .w-feat-item, .w-man-item, .w-proc-item, .w-prom-item {
  transition: transform var(--w-dur-wow) var(--w-ease-wow), border-color var(--w-dur-raise) var(--w-ease-standard), background var(--w-dur-raise) var(--w-ease-standard);
}
.status:hover, .w-feat-item:hover, .w-man-item:hover { transform: translateY(-2px); border-color: color-mix(in srgb, var(--color-accent) 12%, var(--color-border)); }
.status:hover { background: color-mix(in srgb, var(--color-accent) 4%, var(--color-surface)); }
.w-feat-item:hover .w-feat-i { transform: scale(1.06); }
.w-feat-i { transition: transform var(--w-dur-wow) var(--w-ease-wow); }

/* ── Callout ────────────────────────────────────────────────────────────────
   The tone IS the meaning here, so it always arrives with a word. Colour is
   never the only carrier — that rule is enforced in the app and it holds on the
   marketing site too. These and the status legend are the ONLY two places on
   this site where a status hue appears. Never on the map. */

.callout {
  border-radius: var(--radius-lg);
  border: var(--border-ring) solid var(--color-border);
  background: var(--color-surface);
  padding: var(--w-card-pad);
  margin: 0 0 var(--sp-5);
  box-shadow: var(--shadow-md);
}

.callout-w {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-2) !important;
  font-size: var(--w-t-caps);
  font-weight: var(--w-w-display);
  text-transform: uppercase;
  letter-spacing: var(--track-caps-lg);
}

.callout-dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-pill);
  flex: none;
}

.callout-t {
  margin: 0 0 var(--sp-2) !important;
  font-size: var(--w-t-lead);
  line-height: var(--w-t-lead-lh);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-19);
  color: var(--color-text);
}

.callout-b { margin: 0 !important; color: var(--color-text-muted); }

.callout--unknown { border-color: var(--tone-unknown-fg); background: var(--tone-unknown-bg); }
.callout--unknown .callout-w { color: var(--tone-unknown-fg); }
.callout--unknown .callout-dot { background: var(--tone-unknown-fg); }

.callout--warn { border-color: var(--tone-warn-fg); background: var(--tone-warn-bg); }
.callout--warn .callout-w { color: var(--tone-warn-fg); }
.callout--warn .callout-dot { background: var(--tone-warn-fg); }

.callout--danger { border-color: var(--tone-danger-fg); background: var(--tone-danger-bg); }
.callout--danger .callout-w { color: var(--tone-danger-fg); }
.callout--danger .callout-dot { background: var(--tone-danger-fg); }

.callout--ok { border-color: var(--tone-ok-fg); background: var(--tone-ok-bg); }
.callout--ok .callout-w { color: var(--tone-ok-fg); }
.callout--ok .callout-dot { background: var(--tone-ok-fg); }

.callout--accent { border-color: var(--accent-border); background: var(--tone-accent-bg); }
.callout--accent .callout-w { color: var(--tone-accent-fg); }
.callout--accent .callout-dot { background: var(--tone-accent-fg); }

/* ── Cards ──────────────────────────────────────────────────────────────────
   The 12/6/4-column grid from web-tokens.css. Cards opt out of --w-measure. */

.cards {
  max-width: var(--w-page-max);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--w-grid-gap);
  margin: 0 0 var(--sp-5);
}

.card {
  background: var(--color-surface);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--w-card-pad);
  box-shadow: var(--shadow-sm);
  transition: transform var(--w-dur-wow) var(--w-ease-wow), box-shadow var(--w-dur-raise) var(--w-ease-standard), border-color var(--w-dur-raise) var(--w-ease-standard);
}
.card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md), 0 0 0 1px color-mix(in srgb, var(--color-accent) 8%, transparent); border-color: color-mix(in srgb, var(--color-accent) 14%, var(--color-border)); }

/* CONCENTRIC: outer 20 − padding 12 = 8 = RADIUS.xs, exactly on the scale. */
.card-n {
  margin: var(--sp-3) 0 0;
  padding-top: var(--sp-3);
  border-top: var(--hairline) solid var(--color-border);
  border-radius: 0 0 var(--radius-xs) var(--radius-xs);
  font-size: var(--w-t-note);
  line-height: var(--w-t-note-lh);
  letter-spacing: var(--track-13-5);
  color: var(--color-text-muted);
}

.card-t {
  margin: 0 0 var(--sp-2) !important;
  font-size: var(--w-t-lead) !important;
  line-height: var(--w-t-lead-lh);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-19);
}

.card-b {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--w-t-body-sm);
  line-height: var(--w-t-body-sm-lh);
  letter-spacing: var(--track-15-5);
  text-wrap: pretty;
}

/* ── Status legend ──────────────────────────────────────────────────────────
   Four redundant signals per severity, as in lib/status.ts: a WORD, a SHAPE
   (border width + style, readable in greyscale), a GLYPH, and a sentence. The
   border style below IS the shape signal — do not normalise it away. */

.statuses {
  max-width: var(--w-measure);
  margin: 0 0 var(--sp-5);
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--sp-2);
}

.status {
  display: flex;
  gap: var(--sp-3);
  align-items: flex-start;
  padding: var(--sp-3);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
}

.status-g {
  flex: none;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-xs);
  display: grid;
  place-items: center;
  font-size: var(--w-t-note);
  font-weight: var(--w-w-display);
}

.status-w {
  margin: 0 0 2px !important;
  font-size: var(--w-t-body-sm);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-15-5);
}

.status-m {
  margin: 0 !important;
  font-size: var(--w-t-note);
  letter-spacing: var(--track-13-5);
  line-height: var(--w-t-note-lh);
  color: var(--color-text-muted);
  text-wrap: pretty;
}

.status--danger .status-g {
  color: var(--tone-danger-fg);
  background: var(--tone-danger-bg);
  border: var(--status-prohibited-width) var(--status-prohibited-style) var(--tone-danger-fg);
}
.status--danger .status-w { color: var(--tone-danger-fg); }

.status--warn .status-g {
  color: var(--tone-warn-fg);
  background: var(--tone-warn-bg);
  border: var(--status-restricted-width) var(--status-restricted-style) var(--tone-warn-fg);
}
.status--warn .status-w { color: var(--tone-warn-fg); }

/* "Verify locally" shares the warn hue and is separated by its DASHED shape. */
.status--warn:nth-child(3) .status-g {
  border-style: var(--status-verify-style);
  border-width: var(--status-verify-width);
}

.status--ok .status-g {
  color: var(--tone-ok-fg);
  background: var(--tone-ok-bg);
  border: var(--status-allowed-width) var(--status-allowed-style) var(--tone-ok-fg);
}
.status--ok .status-w { color: var(--tone-ok-fg); }

.status--unknown .status-g {
  color: var(--tone-unknown-fg);
  background: var(--tone-unknown-bg);
  border: var(--status-unreviewed-width) var(--status-unreviewed-style) var(--tone-unknown-fg);
}
.status--unknown .status-w { color: var(--tone-unknown-fg); }

/* ── Tables ─────────────────────────────────────────────────────────────────
   Wide content scrolls inside its own container. The page body never scrolls
   sideways, at any width, including the 320px floor. */

.tablewrap, .ledger {
  max-width: var(--w-page-max);
  margin: 0 0 var(--sp-5);
  overflow-x: auto;
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  transition: transform var(--w-dur-wow) var(--w-ease-wow), box-shadow var(--w-dur-raise) var(--w-ease-standard);
}
.tablewrap:hover, .ledger:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg), 0 0 0 1px color-mix(in srgb, var(--color-accent) 8%, transparent);
}
tbody tr { transition: background var(--w-dur-raise) var(--w-ease-standard); }
tbody tr:hover { background: color-mix(in srgb, var(--color-accent) 4%, transparent); }
tbody tr.is-hidden { display: none; }

/* Coverage filter — pill search under ledger caption */
.w-ledger-filter {
  display: flex;
  gap: var(--sp-2);
  align-items: center;
  max-width: var(--w-page-max);
  margin: 0 0 var(--sp-3);
}
.w-ledger-filter input {
  flex: 1 1 auto;
  min-height: 42px;
  padding: 0 var(--sp-4);
  border-radius: var(--radius-pill);
  border: var(--hairline) solid var(--border-on-glass);
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
  font-size: var(--w-t-body-sm);
  outline: none;
}
.w-ledger-filter input:focus { border-color: var(--accent-border); box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 14%, transparent); }
.w-ledger-filter button {
  min-height: 42px;
  padding: 0 var(--sp-4);
  border-radius: var(--radius-pill);
  border: var(--hairline) solid var(--border-on-glass);
  background: var(--fill-card);
  color: var(--color-text);
  font-size: var(--w-t-note);
  font-weight: var(--w-w-label);
  cursor: pointer;
  transition: background var(--w-dur-raise) var(--w-ease-standard), transform var(--w-dur-wow) var(--w-ease-wow);
}
.w-ledger-filter button:hover { background: color-mix(in srgb, var(--color-accent) 10%, var(--fill-card)); transform: translateY(-1px); }
.w-ledger-filter button:active { transform: scale(0.97); }

/* Every UI block — cards, lad columns, proof, man, proc, feat, prom, portal */
.w-lad-col, .w-proof-card, .w-man-item, .w-proc-item, .w-feat-item, .w-prom-item, .w-portal-item, .w-ros-seat, .w-stg-row, .w-jrn-leg, .faq details, .cta, .callout, .entries .entry {
  transition: transform var(--w-dur-wow) var(--w-ease-wow), box-shadow var(--w-dur-raise) var(--w-ease-standard), border-color var(--w-dur-raise) var(--w-ease-standard), background var(--w-dur-raise) var(--w-ease-standard);
}
.w-lad-col:hover, .w-proof-card:hover, .w-prom-item:hover, .w-portal-item:hover, .w-ros-seat:hover, .w-jrn-leg:hover, .w-stg-row:hover {
  transform: translateY(-2px);
}
.w-lad-col:hover { box-shadow: 0 8px 24px color-mix(in srgb, var(--color-accent) 8%, transparent); }
.w-portal-cta { max-width: var(--w-page-max) !important; margin-block: var(--w-sec-y); padding: var(--sp-7); background: var(--color-surface); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-xl); box-shadow: var(--shadow-md); }
.w-portal-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--w-grid-gap); margin: var(--sp-5) 0; padding: 0; list-style: none; }
.w-portal-item { padding: var(--sp-4); background: var(--fill-card); border: var(--hairline) solid var(--border-on-glass-soft); border-radius: var(--radius-lg); }
.w-portal-actions { display: flex; gap: var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-5); }
.h-cta--ghost { background: transparent; color: var(--color-accent); border-color: var(--accent-border); box-shadow: none; }
.h-cta--ghost:hover { background: var(--accent-fill); }

table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--w-t-body-sm);
  letter-spacing: var(--track-15-5);
}

caption {
  caption-side: bottom;
  text-align: start;
  padding: var(--sp-3) var(--sp-4);
  border-top: var(--hairline) solid var(--color-border);
}

thead th {
  text-align: start;
  font-size: var(--w-t-caps);
  font-weight: var(--w-w-display);
  text-transform: uppercase;
  letter-spacing: var(--track-caps-md);
  color: var(--color-text-muted);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: var(--hairline) solid var(--color-border);
  white-space: nowrap;
}

tbody th {
  text-align: start;
  font-weight: var(--w-w-label);
  color: var(--color-text);
  vertical-align: top;
  padding: var(--sp-3) var(--sp-4);
  min-width: 180px;
}

tbody td {
  color: var(--color-text-muted);
  vertical-align: top;
  padding: var(--sp-3) var(--sp-4);
  line-height: 1.4;
}

tbody tr + tr th, tbody tr + tr td {
  border-top: var(--hairline) solid var(--color-border);
}

td.num {
  font-variant-numeric: tabular-nums;
  font-weight: var(--w-w-display);
  color: var(--color-text);
  white-space: nowrap;
}

/* ── Plans ──────────────────────────────────────────────────────────────── */

.plans {
  max-width: var(--w-page-max);
  display: grid;
  /* 260 not 280: at the 320px floor, 28px of gutter leaves 292px, and a 280px
     minimum would fit with nothing to spare. */
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--w-grid-gap);
  margin: 0 0 var(--sp-5);
}

.plan {
  position: relative;
  background: var(--color-surface);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--w-card-pad);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  box-shadow: var(--shadow-md);
  overflow: hidden;
  transition: transform var(--w-dur-wow) var(--w-ease-wow), box-shadow var(--w-dur-raise) var(--w-ease-standard);
}
.plan:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg), 0 0 0 1px color-mix(in srgb, var(--color-accent) 10%, transparent); }

/* The one featured ring — 1.5px, per the app's border rule. */
.plan--feature { border: var(--border-featured) solid var(--accent-border); }

/* Specular sheen: job 1, the ONLY light treatment on a surface. */
.plan--feature::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--gradient-specular);
  pointer-events: none;
}

.plan > * { position: relative; }

.plan .kicker { color: var(--color-accent); margin: 0; }

.plan-n {
  margin: 0 !important;
  font-size: var(--w-t-h4) !important;
  line-height: var(--w-t-h4-lh);
  font-weight: var(--w-w-display);
  letter-spacing: var(--track-21);
}

.plan-tag {
  margin: 0 !important;
  color: var(--color-text-muted);
  font-size: var(--w-t-body-sm);
  letter-spacing: var(--track-15-5);
  line-height: var(--w-t-body-sm-lh);
}

.plan-l {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--sp-2);
  font-size: var(--w-t-body-sm);
  letter-spacing: var(--track-15-5);
  line-height: var(--w-t-body-sm-lh);
  color: var(--color-text-muted);
}

.plan-l li {
  position: relative;
  padding-inline-start: 18px;
}

.plan-l li::before {
  content: "+";
  position: absolute;
  left: 0;
  top: -1px;
  color: var(--color-accent);
  font-weight: var(--w-w-display);
}

.plan-lim {
  margin: 0 !important;
  padding-top: var(--sp-3);
  border-top: var(--hairline) solid var(--color-border);
  font-size: var(--w-t-note);
  letter-spacing: var(--track-13-5);
  line-height: var(--w-t-note-lh);
  color: var(--color-text-muted);
}

/* The limit is stated in a WORD, never an icon or an empty cell. */
.plan-lim-k {
  display: inline-block;
  margin-inline-end: var(--sp-2);
  font-size: var(--w-t-caps);
  font-weight: var(--w-w-display);
  text-transform: uppercase;
  letter-spacing: var(--track-caps-md);
  color: var(--tone-warn-fg);
}

.plan-price {
  margin: auto 0 0 !important;
  padding-top: var(--sp-3);
  font-size: var(--w-t-note);
  letter-spacing: var(--track-13-5);
  color: var(--color-text-muted);
  font-weight: var(--w-w-label);
}

/* ── FAQ ────────────────────────────────────────────────────────────────── */

.faq {
  max-width: var(--w-measure);
  display: grid;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-5);
}

.faq details {
  background: var(--color-surface);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-sm);
  transition: border-color var(--w-dur-raise) var(--w-ease-standard), box-shadow var(--w-dur-raise) var(--w-ease-standard);
}
.faq details:hover { border-color: color-mix(in srgb, var(--color-accent) 14%, var(--color-border)); }
.faq details[open] { border-color: var(--accent-border); box-shadow: var(--shadow-sm); }

.faq summary {
  cursor: pointer;
  list-style: none;
  padding: var(--sp-3) var(--sp-4);
  min-height: var(--min-tap);
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  font-size: var(--w-t-body-sm);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-15-5);
}
.faq summary::-webkit-details-marker { display: none; }

.faq summary::after {
  content: "＋";
  margin-inline-start: auto;
  color: var(--color-text-muted);
  font-weight: var(--w-w-display);
  flex: none;
}
.faq details[open] summary::after { content: "－"; }

.faq-a {
  padding: var(--sp-3) var(--sp-4) var(--sp-4);
  border-top: var(--hairline) solid var(--color-border);
}

.faq-a p {
  margin: 0 0 var(--sp-3);
  color: var(--color-text-muted);
  font-size: var(--w-t-body-sm);
  letter-spacing: var(--track-15-5);
  line-height: var(--w-t-body-sm-lh);
}
.faq-a p:last-child { margin-bottom: 0; }

/* ── CTA ────────────────────────────────────────────────────────────────── */

.cta {
  max-width: var(--w-measure);
  background: var(--color-surface-alt);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-xl);
  padding: var(--w-card-pad);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
  margin: 0 0 var(--sp-5);
  box-shadow: var(--shadow-md);
}

.cta-t {
  margin: 0 !important;
  flex: 1 1 260px;
  font-size: var(--w-t-body);
  letter-spacing: var(--track-17);
  line-height: var(--w-t-body-lh);
}

.cta .fine { flex-basis: 100%; margin: 0; }

/* CONTRAST: --color-bg on --color-accent is 5.82:1 and passes AA at any size.
   White on accent is 3.24:1 and passes only as large text — the app's own
   .cs-cta does exactly that at 16.5/800, which is a pre-existing app-side
   finding reported in docs/WEBSITE.md, not a look this site should mirror. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 49px;
  padding: 0 var(--sp-6);
  border-radius: var(--radius-pill);
  background: var(--color-accent);
  color: var(--color-bg);
  font-size: var(--w-t-cta);
  font-weight: var(--w-w-display);
  letter-spacing: var(--track-19);
  text-decoration: none;
  border: var(--hairline) solid transparent;
  transition: opacity var(--w-dur-press) var(--w-ease-standard),
              transform var(--w-dur-wow) var(--w-ease-wow),
              box-shadow var(--w-dur-raise) var(--w-ease-standard);
  box-shadow: 0 10px 28px color-mix(in srgb, var(--color-accent) 18%, transparent);
}
.btn:hover { opacity: 0.96; transform: translateY(-1px); box-shadow: 0 14px 36px color-mix(in srgb, var(--color-accent) 22%, transparent); }

/* pressFeedback — <Tap>'s pressed opacity and scale, at its settle time. */
.btn:active { opacity: var(--press-dim); transform: scale(0.97); }

/* Accent on --color-accent-soft is 4.83:1: the app's own selected-option
   treatment, and it passes AA at any size. */
.btn--quiet {
  background: var(--color-accent-soft);
  color: var(--color-accent);
  border-color: var(--accent-border);
}

/* ── Changelog entries ──────────────────────────────────────────────────── */

.entries {
  max-width: var(--w-measure);
  margin: 0 0 var(--sp-5);
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--sp-9);
}

.entry {
  border-inline-start: var(--border-ring) solid var(--color-border);
  padding-inline-start: var(--sp-5);
}

.entry-d {
  margin: 0 0 var(--sp-2) !important;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  font-size: var(--w-t-note);
  letter-spacing: var(--track-13-5);
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

.entry-tag {
  font-size: var(--w-t-caps);
  font-weight: var(--w-w-display);
  text-transform: uppercase;
  letter-spacing: var(--track-caps-md);
  color: var(--color-text-muted);
  background: var(--fill-card);
  border: var(--hairline) solid var(--border-on-glass-soft);
  border-radius: var(--radius-pill);
  padding: 3px 8px;
}

.entry-t { margin: 0 0 var(--sp-3) !important; font-size: var(--w-t-h4) !important; }

/* ═══ THE STAGE ══════════════════════════════════════════════════════════════

   THE APP'S ONE COMPOSITION: a quiet hairline map as a backdrop, with a single
   elevated surface floating over it. Not a theme — one card, once per page, on
   five pages. Six of the eleven pages get no stage and no device frame at all,
   and that restraint is precisely what makes the five that do read as deliberate.

   Real Albers geometry from data/us-states.baked.json, painted the way
   components/UsAtlas.tsx paints treatment:"outline". ONE HUE — --color-accent
   and no other, ever. Status red/amber/green is a reviewed legal finding and may
   not appear on a map that has made none.

   Nothing is lit until the reader picks a state. The site does not geolocate,
   does not read a locale hint, and pre-lights nothing: residency is ASKED, never
   guessed (lib/onboarding.ts founding constraint 3).                          */

.w-stage {
  max-width: var(--w-page-max) !important;
  margin-inline: calc(-1 * var(--w-stage-bleed));
  margin-block: var(--sp-7) var(--w-sec-y);
}

/* The HOME hero is exempt from the page-max clamp on purpose: it is the one
   surface that runs to the screen edges (see the full-bleed block below), and
   a max-width here was silently folding it back into the column. */
.w-hero {
  max-width: none !important;
  margin-block: 0 var(--w-sec-y);
}

/* ── THE HOME PAGE OPENS ON THE COMPOSITION ──────────────────────────────────
   .doc--hero trims the document's top padding, because on this one page the
   first thing is not a heading over prose — it is the hero, and it starts at
   the top of the column. */
.doc--hero { padding-block-start: var(--sp-5); }

/* The headline block, held to --w-measure so the h1 breaks where a headline
   should and not at the full 1120 of the stage below it. */
.w-hero-copy {
  max-width: var(--w-measure);
  margin-bottom: var(--sp-6);
}

/* `.doc h1` is (0,1,1) and would otherwise win over a lone class — the same
   reason `.doc h2.w-hero-h` needed the element in its selector. */
/* THE HEADLINE SCALES. --w-t-hero (64px) is sized for a full-width column; in a
   flanking column at 1120 it is ~325px wide and 64px broke the line five times.
   clamp() lets it reach the full hero size on a wide screen and step down to the
   display band (29) rather than shattering. line-height follows in em so the
   1.063 ratio the token declares survives the scaling. */
.doc h1.w-hero-h {
  margin: 0 0 var(--sp-4);
  /* 2.6vw, not 3.1: the copy column is ~0.92fr of the deck, so the headline has
     roughly a quarter of the page to break in. Measured at 1440 this lands at
     37px in a 285px column — four words a line, which is a headline. 3.1vw put
     44px in a 221px column and broke it six times. */
  font-size: clamp(28px, 2.6vw, 46px);
  line-height: 1.07;
  letter-spacing: var(--w-track-em);
  font-weight: var(--w-w-display);
}

.w-hero-lede {
  margin: 0;
  max-width: var(--w-measure);
  font-size: var(--w-t-lead);
  line-height: var(--w-t-lead-lh);
  color: var(--color-text-muted);
}
.w-hero-lede strong { color: var(--color-text); font-weight: var(--w-w-label); }


/* ── THE MAP IS THE HERO'S GROUND, NOT A PANEL IN IT ─────────────────────────
   The atlas fills the entire hero box behind everything, the way the app draws
   it: full-bleed, `top 0, height 470, behind everything. No card, no rectangle`
   (ONBOARDING-HANDOFF §3). A map inside a bordered box is a map being presented;
   a map behind the content is a map being USED, which is the whole argument.

   The hero owns a min-height so the ground has somewhere to be even before the
   copy fills it, and `isolation: isolate` keeps the wash and the atlas in their
   own stacking context so neither can reach the sticky nav above. */
/* THE HERO IS THE WHOLE VIEWPORT'S, NOT THE COLUMN'S. It breaks out of the
   1120px wrap to the screen edges — the "map in a box" was the column's own
   framing — and then pads its children back to the wrap's grid, so the copy,
   the card, the band and the legend still sit on the page's column while the
   GROUND runs edge to edge. body carries overflow-x: clip (below) so the
   100vw arithmetic cannot mint a sideways scrollbar. */
.w-hero {
  position: relative;
  isolation: isolate;
  min-height: clamp(680px, 88vh, 960px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: clamp(48px, 7vh, 76px) var(--sp-6);
  margin-inline: calc(50% - 50vw);
  padding-inline: max(var(--w-page-pad), calc(50vw - var(--w-page-max) / 2 + var(--w-page-pad)));
  background:
    radial-gradient(ellipse 900px 520px at 50% -4%, color-mix(in srgb, var(--color-accent) 8%, transparent) 0%, transparent 62%),
    linear-gradient(180deg, color-mix(in srgb, var(--surface-solid) 18%, transparent) 0%, transparent 34%);
}

/* On HTML, never on BODY: a body with clipped overflow stops propagating to
   the viewport and Chromium then refuses every programmatic window scroll —
   measured here as the jump fix silently no-opping. On the root, the clip
   propagates as x-only: sideways scroll stays impossible (the site's standing
   promise) and vertical scrolling keeps working. */
html { overflow-x: clip; }

/* ── The ground is a CAMERA VIEWPORT, not a picture frame ────────────────────
   At rest (no script) the atlas letterboxes inside the hero and the whole
   country is simply visible — the finished state. When motion.js enhances it
   (.js-pan) the container becomes a native scroll viewport over an OVERSIZED
   atlas: trackpads and touch pan it natively, a mouse drags it (grab cursor,
   scrollLeft/scrollTop — never a transform, so it composes with the :target
   camera), and the script centres it on load and re-centres when a state
   opens. Scrollbars are hidden because the pan is spatial, not documental. */
.w-deck-map {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;    /* re-enabled on the state anchors themselves */
  display: flex;
  align-items: center;
  justify-content: center;
}

.w-deck-map .w-atlas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0.85;
}

.w-deck-map.js-pan {
  pointer-events: auto;
  display: block;
  overflow: auto;
  overscroll-behavior: contain;
  touch-action: pan-x pan-y;
  cursor: grab;
  scrollbar-width: none;
  outline-offset: -3px;
}
.w-deck-map.js-pan::-webkit-scrollbar { display: none; }
.w-deck-map.js-pan.js-grabbing { cursor: grabbing; user-select: none; }

.w-deck-map.js-pan .w-atlas {
  position: static;
  width: 148%;
  max-width: none;   /* the responsive reset would fold 148% back to 100% */
  min-width: 1280px;
  height: auto;
  opacity: 0.85;
}

/* Gradient job 2, the screen wash. It lives on the HERO, not on the scroll
   container, so the bloom stays glued to the viewport while the map pans
   beneath it — light on the instrument's glass, not painted on the map. */
.w-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse at 50% 45%,
      color-mix(in srgb, var(--color-accent) 13%, transparent) 0%,
      transparent 62%),
    linear-gradient(to bottom,
      color-mix(in srgb, var(--color-bg) 55%, transparent) 0%,
      color-mix(in srgb, var(--color-bg) 10%, transparent) 38%,
      color-mix(in srgb, var(--color-bg) 72%, transparent) 100%);
}

/* The one scroll-behavior override the nav fix needs: while motion.js is
   moving the hash, the page must not animate a scroll it is about to undo. */
html.js-nav { scroll-behavior: auto !important; }

/* ── The deck rides ON the ground ────────────────────────────────────────────
   Two columns now, not three: the map left the row and became the floor, so the
   copy and the card are what sit on it, pushed to the outer edges with the map
   showing between them. */
.w-deck {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1.06fr) minmax(360px, 0.94fr);
  align-items: center;
  gap: clamp(48px, 7vw, 96px);
  pointer-events: none;    /* the gap between the columns stays map, and clickable */
}
.w-deck-copy, .w-float { pointer-events: auto; }

.w-deck-copy { min-width: 0; max-width: 520px; }

/* ALL-CAPS micro-label: now a pill badge — surprise, still uppercase, still 1.4px tracking. */
.w-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 var(--sp-4);
  padding: 6px 12px 6px 10px;
  font-size: var(--w-t-caps);
  font-weight: var(--w-w-display);
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--color-accent);
  background: color-mix(in srgb, var(--color-accent) 10%, transparent);
  border: var(--hairline) solid color-mix(in srgb, var(--color-accent) 22%, transparent);
  border-radius: var(--radius-pill);
  box-shadow: 0 2px 10px color-mix(in srgb, var(--color-accent) 10%, transparent);
}
.w-eyebrow::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--color-accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-accent) 18%, transparent);
  flex: none;
}

@media (max-width: 899.98px) {
  .w-hero { min-height: 0; padding-block: var(--sp-6); }
  .w-deck { grid-template-columns: 1fr; gap: var(--sp-6); }
  .w-deck-copy { max-width: 620px; }
}

/* `.doc h1` is (0,1,1) and would otherwise win over a lone class — the same
   reason `.doc h2.w-hero-h` needed the element in its selector. */
/* THE HEADLINE SCALES. --w-t-hero (64px) is sized for a full-width column; in a
   flanking column at 1120 it is ~325px wide and 64px broke the line five times.
   clamp() lets it reach the full hero size on a wide screen and step down to the
   display band (29) rather than shattering. line-height follows in em so the
   1.063 ratio the token declares survives the scaling. */
.doc h1.w-hero-h {
  margin: 0 0 var(--sp-4);
  font-size: clamp(38px, 3.5vw, 56px);
  line-height: 1.02;
  letter-spacing: var(--w-track-em);
  font-weight: var(--w-w-display);
}
.doc h1.w-hero-h .w-hw:nth-last-child(-n+3) {
  background: linear-gradient(90deg, var(--color-accent), color-mix(in srgb, var(--color-ok) 78%, var(--color-accent)));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

.w-hero-lede {
  margin: 0;
  max-width: var(--w-measure);
  font-size: var(--w-t-lead);
  line-height: var(--w-t-lead-lh);
  color: var(--color-text-muted);
}
.w-hero-lede strong { color: var(--color-text); font-weight: var(--w-w-label); }



.w-hero-h {
  margin: 0 0 var(--sp-4);
  font-size: var(--w-t-hero);
  line-height: var(--w-t-hero-lh);
  letter-spacing: var(--w-track-em);
  font-weight: var(--w-w-display);
}

.w-hero .w-hero-lede {
  max-width: 48ch;
  padding-left: 14px;
  border-left: 2px solid color-mix(in srgb, var(--color-accent) 42%, transparent);
}
.w-hero-lede strong { color: var(--color-text); font-weight: var(--w-w-label); }

/* The map's caption. It belongs to the MAP, but the map has no room for a line
   of prose under it any more, so it sits at the foot of the copy column as the
   last thing read before the eye moves to the instrument. */
.w-mapnote {
  margin: var(--sp-4) 0 0;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 5px 10px 5px 8px;
  font-size: var(--w-t-fine);
  line-height: var(--w-t-fine-lh);
  letter-spacing: var(--track-11-5);
  color: var(--color-text-muted);
  background: color-mix(in srgb, var(--surface-solid) 56%, transparent);
  border: var(--hairline) solid var(--border-on-glass-soft);
  border-radius: var(--radius-pill);
  backdrop-filter: blur(8px);
}

.w-mapnote::before {
  content: "";
  width: 7px;
  height: 7px;
  flex: none;
  border-radius: 999px;
  background: var(--color-accent);
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--color-accent) 12%, transparent);
}

.w-hero-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
  margin-top: var(--sp-5);
}

.w-hero-action {
  min-height: var(--min-tap);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 0 18px;
  border-radius: var(--radius-pill);
  font-size: var(--w-t-note);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-13-5);
  text-decoration: none;
  transition: transform var(--w-dur-press) var(--w-ease-standard),
              background var(--w-dur-raise) var(--w-ease-standard),
              color var(--w-dur-raise) var(--w-ease-standard),
              box-shadow var(--w-dur-raise) var(--w-ease-standard);
}

.w-hero-action:hover { text-decoration: none; transform: translateY(-1px); }
.w-hero-action:active { transform: scale(0.98); }
.w-hero-action:focus-visible { outline: var(--border-featured) solid var(--color-accent); outline-offset: 3px; }

.w-hero-action--primary {
  color: var(--color-bg);
  background: var(--color-accent);
  box-shadow: 0 10px 28px color-mix(in srgb, var(--color-accent) 24%, transparent);
  position: relative;
  overflow: hidden;
}
.w-hero-action--primary::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 30%, color-mix(in srgb, white 22%, transparent) 50%, transparent 70%);
  transform: translateX(-100%);
  transition: transform 600ms var(--w-ease-standard);
}
.w-hero-action--primary:hover::after { transform: translateX(100%); }
.w-hero-action--primary:hover { color: var(--color-bg); background: color-mix(in srgb, var(--color-accent) 88%, white); }
.w-hero-action--primary span { transition: transform var(--w-dur-raise) var(--w-ease-standard); }
.w-hero-action--primary:hover span { transform: translateX(3px); }

.w-hero-action--quiet {
  color: var(--color-text);
  background: color-mix(in srgb, var(--surface-solid) 62%, transparent);
  box-shadow: inset 0 0 0 var(--hairline) var(--border-on-glass);
}
.w-hero-action--quiet:hover { background: color-mix(in srgb, var(--surface-solid) 82%, transparent); }

/* ── THE HERO STAGE, SIDE BY SIDE ────────────────────────────────────────────
   The card and the map are now PEERS on one row, not a card sitting on top of a
   backdrop. The card leads because it is the thing that answers a question; the
   map is the thing you ask with. Reversed at the DOM level too: the card comes
   FIRST in source order, so the reading order and the tab order both reach the
   answer before the instrument.

   Below 900 it stacks — map first, then card — because on a phone the map has
   to be seen before a card about it makes any sense. */
/* `.doc h2` is (0,1,1) and would otherwise win over a lone class, printing the
   hero heading at section-opener size with 128px of section rhythm above it. */
.doc h2.w-hero-h {
  font-size: var(--w-t-h3);
  line-height: var(--w-t-h3-lh);
  font-weight: var(--w-w-display);
  letter-spacing: var(--track-29);
  margin: 0 var(--w-stage-bleed) var(--sp-2);
  max-width: var(--w-measure);
  text-wrap: balance;
}

.w-hero-sub {
  margin: 0 var(--w-stage-bleed) var(--sp-5);
  max-width: var(--w-measure);
  color: var(--color-text-muted);
  font-size: var(--w-t-body-sm);
  line-height: var(--w-t-body-sm-lh);
  letter-spacing: var(--track-15-5);
}

/* ── The backdrop ───────────────────────────────────────────────────────────
   Sized in absolute height, never aspect-ratio: the card overlaps it by a
   FRACTION OF THAT HEIGHT, and a percentage margin resolves against the
   container's WIDTH — so an aspect-ratio box with a max-height silently breaks
   the overlap at exactly the widths where it matters. One number, used twice. */
.w-backdrop {
  position: relative;
  height: var(--w-backdrop-h);
  overflow: hidden;          /* the country bleeds; it never boxes */
  pointer-events: none;      /* re-enabled on the anchors below */
}

/* THE MAP FILLS ITS COLUMN. A fixed height letterboxes it: the atlas viewBox is
   960×600 and `preserveAspectRatio: meet` fits the WIDTH, so a 470px column with
   a 600px box drew a 294px map floating in 300px of nothing — the centrepiece
   rendered smaller than the card beside it. Driving height from the viewBox's
   own ratio makes the column width the only variable. */
/* The ratio follows the CONTENT viewBox (see VIEW in hero.mjs), not the baked
   canvas — 726.9 × 545.3. A 960/600 box around a 727-wide country letterboxes
   the centrepiece by a quarter of its width. */
.w-hero .w-backdrop {
  height: auto;
  aspect-ratio: 727 / 545;
  /* The map must out-measure the card beside it at rest, or "centrepiece" is a
     claim the layout does not keep. 380 is the resting card's height at
     --w-page-max; measured, not guessed. */
  min-height: 380px;
}

.w-backdrop .w-atlas { position: absolute; inset: 0; width: 100%; height: 100%; }

/* Gradient job 3 — the legibility scrim, glued to the MAP and not to the card,
   which is where UpgradeSheet.tsx puts it. */
.w-backdrop::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--w-gradient-hero);
  pointer-events: none;
}

/* ── The floating card ──────────────────────────────────────────────────────
   <Glass radius={28} elevation="lg" solid>, exactly.

   NO BORDER. components/Glass.tsx:101-114 removed the white rim twice at the
   owner's insistence; separation is the scrim plus the shadow and nothing else.
   Adding a hairline here is the single most common way a web port of this
   composition stops looking like the app.

   THE RADIUS NEVER SHRINKS. The app uses 28 on a 375pt screen. A card that
   rounds off as it narrows reads as a web template. */
/* Z-INDEX IS LOCAL, AND THAT IS THE FIX. This used to be --z-bottom-sheet (45),
   borrowed from the app's ladder — where 45 is correct, because in the app a
   BottomSheet really does sit above the top bar. On the web it put the card
   above --z-top-bar (40), so scrolling the home page drove the floating card
   straight through the nav pill.

   The app's ladder describes surfaces that share one full-screen root. These two
   do not: the header is a sticky sibling and the card is page content. The only
   thing this card has to out-paint is the atlas directly behind it, so it takes
   the smallest number that does that and joins no ladder at all. */
/* ── THE CAMERA ──────────────────────────────────────────────────────────────
   The per-state transforms are generated in map.css from the bounds in the baked
   atlas. Everything that makes the move READ lives here.

   transform-box: view-box is the load-bearing line — without it an SVG
   transform resolves against the element's own bbox, so every state would fly
   to a different, wrong place. With it, the `px` in the generated transforms
   means viewBox units, the same space the path data is in.

   THE MOVE IS SLOW ON PURPOSE. --w-dur-camera is well above the app's --w-dur-
   device ceiling because this is a camera, not a control responding to a press:
   the app's own fly-to is 1200ms (ONBOARDING-HANDOFF §5) and anything much
   faster reads as a cut rather than a move, which loses the whole point — that
   the country and the state you opened are the same object. */
.w-atlas-zoom {
  transform-box: view-box;
  transform-origin: 0 0;
  transition: transform var(--w-dur-camera) var(--w-ease-camera);
}

.w-st {
  transition: fill var(--w-dur-raise) var(--w-ease-standard),
              stroke var(--w-dur-raise) var(--w-ease-standard),
              stroke-width var(--w-dur-camera) var(--w-ease-camera);
}

/* THE LIFT — markers removed: selected state lifts via its own fill + group glow. */
.w-hero:has(:where(.w-panel:target, .w-panel:has(:target))) .w-atlas-paint {
  filter: drop-shadow(0 0 14px color-mix(in srgb, var(--color-accent) 42%, transparent));
}

/* The unselected country recedes while a state is open. Not hidden — still
   drawn, still the same country — just quieter, so the eye has somewhere to go.
   This is a FOCUS treatment and carries no finding: every other state dims by
   the same amount whatever the ledger says about it. */
.w-hero:has(:where(.w-panel:target, .w-panel:has(:target))) .w-atlas-paint { opacity: 0.72; }
.w-atlas-paint { transition: opacity var(--w-dur-camera) var(--w-ease-camera); }

.w-float {
  position: relative;
  z-index: 1;
  align-self: center;
  margin-inline: auto;
  width: min(100%, 480px);
  max-width: var(--w-float-max);
  padding: clamp(22px, 2.4vw, 30px);
  border-radius: var(--w-r-float);
  background: color-mix(in srgb, var(--surface-solid) 84%, transparent);
  -webkit-backdrop-filter: blur(24px) saturate(1.16);
  backdrop-filter: blur(24px) saturate(1.16);
  box-shadow: var(--shadow-lg), inset 0 0 0 var(--hairline) var(--border-on-glass-soft), 0 12px 32px color-mix(in srgb, var(--color-accent) 10%, transparent);
  border: 0;
  overflow: hidden;
  transition: box-shadow var(--w-dur-raise) var(--w-ease-standard), transform var(--w-dur-wow) var(--w-ease-wow);
}
.w-float:hover { box-shadow: var(--shadow-lg), inset 0 0 0 var(--hairline) var(--accent-border), 0 18px 44px color-mix(in srgb, var(--color-accent) 16%, transparent); transform: translateY(-2px); }

/* Specular — gradient job 1, the only light treatment on a Glass. */
.w-float::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--gradient-specular);
  pointer-events: none;
}
.w-float > * { position: relative; }

.w-float .kicker { color: var(--color-accent); }

.w-float-h {
  margin: 0 0 var(--sp-3) !important;
  font-size: var(--w-t-h4);
  line-height: var(--w-t-h4-lh);
  font-weight: var(--w-w-display);
  letter-spacing: var(--track-21);
  text-wrap: pretty;
}

.w-float-b {
  margin: 0 !important;
  color: var(--color-text-muted);
  font-size: var(--w-t-body-sm);
  line-height: var(--w-t-body-sm-lh);
  letter-spacing: var(--track-15-5);
  text-wrap: pretty;
}

.w-float-b + .w-float-b { margin-top: var(--sp-3) !important; }

.w-float-detail {
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr);
  gap: var(--sp-3);
  align-items: start;
  margin-top: var(--sp-4);
  padding-top: var(--sp-4);
  border-top: var(--hairline) solid var(--border-on-glass-soft);
}

.w-float-detail-mark {
  position: relative;
  width: 18px;
  height: 18px;
  margin-top: 2px;
  border-radius: 6px;
  border: var(--hairline) solid color-mix(in srgb, var(--color-accent) 62%, transparent);
  background: color-mix(in srgb, var(--color-accent) 11%, transparent);
}

.w-float-detail-mark::after {
  content: "";
  position: absolute;
  width: 5px;
  height: 5px;
  inset: 6px;
  border-radius: 999px;
  background: var(--color-accent);
}

.w-float-detail--resting { margin-top: var(--sp-3); padding-top: var(--sp-3); }
.w-float-detail--resting .w-float-detail-mark { border-radius: 999px; }
.w-float-detail .w-float-b { margin: 0 !important; }
.w-float-detail .w-cap {
  margin: 0 !important;
  padding: 0;
  border: 0;
  min-height: 0;
}

/* Below 720 the card stops floating and becomes a block under the map: at that
   width the overlap eats the map rather than sitting on it. */
@media (max-width: 719.98px) {
  .w-float { margin-inline: var(--w-float-inset); }
}

/* overflow: visible, or the 1.2px selected stroke clips at the viewBox edge on
   Maine and Florida. The wrapper is what heroDraw clips, so the caption and the
   legend below are never swept. */
.w-atlas-wrap { overflow: hidden; }
.w-atlas { display: block; width: 100%; height: auto; overflow: visible; }

.w-st {
  fill: var(--w-map-fill);
  stroke: var(--w-map-stroke);
  stroke-width: var(--w-map-width);
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  transition: fill var(--w-dur-raise) var(--w-ease-standard),
              stroke var(--w-dur-raise) var(--w-ease-standard),
              stroke-width var(--w-dur-raise) var(--w-ease-standard);
}

/* stateRaise — now with subtle scale + brighter stroke */
@media (hover: hover) and (pointer: fine) {
  .w-st:hover {
    fill: var(--w-map-fill-hover);
    stroke: var(--w-map-stroke-hover);
    stroke-width: var(--w-map-width-hover);
    filter: drop-shadow(0 2px 6px color-mix(in srgb, var(--color-accent) 18%, transparent));
  }
}
.w-st { transition: fill var(--w-dur-raise) var(--w-ease-standard), stroke var(--w-dur-raise) var(--w-ease-standard), stroke-width var(--w-dur-raise) var(--w-ease-standard), filter var(--w-dur-raise) var(--w-ease-standard); }

/* THE MAP IS TABBABLE, SO THE MAP HAS TO SHOW FOCUS.
   Every state is an <a>, which means the whole country is in the tab order —
   and until now the only feedback was the browser's default ring drawn around
   the path's bounding BOX, which for a state like Florida or Maryland is a
   rectangle bearing no resemblance to the shape it is announcing. The stroke
   carries it instead: the focused state takes the hover treatment plus an
   accent stroke, so the shape itself is what lights up.

   `:focus-visible` only, so a mouse click does not leave a state outlined
   behind the panel it just opened. */
.w-atlas a:focus { outline: none; }
.w-atlas a:focus-visible .w-st,
.w-st:focus-visible {
  fill: var(--w-map-fill-hover);
  stroke: var(--color-accent);
  stroke-width: var(--w-map-width-hover);
}

/* The locator marker. Accent only — a status hue pulsing over someone's own
   state would read as a legal answer. */
.w-mk { display: none; }

/* The caption line, inside the floating card. One element, always in the DOM,
   never reflowing — `min-height` reserves its row so naming a state on hover
   cannot nudge the paragraph under it. */
.w-cap {
  margin: var(--sp-3) 0 0 !important;
  padding-top: var(--sp-3);
  border-top: var(--hairline) solid var(--border-on-glass-soft);
  min-height: 26px;
  font-size: var(--w-t-body-sm);
  line-height: var(--w-t-body-sm-lh);
  letter-spacing: var(--track-15-5);
  color: var(--color-text-muted);
}

.w-cap-n { display: none; }
.w-cap b { color: var(--color-text); font-weight: var(--w-w-label); }

.w-legend {
  margin: var(--sp-4) var(--w-stage-bleed) 0 !important;
  font-size: var(--w-t-fine);
  line-height: var(--w-t-fine-lh);
  letter-spacing: var(--track-11-5);
  color: var(--color-text-muted);
  max-width: var(--w-measure);
  /* Above the atlas layer. Unpositioned, this paragraph painted BENEATH the
     positioned map's washes, which lit some words and dimmed others — a legal
     disclaimer is the one line that must never render patchily. */
  position: relative;
  z-index: 3;
}

/* ── The map's own targets ──────────────────────────────────────────────────
   Every state path is wrapped in an SVG <a href="#s-XX">, so a click on the map
   opens the same panel the list opens and the URL becomes shareable. The paint
   group is aria-hidden and every anchor inside it is tabindex="-1": CLICKABLE,
   NOT FOCUSABLE. Duplicating 51 targets in the SVG and 51 in the list would
   double the tab order and the screen-reader inventory for no gain, and
   aria-hidden on a non-focusable subtree is legal ARIA. */
.w-atlas a { pointer-events: auto; cursor: pointer; }

/* ── The list — the ONE accessible control ─────────────────────────────────
   MEASURED, at three widths, as the smaller bbox side of each state's geometry:

     map width 1160 → 10 of 51 states are under the 44px minimum responder
     map width  860 → 13 of 51
     map width  380 → 50 of 51   (DC 1×1, HI 2×2, RI 4×5)

   That is the proof, not an opinion: the map can never be the primary control
   at any width. Rhode Island is a 44px ROW here, never a 4px target there. */

.w-pick {
  max-width: var(--w-page-max);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  padding: var(--w-card-pad);
  margin: var(--sp-5) 0;
  box-shadow: var(--shadow-sm);
}

.w-pick-legend {
  margin: 0 0 var(--sp-3) !important;
  font-size: var(--w-t-caps);
  font-weight: var(--w-w-display);
  text-transform: uppercase;
  letter-spacing: var(--track-caps-lg);
  color: var(--color-text-muted);
}

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

.w-chip {
  display: flex;
  align-items: center;
  min-height: var(--min-tap);
  padding: 0 var(--sp-3);
  border-radius: var(--radius-sm);
  color: var(--color-text-muted);
  font-size: var(--w-t-note);
  letter-spacing: var(--track-13-5);
  text-decoration: none;
  transition: background var(--w-dur-press) var(--w-ease-standard),
              transform var(--w-dur-press) var(--w-ease-standard);
}
.w-chip:hover { background: var(--fill-card); color: var(--color-text); text-decoration: none; }
.w-chip:active { transform: scale(0.97); opacity: var(--press-dim); }
.w-chip:focus-visible {
  outline: var(--border-featured) solid var(--color-accent);
  outline-offset: 3px;
  border-radius: var(--radius-xs);
}

/* ── The per-state panel ────────────────────────────────────────────────────
   :target, so a click on the map and a click in the list open the same thing,
   the URL is shareable, and the back button works — with no script and with no
   change to `default-src 'none'`.

   Hover and click are deliberately DIFFERENT. Hover only renames the caption
   line; it never opens a panel. Pointing at something is not intent, and a panel
   that appears under a passing cursor turns a legal-adjacent readout into a
   mouse accident. */

/* THE PANEL IS NOW THE CARD'S CONTENTS, not a box below it. Opening a state
   swaps what the floating card says, which is the app's own composition —
   full-bleed map, one card over it, the card is about the place you picked
   (ONBOARDING-HANDOFF §3). It therefore carries no border, no second radius and
   no second background: it IS the card, and a card inside a card is the tell
   that a web port has stopped looking like the app. */
.w-panel {
  display: none;
  scroll-margin-block: 96px;   /* clears the sticky header */
}
/* :has(:target) as well as :target — the pager's anchors point INSIDE the panel,
   so paging must not read as closing it. See the pager note in hero.mjs. */
.w-panel:target,
.w-panel:has(:target) { display: block; animation: w-panel-in var(--w-dur-panel) var(--w-ease-standard) both; }
.w-panel:focus { outline: var(--border-featured) solid var(--color-accent); outline-offset: 3px; }

@keyframes w-panel-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* ── The identity row ────────────────────────────────────────────────────────
   Flag, code, name. Lifted from the paywall's own card header, which opens with
   the jurisdiction chip before it says anything else (see the reference capture
   in onboarding/uploads: "NJ · MERCER COUNTY · NEW JERSEY"). Knowing WHERE you
   are reading about comes before what it says. */
.w-panel-id {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0 0 var(--sp-4) !important;
}

/* A FLAG IS IDENTITY, NEVER A STATUS. No tint, no greyscale, no badge — see
   scripts/make-flags.mjs. The rim is a neutral hairline so a white-heavy flag
   does not dissolve into the card. Redesigned: larger frame, soft shadow, subtle lift. */
.w-flag {
  width: 30px;
  height: auto;
  flex: none;
  border-radius: 5px;
  border: var(--hairline) solid var(--border-on-glass);
  box-shadow: 0 2px 8px color-mix(in srgb, var(--color-bg) 22%, transparent), 0 1px 2px color-mix(in srgb, var(--color-bg) 14%, transparent);
  background: var(--color-surface);
}

.w-panel-iso {
  font-size: var(--w-t-fine);
  font-weight: var(--w-w-display);
  letter-spacing: var(--track-11-5);
  color: var(--color-accent);
  text-transform: uppercase;
}
.w-panel-name {
  font-size: var(--w-t-fine);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-11-5);
  color: var(--color-text-muted);
  text-transform: uppercase;
}


/* The county plate. The background-image is generated per jurisdiction in
   map.css, so the browser fetches exactly one of the 56 — the panel is
   display:none until it is targeted, and nothing downloads a background for a
   box it is not painting. */
.w-county {
  flex: 0 0 190px;
  aspect-ratio: 1;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  border-radius: var(--radius-sm);
  animation: w-county-in var(--w-dur-plate) var(--w-ease-standard) both;
}

@keyframes w-county-in {
  from { opacity: 0; transform: scale(0.94); }
  to   { opacity: 1; transform: none; }
}

/* ── THE CARD IS A FIXED BOX AND THE PAGES SLIDE INSIDE IT ───────────────────
   ONBOARDING-HANDOFF §1: "Nothing scrolls — content that does not fit becomes
   another page. That is a hard rule." A scroll-snap track is the web's honest
   equivalent: it pages by swipe on touch, by arrow key when focused, and by the
   dots below — with no script for any of the three.

   The fixed height is what stops an opened card out-growing the map it exists to
   annotate. That inversion is exactly what this replaced. */
.w-track {
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  height: var(--w-page-h);
  scrollbar-width: none;
  overscroll-behavior-x: contain;
}
.w-track::-webkit-scrollbar { display: none; }

.w-page {
  flex: 0 0 100%;
  min-width: 0;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}
.w-page:focus { outline: none; }

.w-county {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  animation: w-county-in var(--w-dur-plate) var(--w-ease-standard) both;
}

/* ── The pager ───────────────────────────────────────────────────────────────
   Dots, not chevrons: with 2–3 pages a chevron pair spends 88pt of a 300px card
   on two controls that each do half a job, while dots show HOW MANY there are —
   which is the thing a reader actually needs before deciding to page. Each dot
   is a 44pt responder around a 7px mark, per the minimum-responder rule. */
.w-panel-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-top: var(--sp-4);
}

.w-pager { display: flex; gap: 2px; }

.w-pager-d {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--min-tap);
  height: var(--min-tap);
  text-decoration: none;
}
.w-pager-d::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: var(--border-on-glass);
  transition: background var(--w-dur-raise) var(--w-ease-standard),
              width var(--w-dur-raise) var(--w-ease-standard);
}
.w-pager-d:hover::before { background: var(--color-text-muted); }
/* THE ACTIVE DOT, without generating a rule per state. `.w-panel-foot` is a
   SIBLING of `.w-track`, so "the track whose nth page is targeted" can select
   "the nth dot after it" positionally — three pairs cover every panel, because
   no jurisdiction has more than three pages. The resting case (nothing targeted
   inside the panel) lights the first dot, which is the page on screen. */
.w-track:has(.w-page:nth-child(1):target) ~ .w-panel-foot .w-pager-d:nth-child(1)::before,
.w-track:has(.w-page:nth-child(2):target) ~ .w-panel-foot .w-pager-d:nth-child(2)::before,
.w-track:has(.w-page:nth-child(3):target) ~ .w-panel-foot .w-pager-d:nth-child(3)::before,
.w-panel:not(:has(.w-page:target)) .w-pager-d:nth-child(1)::before {
  background: var(--color-accent);
  width: 18px;
}
.w-pager-d:focus-visible::before { background: var(--color-accent); }

.w-panel-h {
  margin: 0 0 var(--sp-3) !important;
  font-size: var(--w-t-h4) !important;
  line-height: var(--w-t-h4-lh);
  font-weight: var(--w-w-display);
  letter-spacing: var(--track-21);
}

.w-panel p {
  margin: 0 0 var(--sp-3);
  color: var(--color-text-muted);
  font-size: var(--w-t-body-sm);
  line-height: var(--w-t-body-sm-lh);
  letter-spacing: var(--track-15-5);
}

.w-panel-f {
  margin: var(--sp-3) 0 0 !important;
  padding-top: var(--sp-3);
  border-top: var(--hairline) solid var(--accent-border);
  font-size: var(--w-t-fine) !important;
  line-height: var(--w-t-fine-lh);
  letter-spacing: var(--track-11-5);
}

.w-panel-actions { margin: var(--sp-4) 0 0 !important; }

.w-panel-clear {
  display: inline-flex;
  align-items: center;
  min-height: var(--min-tap);
  padding: 0 var(--sp-4);
  border-radius: var(--radius-pill);
  background: var(--accent-fill);
  border: var(--hairline) solid var(--accent-border);
  color: var(--color-accent);
  text-decoration: none;
  font-size: var(--w-t-note);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-13-5);
}
.w-panel-clear:hover { background: var(--color-accent-soft); }

/* ── The flag band ──────────────────────────────────────────────────────────
   All 56 jurisdictions at rest, and PART OF THE HERO rather than a strip below
   it. Each cell is a link into the same :target panel the map opens, so it is a
   second route to one destination — which is the only reason it earns its bytes.

   It sits on its own faint surface, tight under the stage (--sp-6, not the
   128px section rhythm), so the eye reads hero → map → "here are all of them"
   as one unit and not as three sections that happen to be adjacent. */
.w-flagband {
  margin: clamp(34px, 5vh, 52px) 0 0;
  padding: 16px 18px 12px;
  border-radius: var(--w-r-float);
  background: color-mix(in srgb, var(--surface-solid) 68%, transparent);
  border: var(--hairline) solid var(--border-on-glass-soft);
  -webkit-backdrop-filter: blur(20px) saturate(1.12);
  backdrop-filter: blur(20px) saturate(1.12);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

/* The prompt IS the old "Fifty-six jurisdictions" heading, which was always
   pointing at this band rather than at the map above it. */
.w-flagband-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-bottom: 10px;
}

.w-flagband-h {
  margin: 0;
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
  font-size: var(--w-t-body-sm);
  line-height: var(--w-t-body-sm-lh);
  letter-spacing: var(--track-15-5);
  color: var(--color-text-muted);
  max-width: var(--w-measure);
}
.w-flagband-h strong { color: var(--color-text); font-weight: var(--w-w-label); }

.w-flagband-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  min-width: 42px;
  height: 26px;
  padding-inline: 9px;
  border-radius: var(--radius-pill);
  background: var(--accent-fill);
  border: var(--hairline) solid var(--accent-border);
  color: var(--color-accent);
  font-size: var(--w-t-fine);
  font-weight: var(--w-w-display);
  letter-spacing: var(--track-11-5);
  font-variant-numeric: tabular-nums;
}

.w-flagrail {
  overflow-x: auto;
  overflow-y: hidden;
  padding: 1px 56px 8px 0;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
  scrollbar-color: var(--color-accent) var(--border-on-glass-soft);
  overscroll-behavior-x: contain;
  /* The cut is announced, exactly as on the gallery track: the right edge
     fades over the last 64px so a half flag reads as "forty more this way",
     and the 56px end padding is what the fade consumes at full scroll. The
     gradient rides an opaque token — the mask reads only its alpha. */
  mask-image: linear-gradient(90deg, var(--color-bg) calc(100% - 64px), transparent);
  -webkit-mask-image: linear-gradient(90deg, var(--color-bg) calc(100% - 64px), transparent);
}
.w-flagrail::-webkit-scrollbar { height: 3px; }
.w-flagrail::-webkit-scrollbar-track { background: var(--border-on-glass-soft); border-radius: var(--radius-pill); }
.w-flagrail::-webkit-scrollbar-thumb { background: var(--color-accent); border-radius: var(--radius-pill); }
.w-flagrail:focus-visible { outline: var(--border-featured) solid var(--color-accent); outline-offset: 3px; border-radius: var(--radius-sm); }

.w-flaggrid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 68px;
  gap: 5px;
  width: max-content;
}
.w-flaggrid .w-flagcell { animation: w-up 360ms var(--w-ease-out) both; }
.w-flaggrid .w-flagcell:nth-child(1) { animation-delay: 680ms; }
.w-flaggrid .w-flagcell:nth-child(2) { animation-delay: 690ms; }
.w-flaggrid .w-flagcell:nth-child(3) { animation-delay: 700ms; }
.w-flaggrid .w-flagcell:nth-child(4) { animation-delay: 710ms; }
.w-flaggrid .w-flagcell:nth-child(5) { animation-delay: 720ms; }
.w-flaggrid .w-flagcell:nth-child(6) { animation-delay: 730ms; }
.w-flaggrid .w-flagcell:nth-child(7) { animation-delay: 740ms; }
.w-flaggrid .w-flagcell:nth-child(8) { animation-delay: 750ms; }
.w-flaggrid .w-flagcell:nth-child(n+9) { animation-delay: 760ms; }

.w-flagcell a {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 9px 6px 10px;
  min-height: var(--min-tap);
  scroll-snap-align: start;
  border-radius: var(--radius-md);
  text-decoration: none;
  border: var(--hairline) solid transparent;
  background: color-mix(in srgb, var(--surface-solid) 42%, transparent);
  box-shadow: 0 1px 0 var(--border-on-glass-soft);
  transition: background var(--w-dur-raise) var(--w-ease-standard),
              border-color var(--w-dur-raise) var(--w-ease-standard),
              transform var(--w-dur-wow) var(--w-ease-wow),
              box-shadow var(--w-dur-raise) var(--w-ease-standard);
}
.w-flagcell a:hover,
.w-flagcell a:focus-visible {
  background: var(--fill-card);
  border-color: color-mix(in srgb, var(--color-accent) 22%, transparent);
  transform: translateY(-3px) scale(1.02);
  box-shadow: 0 8px 18px color-mix(in srgb, var(--color-accent) 14%, transparent), 0 2px 6px color-mix(in srgb, var(--color-bg) 18%, transparent);
}
/* KEYBOARD GETS THE SAME CELL THE MOUSE GETS. The lift was hover-only, so a
   keyboard reader tabbing the band saw the focus ring move across fifty-six
   identical cells with nothing else changing. The ring is kept ON TOP of the
   lift rather than replacing it. */
.w-flagcell a:focus-visible {
  outline: var(--border-featured) solid var(--color-accent);
  outline-offset: 2px;
}
.w-flagcell a:active { transform: translateY(-2px) scale(0.97); }

/* The currently open jurisdiction is marked in the band too, so the band and
   the map never disagree about what is selected. :target is the same control
   the panels already run on — no script, and it survives a deep link. */
.w-flagcell a:target,
.w-flagcell a[aria-current="true"] {
  background: var(--fill-card);
  border-color: var(--color-accent);
}

.w-flagcell-n {
  font-size: var(--w-t-fine);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-11-5);
  color: var(--color-text-muted);
  transition: color var(--w-dur-press) var(--w-ease-standard);
}
.w-flagcell a:hover .w-flagcell-n,
.w-flagcell a:focus-visible .w-flagcell-n { color: var(--color-text); }

/* The flag itself lifts a touch further than its cell — now 1.08 + glow. */
.w-flagcell .w-flag {
  transition: transform var(--w-dur-wow) var(--w-ease-wow),
              filter var(--w-dur-raise) var(--w-ease-standard),
              box-shadow var(--w-dur-raise) var(--w-ease-standard);
}
.w-flagcell a:hover .w-flag,
.w-flagcell a:focus-visible .w-flag {
  transform: scale(1.08);
  filter: saturate(1.18) brightness(1.02);
  box-shadow: 0 4px 12px color-mix(in srgb, var(--color-accent) 16%, transparent);
}

.w-flagcell .w-flag {
  width: 30px;
  /* A FIXED box, not a max: flag aspect ratios differ, and a variable image
     height put every label on its own baseline — DC and HI floated above the
     row. contain letterboxes inside 30×20 and the fifty-six names sit on one
     line. */
  height: 20px;
  object-fit: contain;
}

/* The flag inside a picker chip — smaller than the panel's, since the name is
   doing the work and the flag is only helping the eye land. */
.w-chip-f {
  width: 18px;
  height: auto;
  flex: none;
  border-radius: 2px;
  border: var(--hairline) solid var(--border-on-glass);
}

/* Below 720 the map goes decorative: there is no hover on touch, and a 340px
   map cannot give Rhode Island a 44px target. The list is the only control,
   which is what it already was. */
@media (max-width: 719.98px) {
  .doc--hero { padding-block-start: var(--sp-3); }
  .w-hero { padding-block: var(--sp-5) var(--sp-4); }
  .w-deck { gap: var(--sp-5); }
  .doc h1.w-hero-h { font-size: clamp(34px, 10.4vw, 42px); line-height: 1.04; }
  .w-hero-lede { font-size: 16px; line-height: 23px; }
  .w-hero-actions { align-items: flex-start; flex-direction: column; margin-top: var(--sp-4); }
  .w-hero-action--quiet { background: transparent; box-shadow: none; padding-inline: 0; min-height: 34px; }
  .w-float { width: 100%; margin-inline: 0; padding: 20px; border-radius: 24px; }
  .w-flagband { margin-top: var(--sp-5); padding: 15px 14px 10px; border-radius: 24px; }
  .w-flagband-head { align-items: flex-start; margin-bottom: 8px; }
  .w-flagband-h { display: block; line-height: 20px; }
  .w-flagband-h strong, .w-flagband-h span { display: block; }
  .w-flagband-h span { margin-top: 1px; }
  .w-flaggrid { grid-auto-columns: 68px; }
  .w-atlas { pointer-events: none; }
  .w-pick-grid { grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); }
}

/* ═══ DEVICE FRAMES ══════════════════════════════════════════════════════════

   CODE-DRAWN FROM TOKENS, never shipped as an image — otherwise the token guard
   stops meaning anything. The phone is a CUT-OUT in the page (its bezel is
   --color-bg), not a prop: no side buttons, no camera dot, no chrome gradient,
   no reflection. The app has exactly one light treatment and four sanctioned
   gradient jobs, and a glossy device mock would be a fifth role.

   The corner is expressed with calc() off the frame width rather than as a %:
   a single-value percentage radius on a 201:437 box draws an ELLIPSE. The bezel's
   outer radius is screen radius + bezel, applied by hand — the concentric rule
   the app applies everywhere.                                                  */

.w-dev {
  --w-dev-bezel: clamp(7px, 3.2%, 12px);
  width: var(--w-dev-w);
  flex: none;
  padding: var(--w-dev-bezel);
  background: var(--color-bg);
  border: var(--border-featured) solid var(--color-border);
  border-radius: calc(var(--w-dev-w) * var(--w-dev-corner) + var(--w-dev-bezel));
  box-shadow: var(--shadow-lg);
  position: relative;
}

.w-dev-screen {
  position: relative;
  aspect-ratio: var(--w-dev-shot-ar);
  border-radius: calc(var(--w-dev-w) * var(--w-dev-corner));
  overflow: hidden;
  background: var(--color-bg);
}

.w-dev-shot { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top; }

/* An open-bottom frame: the capture is a crop of the top of the screen, so the
   frame says so by running off the section's lower edge instead of pretending
   the phone ends there. */
.w-dev--open .w-dev-screen { aspect-ratio: auto; }
.w-dev--open {
  padding-bottom: 0;
  border-bottom: 0;
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}
.w-dev--open .w-dev-screen {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

/* The island is drawn at the measured geometry on EVERY frame. Shots 06 and 07
   already contain it as black pixels and it lands exactly on them; 01–05 do not,
   and it supplies the hardware the simulator omitted. One rule, both cases.
   `black` is the one bare colour keyword on this site: it is hardware, not
   palette, and the colour-keyword guard in check-copy.mjs allows it here alone. */
.w-dev-island {
  position: absolute;
  top: var(--w-dev-island-y);
  left: var(--w-dev-island-x);
  width: var(--w-dev-island-w);
  height: var(--w-dev-island-h);
  border-radius: var(--radius-pill);
  background: black;
}

/* screenGlow. No lift and no scale: a phone that moves on hover reads as a
   carousel control, and this is a picture of the app — it must never look
   pressable. */
@media (hover: hover) and (pointer: fine) {
  .w-dev { transition: box-shadow var(--w-dur-raise) var(--w-ease-standard); }
  .w-dev-screen {
    box-shadow: inset 0 0 0 var(--border-featured) var(--color-border);
    transition: box-shadow var(--w-dur-raise) var(--w-ease-standard);
  }
  .w-dev:hover .w-dev-screen { box-shadow: inset 0 0 0 var(--border-featured) var(--accent-border); }
}

/* ── Showcase: one frame beside a three-line caption, alternating side ────── */

.w-show {
  max-width: var(--w-page-max) !important;
  margin: 0 0 var(--sp-5);
  display: grid;
  gap: var(--w-grid-gap) var(--sp-9);
  align-items: center;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
}

.w-show-cap { max-width: 46ch; }

.w-show-e {
  margin: 0 0 var(--sp-2) !important;
  font-size: var(--w-t-caps-lg);
  font-weight: var(--w-w-display);
  text-transform: uppercase;
  letter-spacing: var(--track-caps-lg);
  color: var(--color-accent);
}

.w-show-t {
  margin: 0 0 var(--sp-3) !important;
  font-size: var(--w-t-h3b) !important;
  line-height: var(--w-t-h3b-lh);
  font-weight: var(--w-w-display);
  letter-spacing: var(--track-34);
  text-wrap: balance;
}

.w-show-b {
  margin: 0 !important;
  color: var(--color-text-muted);
  font-size: var(--w-t-body-sm);
  line-height: var(--w-t-body-sm-lh);
  letter-spacing: var(--track-15-5);
  text-wrap: pretty;
}

.w-show--center { justify-items: center; text-align: start; }
.w-show--center .w-show-cap { max-width: var(--w-measure); }

@media (min-width: 1024px) {
  .w-show { grid-template-columns: minmax(0, 1fr) auto; justify-items: start; }
  .w-show--left { grid-template-columns: auto minmax(0, 1fr); }
  .w-show--left .w-dev { grid-column: 1; grid-row: 1; }
  .w-show--left .w-show-cap { grid-column: 2; grid-row: 1; }
  .w-show--center { grid-template-columns: minmax(0, 1fr); justify-items: center; }
}

/* The em-continuation hands back to the app's own step function below 1024. */
@media (max-width: 1023.98px) { .w-show-t { letter-spacing: var(--track-29); } }
@media (max-width: 719.98px)  { .w-show-t { letter-spacing: var(--track-24); } }

/* ── Rail: 2–4 frames. ONCE PER SITE — a second rail makes the first look like
   a template. Below 720 it is a scroll-snap rail, never a grid: three 240px
   frames in a 4-column grid produce three 100px phones. ──────────────────── */

.w-rail {
  max-width: var(--w-page-max) !important;
  margin: 0 0 var(--sp-5);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--w-grid-gap);
  align-items: start;
  justify-items: center;
}

.w-rail-item { display: grid; gap: var(--sp-3); justify-items: center; max-width: 320px; }
.w-rail-item .w-show-cap { text-align: start; }
.w-rail-item .w-show-t { font-size: var(--w-t-h4) !important; line-height: var(--w-t-h4-lh); letter-spacing: var(--track-21); }

@media (max-width: 719.98px) {
  .w-rail {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding-bottom: var(--sp-3);
    gap: var(--sp-5);
  }
  .w-rail-item { scroll-snap-align: start; flex: none; width: 240px; }
}

/* ── Footer ─────────────────────────────────────────────────────────────── */

.f {
  border-top: var(--hairline) solid var(--color-border);
  background: var(--color-surface);
  padding-block: var(--sp-7) var(--sp-6);
  margin-top: var(--w-sec-y);
}

.f-top {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-7);
}

.f-brand { max-width: 42ch; }

.f-mark {
  margin: 0 0 var(--sp-2);
  font-size: var(--w-t-body);
  font-weight: var(--w-w-display);
  letter-spacing: var(--track-17);
}

.f-note {
  margin: 0 0 var(--sp-3);
  font-size: var(--w-t-note);
  letter-spacing: var(--track-13-5);
  line-height: var(--w-t-note-lh);
  color: var(--color-text-muted);
}

.f-state {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0;
  font-size: var(--w-t-note);
  letter-spacing: var(--track-13-5);
  color: var(--color-text-muted);
}

.dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-pill);
  flex: none;
}
.dot--unknown { background: var(--color-unknown); }

.f-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--sp-5);
}

.f-col { display: grid; gap: var(--sp-1); align-content: start; }

.f-h {
  margin: 0 0 var(--sp-2);
  font-size: var(--w-t-caps);
  font-weight: var(--w-w-display);
  text-transform: uppercase;
  letter-spacing: var(--track-caps-lg);
  color: var(--color-text-muted);
}

.f-col a {
  color: var(--color-text-muted);
  text-decoration: none;
  font-size: var(--w-t-note);
  letter-spacing: var(--track-13-5);
  display: inline-flex;
  align-items: center;
  min-height: 32px;
}
.f-col a:hover { color: var(--color-text); text-decoration: underline; }

.f-fine {
  margin: var(--sp-7) 0 0;
  padding-top: var(--sp-4);
  border-top: var(--hairline) solid var(--color-border);
  font-size: var(--w-t-fine);
  letter-spacing: var(--track-11-5);
  line-height: var(--w-t-fine-lh);
  color: var(--color-text-muted);
}

@media (min-width: 1024px) {
  .f-top { grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); }
  .cards { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ═══ 2026 SITE SYSTEM PASS ═════════════════════════════════════════════════

   The map line is the connective tissue. Surfaces exist only when they have a
   job: the hero truth card, evidence callouts, and grouped plan scopes. Long
   documents use an index and open ruled rows instead of a procession of boxes.
   Every colour remains token-derived and every animation below reuses the
   travel-only motion already defined by this file.                              */

/* The old map wash ended at the hero's exact rectangle. The map never did;
   only the wash did, which made a phantom box visible around otherwise open
   geometry. Fade the entire visual field at its edges and make the wash itself
   transparent at the perimeter, so the country belongs to the page again. */
.w-deck-map {
  -webkit-mask-image: radial-gradient(ellipse 88% 84% at 50% 46%, var(--color-text) 0 66%, transparent 100%);
  mask-image: radial-gradient(ellipse 88% 84% at 50% 46%, var(--color-text) 0 66%, transparent 100%);
}
.w-deck-map::after {
  background:
    radial-gradient(ellipse at 50% 44%, color-mix(in srgb, var(--color-accent) 11%, transparent) 0%, transparent 65%),
    linear-gradient(to bottom, transparent 0%, color-mix(in srgb, var(--color-bg) 24%, transparent) 72%, var(--color-bg) 100%);
}

/* ── Indexed document shell ─────────────────────────────────────────────── */
.page-shell { min-width: 0; }

.page-index {
  display: none;
  min-width: 0;
}
.page-index ol {
  margin: 0;
  padding: 0;
  list-style: none;
}
.page-index li { position: relative; }
.page-index a {
  position: relative;
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr);
  align-items: center;
  gap: 10px;
  min-height: 42px;
  color: var(--color-text-muted);
  font-size: var(--w-t-note);
  line-height: var(--w-t-note-lh);
  letter-spacing: var(--track-13-5);
  text-decoration: none;
  transition: color var(--w-dur-raise) var(--w-ease-standard), transform var(--w-dur-raise) var(--w-ease-standard);
}
.page-index a::before {
  content: "";
  position: absolute;
  left: 6px;
  inset-block: 0;
  width: var(--hairline);
  background: var(--color-border);
}
.page-index a span {
  position: relative;
  z-index: 1;
  width: 13px;
  height: 13px;
  border-radius: var(--radius-pill);
  background: var(--color-bg);
  border: var(--hairline) solid var(--color-text-faint);
  transition: border-color var(--w-dur-raise) var(--w-ease-standard),
              background var(--w-dur-raise) var(--w-ease-standard),
              box-shadow var(--w-dur-raise) var(--w-ease-standard);
}
.page-index a:hover { color: var(--color-text); transform: translateX(2px); }
.page-index a:hover span,
.page-index a.page-index-first span {
  border-color: var(--color-accent);
  background: var(--accent-fill);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-accent) 10%, transparent);
}
.page-index a.page-index-first { color: var(--color-accent); }

@media (min-width: 1024px) {
  .page-shell--indexed {
    display: grid;
    grid-template-columns: 190px minmax(0, 1fr);
    column-gap: clamp(42px, 6vw, 76px);
    align-items: start;
  }
  .page-shell--indexed .page-index {
    display: block;
    position: sticky;
    top: 98px;
    padding-block: 68px var(--sp-6);
    max-height: calc(100vh - 116px);
    overflow-y: auto;
  }
  .page-shell--indexed .doc { min-width: 0; }
}

@media (min-width: 720px) and (max-width: 1023.98px) {
  .page-shell--indexed .page-index {
    display: block;
    position: sticky;
    top: 78px;
    z-index: calc(var(--z-top-bar) - 1);
    margin-inline: calc(-1 * var(--w-page-pad));
    padding-inline: var(--w-page-pad);
    background: color-mix(in srgb, var(--color-bg) 90%, transparent);
    -webkit-backdrop-filter: blur(16px);
    backdrop-filter: blur(16px);
    border-bottom: var(--hairline) solid var(--color-border);
  }
  .page-index ol { display: flex; overflow-x: auto; scrollbar-width: none; }
  .page-index ol::-webkit-scrollbar { display: none; }
  .page-index li { flex: none; }
  .page-index a { grid-template-columns: 10px auto; padding-inline-end: var(--sp-5); }
  .page-index a::before { display: none; }
  .page-index a span { width: 8px; height: 8px; }
}

/* ── Editorial document rhythm ──────────────────────────────────────────── */
.page-shell--indexed .doc { padding-block-start: clamp(52px, 7vw, 82px); }
.page-shell--indexed .doc h1 { max-width: 20ch; margin-bottom: var(--sp-5); }
.page-shell--indexed .doc > .lede { max-width: 62ch; color: var(--color-text-muted); }
.page-shell--indexed .doc h2 {
  position: relative;
  max-width: var(--w-page-max);
  margin-top: 88px;
  padding-top: 28px;
  border-top: var(--hairline) solid var(--color-border);
}
.page-shell--indexed .doc h2::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  width: 72px;
  height: 2px;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--color-accent), transparent);
}
.page-shell--indexed .doc h2::after {
  content: "";
  position: absolute;
  top: -5px;
  left: 0;
  width: 8px;
  height: 8px;
  border: var(--hairline) solid var(--color-accent);
  border-radius: var(--radius-pill);
  background: var(--color-bg);
}
.page-shell--indexed .doc h2 + p { color: var(--color-text-muted); }

/* Status panels stay explicit, but they are now quiet instruments rather than
   heavy boxes dropped into an article. */
.page-shell--indexed .callout {
  position: relative;
  max-width: 720px;
  margin-block: var(--sp-6);
  padding: 22px 24px 22px 28px;
  border-width: var(--hairline);
  background: color-mix(in srgb, var(--color-surface) 78%, transparent);
  box-shadow: var(--shadow-sm), inset 3px 0 0 currentColor;
  overflow: hidden;
}
.page-shell--indexed .callout::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--gradient-specular);
  opacity: 0.35;
  pointer-events: none;
}
.page-shell--indexed .callout > * { position: relative; z-index: 1; }

/* Tables carry evidence in open ruled rows. The page is the container. */
.page-shell--indexed :where(.tablewrap, .ledger) {
  border-inline: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
}
.page-shell--indexed thead th { background: var(--accent-fill); }
.page-shell--indexed tbody tr { transition: background var(--w-dur-raise) var(--w-ease-standard); }
.page-shell--indexed tbody tr:hover { background: var(--fill-card); }
.page-shell--indexed tbody th,
.page-shell--indexed tbody td { padding-block: 15px; }

/* ── Homepage: manifesto, evidence path and media cadence ───────────────── */
.doc--home > h2 {
  max-width: 16ch;
  font-size: clamp(36px, 4.2vw, 54px);
  line-height: 1.04;
  margin-top: 104px;
}
.doc--home > h2 + p { max-width: 58ch; color: var(--color-text-muted); }

.doc--home > .cards {
  counter-reset: home-card;
  grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  gap: 0 clamp(34px, 5vw, 68px);
  border-top: var(--hairline) solid var(--color-border);
}
.doc--home > .cards .card {
  counter-increment: home-card;
  position: relative;
  min-height: 0;
  padding: 24px 0 26px 42px;
  border: 0;
  border-bottom: var(--hairline) solid var(--color-border);
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  transition: transform var(--w-dur-raise) var(--w-ease-standard), background var(--w-dur-raise) var(--w-ease-standard);
}
.doc--home > .cards .card::before {
  content: counter(home-card, decimal-leading-zero);
  position: absolute;
  left: 0;
  top: 26px;
  color: var(--color-accent);
  font-size: var(--w-t-fine);
  font-weight: var(--w-w-display);
  letter-spacing: var(--track-11-5);
  font-variant-numeric: tabular-nums;
}
.doc--home > .cards .card:hover { transform: translateX(4px); background: linear-gradient(90deg, var(--accent-fill), transparent 54%); }
.doc--home > .cards .card-n { border-color: var(--border-on-glass-soft); }

.doc--home > .steps {
  position: relative;
  max-width: var(--w-page-max);
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--w-grid-gap);
  padding-top: 20px;
}
.doc--home > .steps::before {
  content: "";
  position: absolute;
  left: 14px;
  right: 14px;
  top: 34px;
  height: var(--hairline);
  background: linear-gradient(90deg, var(--color-accent), var(--accent-border) 86%, transparent);
}
.doc--home > .steps li { display: grid; gap: var(--sp-3); position: relative; }
.doc--home > .steps .steps-n { background: var(--color-bg); box-shadow: 0 0 0 6px var(--color-bg); }

.doc--home > .w-show {
  position: relative;
  min-height: 520px;
  margin-block: 72px;
  padding: clamp(34px, 5vw, 64px);
  border-block: var(--hairline) solid var(--color-border);
  background:
    linear-gradient(90deg, color-mix(in srgb, var(--color-accent) 5%, transparent), transparent 48%),
    radial-gradient(ellipse at 72% 48%, color-mix(in srgb, var(--color-accent) 9%, transparent), transparent 58%);
  overflow: hidden;
}
.doc--home > .w-show .w-dev { --w-dev-w: clamp(286px, 26vw, 326px); }
.doc--home > .w-show::after {
  content: "";
  position: absolute;
  right: -10%;
  bottom: -34%;
  width: 58%;
  aspect-ratio: 1;
  border: var(--hairline) solid var(--accent-border);
  border-radius: 48% 52% 44% 56%;
  transform: rotate(-12deg);
  opacity: 0.28;
  pointer-events: none;
}
.doc--home > .w-show > * { position: relative; z-index: 1; }

.doc--home > .w-rail {
  display: flex;
  overflow-x: auto;
  justify-items: initial;
  gap: var(--sp-5);
  padding: 4px 0 var(--sp-5);
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
  scrollbar-color: var(--color-accent) var(--border-on-glass-soft);
}
.doc--home > .w-rail::-webkit-scrollbar { height: 3px; }
.doc--home > .w-rail::-webkit-scrollbar-track { background: var(--border-on-glass-soft); }
.doc--home > .w-rail::-webkit-scrollbar-thumb { background: var(--color-accent); }
.doc--home > .w-rail .w-rail-item { flex: 0 0 270px; scroll-snap-align: start; }
.doc--home > .w-rail .w-dev { --w-dev-w: 246px; }

/* ── Plans: one coverage continuum, then the honest future queue ────────── */
.doc--plans > .plans { display: block; }
.plans-label {
  margin: 0 0 var(--sp-3) !important;
  color: var(--color-text);
  font-size: var(--w-t-lead);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-19);
}
.plans-label--extensions { margin-top: var(--sp-7) !important; }
.plans-scope,
.plans-extensions {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0;
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-xl);
  background: color-mix(in srgb, var(--color-surface) 62%, transparent);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.plans-scope { position: relative; }
.plans-scope::after {
  content: "";
  position: absolute;
  left: 5%;
  right: 5%;
  top: 164px;
  height: 2px;
  background: linear-gradient(90deg, var(--color-text-faint), var(--color-accent) 48%, var(--color-accent));
  opacity: 0.72;
  pointer-events: none;
}
.plans-scope .plan,
.plans-extensions .plan {
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  padding: 26px;
}
.plans-scope .plan + .plan,
.plans-extensions .plan + .plan { border-inline-start: var(--hairline) solid var(--color-border); }
.plans-scope .plan { padding-bottom: 28px; }
.plans-scope .plan::after {
  content: "";
  width: 12px;
  height: 12px;
  margin-block: 14px 2px;
  border-radius: var(--radius-pill);
  background: var(--color-bg);
  border: var(--border-featured) solid var(--color-accent);
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--color-accent) 12%, transparent);
  order: 2;
  position: relative;
  z-index: 2;
}
.plans-scope .plan-tag { min-height: 68px; }
.plans-scope .plan-l { order: 3; margin-top: var(--sp-2); }
.plans-scope .plan-lim { order: 4; }
.plans-scope .plan-price { order: 5; }
.plans-scope .plan--feature { background: linear-gradient(180deg, var(--accent-fill), transparent 38%); }
.plans-extensions { background: transparent; }
.plans-extensions .plan { opacity: 0.86; transition: opacity var(--w-dur-raise) var(--w-ease-standard), background var(--w-dur-raise) var(--w-ease-standard); }
.plans-extensions .plan:hover { opacity: 1; background: var(--fill-card); }

/* FAQs are one continuous question surface, not a stack of mini cards. */
.page-shell--indexed .faq { max-width: 820px; gap: 0; border-block: var(--hairline) solid var(--color-border); }
.page-shell--indexed .faq details { border: 0; border-radius: 0; background: transparent; }
.page-shell--indexed .faq details + details { border-top: var(--hairline) solid var(--color-border); }
.page-shell--indexed .faq summary { padding-inline: 0; }
.page-shell--indexed .faq-a { padding-inline: 0; border-top-color: var(--border-on-glass-soft); }

/* Footer closes on the same open geometry as the rest of the site. */
.f {
  position: relative;
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--color-accent) 4%, transparent), transparent 54%),
    var(--color-surface);
  overflow: hidden;
}
.f::after {
  content: "";
  position: absolute;
  width: 520px;
  height: 360px;
  right: -180px;
  top: -160px;
  border: var(--hairline) solid var(--accent-border);
  border-radius: 50%;
  opacity: 0.34;
  pointer-events: none;
}

@media (max-width: 719.98px) {
  .page-shell--indexed .page-index {
    display: block;
    margin-inline: calc(-1 * var(--w-page-pad));
    padding-inline: var(--w-page-pad);
    border-bottom: var(--hairline) solid var(--color-border);
  }
  .page-index ol {
    display: flex;
    overflow-x: auto;
    scrollbar-width: none;
    scroll-snap-type: x proximity;
  }
  .page-index ol::-webkit-scrollbar { display: none; }
  .page-index li { flex: none; scroll-snap-align: start; }
  .page-index a { grid-template-columns: 8px auto; padding-inline-end: var(--sp-5); }
  .page-index a::before { display: none; }
  .page-index a span { width: 7px; height: 7px; }
  .page-shell--indexed .doc { padding-block-start: var(--sp-6); }
  .page-shell--indexed .doc h2 { margin-top: 72px; padding-top: 22px; }
  .doc--home > h2 { font-size: var(--w-t-h2); line-height: var(--w-t-h2-lh); }
  .doc--home > .cards { grid-template-columns: 1fr !important; }
  .doc--home > .steps { grid-template-columns: 1fr; gap: var(--sp-5); padding-top: 0; }
  .doc--home > .steps::before { left: 14px; right: auto; top: 14px; bottom: 14px; width: var(--hairline); height: auto; }
  .doc--home > .steps li { grid-template-columns: 28px minmax(0, 1fr); }
  .doc--home > .w-show { min-height: 0; margin-block: 56px; padding: 24px 0; background: transparent; }
  .doc--home > .w-show .w-dev { --w-dev-w: min(82vw, 310px); }
  .doc--home > .w-show::after { display: none; }
  .doc--home > .w-rail .w-dev { --w-dev-w: 224px; }
  .plans-scope,
  .plans-extensions { grid-template-columns: 1fr; }
  .plans-scope::after { left: 35px; right: auto; top: 32px; bottom: 32px; width: 2px; height: auto; }
  .plans-scope .plan + .plan,
  .plans-extensions .plan + .plan { border-inline-start: 0; border-top: var(--hairline) solid var(--color-border); }
  .plans-scope .plan-tag { min-height: 0; }
}

/* ═══ HOME, BELOW THE HERO — the 2026-08 redesign ════════════════════════════

   Eight composed movements instead of a document with decorations. The design
   language is the app's own: hairline rules, one quiet surface tone, the
   accent used as a pointer and never as a mood. Nothing here introduces a
   colour, a font, or a shadow the token mirror does not already hold.

   STRUCTURAL RULES CARRIED FORWARD: no section may use .w-float (the hero owns
   the page's one elevated surface), status hues stay reserved for the status
   legend and callouts, and every glyph is aria-hidden inline SVG on
   currentColor — the words carry the meaning.                                 */

/* ── The shared section header ─────────────────────────────────────────────── */

.w-sec-head { max-width: var(--w-measure); margin: 0 0 var(--sp-7); }
.w-sec-head h2 { margin: 0 0 var(--sp-3); }
.w-sec-lead {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--w-t-lead);
  line-height: var(--w-t-lead-lh);
  letter-spacing: var(--track-19);
  max-width: 58ch;
  text-wrap: pretty;
}
.w-sec-note { margin: var(--sp-5) 0 0; max-width: var(--w-measure); }

.w-man, .w-proc, .w-gal, .w-feat, .w-plan, .w-prom, .w-fin, .w-proof, .w-word, .w-lad {
  max-width: var(--w-page-max) !important;
  margin-block: var(--w-sec-y);
}

/* ── Proof: three figures the page can stand behind ────────────────────────────
   Structural facts only — scope, method, refusal. tabular-nums so the figures
   sit still; the caption and sentence carry the meaning, never the digit alone. */

.w-proof { margin-block-start: var(--sp-9); }

.w-proof-grid {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--w-grid-gap);
}

.w-proof-card {
  background: var(--color-surface);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--sp-6);
}

.w-proof-n {
  margin: 0;
  font-size: var(--w-t-num);
  line-height: var(--w-t-num-lh);
  font-weight: var(--w-w-display);
  letter-spacing: var(--track-34);
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
}
.w-proof-card { min-height: 148px; display: flex; flex-direction: column; align-items: flex-start; }

.w-proof-k {
  margin: var(--sp-2) 0 0;
  font-size: var(--w-t-caps);
  font-weight: var(--w-w-label);
  text-transform: uppercase;
  letter-spacing: var(--track-caps-lg);
  color: var(--color-accent);
}

.w-proof-b {
  margin: var(--sp-3) 0 0;
  color: var(--color-text-muted);
  font-size: var(--w-t-note);
  line-height: var(--w-t-note-lh);
  letter-spacing: var(--track-13-5);
  text-wrap: pretty;
}

/* ── Oneword: the one honest word, defined by exclusion ────────────────────────
   One quiet panel. Each row's DOTTED left edge is the unknown-tone shape from
   the status legend — the word and its shape travel together here too. */

.w-word {
  background: var(--color-surface);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-xxl);
  padding: clamp(28px, 4.5vw, 64px);
}

.w-word .w-sec-head { margin-bottom: var(--sp-6); }

.w-word-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--sp-3);
  max-width: var(--w-measure);
}

.w-word-item {
  display: grid;
  grid-template-columns: minmax(0, 8.5rem) minmax(0, 1fr);
  gap: var(--sp-2) var(--sp-5);
  align-items: baseline;
  padding: var(--sp-4) var(--sp-5);
  border-radius: var(--radius-md);
  background: var(--color-surface-alt);
  border-left: 2px dotted var(--color-text-faint);
}

.w-word-k {
  font-size: var(--w-t-caps);
  font-weight: var(--w-w-label);
  text-transform: uppercase;
  letter-spacing: var(--track-caps-lg);
  color: var(--color-text-faint);
}

.w-word-b {
  margin: 0;
  font-size: var(--w-t-lead);
  line-height: var(--w-t-lead-lh);
  letter-spacing: var(--track-19);
  font-weight: var(--w-w-quiet);
  color: var(--color-text);
  text-wrap: pretty;
}

@media (max-width: 719px) {
  .w-word-item { grid-template-columns: minmax(0, 1fr); }
}

/* ── Ladders: now, next, never ─────────────────────────────────────────────────
   Three equal columns. "Never" is NOT visually demoted — same width, same type
   scale, same weight of rule — because the whole argument is that the wall is
   load-bearing rather than a gap awaiting a sprint. Tone tints the word and the
   top rule only; the body stays in the reading colour so a tone can never make
   a sentence harder to read than its neighbour. */

/* THE GRID ASKS ITS CONTAINER, NOT THE VIEWPORT. This section renders in two
   very different columns: full width on a page with no index, and ~554px
   beside the on-this-page nav that layout.mjs adds to any page with two or
   more h2s. A viewport media query cannot tell those apart — it reads 1440px
   for both — and `auto-fit` split the difference by wrapping 2 + 1, which
   orphans "Never" onto its own row and reads as an afterthought. That is the
   one arrangement the section cannot have: the three columns argue with each
   other, so they are either all abreast or all stacked, never two-and-a-spare. */
.w-lad { container-type: inline-size; }

.w-lad-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--w-grid-gap);
}

@container (min-width: 820px) {
  .w-lad-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.w-lad-col {
  padding: var(--sp-6) 0 0;
  border-top: 2px solid var(--color-text-faint);
}
.w-lad-col--accent { border-top-color: var(--color-accent); }
.w-lad-col--quiet { border-top-color: var(--color-text-muted); }
.w-lad-col--warn { border-top-color: var(--color-warn); }

.w-lad-w {
  margin: 0;
  font-size: var(--w-t-h4);
  font-weight: var(--w-w-display);
  letter-spacing: var(--track-19);
  color: var(--color-text-faint);
}
.w-lad-col--accent .w-lad-w { color: var(--color-accent); }
.w-lad-col--quiet .w-lad-w { color: var(--color-text-muted); }
.w-lad-col--warn .w-lad-w { color: var(--color-warn); }

.w-lad-k {
  margin: var(--sp-2) 0 0;
  font-size: var(--w-t-caps);
  font-weight: var(--w-w-label);
  text-transform: uppercase;
  letter-spacing: var(--track-caps-lg);
  color: var(--color-text-faint);
}

.w-lad-b {
  margin: var(--sp-3) 0 0;
  color: var(--color-text-muted);
  font-size: var(--w-t-note);
  line-height: var(--w-t-note-lh);
  letter-spacing: var(--track-13-5);
  text-wrap: pretty;
}

.w-lad-list {
  margin: var(--sp-5) 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--sp-3);
}

.w-lad-item {
  padding-top: var(--sp-3);
  border-top: var(--hairline) solid var(--color-border);
  font-size: var(--w-t-body-sm);
  line-height: var(--w-t-body-sm-lh);
  letter-spacing: var(--track-15-5);
  color: var(--color-text);
  text-wrap: pretty;
}

/* ── Manifesto: the refusal list ───────────────────────────────────────────── */

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

.w-man-item {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr);
  gap: var(--sp-4);
  align-items: start;
  padding-block: var(--sp-5);
  border-top: var(--hairline) solid var(--color-border);
}
.w-man-item:last-child { border-bottom: var(--hairline) solid var(--color-border); }

.w-man-ring {
  width: 44px;
  height: 44px;
  border-radius: var(--radius-pill);
  background: var(--accent-fill);
  border: var(--hairline) solid var(--accent-border);
  color: var(--color-accent);
  display: grid;
  place-items: center;
}
.w-man-ring svg { width: 22px; height: 22px; }

.w-man-t {
  margin: 2px 0 var(--sp-2) !important;
  font-size: var(--w-t-lead) !important;
  line-height: var(--w-t-lead-lh);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-19);
}
.w-man-b {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--w-t-body-sm);
  line-height: var(--w-t-body-sm-lh);
  letter-spacing: var(--track-15-5);
  text-wrap: pretty;
  max-width: 58ch;
}

@media (min-width: 1024px) {
  .w-man {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    gap: var(--sp-9);
    align-items: start;
  }
  .w-man .w-sec-head { position: sticky; top: calc(58px + 12px + 10px + 16px); margin: 0; }
}

/* ── Layers: the anatomy of a jurisdiction, exploded ─────────────────────────

   Three planes in an isometric stack, every one real geometry: the baked
   boundary path, the shipped county plate, the app's marker vocabulary. The
   separation is driven by ONE custom property (--lay-gap), so the assemble
   entrance, the hover breathe, and the reduced-motion end state are the same
   arithmetic at three values — there is no second layout to drift.

   THE BLUR TRAP DOES NOT APPLY: planes are alpha fills via color-mix, never
   backdrop-filter, so 3D transforms on them are safe everywhere.             */

.w-lay { --lay-gap: 64px; }

.w-lay-wrap {
  display: grid;
  gap: var(--sp-8);
  align-items: center;
  justify-items: center;
}

.w-lay-stage {
  perspective: 1400px;
  width: min(100%, 480px);
  aspect-ratio: 10 / 8.4;
  position: relative;
}

/* On fine pointers the stack breathes apart a little under the cursor —
   layerLift, 320ms. A diagram invites a look, not a press: no scale, no glow. */
@media (hover: hover) and (pointer: fine) {
  .w-lay-stage:hover { --lay-gap: 84px; }
}

.w-lay-plane {
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(74%, 340px);
  aspect-ratio: 1;
  border-radius: var(--radius-lg);
  border: var(--hairline) solid var(--color-border);
  /* Depth is also LIGHT: the nearer the plane, the more surface it carries.
     Same token, three mixes — never a second colour. */
  background: color-mix(in srgb, var(--color-surface) var(--lay-mix, 72%), transparent);
  box-shadow: var(--shadow-md);
  transform: translate(-50%, -50%) rotateX(55deg) rotateZ(-42deg) translateZ(var(--lay-z));
  transition: transform var(--w-dur-rise) var(--w-ease-out);
}

.w-lay-plane--bounds   { --lay-z: calc(-1 * var(--lay-gap)); --lay-mix: 62%; }
.w-lay-plane--counties { --lay-z: 0px; --lay-mix: 72%; }
.w-lay-plane--places   { --lay-z: var(--lay-gap); --lay-mix: 80%; }

.w-lay-plane--bounds svg {
  position: absolute;
  inset: 7%;
  width: 86%;
  height: 86%;
  color: var(--color-accent);
}

.w-lay-plane--counties { display: grid; place-items: center; }
.w-lay-plate { width: 92%; height: 92%; display: block; }

/* The top plane speaks the app's marker vocabulary: accent dots for counted
   records, one dashed ring for the advisory distance. Decoration only — the
   legend says so in words. */
.w-lay-dot {
  position: absolute;
  width: 8px;
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--color-accent);
  box-shadow: 0 0 0 4px var(--accent-fill);
}
.w-lay-dot--1 { left: 62%; top: 30%; }
.w-lay-dot--2 { left: 74%; top: 44%; }
.w-lay-dot--3 { left: 46%; top: 52%; }
.w-lay-dot--4 { left: 30%; top: 62%; }

.w-lay-ringmark {
  position: absolute;
  left: 58%;
  top: 26%;
  width: 26%;
  aspect-ratio: 1;
  border: 1.5px dashed var(--color-accent);
  border-radius: var(--radius-pill);
  opacity: 0.7;
}

/* The legend: the words, in stacking order, ruled like the promise columns. */
.w-lay-legend {
  margin: 0;
  padding: 0;
  list-style: none;
  max-width: 52ch;
}

.w-lay-item {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr);
  gap: var(--sp-4);
  align-items: start;
  padding-block: var(--sp-5);
  border-top: var(--hairline) solid var(--color-border);
}
.w-lay-item:last-child { border-bottom: var(--hairline) solid var(--color-border); }

.w-lay-n {
  width: 34px;
  height: 34px;
  border-radius: var(--radius-pill);
  background: var(--accent-fill);
  border: var(--hairline) solid var(--accent-border);
  color: var(--color-accent);
  font-size: var(--w-t-note);
  font-weight: var(--w-w-display);
  display: grid;
  place-items: center;
  font-variant-numeric: tabular-nums;
}

.w-lay-t {
  margin: 4px 0 var(--sp-2) !important;
  font-size: var(--w-t-lead) !important;
  line-height: var(--w-t-lead-lh);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-19);
}
.w-lay-b {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--w-t-body-sm);
  line-height: var(--w-t-body-sm-lh);
  letter-spacing: var(--track-15-5);
  text-wrap: pretty;
}

@media (min-width: 1024px) {
  .w-lay-wrap {
    grid-template-columns: minmax(0, 6.5fr) minmax(0, 5.5fr);
    gap: var(--sp-9);
  }
  .w-lay-stage { width: min(100%, 600px); }
  .w-lay-plane { width: min(74%, 430px); }
  .w-lay-legend { justify-self: start; }
}

@media (max-width: 719.98px) {
  .w-lay { --lay-gap: 44px; }
}

/* ── Process: five stations on a dotted line ───────────────────────────────── */

.w-proc-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--sp-6);
}

.w-proc-item { position: relative; padding-left: 52px; }

.w-proc-n {
  position: absolute;
  left: 0;
  top: 0;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-pill);
  background: var(--accent-fill);
  border: var(--hairline) solid var(--accent-border);
  color: var(--color-accent);
  font-size: var(--w-t-note);
  font-weight: var(--w-w-display);
  display: grid;
  place-items: center;
  font-variant-numeric: tabular-nums;
}

/* The dotted continuation. A path between stations, never a progress claim. */
.w-proc-item:not(:last-child)::before {
  content: "";
  position: absolute;
  left: 16px;
  top: 40px;
  bottom: calc(-1 * var(--sp-6) + 6px);
  border-left: 1px dotted var(--color-border);
}

.w-proc-t {
  margin: 5px 0 var(--sp-2) !important;
  font-size: var(--w-t-lead) !important;
  line-height: var(--w-t-lead-lh);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-19);
}
.w-proc-b {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--w-t-body-sm);
  line-height: var(--w-t-body-sm-lh);
  letter-spacing: var(--track-15-5);
  text-wrap: pretty;
}

@media (min-width: 1024px) {
  .w-proc-list { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--w-grid-gap); }
  .w-proc-item { padding-left: 0; padding-top: 52px; }
  .w-proc-n { top: 0; left: 0; }
  .w-proc-item:not(:last-child)::before {
    left: 42px;
    top: 17px;
    bottom: auto;
    right: calc(-1 * var(--w-grid-gap) + 8px);
    border-left: 0;
    border-top: 1px dotted var(--color-border);
  }
  .w-proc-t { margin-top: 0 !important; }
}

/* ── Gallery: the walkthrough track ────────────────────────────────────────── */

.w-gal-track {
  display: flex;
  gap: var(--sp-6);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: var(--sp-4);
  scrollbar-width: thin;
  /* THE CUT IS ANNOUNCED, NEVER ACCIDENTAL. While the track scrolls, its right
     edge fades over the last 72px, so a half-visible frame reads as "there is
     more" instead of "something broke". The gradient rides a TOKEN — the mask
     only reads the alpha channel, and an opaque token is alpha 1 — so the
     colour guard stays meaningful. The 72px of end padding is what the fade
     consumes at full scroll: the last caption always clears it. */
  padding-right: 72px;
  mask-image: linear-gradient(90deg, var(--color-bg) calc(100% - 72px), transparent);
  -webkit-mask-image: linear-gradient(90deg, var(--color-bg) calc(100% - 72px), transparent);
}

.w-gal-item {
  margin: 0;
  flex: none;
  width: 236px;
  scroll-snap-align: start;
}
.w-gal-item .w-dev { --w-dev-w: 236px; }

/* At 1180px the five frames FIT — 5 × 198px + 4 × 20px = 1070 inside the
   1072px content column — so the track stops scrolling, the fade and the end
   padding come off, and the walkthrough reads as one composed row. */
@media (min-width: 1180px) {
  .w-gal-track {
    padding-right: 0;
    gap: var(--w-grid-gap);
    mask-image: none;
    -webkit-mask-image: none;
  }
  .w-gal-item { width: 198px; }
  .w-gal-item .w-dev { --w-dev-w: 198px; }
}
.w-gal-item figcaption { margin-top: var(--sp-4); }

.w-gal-t {
  margin: 0 0 var(--sp-1) !important;
  font-size: var(--w-t-body) !important;
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-15-5);
}
.w-gal-b {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--w-t-note);
  line-height: var(--w-t-note-lh);
  letter-spacing: var(--track-13-5);
  text-wrap: pretty;
}

/* ── Featgrid: the rest of the app, ruled not boxed ────────────────────────── */

.w-feat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
  gap: 0 var(--w-grid-gap);
}

.w-feat-item {
  padding-block: var(--sp-6);
  border-top: var(--hairline) solid var(--color-border);
}

.w-feat-i {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-sm);
  background: var(--accent-fill);
  border: var(--hairline) solid var(--accent-border);
  color: var(--color-accent);
  display: grid;
  place-items: center;
  margin-bottom: var(--sp-4);
}
.w-feat-i svg { width: 21px; height: 21px; }

.w-feat-t {
  margin: 0 0 var(--sp-2) !important;
  font-size: var(--w-t-lead) !important;
  line-height: var(--w-t-lead-lh);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-19);
}
.w-feat-b {
  margin: 0 0 var(--sp-3);
  color: var(--color-text-muted);
  font-size: var(--w-t-body-sm);
  line-height: var(--w-t-body-sm-lh);
  letter-spacing: var(--track-15-5);
  text-wrap: pretty;
}
.w-feat-n {
  margin: 0;
  font-size: var(--w-t-note);
  line-height: var(--w-t-note-lh);
  letter-spacing: var(--track-13-5);
  color: var(--color-text-faint);
}

/* ── Planpanel: what money changes, as one surface ─────────────────────────── */

.w-plan {
  background: var(--color-surface);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: clamp(28px, 4.5vw, 64px);
  overflow: hidden;
  display: grid;
  gap: var(--sp-7);
  align-items: end;
  justify-items: center;
}
.w-plan .w-show-cap { max-width: 52ch; justify-self: start; }
.w-plan h2 { margin: 0 0 var(--sp-3); }
.w-plan .btn { margin-top: var(--sp-5); }

.w-plan-list {
  margin: var(--sp-5) 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--sp-3);
}
.w-plan-list li {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr);
  gap: var(--sp-3);
  align-items: start;
  color: var(--color-text-muted);
  font-size: var(--w-t-body-sm);
  line-height: var(--w-t-body-sm-lh);
  letter-spacing: var(--track-15-5);
}

.w-check { color: var(--color-accent); display: grid; place-items: center; }
.w-check svg { width: 21px; height: 21px; margin-top: 1px; }

/* The open frame runs off the panel's lower edge, exactly as it does in a
   showcase — the capture is a crop of the top of the screen and the panel
   says so by cutting it. */
.w-plan .w-dev { --w-dev-w: min(76vw, 330px); margin-bottom: calc(-1 * clamp(28px, 4.5vw, 64px)); }

@media (min-width: 1024px) {
  .w-plan { grid-template-columns: minmax(0, 1fr) auto; }
  .w-plan .w-dev { --w-dev-w: 360px; }
}

/* ── Promise: three ruled columns ──────────────────────────────────────────── */

.w-prom-grid {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 0 var(--w-grid-gap);
}

.w-prom-item {
  padding-block: var(--sp-6);
  border-top: var(--hairline) solid var(--color-border);
}
.w-prom-item .w-check { justify-content: start; display: inline-grid; margin-bottom: var(--sp-3); }

.w-prom-t {
  margin: 0 0 var(--sp-2) !important;
  font-size: var(--w-t-lead) !important;
  line-height: var(--w-t-lead-lh);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-19);
}
.w-prom-b {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--w-t-body-sm);
  line-height: var(--w-t-body-sm-lh);
  letter-spacing: var(--track-15-5);
  text-wrap: pretty;
}

/* ── Roster: nine office seats, empties included ────────────────────────────
   The seat is a PLATE, and an unfilled plate is drawn at the same size as a
   filled one. Every tone here is carried by the dot AND the word beside it —
   the tint on the plate is the third signal, never the first. */

.dot--ok { background: var(--color-ok); }
.dot--warn { background: var(--color-warn); }
.dot--danger { background: var(--color-danger); }

/* Nine seats is a lot of vertical run before the feature grid, so the grid
   packs to three columns as soon as there is room for three readable ones —
   nine plates then cost three rows instead of nine. `auto-fit` alone gave four
   narrow columns on a wide screen and left an orphan on the second row; the
   explicit cap keeps the last row full. */
.w-ros-grid {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
  gap: var(--w-grid-gap);
}
@media (min-width: 1024px) {
  .w-ros-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.w-ros-seat {
  position: relative;
  padding: var(--sp-5);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  transition: border-color var(--w-dur-raise) var(--w-ease-standard),
    background var(--w-dur-raise) var(--w-ease-standard),
    transform var(--w-dur-raise) var(--w-ease-standard);
}
.w-ros-seat:hover { transform: translateY(-2px); border-color: var(--border-on-glass); }

/* The empty seats read QUIETER, never absent. An unfilled office is still an
   office; dimming it to invisibility would be the render that lies. */
.w-ros-seat--unknown { background: transparent; border-style: dashed; }

.w-ros-i {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  margin-bottom: var(--sp-4);
  color: var(--color-text-muted);
}
.w-ros-i svg { width: 100%; height: 100%; }

.w-ros-scope {
  margin: 0 0 var(--sp-2);
  font-size: var(--w-t-caps);
  font-weight: var(--w-w-display);
  text-transform: uppercase;
  letter-spacing: var(--track-caps-sm);
  color: var(--color-text-muted);
}
.w-ros-t {
  margin: 0 0 var(--sp-3) !important;
  font-size: var(--w-t-lead) !important;
  line-height: var(--w-t-lead-lh);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-19);
}
.w-ros-state {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-3);
  font-size: var(--w-t-note);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-13-5);
}
.w-ros-seat--ok .w-ros-state { color: var(--tone-ok-fg); }
.w-ros-seat--warn .w-ros-state { color: var(--tone-warn-fg); }
.w-ros-seat--unknown .w-ros-state { color: var(--tone-unknown-fg); }
.w-ros-b {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--w-t-body-sm);
  line-height: var(--w-t-body-sm-lh);
  letter-spacing: var(--track-15-5);
  text-wrap: pretty;
}

/* ── Stages: the bill ladder ────────────────────────────────────────────────
   A LADDER, not a progress bar. The rail is a hairline that runs the height of
   the list; it carries no fill and no percentage, because a bill's stage is a
   state and not a completion. */

.w-stg-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 0;
}

.w-stg-row {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
  gap: var(--sp-3) var(--sp-6);
  align-items: baseline;
  padding: var(--sp-4) 0 var(--sp-4) var(--sp-6);
  border-top: var(--hairline) solid var(--color-border);
  transition: background var(--w-dur-raise) var(--w-ease-standard);
}
.w-stg-row:last-child { border-bottom: var(--hairline) solid var(--color-border); }
.w-stg-row:hover { background: var(--color-surface); }

/* The rung marker. Colour repeats the dot and the word; it is never alone. */
.w-stg-rail {
  position: absolute;
  inset-inline-start: 0;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--color-border);
}
.w-stg-row--ok .w-stg-rail { background: var(--color-ok); }
.w-stg-row--warn .w-stg-rail { background: var(--color-warn); }
.w-stg-row--danger .w-stg-rail { background: var(--color-danger); }
.w-stg-row--unknown .w-stg-rail { background: var(--color-unknown); }

.w-stg-w {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0;
  font-size: var(--w-t-lead);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-19);
}
.w-stg-e {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--w-t-body-sm);
  line-height: var(--w-t-body-sm-lh);
  letter-spacing: var(--track-15-5);
  text-wrap: pretty;
}

/* ── Journey: the route, cut at every line ──────────────────────────────────
   Legs run down a single spine with a node per entry. The SHORT LEG is not
   drawn short — the whole argument of the section is that a twelve-mile clip
   gets the same weight as a three-hour run. */

.w-jrn-legs {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 0;
}

.w-jrn-leg {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 12rem) minmax(0, 1fr);
  gap: var(--sp-2) var(--sp-6);
  padding: var(--sp-5) 0 var(--sp-5) var(--sp-7);
  border-top: var(--hairline) solid var(--color-border);
}
.w-jrn-leg:last-child { border-bottom: var(--hairline) solid var(--color-border); }

/* THE SPINE IS DRAWN BY THE LEG, NOT BY THE NODE.
   It was the node's ::after first, at `top: 100%; height: 999px`. Nothing on
   this page clips overflow, so that hairline did not stop at the next leg —
   it ran 999px straight down through the feature grid, the paywall panel and
   whatever else was below it. A decorative rule cutting through three unrelated
   sections is a layout defect that only shows up once the section has content
   under it, which is exactly when nobody looks again.
   The leg owns its own segment instead: 100% of the leg's height, so the line
   can never be longer than the thing it belongs to, and the last leg simply
   does not draw one. */
.w-jrn-leg::before {
  content: "";
  position: absolute;
  inset-inline-start: 5px;
  top: 0;
  height: 100%;
  width: var(--hairline);
  background: var(--color-border);
}
.w-jrn-leg:last-child::before { display: none; }

.w-jrn-node {
  position: absolute;
  inset-inline-start: 0;
  top: calc(var(--sp-5) + 0.55em);
  width: 11px;
  height: 11px;
  border-radius: var(--radius-pill);
  border: 2px solid var(--color-unknown);
  background: var(--color-bg);
}
.w-jrn-leg--ok .w-jrn-node { border-color: var(--color-ok); }
.w-jrn-leg--warn .w-jrn-node { border-color: var(--color-warn); }
.w-jrn-leg--unknown .w-jrn-node { border-color: var(--color-unknown); }

/* The clipped leg is the one the section exists for, so it is marked — with a
   tint AND the word "Short leg" in its own status line. The node's own fill has
   to follow the row it sits on, or the tinted leg gets a punched-out dot. */
.w-jrn-leg--clip { background: var(--tone-warn-bg); }
.w-jrn-leg--clip .w-jrn-node { background: var(--tone-warn-bg); }

.w-jrn-where {
  margin: 0;
  font-size: var(--w-t-lead);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-19);
}
.w-jrn-dist {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--w-t-note);
  letter-spacing: var(--track-13-5);
}
.w-jrn-w {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-2);
  font-size: var(--w-t-note);
  font-weight: var(--w-w-label);
  letter-spacing: var(--track-13-5);
}
.w-jrn-leg--ok .w-jrn-w { color: var(--tone-ok-fg); }
.w-jrn-leg--warn .w-jrn-w { color: var(--tone-warn-fg); }
.w-jrn-leg--unknown .w-jrn-w { color: var(--tone-unknown-fg); }
.w-jrn-b {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--w-t-body-sm);
  line-height: var(--w-t-body-sm-lh);
  letter-spacing: var(--track-15-5);
  text-wrap: pretty;
}

@media (max-width: 719px) {
  .w-stg-row,
  .w-jrn-leg { grid-template-columns: minmax(0, 1fr); }
  .w-jrn-leg { padding-inline-start: var(--sp-6); }
}

/* ── Finale: the ask ───────────────────────────────────────────────────────── */

.w-fin {
  text-align: center;
  padding-top: var(--sp-7);
  display: grid;
  justify-items: center;
  /* The closing section hands off to the footer, not to more page: half the
     section rhythm below, so the ask and the footer read as one ending. */
  margin-bottom: calc(var(--w-sec-y) / 2);
}
.w-fin h2 { margin: 0 0 var(--sp-3); }
.w-fin .w-sec-lead { margin: 0 auto; max-width: 54ch; }
.w-fin .w-sec-note { max-width: 54ch; }

.w-fin-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  justify-content: center;
  margin-top: var(--sp-6);
}

.w-fin-card {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-4);
  margin-top: var(--sp-7);
  padding: var(--sp-4) var(--sp-6);
  background: var(--color-surface);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-lg);
  text-align: start;
}
.w-fin-card img { border-radius: var(--radius-sm); display: block; }
.w-fin-name { margin: 0 !important; font-weight: var(--w-w-label); letter-spacing: var(--track-15-5); }
.w-fin-sub {
  margin: 0 !important;
  font-size: var(--w-t-note);
  line-height: var(--w-t-note-lh);
  letter-spacing: var(--track-13-5);
  color: var(--color-text-muted);
}

/* ── Showcase, panel variant: the map on its own surface ───────────────────── */

.w-show.w-show--panel {
  background: var(--color-surface);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: clamp(28px, 4.5vw, 64px);
  overflow: hidden;
}

/* ═══ MOTION ═════════════════════════════════════════════════════════════════

   NINETEEN animations and no twentieth. Every duration is an app timing or
   the settle time of an app spring, and every one names its reduced-motion END
   STATE — per lib/motion.ts, Reduce Motion removes the TRAVEL, never the state
   change. NOTHING EXCEEDS 480ms unless it is an app constant, and the only
   three that are are SWEEP_MS 1500, PULSE_MS 2400, and the 900ms camera.

   Two of the nineteen are load-time additions (2026-08-04, "animate the
   opening"): heroChoreo, the staged document-timeline entrance of the hero's
   eight movements (eyebrow → headline word-by-word → lede → note → actions →
   card → band → legend, 420–480ms each, reduced-motion: the opening is simply
   open); and markerFade, the 51 markers breathing in as one body at 900ms.
   Plus one non-animation: panGrab — the hero map's grab/grabbing cursor while
   dragging, a STATE, with no duration at all.

   The first nine run with no script, on the document timeline or a scroll()/
   view() timeline, and are TRAVEL-ONLY where a timeline could misbehave:

     heroDraw       the country draws itself in, once, on load    1500ms
                    (re-wired 2026-08-04: its old selector matched no markup
                    and the sweep had silently never run)
     stateRaise     hover/focus on a state                         140ms  (transition, above)
     statePick      a state becomes the targeted one               140ms  (same transition)
     markerPulse    the locator on the targeted state             2400ms  (map.css)
     sectionRise    sections settle as they enter                  320ms
     pressFeedback  :active on any control                          90ms  (transition, above)
     cardLift       the floating card settles onto its backdrop    320ms
     deviceRise     device frames enter, staggered                 420ms
     screenGlow     hover a device frame                           140ms  (above)

   The next SEVEN are OBSERVER-OWNED (dist/motion.js): the script proves an
   element is below the viewport before hiding it, and the base state — no
   script, no observer, reduced motion — is the finished page. Only these seven
   may touch opacity, because only these seven are owned by something that
   always reaches its end state. Reduced-motion end state for all seven: the
   content is simply there, because the script exits before adding a class.

     sectionReveal  a section rises and fades in as one piece      420ms  (.mo)
     groupReveal    cards/statuses/steps/rail children stagger     420ms  (.mo-g, 55ms steps,
                    capped at nine slots — the roster's nine seats and the bill
                    ladder's nine rungs. A tenth would land after the first had
                    finished settling, which reads as a queue and not a cascade.)
     chipPop        step numbers and status glyphs settle          320ms  (inside .mo-g; the
                    roster seat mark scales from 0.6 like the rest, the route node
                    from 0.5 about its own centre because it is positioned against
                    the spine and a bare scale would walk it off the line)
     capSlide       a showcase caption slides in from its side     420ms  (.mo-show, ≥1024 only)
     frameRise      the showcase frame rises one beat later        420ms  (.mo-show, +90ms)
     ruleDraw       the horizontal rule draws to width             420ms  (travel-only)
     layerAssemble  the anatomy stack explodes into place          420ms  (.mo-lay, 70ms steps;
                    reduced-motion end state: the stack is simply exploded — its
                    resting transform is 3D and is never forced to none)
     layerLift      the stack breathes apart under a fine pointer  320ms  (transition, hover only;
                    reduced-motion: the gap changes instantly — a state, not a travel)

   Plus one non-animation: navChrome, the sticky header taking its glass at 64px
   of scroll — 170ms, a TIMING and never a spring, because a spring to zero
   overshoots below zero.

   REJECTED BY NAME: `atlasBreathe`, a slow ambient pulse on the hairline country.
   It would be the site's only ambient loop, the app has none, and a breathing
   legal map implies live data on a page that makes no requests. Named here so it
   does not get added later as "a small touch".

   ⚠ THE BLUR TRAP, RESTATED FOR THE WEB. `.w-float` is an ALPHA FILL with no
   backdrop-filter, so a transform on it is safe. `.h` IS a backdrop-filter
   element and is a direct child of <body> for exactly that reason. RULE:
   .w-stage, .w-backdrop, .w-float and .w-dev may never be given backdrop-filter.
   If anyone adds one, cardLift and deviceRise kill it in Safari, silently.

   heroDraw REVEALS THE RESTING MAP, NOT A LIT ONE. The app's sweep ends with the
   country filled at 0.50 because that screen is saying "a plan opens all of
   this". A marketing hero ending with fifty states lit reads as "fifty states
   reviewed", which is the exact claim this product does not make.               */

@keyframes w-draw {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}

/* The app's cosine bump, (1 − cos 2πp)/2, at PULSE_MS. */
@keyframes w-pulse {
  0%   { opacity: 0.35; transform: scale(1); }
  50%  { opacity: 0.9;  transform: scale(1.06); }
  100% { opacity: 0.35; transform: scale(1); }
}

/* sectionRise is TRAVEL ONLY — no opacity. An opacity entrance on a static site
   can leave content invisible if the timeline misbehaves, and this site's whole
   contract is that every word is readable with no script running. 18px of settle
   is the app's entrance distance; nothing is ever hidden to achieve it. */
@keyframes w-rise {
  from { transform: translateY(18px); }
  to   { transform: none; }
}

/* cardLift and deviceRise are TRAVEL ONLY too, and for the same reason. */
@keyframes w-lift {
  from { transform: translateY(18px); }
  to   { transform: none; }
}

@keyframes w-device {
  from { transform: translateY(22px); }
  to   { transform: none; }
}

/* THE NAV SEAM, AND WHY THE FILTER IS IN THESE KEYFRAMES.
   This animated `background` to transparent at scroll 0 but left the base
   rule's `backdrop-filter: blur(20px) saturate(140%)` running the whole time.
   A backdrop-filter does not care that the fill above it is transparent — it
   still blurs and SATURATES whatever it is over. What it is over at the top of
   this page is `--gradient-wash`, which opens at rgba(79,141,247,0.22): a blue
   band. Saturating a blue gradient by 140% and blurring it 20px paints a
   visibly different-coloured pill exactly where the header sits, against a
   background that is otherwise flat --color-bg. That is the block of "different
   background colour by the nav bar" seen on first load, before any scrolling.

   So the filter rides the same range as the fill. `blur(0px) saturate(100%)` is
   the identity value and is interpolatable, which `none` is not — animating
   from `none` gives a hard jump at the first frame instead of a ramp.

   BORDER-COLOR, NOT BORDER-BOTTOM-COLOR. The base rule sets a border on all
   four sides and this only ever cleared the bottom one, so at scroll 0 the pill
   kept three hairlines and read as an empty outlined shape floating over the
   wash. The whole border resolves together now. */
@keyframes w-nav {
  from {
    background: var(--surface-nav);
    border-color: var(--border-on-glass);
    box-shadow: var(--shadow-lg), inset 0 1px 0 var(--border-on-glass-soft);
    -webkit-backdrop-filter: blur(20px) saturate(100%);
    backdrop-filter: blur(20px) saturate(100%);
  }
  to {
    background: var(--surface-nav);
    border-color: var(--border-on-glass);
    box-shadow: var(--shadow-lg), inset 0 1px 0 var(--border-on-glass-soft);
    -webkit-backdrop-filter: blur(20px) saturate(140%);
    backdrop-filter: blur(20px) saturate(140%);
  }
}

/* heroDraw, wired to the elements that actually exist: the previous selector
   (.w-atlas-wrap) appeared in no markup, so the country had never once drawn
   itself in. It does now, on the hero and on the stage pages alike. */
.w-deck-map .w-atlas,
.w-backdrop .w-atlas {
  animation: w-draw var(--w-dur-draw) var(--w-ease-out) both;
}

/* ── heroChoreo: the opening, staged ─────────────────────────────────────────
   One keyframe, eight entrances, every one on the DOCUMENT timeline — a load
   animation always reaches its last frame, so opacity is safe here in a way a
   scroll timeline never earned. The stagger walks the reading order: eyebrow,
   headline word by word, lede, note, actions, card, band, legend — while the
   country draws beneath them and the markers breathe in last. Nothing exceeds
   480ms; the CHOREOGRAPHY is long, each movement is not. */
@keyframes w-up {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
@keyframes w-fadein {
  from { opacity: 0; }
}

.w-eyebrow { animation: w-up 420ms var(--w-ease-out) both; }

.w-hw { display: inline-block; animation: w-up 480ms var(--w-ease-out) both; }
.w-hw:nth-child(1)  { animation-delay: 40ms; }
.w-hw:nth-child(2)  { animation-delay: 85ms; }
.w-hw:nth-child(3)  { animation-delay: 130ms; }
.w-hw:nth-child(4)  { animation-delay: 175ms; }
.w-hw:nth-child(5)  { animation-delay: 220ms; }
.w-hw:nth-child(6)  { animation-delay: 265ms; }
.w-hw:nth-child(7)  { animation-delay: 310ms; }
.w-hw:nth-child(8)  { animation-delay: 355ms; }
.w-hw:nth-child(9)  { animation-delay: 400ms; }
.w-hw:nth-child(10) { animation-delay: 445ms; }
.w-hw:nth-child(n+11) { animation-delay: 490ms; }

.w-hero-lede    { animation: w-up 440ms var(--w-ease-out) 360ms both; }
.w-mapnote      { animation: w-up 420ms var(--w-ease-out) 430ms both; }
.w-hero-actions { animation: w-up 440ms var(--w-ease-out) 500ms both; }

/* The hero's floating card joins the choreography then breathes —
   w-up on document timeline, then floatBreath infinite. `auto` unhooks it from
   cardLift's view() timeline. */
.w-hero .w-float {
  animation: w-up 480ms var(--w-ease-out) 560ms both, floatBreath 3800ms var(--w-ease-sine) 1100ms infinite alternate;
  animation-timeline: auto, auto;
  animation-range: normal, normal;
}
@keyframes floatBreath { from { transform: translateY(0); } to { transform: translateY(-4px); } }

.w-flagband { animation: w-up 480ms var(--w-ease-out) 640ms both; }
.w-legend   { animation: w-up 420ms var(--w-ease-out) 720ms both; }

/* markerFade — the 51 markers breathe in as a body once the sweep has crossed
   them. A group fade, not 51 staggers: the markers are counts at rest, and a
   popcorn entrance would read as activity the page does not have. */
.w-atlas-marks { animation: w-fadein 900ms var(--w-ease-out) 600ms both; }

/* Scroll-driven, so it needs no observer and therefore no script. Where the
   browser does not support it, sections are simply in place — which is also the
   reduced-motion end state, so there is one behaviour to reason about. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .doc > h2, .doc > .callout, .doc > .w-show, .doc > .cards,
    .doc > .steps, .doc > .statuses, .doc > .plans, .doc > .entries,
    .doc > .ledger, .doc > .tablewrap, .doc > .faq, .doc > .cta {
      animation: w-rise var(--w-dur-rise) var(--w-ease-out) both;
      animation-timeline: view();
      animation-range: entry 0% entry 60%;
    }

    /* cardLift — 320ms is --w-dur-rise, the paywall entrance already in this
       file. Travel only: no opacity, no scale. */
    .w-float {
      animation: w-lift var(--w-dur-rise) var(--w-ease-out) both;
      animation-timeline: view();
      animation-range: entry 0% entry 55%;
    }

    /* deviceRise. `animation-delay` on a view() timeline is a RANGE offset, not
       a time delay — a stagger written as a delay does nothing at all. So the
       stagger is written as literal ranges, which is what it actually is. */
    .w-dev {
      animation: w-device var(--w-dur-device) var(--w-ease-out) both;
      animation-timeline: view();
      animation-range: entry 0% entry 65%;
    }
    .w-rail-item:nth-child(2) .w-dev { animation-range: entry 6% entry 71%; }
    .w-rail-item:nth-child(3) .w-dev { animation-range: entry 12% entry 77%; }
    .w-rail-item:nth-child(4) .w-dev { animation-range: entry 18% entry 83%; }
  }
}

@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .h {
      animation: w-nav var(--w-dur-chrome) var(--w-ease-standard) both;
      animation-timeline: scroll(root);
      animation-range: 0 64px;
    }
  }
}

/* ── THE SCROLL-REVEAL, AND WHO OWNS IT ──────────────────────────────────────
   The first reveal built here used `animation-timeline: view()` and was REMOVED
   after being measured in the browser: `@supports` reported true, and sections
   still sat at `opacity: 0` while fully on screen — the ViewTimeline reported
   `currentTime: -28%`, permanently before its own entry range, so the `both`
   fill pinned every one of them at the `from` keyframe. A BLANK LANDING PAGE,
   on the one page the ad spend points at, in a browser that had just claimed
   support.

   The comment that used to stand here ended: "If a scroll reveal is wanted
   later it needs an IntersectionObserver adding a class, which is a script,
   which is a CSP change reviewed as one." That review happened (2026-08-04, jj:
   go big with CSS and a JS layer), and THIS is that reveal, built the only way
   the incident above permits:

     · dist/motion.js hides an element ONLY after proving it sits below the
       viewport, and only after the observer that will reveal it exists.
     · The base state of every rule below is THE FINISHED PAGE. With no script,
       a failed script, a browser without the observer, Reduce Motion on, or a
       bfcache restore, nothing on this page is ever hidden.
     · Everything runs as a TRANSITION on the document timeline, which always
       reaches its last keyframe. No view() timeline touches opacity, ever.
     · `animation: none !important` below is the hand-off: while the observer
       owns an element's entrance, the scroll-driven sectionRise above lets go,
       so an element never has two owners.

   The selectors are class-based (.mo, .mo-g, .mo-show) rather than structural
   so that this block styles ONLY what the script has claimed. The hero gets no
   class and is never claimed. */

/* THE TRIPLED CLASS IS SPECIFICITY, NOT A TYPO. Components own their resting
   transitions at up to three-class strength — `.doc--home > .cards .card` sets
   `transition: transform, background` at (0,3,0), and a weaker reveal selector
   loses the cascade for the transition property, which was MEASURED here as
   opacity snapping 0→1 with no travel at all. `.mo-g.mo-g.mo-g` is (0,3,0) at
   the end of the file, so the reveal owns the transition exactly while the
   element is claimed and hands it back the moment the classes come off. */

.mo, .mo-g, .mo-show, .mo-lay { animation: none !important; }
.mo-g .w-dev, .mo-show .w-dev { animation: none !important; }

/* layerAssemble — the stack is claimed collapsed (gap zero, planes faded) and
   explodes on entry, boundary first, places last. The separation rides the
   SAME --lay-gap arithmetic as the resting layout and the hover breathe, so
   the settle hand-off changes nothing on screen. Transform is never forced to
   `none` for these planes anywhere — their resting state IS a 3D transform. */
.mo-lay.mo-lay.mo-lay .w-lay-plane {
  transition:
    opacity var(--w-dur-device) var(--w-ease-out),
    transform var(--w-dur-device) var(--w-ease-out);
}
.mo-lay.mo-lay.mo-lay:not(.mo-in) { --lay-gap: 0px; }
.mo-lay.mo-lay.mo-lay:not(.mo-in) .w-lay-plane { opacity: 0; }
.mo-lay.mo-lay.mo-lay.mo-in .w-lay-plane--counties { transition-delay: 70ms; }
.mo-lay.mo-lay.mo-lay.mo-in .w-lay-plane--places { transition-delay: 140ms; }

/* sectionReveal — one piece, one rise. */
.mo.mo.mo {
  transition:
    opacity var(--w-dur-device) var(--w-ease-out),
    transform var(--w-dur-device) var(--w-ease-out);
}
.mo.mo.mo:not(.mo-in) { opacity: 0; transform: translateY(24px); }

/* ruleDraw — the one .mo that draws instead of rising. Travel-only: a hairline
   at opacity 0 and a hairline at scaleX(0) look identical, so keep the version
   whose failure mode is "a rule, slightly early". */
.rule.mo.mo.mo { transform-origin: left; transition: transform var(--w-dur-device) var(--w-ease-out); }
.rule.mo.mo.mo:not(.mo-in) { opacity: 1; transform: scaleX(0); }

/* groupReveal — the group is claimed, its children travel. The stagger is
   55ms — under --w-dur-raise, so a six-card grid is fully seated in under
   700ms and the eye reads a cascade, not a queue. The delay rows sit above
   the shorthand on purpose: a `transition:` shorthand resets delay to 0. */
.mo-g.mo-g.mo-g > * {
  transition:
    opacity var(--w-dur-device) var(--w-ease-out),
    transform var(--w-dur-device) var(--w-ease-out);
}
.mo-g.mo-g.mo-g:not(.mo-in) > * { opacity: 0; transform: translateY(18px); }
.mo-g.mo-g.mo-g.mo-in > :nth-child(2) { transition-delay: 55ms; }
.mo-g.mo-g.mo-g.mo-in > :nth-child(3) { transition-delay: 110ms; }
.mo-g.mo-g.mo-g.mo-in > :nth-child(4) { transition-delay: 165ms; }
.mo-g.mo-g.mo-g.mo-in > :nth-child(5) { transition-delay: 220ms; }
.mo-g.mo-g.mo-g.mo-in > :nth-child(6) { transition-delay: 275ms; }
/* The roster is nine seats and the bill ladder is nine rungs, so the stagger
   runs to nine. It stops there: 8×55ms is 440ms of lead-in, already past
   --w-dur-device, and a tenth slot would have the last row arriving after the
   first has finished settling — a queue, which is the thing this is not. */
.mo-g.mo-g.mo-g.mo-in > :nth-child(7) { transition-delay: 330ms; }
.mo-g.mo-g.mo-g.mo-in > :nth-child(8) { transition-delay: 385ms; }
.mo-g.mo-g.mo-g.mo-in > :nth-child(n + 9) { transition-delay: 440ms; }

/* chipPop — the numbered and glyph anchors (step numbers, status glyphs, the
   manifesto rings, the process stations, the feature icons, the promise
   checks) arrive with their row but settle from a 0.6 scale, so each row
   reads as landing rather than fading. `transition-delay: inherit` rides the
   row's own stagger slot. */
.mo-g.mo-g.mo-g .steps-n, .mo-g.mo-g.mo-g .status-g,
.mo-g.mo-g.mo-g .w-proc-n, .mo-g.mo-g.mo-g .w-man-ring,
.mo-g.mo-g.mo-g .w-feat-i, .mo-g.mo-g.mo-g .w-check,
.mo-g.mo-g.mo-g .w-ros-i, .mo-g.mo-g.mo-g .w-jrn-node {
  transition:
    opacity var(--w-dur-rise) var(--w-ease-out),
    transform var(--w-dur-rise) var(--w-ease-out);
  transition-delay: inherit;
}
.mo-g.mo-g.mo-g:not(.mo-in) .steps-n,
.mo-g.mo-g.mo-g:not(.mo-in) .status-g,
.mo-g.mo-g.mo-g:not(.mo-in) .w-proc-n,
.mo-g.mo-g.mo-g:not(.mo-in) .w-man-ring,
.mo-g.mo-g.mo-g:not(.mo-in) .w-feat-i,
.mo-g.mo-g.mo-g:not(.mo-in) .w-ros-i,
.mo-g.mo-g.mo-g:not(.mo-in) .w-check { opacity: 0; transform: scale(0.6); }
/* The route node keeps its own transform origin — it is absolutely positioned
   against the spine, so a bare scale() would walk it off the line. */
.mo-g.mo-g.mo-g:not(.mo-in) .w-jrn-node { opacity: 0; transform: scale(0.5); }

/* capSlide + frameRise — the caption slides in from the side it sits on, and
   the frame follows one beat (90ms, under --w-dur-raise) behind it. The slide
   is GATED TO ≥1024px, where the two-column layout exists: below that the
   showcase stacks, and a translateX on a stacked child is sideways overflow —
   the one scroll this site promises never to have. */
.mo-show.mo-show.mo-show .w-show-cap, .mo-show.mo-show.mo-show .w-dev {
  transition:
    opacity var(--w-dur-device) var(--w-ease-out),
    transform var(--w-dur-device) var(--w-ease-out);
}
.mo-show.mo-show.mo-show:not(.mo-in) .w-show-cap { opacity: 0; transform: translateY(18px); }
.mo-show.mo-show.mo-show:not(.mo-in) .w-dev { opacity: 0; transform: translateY(28px); }
.mo-show.mo-show.mo-show.mo-in .w-dev { transition-delay: 90ms; }

@media (min-width: 1024px) {
  .mo-show.mo-show.mo-show:not(.mo-in) .w-show-cap { transform: translateX(-32px); }
  .mo-show.mo-show.mo-show.w-show--left:not(.mo-in) .w-show-cap { transform: translateX(32px); }
  .mo-show.mo-show.mo-show.w-show--center:not(.mo-in) .w-show-cap { transform: translateY(18px); }
}

/* Print safety for the reveal lives INSIDE the site's one @media print block
   below (§ PRINT): check-copy.mjs exempts exactly one print block from the
   colour guard — the first — so a second block here would steal the exemption
   and fail the build on the real one's black-and-white. */

@media (prefers-reduced-motion: reduce) {
  /* End states, named one by one. Nothing here removes a state change. */
  .w-deck-map .w-atlas,
  .w-backdrop .w-atlas { animation: none; }        /* the country is simply drawn */
  .w-eyebrow, .w-hw, .w-hero-lede, .w-mapnote, .w-hero-actions,
  .w-hero .w-float, .w-flagband, .w-legend,
  .w-atlas-marks { animation: none !important; }   /* the opening is simply open */
  .w-mk { animation: none !important; }            /* the marker rests at its 0.9 */
  .w-float { animation: none; transform: none; }   /* the card is simply there */
  .w-dev { animation: none; transform: none; }     /* the frame is simply there */
  .w-panel:target { animation: none; }             /* the card simply says the new thing */
  .w-county { animation: none; }                   /* the outline is simply there */
  .w-hero .w-float { animation: none !important; }
  .w-flagcell a:hover { transform: none; }         /* the cell still lights, it just does not lift */
  /* The six observer-owned reveals: motion.js exits before claiming anything,
     so these rules are belt-and-braces for a preference that changes mid-visit.
     The content is simply there. */
  .mo, .mo-g > *, .mo-g .steps-n, .mo-g .status-g,
  .mo-g .w-proc-n, .mo-g .w-man-ring, .mo-g .w-feat-i, .mo-g .w-check,
  .mo-show .w-show-cap, .mo-show .w-dev, .rule.mo {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  /* The layer stack keeps its 3D resting transform — only the collapse and
     the fade are removed. The stack is simply exploded. */
  .mo-lay:not(.mo-in) { --lay-gap: inherit !important; }
  .mo-lay .w-lay-plane { opacity: 1 !important; transition: none !important; }
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    transition-duration: 0.001ms !important;       /* feedback still appears, instantly */
    animation-iteration-count: 1 !important;
  }
  body::after { animation: none !important; }
  .card:hover, .plan:hover, .status:hover, .w-feat-item:hover, .tablewrap:hover, .ledger:hover { transform: none !important; }
}

/* ═══ DARK ONLY ══════════════════════════════════════════════════════════════

   There is no light mode and there is no prefers-color-scheme: light branch.
   lib/theme.ts has no light values to consume; app/_layout.tsx hard-sets a light
   status bar over COLORS.bg. A light palette would have to be invented at the
   marketing layer, which is the precise drift this pipeline exists to prevent —
   and the status colours are legal information tuned on #0B1120, so re-deriving
   them for a white ground would be a legibility change to legal signalling, not
   a theme toggle. A visitor arriving from a light-mode OS and opening the app
   must not experience a colour change; the site is the app's first screenshot.

   What follows are not a light mode. They are accessibility and paper.         */

/* PRINT. A dark 40-page terms document is a printer-cartridge tax, and App
   Review does print these. Paper is not a theme: it is one medium, monochrome,
   and it names the colours it uses rather than taking them from a palette the
   app does not have. */
@media print {
  html, body { background: #fff; color: #000; }
  .h, .f-cols, .skip, .w-backdrop, .w-pick, .w-dev, .consent, body::before { display: none; }
  .doc > * { max-width: none; }
  /* The map does not print; the selected panel prints as text. */
  .w-panel { display: block; margin-top: 0; }
  .w-float { margin-top: 0; box-shadow: none; }
  a { color: #000; text-decoration: underline; }
  /* A printed page cannot be clicked; the destination has to be on the paper. */
  .doc a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 0.85em; }
  .callout, .card, .plan, .cta, .status, .faq details, .w-panel, .w-float,
  .tablewrap, .ledger, .w-ros-seat, .w-jrn-leg--clip {
    background: #fff;
    color: #000;
    border: 1px solid #000;
    box-shadow: none;
  }
  .callout-b, .card-b, .status-m, .fine, .f-note, .f-fine, .plan-tag,
  .plan-lim, .card-n, .list li, tbody td, .w-float-b, .w-show-b, .w-panel p,
  .w-ros-b, .w-stg-e, .w-jrn-b, .w-jrn-dist { color: #000; }
  /* The tone rails and nodes are colour-only signals, and paper here is
     monochrome. Every one of them repeats a WORD that prints beside it, so
     dropping the mark loses decoration and never meaning — which is the whole
     reason the rule "a tone never travels without its word" is enforced. */
  .w-stg-rail, .w-jrn-node, .w-jrn-leg::before, .w-ros-i { display: none; }
  .w-stg-row, .w-jrn-leg { padding-inline-start: 0; break-inside: avoid; }
  .w-ros-seat { break-inside: avoid; }
  .faq details { break-inside: avoid; }
  .w-show, .w-rail { display: block; }
  /* Paper is a timeline-free medium; nothing the observer claimed may print
     hidden. (motion.js never runs in a print context, but a page printed
     mid-scroll still holds whatever classes it held on screen.) */
  .mo, .mo-g > *, .mo-g .steps-n, .mo-g .status-g,
  .mo-g .w-proc-n, .mo-g .w-man-ring, .mo-g .w-feat-i, .mo-g .w-check,
  .mo-show .w-show-cap, .mo-show .w-dev {
    opacity: 1 !important;
    transform: none !important;
  }
  .mo-lay:not(.mo-in) { --lay-gap: inherit !important; }
  .mo-lay .w-lay-plane { opacity: 1 !important; }
}

/* FORCED COLORS. Never rely on a fill alpha here — forced-colors flattens it.
   The map keeps its shape by stroke alone, and the picked state takes the
   system's own highlight. */
@media (forced-colors: active) {
  /* Fill alphas flatten here, so the ONLY signal the selected state can keep is
     stroke weight — and the caption and the panel remain the real carriers.
     The generated per-state rule in map.css re-states the weight; this block is
     what makes the flattened fill harmless. */
  .w-st { fill: Canvas; stroke: CanvasText; stroke-width: 1px; }
  .w-mk { fill: Highlight; stroke: HighlightText; }
  .btn { border: 1px solid ButtonText; }
  .h { border-bottom: 1px solid CanvasText; }
  .callout, .card, .plan, .status, .w-panel, .w-float, .w-dev,
  .w-ros-seat { border: 1px solid CanvasText; }
  /* Fill alphas flatten here, so the clipped leg's tint disappears and the tone
     dots lose their colours. Both already carry words — "Short leg", and the
     stage label on every rung — so the flattening costs nothing. The dot keeps
     a visible shape via CanvasText rather than vanishing into the background. */
  .dot, .w-stg-rail { background: CanvasText; }
  .w-jrn-node { border-color: CanvasText; background: Canvas; }
  .w-jrn-leg--clip { border: 1px solid CanvasText; }
  .w-dev-island { background: CanvasText; }
  .w-chip:focus { text-decoration: underline; }
}
