/* =============================================================================
   The Tide Chart — tide.css
   Foundation stylesheet: tokens · reset · type · layout · components · charts ·
   states · print. Served as-is from public/css/tide.css (no build step), so
   every hex literal in the product lives here and nowhere else.

   Source of truth: design/web/"Pages P0.dc.html" (token block + base components)
   and uploads/06-design-system-web.md (the written design system).
   ========================================================================== */

/* =============================================================================
   1. TOKENS
   Light values sit on bare :root. Dark values are redefined in BOTH guarded
   blocks (media query + explicit [data-theme]) with identical values, so an
   explicit choice beats the OS preference and nothing is ever defined only
   inside a media query.
   ========================================================================== */

:root {
  /* -- type stacks ------------------------------------------------------- */
  --font-ui: 'Roboto', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-dsp: 'Roboto Condensed', 'Roboto', system-ui, -apple-system, sans-serif;
  --font-mn: 'Roboto Mono', 'JetBrains Mono', ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace;

  /* ── Type scale (design system §3, tokenised 2026-08-30) ──────────────────
     Eight steps carry ~95% of the UI text; the display roles above them stay
     on their clamp() classes (.h1 .h2 .h3, .tile__val, the hero numerals).
     Pick the STEP for the ROLE — never a bare px: the audit that introduced
     these found 45 ad-hoc sizes on a spec that names 12 roles, drifting in
     half-pixel increments nobody can see.
       micro   table headers, chart axis caps      meta   eyebrows, badges
       data    dense numerals, pills, hour strips  ui     links, cells, notes
       ui-lg   buttons, inputs, prominent controls sub    nav, small headings
       body    prose                               lede   intros, FAQ questions
     --fs-meta is 10px per the design boards; the spec TEXT says 11–13px — a
     recorded, unresolved contradiction (boards win, as ruled for Task 4.1).
     Change it HERE if that ruling ever flips. */
  --fs-micro: 9.5px;
  --fs-meta: 10px;
  --fs-data: 11px;
  --fs-ui: 12.5px;
  --fs-ui-lg: 13px;
  --fs-sub: 14px;
  --fs-body: 15px;
  --fs-lede: 16px;

  /* -- surface + ink (light) --------------------------------------------- */
  --bg: #FAF7F1;
  --card: #FFFFFF;
  --bd: #E7E1D2;
  --dv: #F0EBDE;
  --ink: #23301F;
  --ink2: #5D6B58;
  --ink3: #6E7466;

  /* -- brand, pro, tide state (light) ------------------------------------ */
  --grn: #3E8460;
  --grnd: #2E6A4B;
  --tint: #EFF3E7;
  --gold: #96661C;
  --goldt: #F6EEDC;
  --red: #C6512F;
  --redt: #FDEFEA;
  --amb: #8D6916;
  --ambt: #FBF3DE;

  /* -- metric accents (constant across themes — see design system §2.4) --- */
  --wind: #2E8F86;
  --wave: #5D8FA6;
  --waved: #3F6B80;
  --sun: #F2B33D;
  --minor: #E0B14B;
  --sunset: #E38B4F;

  /* -- inverted card + shadow key (light) -------------------------------- */
  --inverted-bg: #23301F;
  --inverted-text: #FAF7F1;
  /* Shadows key off their own token: --ink would read as a glow in dark mode. */
  --shadow-rgb: 35, 48, 31;

  /* -- sticky chrome backdrop (light). Opaque on purpose: see .site-header. */
  --frost: #FAF7F1;

  /* -- tidal coefficient, 4 bands (constant across themes) --------------- */
  --coef-low: #CFC9B8;
  --coef-avg: #E0B14B;
  --coef-high: #7FA65A;
  --coef-vhigh: #3E8460;
  /* badge tints (light) */
  --coef-low-t: #F1EFE8;
  --coef-avg-t: #FBF3DE;
  --coef-high-t: #ECF2E0;
  --coef-vhigh-t: #E2EFDF;

  /* -- air quality, 6 bands (constant across themes) --------------------- */
  --aq-1: #3E8460;
  --aq-2: #7FA65A;
  --aq-3: #E0B14B;
  --aq-4: #E0883E;
  --aq-5: #C6512F;
  --aq-6: #5D6B58;

  /* -- UV index, 5 bands (constant across themes) ------------------------ */
  --uv-1: #8FBF5A;
  --uv-2: #E0B14B;
  --uv-3: #E38B4F;
  --uv-4: #C6512F;
  --uv-5: #B87FB8;

  /* -- weather / probability, 8 states (constant across themes) ----------
     --wx-snow sits within a hair of --bg: any swatch or fill using it needs
     a 1px hairline border to stay legible. */
  --wx-clear: #F2B33D;
  --wx-overcast: #ADA695;
  --wx-windy: #2E8F86;
  --wx-rain: #5D8FA6;
  --wx-fog: #D9D3C4;
  --wx-thunder: #23301F;
  --wx-snow: #F7F5EE;
  --wx-frost: #BFE3D3;

  /* -- moon card: always dark, in every theme, so one fixed set ----------- */
  --moon-bg: #141C12;
  --moon-ink: #F1EFE8;
  --moon-ink2: #8E9A85;
  --moon-bd: #2C3826;
  --moon-dv: #26311F;
  /* Gold on the always-dark moon card: the light theme's --gold is 3.50:1 there. */
  --moon-gold: #D9A94A;

  /* -- spacing, 4px steps ------------------------------------------------ */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-7: 28px;
  --space-8: 32px;
  --space-9: 36px;
  --space-10: 40px;

  /* -- radii ------------------------------------------------------------- */
  --r-sm: 6px;
  --r-md: 10px;
  --r-lg: 14px;
  --r-xl: 22px;
  --r-pill: 999px;

  /* -- stacking ---------------------------------------------------------- */
  --z-sticky: 20;
  --z-rail: 30;
  --z-popover: 40;
  --z-sheet: 50;
  --z-modal: 60;
  --z-toast: 70;

  /* -- motion ------------------------------------------------------------ */
  --dur-fast: 120ms;
  --dur-base: 200ms;
  --dur-slow: 320ms;
  --ease: cubic-bezier(.2, .8, .2, 1);

  /* -- layout metrics ---------------------------------------------------- */
  --content-max: 1200px;
  --content-max-tool: 1320px;
  --header-h: 60px;
  /* The anchor rail's own height (one 28px link row inside space-2 padding
     plus its border) — what the rail's fixed date picker hangs under. */
  --anchor-rail-h: 53px;
  --tool-bar-h: 56px;
  --rail-w: 220px;
  --rail-w-icon: 64px;

  /* -- shadows: floating things only, never a card in the page flow ------ */
  --shadow-sm: 0 8px 24px -10px rgba(var(--shadow-rgb), .4);
  --shadow-md: 0 14px 34px -22px rgba(var(--shadow-rgb), .35);
  --shadow-lg: 0 18px 50px -20px rgba(var(--shadow-rgb), .35);
  --shadow-xl: 0 24px 50px -20px rgba(var(--shadow-rgb), .35);
  --shadow-tip: 0 12px 30px -12px rgba(var(--shadow-rgb), .55);

  color-scheme: light dark;
}

@media screen and (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #141C12;
    --card: #1C2619;
    --bd: #2C3826;
    --dv: #26311F;
    --ink: #F1EFE8;
    --ink2: #A9B29C;
    --ink3: #808E75;
    --grn: #5AC490;
    --grnd: #4FAE80;
    --tint: #16261C;
    --gold: #D9A94A;
    --goldt: #3A2E14;
    --red: #E2764C;
    --redt: #2E1B14;
    --amb: #D9A94A;
    --ambt: #2A2416;
    --inverted-bg: #1F3B2A;
    --inverted-text: #F1EFE8;
    --shadow-rgb: 0, 0, 0;
    --frost: #141C12;
    --coef-low-t: #2A2A24;
    --coef-avg-t: #2A2416;
    --coef-high-t: #1E2A19;
    --coef-vhigh-t: #16261C;
    /* Semantic hues stay constant across themes — restated, not changed. */
    --wx-clear: #F2B33D;
    --wx-overcast: #ADA695;
    --wx-windy: #2E8F86;
    --wx-rain: #5D8FA6;
    --wx-fog: #D9D3C4;
    --wx-thunder: #23301F;
    --wx-snow: #F7F5EE;
    --wx-frost: #BFE3D3;
  }
}

:root[data-theme="dark"] {
  --bg: #141C12;
  --card: #1C2619;
  --bd: #2C3826;
  --dv: #26311F;
  --ink: #F1EFE8;
  --ink2: #A9B29C;
  --ink3: #808E75;
  --grn: #5AC490;
  --grnd: #4FAE80;
  --tint: #16261C;
  --gold: #D9A94A;
  --goldt: #3A2E14;
  --red: #E2764C;
  --redt: #2E1B14;
  --amb: #D9A94A;
  --ambt: #2A2416;
  --inverted-bg: #1F3B2A;
  --inverted-text: #F1EFE8;
  --shadow-rgb: 0, 0, 0;
  --frost: #141C12;
  --coef-low-t: #2A2A24;
  --coef-avg-t: #2A2416;
  --coef-high-t: #1E2A19;
  --coef-vhigh-t: #16261C;
  /* Semantic hues stay constant across themes — restated, not changed. */
  --wx-clear: #F2B33D;
  --wx-overcast: #ADA695;
  --wx-windy: #2E8F86;
  --wx-rain: #5D8FA6;
  --wx-fog: #D9D3C4;
  --wx-thunder: #23301F;
  --wx-snow: #F7F5EE;
  --wx-frost: #BFE3D3;
}

/* =============================================================================
   2. RESET
   ========================================================================== */

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

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 400 var(--fs-body)/1.55 var(--font-ui);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  min-height: 100%;
}

h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd { margin: 0; }
ul, ol { margin: 0; padding: 0; }
li { list-style: none; }

img, svg, video, canvas { max-width: 100%; }
img, video { height: auto; display: block; }

a { color: var(--grnd); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

button, input, select, textarea { font: inherit; color: inherit; margin: 0; }

button { background: none; border: 0; padding: 0; cursor: pointer; }

table { border-collapse: collapse; }

hr { border: 0; border-top: 1px solid var(--dv); margin: var(--space-5) 0; }

[hidden] { display: none !important; }

/* Alpine: hide pre-init markup so a page never flashes its raw state. */
[x-cloak] { display: none !important; }

::selection { background: var(--tint); color: var(--ink); }

/* One global focus token — never suppressed, never replaced by outline:none. */
:focus-visible { outline: 2px solid var(--grn); outline-offset: 2px; }

/* Sticky chrome must never obscure a focused/anchored target. */
:target,
[id] { scroll-margin-top: calc(var(--header-h) + var(--space-6)); }

@media (prefers-reduced-motion: reduce) {
  :root { --dur-fast: 0ms; --dur-base: 0ms; --dur-slow: 0ms; }

  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* =============================================================================
   3. TYPE
   Roboto Condensed 800/700 for editorial headlines, Roboto for UI, Roboto Mono
   for every number a user reads as data — at every size, hero scale included.
   ========================================================================== */

.mn { font-family: var(--font-mn); letter-spacing: -.03em; font-variant-numeric: tabular-nums; }

.dsp { font-family: var(--font-dsp); letter-spacing: -.01em; }

.eb,
.plan-window__kind { font: 700 var(--fs-meta)/1.3 var(--font-ui); letter-spacing: .14em; text-transform: uppercase; color: var(--ink3); }

.h1 {
  font-family: var(--font-dsp);
  font-weight: 800;
  font-size: clamp(36px, 1.05rem + 4.6vw, 62px);
  line-height: .99;
  letter-spacing: -.01em;
  color: var(--ink);
}

.h2 {
  font-family: var(--font-dsp);
  font-weight: 700;
  font-size: clamp(22px, .95rem + 1.4vw, 32px);
  line-height: 1.15;
  letter-spacing: -.01em;
  color: var(--ink);
}

.h3 {
  font-family: var(--font-dsp);
  font-weight: 700;
  font-size: clamp(17px, .9rem + .5vw, 22px);
  line-height: 1.2;
  letter-spacing: -.01em;
  color: var(--ink);
}

/* Display numeral + unit is always a baseline pair, never one string. */
.num-pair { display: flex; align-items: baseline; gap: .1em; font-family: var(--font-mn); letter-spacing: -.03em; }

.num-pair__val { font-weight: 700; line-height: 1.05; font-variant-numeric: tabular-nums; }

/* .36em is the spec's ~36%-of-numeral ratio — sized for hero numerals. The 9px
   floor is for a .num-pair that lands on body-sized text (the air-quality chip
   shipped a 5px µg/m³ this way): a unit may be small, but it must stay TEXT. */
.num-pair__unit { font-size: max(9px, .36em); font-weight: 600; color: var(--ink2); letter-spacing: 0; }

/* =============================================================================
   4. LAYOUT
   ========================================================================== */

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

.skip-link {
  position: fixed;
  left: var(--space-2);
  top: var(--space-2);
  z-index: calc(var(--z-toast) + 10);
  transform: translateY(-250%);
  background: var(--grnd);
  color: #FFFFFF;
  padding: 11px 18px;
  border-radius: var(--r-sm);
  font: 700 var(--fs-sub)/1 var(--font-ui);
  text-decoration: none;
  transition: transform var(--dur-fast) var(--ease);
}

.skip-link:hover { text-decoration: none; }

/* Dark theme's --grnd is a LIGHT green: white on it is 2.73:1. Flip the ink to
   the page ground, as .btn--primary does. */
:root[data-theme="dark"] .skip-link { color: var(--bg); }

@media screen and (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .skip-link { color: var(--bg); }
}
.skip-link:focus,
.skip-link:focus-visible { transform: translateY(0); }

.container { width: 100%; max-width: var(--content-max); margin-inline: auto; padding-inline: var(--space-6); }

.container--tool { max-width: var(--content-max-tool); }

@media (max-width: 639px) {
  .container { padding-inline: var(--space-4); }
}

/* --- 4.1 PAGE LAYOUT PRIMITIVES — stack/cols/split/prose ------------------
   The primitives every non-tool page composes from: a page's vertical rhythm,
   an equal-column card row, a body + aside split, a reading measure and the
   small copy roles. Deliberately generic (never .pricing__*) so pricing,
   about, the legal pages, contact and the SEO/marketing pages share one set. */

.page { display: flex; flex-direction: column; gap: var(--space-8); padding: var(--space-8) 0 var(--space-10); }

.page__head { display: flex; flex-direction: column; gap: var(--space-3); }
.page__head--center { align-items: center; text-align: center; }
/* The headline gets the full column; only the lede is held to a measure. */
.page__lede { font: 500 var(--fs-lede)/1.6 var(--font-ui); color: var(--ink2); max-width: 68ch; }

/* Equal-width columns that collapse to one the moment they run out of room.
   auto-fit collapses the empty tracks, so two cards always split 50/50. */
.cols { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); }
.cols--pair { grid-template-columns: repeat(auto-fit, minmax(min(100%, 420px), 1fr)); }

/* Vertical stack — the one spacing utility; everything else composes it. */
.stack { display: flex; flex-direction: column; gap: var(--space-3); }
.stack--tight { gap: var(--space-2); }
.stack--loose { gap: var(--space-5); }

/* Leading-icon row: feature ticks, licence rows, card headers. */
.bullet { display: flex; gap: var(--space-2); align-items: flex-start; font: 500 var(--fs-ui-lg)/1.5 var(--font-ui); }
.bullet > .ic-svg { flex: none; margin-top: 3px; color: var(--grnd); }
.bullet--mid { align-items: center; gap: var(--space-3); }
.bullet--mid > .ic-svg { margin-top: 0; }

/* Body column + narrow aside. One column until there is room for both. */
.split { display: grid; gap: var(--space-6); grid-template-columns: minmax(0, 1fr); align-items: start; }

@media (min-width: 921px) {
  .split { grid-template-columns: minmax(0, 1fr) 320px; }
}

/* Reading measure for long-form legal / editorial copy. */
.prose { max-width: 72ch; }

/* Copy roles. `.copy` is body text; these are the only variants in use. */
.copy { font: 400 var(--fs-body)/1.7 var(--font-ui); color: var(--ink2); }
.copy--muted { font-size: var(--fs-ui); line-height: 1.55; }
.copy--accent { font-weight: 600; font-size: var(--fs-ui-lg); color: var(--grnd); }
.copy--block { display: block; }
.copy--rule { border-top: 1px solid var(--dv); padding-top: var(--space-3); margin-top: var(--space-1); }

/* An eyebrow captioning an unpadded card (one whose body is a table). */
.card__cap { padding: 13px 16px 9px; }

/* Pricing tiers. Two cards ARE the comparison (see the view), so they must
   read as one row: equal columns, feature lists that start at the same line and
   CTAs pinned to the bottom edge whatever a list's length. */
.tiers {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
  align-items: stretch;
}

.tier { display: flex; flex-direction: column; gap: var(--space-4); }
.tier__price { font-size: 34px; font-weight: 700; line-height: 1.05; color: var(--ink); }
.tier__list { flex: 1; }

/* The bullets that differ between tiers — the ones a visitor is comparing. */
.tier__diff { font-weight: 700; }

.tier__foot { margin-top: auto; display: flex; flex-direction: column; gap: var(--space-2); align-items: flex-start; }

.noads { display: flex; flex-direction: column; gap: var(--space-1); }
.noads__head { color: var(--grnd); }

@media (max-width: 900px) {
  .tiers { grid-template-columns: minmax(0, 1fr); }
}


/* =============================================================================
   5. COMPONENTS
   ========================================================================== */

/* --- 5.1 Cards ------------------------------------------------------------
   Elevation is a 1px hairline border by default; shadows are reserved for
   things that genuinely float. A card never shrinks inside a fixed-height
   flex column — flex:none is load-bearing, not cosmetic. */

.card { background: var(--card); border: 1px solid var(--bd); border-radius: var(--r-lg); flex: none; }

.card--tint { background: var(--tint); border-color: transparent; }

.card--inverted { background: var(--inverted-bg); color: var(--inverted-text); border-color: transparent; }

:root[data-theme="dark"] .card--inverted { border-color: rgba(62, 132, 96, .3); }

@media screen and (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .card--inverted { border-color: rgba(62, 132, 96, .3); }
}

/* Every type role inside an inverted card takes the SURFACE's ink, not the page's
   - .h1/.h2/.h3 hard-set color:var(--ink), so without this a heading on the dark
   panel would paint near-black on near-black. Fix the token here, once, so no
   inverted panel needs a private heading class. */
.card--inverted .h1,
.card--inverted .h2,
.card--inverted .h3,
.card--inverted .sec__body { color: var(--inverted-text); }

.card--inverted .eb,
.card--inverted .num-pair__unit { color: var(--inverted-text); opacity: .68; }

.card--inverted .sec__note { color: var(--inverted-text); opacity: .7; }

.card--pad { padding: 18px; }

/* --- 5.2 Stat tiles -------------------------------------------------------
   Fixed 8-tile at-a-glance grid: 4 columns wide, 2 columns from tablet down. */

.tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-3); }

@media (max-width: 920px) {
  .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 639px) {
  .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2); }
}

.tile {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  background: var(--card);
  border: 1px solid var(--bd);
  border-radius: 12px;
  padding: var(--space-3);
  flex: none;
  color: inherit;
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease);
}

a.tile:hover,
button.tile:hover { text-decoration: none; border-color: var(--grn); }

.tile__ic {
  width: 30px;
  height: 30px;
  border-radius: var(--r-pill);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  background: var(--tint);
  color: var(--grnd);
  transition: background var(--dur-fast) var(--ease);
}

/* Design system 6.9: hover lifts the TINTED circle one step darker - a warm grey
   read as "disabled", not "live". */
a.tile:hover .tile__ic,
button.tile:hover .tile__ic { background: color-mix(in srgb, var(--tint) 84%, var(--grn)); }

.tile__val,
.swell-card__val {
  /* The unit symbol is a sibling element, so the source newline between them
     would render as a full mono space; a baseline flex row drops the anonymous
     whitespace box and gives the board's 3px gap. */
  display: flex;
  align-items: baseline;
  gap: 3px;
  font-family: var(--font-mn);
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: clamp(20px, 1.1rem + .5vw, 26px);
  line-height: 1.1;
  color: var(--ink);
}

/* Board P2a sets the unit at ~0.6x the value, not the 0.42x the hero's num-pair
   uses - a tile value is a third of the hero's size, so the same ratio collapses
   "m" into an illegible smudge. */
.tile__unit { font-family: var(--font-mn); font-size: .56em; font-weight: 600; color: var(--ink2); letter-spacing: 0; }

/* Row layout — board P1b's "Sea & weather now" card: a bigger circle beside the
   text instead of above it. Was a parallel    near-identical tint map. */
.tile--row { flex-direction: row; align-items: center; gap: var(--space-3); padding: 13px 15px; }
.tile--row .tile__ic { width: 38px; height: 38px; }
.tile--row .tile__val { font-size: 22px; line-height: 1.15; }
.tile--row .tile__sub { font-size: var(--fs-ui); line-height: 1.4; }
.tile__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

.tile__sub { font: 500 var(--fs-data)/1.35 var(--font-ui); color: var(--ink2); }

/* --- 5.3 Pills and badges ------------------------------------------------- */

.pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border-radius: var(--r-pill);
  padding: 4px 9px;
  font: 700 var(--fs-data)/1.2 var(--font-ui);
  white-space: nowrap;
  min-height: 24px;
  color: var(--ink2);
}

.pill--rising { background: var(--tint); color: var(--grnd); }
.pill--falling { background: var(--redt); color: var(--red); }

/* Gauge = filled dot, interpolated = hollow ring — never colour alone. */
.pill--gauge,
.pill--interp { padding-left: 8px; }

.pill--gauge { background: var(--tint); color: var(--grnd); }
.pill--interp { background: var(--ambt); color: var(--amb); }

.pill--gauge::before,
.pill--interp::before { content: ""; width: 8px; height: 8px; border-radius: var(--r-pill); flex: none; }

.pill--gauge::before { background: currentColor; }
.pill--interp::before { border: 1.5px solid currentColor; }

/* Coefficient bands — tint + a band-coloured dot + the band word in markup. */
.pill--coef { --_c: var(--coef-avg); background: var(--coef-avg-t); color: var(--amb); padding-left: 8px; }

.pill--coef::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--_c);
  flex: none;
}

.pill--coef[data-band="low"] { --_c: var(--coef-low); background: var(--coef-low-t); color: var(--ink2); }
.pill--coef[data-band="avg"] { --_c: var(--coef-avg); background: var(--coef-avg-t); color: var(--amb); }
.pill--coef[data-band="high"] { --_c: var(--coef-high); background: var(--coef-high-t); color: var(--grnd); }
.pill--coef[data-band="vhigh"] { --_c: var(--coef-vhigh); background: var(--coef-vhigh-t); color: var(--grnd); }

/* Prime solunar window — the one emphatic, filled data pill. */
.pill--prime { background: var(--grnd); color: #FFFFFF; }

/* Staleness ladder, 30 min – 24 h step. */
.pill--stale { background: var(--ambt); color: var(--amb); }

/* PRO — the only place gold appears. Always paired with a padlock glyph. */
.pro {
  background: var(--goldt);
  color: var(--gold);
  font: 700 var(--fs-micro) var(--font-ui);
  letter-spacing: .1em;
  padding: 4px 6px;
  border-radius: 5px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
  text-transform: uppercase;
}

/* --- 5.4 Buttons ---------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  border-radius: var(--r-md);
  font: 700 var(--fs-sub)/1 var(--font-ui);
  height: 44px;
  padding: 0 18px;
  white-space: nowrap;
  cursor: pointer;
  border: 1px solid transparent;
  background: transparent;
  color: var(--ink);
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              opacity var(--dur-fast) var(--ease);
}

.btn:hover { text-decoration: none; }
.btn:active { transform: scale(.98); transition: none; }

.btn[disabled],
.btn[aria-disabled="true"] { opacity: .4; pointer-events: none; transform: none; }

.btn--primary { background: var(--grnd); color: #FFFFFF; }

.btn--primary:hover { background: #254F39; }

:root[data-theme="dark"] .btn--primary { color: var(--bg); }
:root[data-theme="dark"] .btn--primary:hover { background: var(--grn); }

@media screen and (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .btn--primary { color: var(--bg); }
  :root:not([data-theme="light"]) .btn--primary:hover { background: var(--grn); }
}

.btn--secondary { background: var(--card); border-color: var(--bd); color: var(--ink); }

.btn--secondary:hover { background: var(--dv); }

.btn--ghost { background: transparent; color: var(--grnd); padding: 0 var(--space-2); }

.btn--ghost:hover { text-decoration: underline; text-underline-offset: 3px; }

.btn--danger { background: var(--redt); color: var(--red); }

.btn--danger:hover { background: var(--redt); border-color: var(--red); }

.btn--sm { height: 36px; font-size: var(--fs-ui-lg); padding: 0 14px; border-radius: var(--r-sm); }

.btn--icon { width: 44px; height: 44px; padding: 0; border-radius: var(--r-pill); color: var(--ink2); }

.btn--icon:hover { background: var(--tint); color: var(--grnd); }
.btn--icon.btn--sm { width: 36px; height: 36px; }

/* --- 5.5 Segmented control and tabs ---------------------------------------
   Filled pills are reserved for unit/zoom toggles; in-section tabs underline,
   so the two control types never look alike. */

.seg {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  background: var(--dv);
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  padding: 3px;
}

.seg__opt {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  min-height: 26px;
  padding: 0 12px;
  border-radius: var(--r-pill);
  font: 700 var(--fs-ui)/1 var(--font-ui);
  color: var(--ink2);
  background: transparent;
  border: 0;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.seg__opt:hover { color: var(--grnd); }

/* Design-system board: the chosen option is a raised LIGHT pill on the recessed
   track (never a green fill - green is the brand's action colour, not a state).
   Dark mode inverts the relationship: the track drops to --bg and the raised pill
   steps UP to --bd, because --card is darker than --dv there. */
.seg__opt[aria-pressed="true"] { background: var(--card); color: var(--ink); }

:root[data-theme="dark"] .seg { background: var(--bg); }
:root[data-theme="dark"] .seg__opt[aria-pressed="true"] { background: var(--bd); }

@media screen and (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .seg { background: var(--bg); }
  :root:not([data-theme="light"]) .seg__opt[aria-pressed="true"] { background: var(--bd); }
}

/* Tabs — the <x-tabs> tablist, a row of pills over panels already in the page.
   (This replaced an underline-strip .tabs/.tab vocabulary the redesign left
   behind with no markup using it.)

   The three state rules sit at ONE specificity deliberately. Written unevenly —
   a :hover scoped to the wrapper, a bare [aria-selected] — the hover wins on
   the active tab and paints its ink over its own fill, which is exactly the bug
   this component was extracted after. tests/js/css-states.test.mjs enforces it. */

.tabs { display: flex; flex-wrap: wrap; gap: 7px; }

.tabs__tab {
  min-height: 34px;
  padding: 8px 14px;
  font-size: var(--fs-ui);
  border: 1px solid var(--bd);
  color: var(--ink2);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}

.tabs__tab:not([aria-selected="true"]):hover { border-color: var(--grn); color: var(--grnd); }

.tabs__tab[aria-selected="true"] { background: var(--grnd); border-color: var(--grnd); color: var(--bg); }

.tabs__count { font-size: var(--fs-meta); opacity: .75; }

/* --- 5.6 Form fields ------------------------------------------------------
   Label above the field, never placeholder-as-label. */

.field { display: flex; flex-direction: column; gap: 6px; }

.field__label { font: 700 var(--fs-ui-lg)/1.3 var(--font-ui); color: var(--ink); }

.field__input {
  width: 100%;
  background: var(--card);
  border: 1px solid var(--bd);
  border-radius: 8px;
  padding: 11px 13px;
  font: 400 var(--fs-body)/1.35 var(--font-ui);
  color: var(--ink);
  transition: border-color var(--dur-fast) var(--ease);
}

.field__input::placeholder { color: var(--ink3); }
.field__input:hover { border-color: var(--ink3); }

.field__input:focus-visible { border-color: var(--grn); outline: 2px solid var(--grn); outline-offset: 2px; }

.field__input[disabled] { opacity: .5; cursor: not-allowed; }

.field__hint { font: 400 var(--fs-ui)/1.45 var(--font-ui); color: var(--ink2); }

.field--error .field__input { border-color: var(--red); background: var(--redt); }
.field--error .field__hint { color: var(--red); }
.field--error .field__label { color: var(--red); }
.field--error .auth__check { color: var(--red); }

/* --- 5.6b Auth screens (W17) ----------------------------------------------
   Centred 420px card + a 400px inverted "what an account does" panel; the two
   stack (card first) below 920. `.auth--single` is forgot/reset: card only. */

.auth {
  display: flex;
  align-items: stretch;
  justify-content: center;
  gap: var(--space-5);
  padding-block: var(--space-8) var(--space-10);
}

.auth__card {
  width: 420px;
  max-width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: 26px 28px;
}

.auth__aside {
  width: 400px;
  flex: none;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-4);
  padding: 30px 32px;
}

@media (max-width: 920px) {
  .auth { flex-direction: column; align-items: center; }
  .auth__aside { width: 420px; max-width: 100%; }
}

/* Segmented Sign in / Create account - real links, so aria-current, not aria-pressed. */
.auth__tabs { display: flex; width: 100%; }
.auth__tabs .seg__opt { flex: 1 1 0; min-height: 34px; font-size: var(--fs-ui-lg); }
.auth__tabs .seg__opt:hover { text-decoration: none; }
/* Links, so they carry aria-current where a button would carry aria-pressed -
   the raised-pill state itself is the shared .seg one. */
.auth__tabs .seg__opt[aria-current] { background: var(--card); color: var(--ink); }
:root[data-theme="dark"] .auth__tabs .seg__opt[aria-current] { background: var(--bd); }

@media screen and (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .auth__tabs .seg__opt[aria-current] { background: var(--bd); }
}

.auth__title { font-size: 26px; line-height: 1.1; }
.auth__lede { font: 500 var(--fs-ui-lg)/1.5 var(--font-ui); color: var(--ink2); }
.auth__fields { display: flex; flex-direction: column; gap: var(--space-4); }

.auth__row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
.auth__row--terms { display: block; }
.auth__check { display: flex; align-items: flex-start; gap: 9px; font: 500 var(--fs-ui)/1.5 var(--font-ui); color: var(--ink2); cursor: pointer; }
.auth__check input { width: 18px; height: 18px; flex: none; margin-top: 1px; accent-color: var(--grnd); }

.auth__actions { margin-top: var(--space-1); }
.auth__submit { width: 100%; height: 48px; }
.auth__legal { font: 400 var(--fs-data)/1.5 var(--font-ui); color: var(--ink3); text-align: center; }

/* `Show` toggle sits inside the password input (label 17px + 6px gap above it). */
.field--pw { position: relative; }
.field__reveal { position: absolute; right: 6px; top: 28px; height: 32px; font-size: var(--fs-data); }

/* Shares the `.h2` role; only the size is board-specific (24px, not the 32px the
   role clamps to at 1280) - the ink comes from `.card--inverted` in section 5. */
.auth__aside-title { font-weight: 800; font-size: 24px; line-height: 1.2; }
.auth__perks { display: flex; flex-direction: column; gap: var(--space-3); }
.auth__perk { display: flex; align-items: flex-start; gap: 11px; }
.auth__perk-ic {
  width: 34px;
  height: 34px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--r-pill);
  background: rgba(90, 196, 144, .16);
  color: #5AC490;
}
.auth__perk-t { font: 700 var(--fs-sub)/1.3 var(--font-ui); }
.auth__perk-b { font: 400 var(--fs-ui)/1.45 var(--font-ui); opacity: .72; margin-top: 2px; }
.auth__note {
  border-top: 1px solid color-mix(in srgb, var(--inverted-text) 16%, transparent);
  padding-top: 14px;
  font: 400 var(--fs-ui)/1.5 var(--font-ui);
  opacity: .72;
}

/* "Continue with Google". The one place in this stylesheet that does NOT use
   our own palette: Google's identity guidelines fix the surface, the stroke
   and the text colour per theme, and forbid recolouring the mark. So these are
   literals on purpose — do not "tidy" them onto tokens. Height and radius are
   ours (48px to sit level with .auth__submit); Google sets a minimum, not a
   maximum. */

.btn--google {
  width: 100%;
  height: 48px;
  gap: 12px;
  background: #FFFFFF;
  border-color: #747775;
  color: #1F1F1F;
  font-weight: 500;
}

.btn--google:hover { background: #F2F2F2; }
.btn--google:focus-visible { outline-color: #1F1F1F; }

:root[data-theme="dark"] .btn--google {
  background: #131314;
  border-color: #8E918F;
  color: #E3E3E3;
}

:root[data-theme="dark"] .btn--google:hover { background: #1E1F20; }
:root[data-theme="dark"] .btn--google:focus-visible { outline-color: #E3E3E3; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .btn--google {
    background: #131314;
    border-color: #8E918F;
    color: #E3E3E3;
  }

  :root:not([data-theme="light"]) .btn--google:hover { background: #1E1F20; }
  :root:not([data-theme="light"]) .btn--google:focus-visible { outline-color: #E3E3E3; }
}

/* The mark keeps its four colours in both themes — it is never tinted, and a
   `fill` on the button must not cascade into it. */
.btn--google svg { flex: none; }

/* "or" between the Google button and the e-mail form. */
.auth__or {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--ink3);
  font: 700 var(--fs-micro) var(--font-ui);
  letter-spacing: .1em;
  text-transform: uppercase;
}

.auth__or::before,
.auth__or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--dv);
}

/* --- 5.7 Tables -----------------------------------------------------------
   Real semantic <table> markup everywhere; below 640px rows become stacked
   day cards, never a table wider than the screen. */

.tbl { width: 100%; border-collapse: collapse; }

.tbl th {
  text-align: left;
  font: 700 var(--fs-micro) var(--font-ui);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink3);
  padding: 7px 9px;
  white-space: nowrap;
}

.tbl td { padding: 7px 9px; font: 500 var(--fs-ui) var(--font-ui); border-top: 1px solid var(--dv); color: var(--ink); }

.tbl caption {
  caption-side: top;
  text-align: left;
  padding: 0 9px var(--space-2);
  font: 500 var(--fs-ui) var(--font-ui);
  color: var(--ink2);
}

.tbl--hover tbody tr { transition: background var(--dur-fast) var(--ease); }
.tbl--hover tbody tr:hover { background: var(--tint); }

.tbl-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

@media (max-width: 639px) {
  .stack-cards,
  .stack-cards tbody,
  .stack-cards tr,
  .stack-cards td { display: block; width: 100%; }

  .stack-cards thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .stack-cards tr {
    background: var(--card);
    border: 1px solid var(--bd);
    border-radius: var(--r-md);
    padding: var(--space-3);
    margin-bottom: var(--space-3);
  }

  .stack-cards td {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
    border-top: 0;
    padding: 3px 0;
  }

  .stack-cards td[data-label]::before {
    content: attr(data-label);
    font: 700 var(--fs-micro) var(--font-ui);
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--ink3);
    flex: none;
  }

  .stack-cards tr > td:first-child {
    font: 700 var(--fs-sub) var(--font-dsp);
    letter-spacing: -.01em;
    padding-bottom: var(--space-2);
    margin-bottom: var(--space-1);
    border-bottom: 1px solid var(--dv);
  }
}

/* --- 5.8 Site header ------------------------------------------------------ */

.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  /* White chrome, not the board's cream frost (user call), and OPAQUE: the
     board's translucent bar let whatever scrolled under it bleed through, which
     over the Nearby map turned the nav into a smear of map tiles. Sticky chrome
     has to stay legible over arbitrary content, so no frost and no blur here. */
  background: var(--card);
  border-bottom: 1px solid var(--bd);
}

.site-header__inner {
  height: var(--header-h);
  display: flex;
  align-items: center;
  gap: var(--space-5);
  width: 100%;
  max-width: var(--content-max);
  margin-inline: auto;
  padding-inline: var(--space-6);
}

@media (max-width: 639px) {
  .site-header__inner { gap: var(--space-3); padding-inline: var(--space-4); }
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--ink);
  text-decoration: none;
  flex: none;
}

.brand:hover { text-decoration: none; }

.brand__mark {
  width: 28px;
  height: 28px;
  border-radius: var(--r-pill);
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--grnd);
  color: #FFFFFF;
  overflow: hidden;
}

.brand__word {
  font-family: var(--font-dsp);
  font-weight: 800;
  font-size: 17px;
  letter-spacing: -.01em;
  line-height: 1;
  color: var(--ink);
  white-space: nowrap;
}

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

.nav__link {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  padding: 6px 12px;
  border-radius: var(--r-pill);
  font: 700 var(--fs-sub)/1 var(--font-ui);
  color: var(--ink2);
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.nav__link:hover { background: var(--tint); color: var(--grnd); text-decoration: none; }

.nav__link[aria-current="true"],
.nav__link[aria-current="page"] { color: var(--grnd); background: var(--tint); }

/* --- 5.8b Header actions, menus, search -----------------------------------
   Everything right of the nav: the icon cluster, the two popovers it anchors,
   and the station search. Responsive plan — 768: the inline search and the
   "Get started" CTA drop (both live on in the sheet / the pages themselves);
   640: the nav drops too and a hamburger opens the #site-menu sheet, leaving
   brand + theme + units + a compact Sign in in the bar. */

.site-header__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: none;
  margin-left: auto;
}

/* Positioning context for a .popover owned by one trigger. */
.popover-anchor { position: relative; display: inline-flex; }

/* Theme toggle carries three glyphs; CSS shows the one for the CHOSEN theme
   (sun = Light, moon = Dark, monitor = System), so the button needs no JS to
   stay right. Keyed on the data-theme stamp ALONE — an absent stamp is the
   System choice — and deliberately NOT on prefers-color-scheme: following the
   OS made System wear the same glyph as Light on a light OS (and as Dark on a
   dark one), so two of the three states looked identical. */
.theme-toggle__sun, .theme-toggle__moon { display: none; }

:root[data-theme="light"] .theme-toggle__monitor { display: none; }
:root[data-theme="light"] .theme-toggle__sun { display: block; }

:root[data-theme="dark"] .theme-toggle__monitor { display: none; }
:root[data-theme="dark"] .theme-toggle__moon { display: block; }

.avatar-btn { background: var(--tint); color: var(--grnd); font: 700 var(--fs-sub)/1 var(--font-ui); }

/* A .popover row: the nav pill unrolled into a full-width menu line. */
.menu-item { width: 100%; justify-content: flex-start; gap: 9px; border-radius: var(--r-md); }

.account-menu { right: 0; top: calc(100% + 8px); display: flex; flex-direction: column; gap: 2px; }

.account-menu__id { display: block; min-width: 0; padding: 2px 10px 8px; }

.account-menu__name { display: block; font: 700 var(--fs-sub)/1.3 var(--font-dsp); letter-spacing: -.01em; color: var(--ink); }

.account-menu__email {
  display: block;
  font: 400 var(--fs-ui)/1.4 var(--font-ui);
  color: var(--ink2);
  overflow: hidden;
  text-overflow: ellipsis;
}

.account-menu__out { margin-top: 2px; padding-top: 4px; border-top: 1px solid var(--dv); }

.units-popover { right: 0; top: calc(100% + 8px); width: min(92vw, 340px); display: flex; flex-direction: column; gap: var(--space-2); }

.units-popover__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* --- station search --- */

.station-search { position: relative; display: flex; align-items: center; flex: 1 1 0; min-width: 0; max-width: 320px; }

.station-search__icon { position: absolute; left: 12px; display: flex; color: var(--ink3); pointer-events: none; }

.station-search__input { height: 38px; width: 100%; min-width: 0; padding-left: 36px; border-radius: var(--r-pill); }

/* On the now-white header the field's own --card fill would vanish into the
   bar; the page ground puts it back as an inset control. */
.site-header .station-search__input { background: var(--bg); }
.site-header .station-search__input:focus-visible { background: var(--card); }

/* The results panel and its rows are shared by all three search fields — the
   header, the phone menu sheet and the hero — so the row styling lives here
   rather than under .home-search, which is only the large variant. */
.station-search__results { left: 0; right: 0; top: calc(100% + 8px); max-height: 60vh; overflow-y: auto; text-align: left; }

/* --- phone menu sheet --- */

.site-nav-toggle { display: none; flex: none; }

.site-menu { display: flex; flex-direction: column; gap: var(--space-4); }

.site-menu__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }

/* The sheet is the touch surface, so its close button gets a touch target: the
   shared .btn--sm is 36px, under the 44px every phone guideline asks for. */
.site-menu__head .btn--icon { width: 44px; height: 44px; }

.site-menu__nav { display: flex; flex-direction: column; gap: 2px; }

.site-menu__link { width: 100%; justify-content: flex-start; min-height: 44px; font-size: var(--fs-body); border-radius: var(--r-md); }

.site-menu__actions { display: flex; flex-direction: column; gap: var(--space-2); }

.site-menu .station-search { max-width: none; }

.site-menu .station-search__input { height: 44px; }

body.is-sheet-open { overflow: hidden; }

/* One compact mode, not two overlapping ones. The bar used to drop the search
   at 767 but only grow a hamburger at 639, which left 640-767 with no way to
   search at all — and the two rules disagreeing is also why the bar overflowed
   the viewport by up to 73px at 320-345, 640-685 and 768-808. The rule now:
   whatever the bar cannot hold, the sheet carries, and the hamburger appears
   at the same width the bar starts dropping things. */

/* "Get started" is the first thing to go: it is the widest item in the bar and
   the one the sheet repeats. Below 900 the search is worth more than the CTA —
   without this the field was squeezed to ZERO width from 768 to 833. */
@media (max-width: 899px) {
  .site-header__cta { display: none; }
}

@media (max-width: 767px) {
  .site-header .station-search,
  .site-header .nav,
  /* the guest "Sign in" text link; a signed-in visitor keeps the avatar */
  .site-header__actions .nav__link { display: none; }

  .site-nav-toggle { display: inline-flex; }

  .site-header__inner { gap: var(--space-2); }
}

@media (max-width: 639px) {
  .brand__word { font-size: var(--fs-body); }
}

/* --- 5.9 In-page anchor rail ---------------------------------------------- */

.anchor-rail {
  position: sticky;
  top: var(--header-h);
  z-index: calc(var(--z-sticky) - 1);
  background: var(--frost);
  border-bottom: 1px solid var(--bd);
  display: flex;
  gap: var(--space-1);
  overflow-x: auto;
  scrollbar-width: none;
  padding: var(--space-2) 0;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%);
}

.anchor-rail::-webkit-scrollbar { display: none; }

/* Boards P0/W1 draw these as pills, not underlined tabs: rounded, the current
   one filled solid green. The ink is var(--card), not a white literal — that is
   #FFF on the light green fill (exactly the board) and near-black on dark
   mode's lighter --grnd, where white would sit at ~2.3:1. */
.anchor-rail__link {
  display: inline-flex;
  align-items: center;
  min-height: 28px;
  padding: 5px 11px;
  border-radius: var(--r-pill);
  font: 700 var(--fs-ui)/1 var(--font-ui);
  color: var(--ink2);
  white-space: nowrap;
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}

.anchor-rail__link:hover { color: var(--grnd); background: var(--tint); text-decoration: none; }

.anchor-rail__link[aria-current="true"],
.anchor-rail__link[aria-current="page"] { background: var(--grnd); color: var(--card); }

/* --- 5.10 Footer ----------------------------------------------------------
   A fat, fully crawlable link map — every link a real <a href>. */

.site-footer {
  background: var(--card);
  border-top: 1px solid var(--bd);
  padding: var(--space-8) 0 0;
  margin-top: var(--space-10);
}

.foot__col { display: flex; flex-direction: column; gap: 6px; min-width: 0; }

.foot__link {
  font: 400 var(--fs-ui-lg)/1.5 var(--font-ui);
  color: var(--ink2);
  padding-block: 3px;
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease);
  /* A flex item is blockified, so these anchors stretched the full column:
     "England" was a 48px word inside a 185px hit area, and the cursor turned
     into a hand halfway across empty space. Shrink each one to its own text. */
  align-self: flex-start;
}

/* Board P3b sizes the column headings a step up from the default eyebrow. */
.site-footer .eb { font-size: var(--fs-data); margin-bottom: 3px; }

.foot__link:hover { color: var(--grnd); text-decoration: underline; text-underline-offset: 3px; }

.site-footer__grid {
  display: grid;
  /* min() lets the brand column shrink below its 260px ideal instead of forcing
     the page to scroll sideways; the link columns stack outright on a phone. */
  grid-template-columns: minmax(min(100%, 300px), 1.4fr) repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--space-6) var(--space-7);
}

/* Four link columns beside the brand block need a wider page than three did:
   pair them up on a tablet rather than letting auto-fit squeeze all five. */
@media (max-width: 1023px) {
  .site-footer__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .foot__brand { grid-column: 1 / -1; }
}

@media (max-width: 639px) {
  .site-footer__grid { grid-template-columns: minmax(0, 1fr); }
}

/* A column heading that is also its hub link (Guides -> /guides, both
   geographic columns -> the picker). It inherits the eyebrow's size and
   letter-spacing from .site-footer .eb above; only the link reset is here. */
.foot__head { color: inherit; text-decoration: none; }

.foot__head:hover { color: var(--grnd); text-decoration: underline; text-underline-offset: 3px; }

.foot__brand { gap: var(--space-2); }

.foot__brand .brand__mark { width: 22px; height: 22px; }

.foot__brand .brand__word { font-size: var(--fs-body); }

.foot__note { font: 400 var(--fs-ui)/1.6 var(--font-ui); color: var(--ink3); margin-top: 4px; }

.foot__note--warn { font-weight: 600; font-size: var(--fs-data); line-height: 1.5; margin-top: var(--space-2); }

.foot__legal { font: 400 var(--fs-data)/1.5 var(--font-ui); color: var(--ink3); }

/* Scale, printed beside the mark the way a marketplace prints its inventory:
   it is the one number that separates us from a single-country gauge list. */
.foot__stat { font: 400 var(--fs-ui-lg)/1.5 var(--font-ui); color: var(--ink2); margin-top: 4px; }

.foot__stat__sub { color: var(--ink3); font-size: var(--fs-ui); }

/* The base bar: legal, disclaimer and the licence credits, under a rule.
   This block used to sit inside the brand column, where five lines of
   attribution were the loudest thing in the footer while being the part
   nobody reads. Compressed to one line — the wording shortens, the credits
   never do. */
.site-footer__base {
  margin-top: var(--space-8);
  padding-block: var(--space-5) var(--space-8);
  border-top: 1px solid var(--bd);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px var(--space-5);
}

.site-footer__base .foot__note { margin-top: 0; }

/* The credit line takes the whole width below the two short ones rather than
   being squeezed into a third of the row. */
.site-footer__base .foot__note:last-child { flex: 1 0 100%; margin-top: var(--space-2); }

@media (max-width: 639px) {
  .site-footer__base { gap: var(--space-2); }
  .site-footer__base .foot__note--warn { flex: 1 0 100%; }
}

/* Store badges: plain outlined labels, not store artwork — there is no app to
   download yet, so they must not read as buttons. */
.foot__store { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-3); }

.foot__badge {
  border: 1px solid var(--bd);
  border-radius: 7px;
  padding: 6px 11px;
  font: 700 var(--fs-meta) var(--font-ui);
  color: var(--ink3);
  white-space: nowrap;
}

/* --- 5.11 Tool shell ------------------------------------------------------
   grid: bar / rail / main. Rail crossover at 1024 (icon rail) and 768 (strip). */

.tool {
  display: grid;
  grid-template-columns: var(--rail-w) minmax(0, 1fr);
  grid-template-areas:
    "bar bar"
    "rail main";
  width: 100%;
  max-width: var(--content-max-tool);
  margin-inline: auto;
  align-items: start;
}

.tool-bar {
  grid-area: bar;
  position: sticky;
  /* The tool shell has no site header above it, so the bar sticks to the very
     top — and it is FLAT AND OPAQUE, never translucent: data scrolls under it. */
  top: 0;
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  min-height: var(--tool-bar-h);
  padding: var(--space-2) var(--space-4);
  background: var(--card);
  border-bottom: 1px solid var(--bd);
}

.tool-bar__group { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }

/* The date pager is a group of its own INSIDE the left group — it must not be a
   .tool-bar__group itself, or the phone override below (flex-wrap:wrap) would
   let "< Sat, Aug 8 >" break across two lines. */
.tool-bar__pager { display: flex; align-items: center; gap: var(--space-2); }

.tool-bar__station {
  font: 700 var(--fs-body) var(--font-dsp);
  letter-spacing: -.01em;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Below 768 the bar has no room for Copy link / Share / Print as three separate
   controls (spec 5.1), so they fold into one "···" menu. Both halves are always
   in the DOM; only which one is displayed changes, so nothing needs JS. */
.tool-bar__more { display: none; }

.tool-bar__menu { right: 0; top: calc(100% + 8px); display: flex; flex-direction: column; gap: 2px; }

.tool-bar__menu .menu-item { font-size: var(--fs-sub); }

@media (max-width: 767px) {
  .tool-bar__collapse { display: none; }
  .tool-bar__more { display: inline-flex; }

  /* min-width:0 lets a group shrink below its content, so the flex line thinks
     it fits and the last controls spill off-screen instead of wrapping. On
     phones the groups size to their content and wrap as whole rows; the station
     name keeps its own cap so it — and only it — still ellipsises. */
  .tool-bar__group { min-width: auto; flex-wrap: wrap; }

  .tool-bar__station { max-width: 44vw; }
}

/* Under ~414px the chip and the date pager cannot share a line, so instead of
   letting them wrap wherever they run out of room, give each its own row: the
   chip stretches across (board P2a2) and the pager centres beneath it. Above
   this width they still fit side by side, and the bar stays one row shorter. */
@media (max-width: 413px) {
  .tool-bar__chip { flex: 1 1 auto; justify-content: flex-start; }
  .tool-bar__chip > .ic-svg:last-child { margin-left: auto; }

  .tool-bar__pager { flex: 1 1 100%; justify-content: center; }

  .tool-bar__station { max-width: 62vw; }
}

/* Date grid — a month of 34x32 targets, shared by the tool's date popover
   (#date-popover, whose cells NAVIGATE to a dated URL) and the SEO rail's
   picker (.date-grid--rail, whose cells only redraw the page). The selected day
   is filled, not just coloured; days spilling in from the neighbouring month
   are dimmed. Cells are <a> in one and <button> in the other, so this block
   never assumes either — but it must not set font-family, or it would beat .mn
   and take the tabular numerals off the days. */
.date-grid { width: max-content; max-width: min(92vw, 340px); }

.date-grid .tbl th { padding: 4px 0; text-align: center; }

.date-grid .tbl td { padding: 1px; border-top: 0; text-align: center; }

.date-grid [data-date] {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 32px;
  padding: 0;
  border: 0;
  background: none;
  border-radius: var(--r-md);
  font-size: var(--fs-ui);
  color: var(--ink);
  text-decoration: none;
  cursor: pointer;
}

.date-grid [data-date]:hover { background: var(--tint); color: var(--grnd); text-decoration: none; }

.date-grid [data-outside] { color: var(--ink3); }

/* Today, once another day is the one on screen — an outline, so it never reads
   as the selection. */
.date-grid .is-today:not([aria-current="date"]) { box-shadow: inset 0 0 0 1px var(--grn); }

/* var(--bg), not #FFF: --grnd is a LIGHT green in dark mode, where white
   text on it measures 2.73:1. The page ground reads 6.4:1 in light and
   8.6:1 in dark — the same swap .tabs__tab and .pager__item already make. */
.date-grid [aria-current="date"] { background: var(--grnd); color: var(--bg); font-weight: 700; }

.date-grid__label { margin: 0 0 var(--space-2); text-align: center; }

/* The rail calendar's month stepper (‹ September 2026 ›). Three columns so the
   label stays optically centred whatever the arrows measure, and the label's own
   bottom margin steps aside — the row carries the spacing now. Hidden until
   tide-seo.js reveals it: with no JS there is no month to fetch. */
.date-grid__nav {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

.date-grid__nav .date-grid__label { margin: 0; }

/* A month is a fetch, and a cold one is ~1s. `aria-busy` is already the honest
   announcement; this is the visible half of it — the panel dims and stops
   taking clicks until the new month has landed. */
.date-grid[aria-busy="true"] { opacity: .55; pointer-events: none; }

.date-grid__note { margin: var(--space-2) 0 0; text-align: center; }

/* The rail's picker is FIXED, not absolute: it is a sibling of .anchor-rail
   (which clips — see header-nav.blade.php), so it anchors to the viewport
   instead, under the rail and flush with the container's right edge. */
/* `.popover` is declared further down this sheet and would win `position` on
   equal specificity, so this rule takes both classes. */
.popover.date-grid--rail {
  position: fixed;
  top: calc(var(--header-h) + var(--anchor-rail-h) + var(--space-2));
  right: calc(max(0px, (100vw - var(--content-max)) / 2) + var(--space-6));
}

@media (max-width: 639px) {
  .popover.date-grid--rail { right: var(--space-4); }
}

/* Keycaps — the shortcuts sheet (and any inline "press ?" hint). Bottom border
   is doubled so a cap reads as a physical key without a shadow. */
kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  min-height: 24px;
  padding: 2px 7px;
  border: 1px solid var(--bd);
  border-bottom-width: 2px;
  border-radius: var(--r-sm);
  background: var(--bg);
  font: 600 var(--fs-ui) var(--font-mn);
  letter-spacing: -.03em;
  color: var(--ink2);
}

#shortcuts-sheet .tbl td { font-size: var(--fs-ui-lg); }

#shortcuts-sheet .tbl td:first-child { width: 1%; white-space: nowrap; }

.tool__main { grid-area: main; min-width: 0; padding: var(--space-5) var(--space-4) var(--space-10); }

.tool > main { grid-area: main; min-width: 0; }

.rail {
  grid-area: rail;
  position: sticky;
  top: var(--tool-bar-h);
  z-index: var(--z-rail);
  align-self: start;
  max-height: calc(100vh - var(--tool-bar-h));
  overflow-y: auto;
  width: var(--rail-w);
  border-right: 1px solid var(--bd);
  background: var(--card);
  padding: var(--space-3) var(--space-2);
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.rw {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 10px;
  min-height: 34px;
  border-radius: 9px;
  font: 700 var(--fs-ui-lg) var(--font-ui);
  color: var(--ink2);
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.rw:hover { background: var(--tint); color: var(--grnd); text-decoration: none; }

.rw--on { background: var(--tint); color: var(--grnd); }

.rw--sub { padding: 6px 10px 6px 30px; font: 500 var(--fs-ui) var(--font-ui); min-height: 28px; }

.rw--sub.rw--on { background: var(--tint); }

@media (max-width: 1023px) {
  .tool { grid-template-columns: var(--rail-w-icon) minmax(0, 1fr); }
  .rail { width: var(--rail-w-icon); padding: var(--space-3) var(--space-2); align-items: center; }
  .rw { justify-content: center; padding: 8px; width: 44px; height: 44px; }
  .rw span,
  .rw--sub { display: none; }
}

@media (max-width: 767px) {
  .tool { grid-template-columns: minmax(0, 1fr); grid-template-areas: "bar" "rail" "main"; }

  .rail { display: none; }
  .tool__main,
  .tool > main { padding-inline: var(--space-4); }
}

/* Phone: the rail collapses into a sticky horizontal segmented strip. */
.strip {
  display: none;
  position: sticky;
  top: var(--tool-bar-h);
  z-index: var(--z-rail);
  gap: var(--space-1);
  overflow-x: auto;
  scrollbar-width: none;
  padding: var(--space-2) var(--space-4);
  background: var(--frost);
  border-bottom: 1px solid var(--bd);
}

.strip::-webkit-scrollbar { display: none; }

.strip__item {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-height: 32px;
  padding: 0 12px;
  border-radius: var(--r-pill);
  font: 700 var(--fs-ui)/1 var(--font-ui);
  color: var(--ink2);
  white-space: nowrap;
  text-decoration: none;
}

.strip__item:hover { color: var(--grnd); text-decoration: none; }

.strip__item[aria-current="true"],
.strip__item[aria-current="page"] { background: var(--grnd); color: #FFFFFF; }

@media (max-width: 767px) {
  /* min-width:0 or the strip's five nowrap pills become the grid's minimum and
     widen the whole page instead of scrolling inside their own overflow. */
  .strip { display: flex; grid-area: rail; min-width: 0; }
}

/* --- 5.12 Popover, sheet, modal ------------------------------------------- */

.popover {
  position: absolute;
  z-index: var(--z-popover);
  min-width: 220px;
  background: var(--card);
  border: 1px solid var(--bd);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--space-3);
}

.sheet__scrim,
.modal__scrim { position: fixed; inset: 0; background: rgba(var(--shadow-rgb), .4); border: 0; }

.sheet__scrim { z-index: calc(var(--z-sheet) - 1); }
.modal__scrim { z-index: calc(var(--z-modal) - 1); }

.sheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: var(--z-sheet);
  max-height: 86vh;
  overflow-y: auto;
  background: var(--card);
  border-top: 1px solid var(--bd);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  box-shadow: var(--shadow-lg);
  padding: var(--space-5) var(--space-4) var(--space-8);
  animation: sheet-in var(--dur-slow) var(--ease);
}

@keyframes sheet-in {
  from { transform: translateY(12%); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

@media (min-width: 768px) {
  .sheet {
    left: 50%;
    right: auto;
    bottom: auto;
    top: 50%;
    transform: translate(-50%, -50%);
    width: min(560px, calc(100vw - var(--space-8)));
    border-radius: 16px;
    animation: none;
  }
}

.modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: var(--z-modal);
  width: min(560px, calc(100vw - var(--space-8)));
  max-height: 86vh;
  overflow-y: auto;
  background: var(--card);
  border: 1px solid var(--bd);
  border-radius: 16px;
  box-shadow: var(--shadow-lg);
  padding: var(--space-6);
}

/* --- 5.13 Toast -----------------------------------------------------------
   Borrows the chart tooltip's dark-chip language — the only floating dark
   surface precedent in the system. */

.toast-host {
  position: fixed;
  left: 50%;
  bottom: var(--space-6);
  transform: translateX(-50%);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  width: min(420px, calc(100vw - var(--space-8)));
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--inverted-bg);
  color: var(--inverted-text);
  border-radius: 12px;
  box-shadow: var(--shadow-md);
  padding: 12px var(--space-4);
  font: 500 var(--fs-ui-lg)/1.4 var(--font-ui);
  pointer-events: auto;
}

.toast--action { justify-content: space-between; gap: var(--space-5); }

.toast--action .btn,
.toast--action .btn--ghost { color: var(--grn); height: 28px; padding: 0; font-size: var(--fs-ui-lg); font-weight: 700; }

@media (max-width: 639px) {
  .toast-host { left: var(--space-3); right: var(--space-3); bottom: var(--space-3); width: auto; transform: none; }
}

/* --- 5.14 Home page (P3b) --------------------------------------------------
   resources/views/public/home.blade.php. Folded in from the former
   public/css/home.css (page-scoped stylesheet, deleted once merged here).
   Tokens only: no colour literal appears in this section. */

/* --- hero ----------------------------------------------------------------- */

.home-hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: var(--space-10) 0 var(--space-8);
}

.home-hero__title { max-width: 880px; }

.home-hero__sub {
  max-width: 680px;
  margin-top: var(--space-4);
  font: 500 19px/1.55 var(--font-ui);
  color: var(--ink2);
  text-wrap: pretty;
}

/* --- hero search: the large variant of .station-search ---------------------
   Two-class selector on purpose: tide.css caps .station-search at 320px and is
   loaded AFTER this file (base.blade.php emits @stack('head') above the
   stylesheet link), so an equal-specificity rule here would lose. */

.station-search.home-search {
  position: relative;
  display: flex;
  align-items: center;
  width: min(640px, 100%);
  max-width: none;
  margin-top: var(--space-7);
  flex: none;
}

.home-search .station-search__icon { left: 20px; }

.home-search .station-search__input {
  height: 56px;
  padding-left: 50px;
  padding-right: 118px;
  font-size: var(--fs-lede);
  border-color: var(--grn);
  box-shadow: var(--shadow-md);
}

.home-search .home-search__near {
  position: absolute;
  right: 10px;
  min-height: 32px;
  padding: 0 12px;
  font-size: var(--fs-ui);
  background: var(--tint);
  color: var(--grnd);
}

.home-search .home-search__near:hover { box-shadow: inset 0 0 0 1px var(--grn); }

.station-search__result {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  padding: 9px 12px;
  border-radius: var(--r-md);
  color: var(--ink);
}

.station-search__result:hover,
.station-search__result[aria-selected="true"] { background: var(--tint); text-decoration: none; }

.station-search__name { font: 700 var(--fs-sub)/1.35 var(--font-ui); }

.station-search__where {
  flex: 1 1 0;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: right;
  font: 400 var(--fs-ui)/1.35 var(--font-ui);
  color: var(--ink3);
}

.station-search__empty { padding: 9px 12px; font: 400 var(--fs-ui-lg)/1.4 var(--font-ui); color: var(--ink3); }

/* --- trust chips ---------------------------------------------------------- */

.home-trust {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3) var(--space-8);
  margin-top: var(--space-6);
}

.home-trust__item {
  display: flex;
  align-items: center;
  gap: 7px;
  font: 700 var(--fs-ui-lg)/1.4 var(--font-ui);
  color: var(--ink2);
}

.home-trust__item svg { color: var(--grnd); flex: none; }

/* --- content row: world map beside the live cards -------------------------- */

.home-row {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: var(--space-5);
  align-items: stretch;
  padding-bottom: var(--space-8);
}

.home-col { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }

/* --- world-map card ------------------------------------------------------- */

.card.home-map {
  position: relative;
  display: block;
  overflow: hidden;
  min-height: 262px;
  color: inherit;
}

.home-map__scrim:hover { text-decoration: none; }
.home-map__scrim:hover .home-map__title { color: var(--grnd); }

/* Continent pins — the old homepage's clickable continents on the token
   grammar. Anchors to the crawl-band tabs; hidden at phone width, where the
   band's own tabs are the browse control. */
.home-map__pin {
  position: absolute;
  transform: translate(-50%, -50%);
  padding: 5px 12px;
  border: 1px solid var(--bd);
  border-radius: 999px;
  background: color-mix(in srgb, var(--card) 88%, transparent);
  font: 700 var(--fs-ui) var(--font-ui);
  color: var(--ink);
  white-space: nowrap;
  box-shadow: var(--shadow-sm);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}

.home-map__pin:hover,
.home-map__pin:focus-visible {
  background: var(--grnd);
  border-color: var(--grnd);
  color: #FFFFFF;
  text-decoration: none;
}

/* Dark-theme green is a light green: ink flips to the page ground, exactly as
   .btn--primary does, or the label sits at 2.7:1 on its own background. */
:root[data-theme="dark"] .home-map__pin:hover,
:root[data-theme="dark"] .home-map__pin:focus-visible { color: var(--bg); }

@media screen and (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .home-map__pin:hover,
  :root:not([data-theme="light"]) .home-map__pin:focus-visible { color: var(--bg); }
}

/* Anchors in % of the card, eyeballed against the cover-cropped equirect. */
.home-map__pin--north-america { left: 21%; top: 34%; }
.home-map__pin--south-america { left: 30%; top: 66%; }
.home-map__pin--europe { left: 50%; top: 21%; }
.home-map__pin--africa { left: 53%; top: 52%; }
.home-map__pin--asia { left: 71%; top: 27%; }
.home-map__pin--oceania { left: 86%; top: 62%; }

@media (max-width: 639px) {
  .home-map__pin { display: none; }
}

.home-map__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 42%;
  /* The source is a saturated satellite render; warm and calm it into the
     cream palette so the card reads as a map, not a photograph. */
  filter: saturate(.38) sepia(.32) brightness(1.1) contrast(.94);
}

.home-map__scrim {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: var(--space-4);
  color: inherit;
  background: linear-gradient(rgba(var(--shadow-rgb), 0), var(--bg) 46%);
}

.home-map__title { display: block; }

.home-map__sub {
  display: block;
  margin-top: 3px;
  font: 400 var(--fs-ui)/1.45 var(--font-ui);
  color: var(--ink2);
}

/* The base map is a light-mode illustration: dim it so it is not a lantern
   against the dark page. Both dark selectors, per the theme cascade rule. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .home-map__img { filter: saturate(.4) sepia(.12) brightness(.6); }
}

:root[data-theme="dark"] .home-map__img { filter: saturate(.4) sepia(.12) brightness(.6); }

/* --- featured live card --------------------------------------------------- */

.home-feat__head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: 7px;
}

.home-feat__name { color: var(--ink); }

.home-feat__now {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: 10px;
}

.home-feat__arrow { flex: none; color: var(--grnd); }
.home-feat__arrow--down { color: var(--red); }

.home-feat__val { font-size: 26px; font-weight: 700; line-height: 1.05; color: var(--ink); }

.home-feat__next { font: 500 var(--fs-ui)/1.4 var(--font-ui); color: var(--ink2); }

.home-feat__spark { display: block; width: 100%; height: 52px; margin-top: 10px; color: var(--grn); }

.home-feat__note { margin-top: 10px; font: 400 var(--fs-data)/1.5 var(--font-ui); color: var(--ink3); }

/* --- link clouds (popular stations, country anchors) ----------------------- */

.card.home-popular { flex: 1 1 auto; }

.home-cloud {
  display: flex;
  flex-wrap: wrap;
  column-gap: var(--space-5);
  row-gap: 2px;
  margin-top: 9px;
}

.home-link { font: 500 var(--fs-ui-lg)/1.7 var(--font-ui); color: var(--grnd); }
.home-link--muted { color: var(--ink2); }

/* "Near you" — the location-aware card the home script fills in. Rows are a
   station link with its region and distance; the popular part reuses the
   cloud. */
.home-near__list { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.home-near__row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; min-width: 0; }
.home-near__row .home-link { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.home-near__meta { flex: none; font: 400 var(--fs-data)/1.4 var(--font-ui); color: var(--ink3); }
.home-near__hint { margin: 8px 0 0; font: 400 var(--fs-ui)/1.5 var(--font-ui); color: var(--ink2); }
.home-near__sub { display: block; margin-top: 14px; }
.home-near__precise { margin-top: 12px; }

/* --- continent → country crawl band --------------------------------------- */

.home-crawl { margin-bottom: var(--space-9); }

.home-crawl__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.home-crawl__meta { font-size: var(--fs-data); color: var(--ink3); text-transform: uppercase; }

.home-crawl__tabs { margin-top: var(--space-4); }

.home-crawl__grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 4px var(--space-6);
  margin-top: var(--space-4);
}

.home-crawl__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--dv);
}

.home-crawl__hint { font: 400 var(--fs-data)/1.5 var(--font-ui); color: var(--ink3); }

/* --- responsive: 4-stop scale (920 tablet, 639 phone) --------------------- */

@media (max-width: 920px) {
  .home-hero { padding: var(--space-8) 0 var(--space-6); }
  .home-row { grid-template-columns: 1fr; }
  .card.home-map { min-height: 220px; }
  .home-crawl__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 639px) {
  .home-hero { padding: var(--space-6) 0 var(--space-5); }
  .home-hero__sub { font-size: var(--fs-lede); }
  .station-search.home-search { margin-top: var(--space-5); flex-wrap: wrap; }
  .home-search .station-search__input { height: 50px; padding-left: 44px; padding-right: 14px; }

  /* The chip drops below the field (inside it, it would eat the placeholder),
     which makes the form two rows tall — so the magnifier can no longer ride
     the form's centre line and is pinned to the input's instead. */
  .home-search .station-search__icon { top: 25px; left: 16px; transform: translateY(-50%); }
  .home-search .home-search__near { position: static; margin: var(--space-2) auto 0; }

  .home-trust { gap: var(--space-2) var(--space-5); }
  .home-crawl__grid { grid-template-columns: 1fr; }
  .home-cloud { column-gap: var(--space-4); }
}

/* --- 5.15 Geo surfaces — station picker (W11) + country/region browse (W12)
   Folded in from the interim public/css/tide-geo.css so the site is back to one
   stylesheet. Everything is namespaced (.picker*, .res*, .mk-pop*, .fchip*,
   .geo-*, .pager*) and token-only — nothing here redefines an earlier class.
   -------------------------------------------------------------------------- */

/* --- Breadcrumb ----------------------------------------------------------- */

.geo-crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  /* The country/region pages open straight onto this crumb rather than a
     padded .page wrapper, so it has to hold itself off the sticky header —
     otherwise it sits flush against the bar. Same top step .page uses. */
  margin-top: var(--space-8);
  font: 400 var(--fs-ui)/1.4 var(--font-ui);
  color: var(--ink2);
}

.geo-crumbs a { color: var(--grnd); font-weight: 600; }
.geo-crumbs__sep { color: var(--ink3); }
.geo-crumbs__here { color: var(--ink); }

/* --- Browse page head ----------------------------------------------------- */

.geo-head { display: flex; flex-direction: column; gap: var(--space-3); margin-top: var(--space-4); }
.geo-head__intro { font: 500 var(--fs-body)/1.55 var(--font-ui); color: var(--ink2); max-width: 66ch; text-wrap: pretty; }
.geo-stats { display: flex; flex-wrap: wrap; gap: var(--space-2); }

/* W12 lays a 280px "jump to a region" card beside the intro column. */
.geo-head--split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 280px;
  align-items: start;
  gap: var(--space-6);
}

.geo-head__main { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }

.geo-jump { display: flex; flex-direction: column; gap: var(--space-3); padding: 14px 16px; }
.geo-jump__chips { display: flex; flex-wrap: wrap; gap: 6px; }

.geo-chip {
  display: inline-flex;
  align-items: center;
  min-height: 26px;
  padding: 0 11px;
  border: 1px solid var(--bd);
  border-radius: var(--r-pill);
  background: var(--card);
  font: 700 var(--fs-data)/1 var(--font-ui);
  color: var(--ink);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}

.geo-chip:hover { border-color: var(--grn); background: var(--tint); color: var(--grnd); text-decoration: none; }
.geo-chip--all { color: var(--grnd); border-style: dashed; }

@media (max-width: 919px) {
  .geo-head--split { grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
}

/* --- Region / station tile grid ------------------------------------------- */

.geo-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-3);
}

@media (max-width: 920px) { .geo-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 639px) { .geo-grid { grid-template-columns: minmax(0, 1fr); } }

.geo-card {
  display: block;
  padding: 14px 16px;
  color: inherit;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}

.geo-card:hover { border-color: var(--grn); background: var(--tint); text-decoration: none; }

.geo-card__top { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.geo-card__name { font: 700 17px/1.2 var(--font-dsp); letter-spacing: -.01em; color: var(--ink); min-width: 0; }
.geo-card__chev { color: var(--ink3); flex: none; }

/* Both sit on their own line under the name — the mono count is an eyebrow. */
.geo-card__count { display: block; font-family: var(--font-mn); letter-spacing: -.03em; font-size: var(--fs-data); text-transform: uppercase; color: var(--ink3); margin-top: var(--space-1); }
.geo-card__sample { display: block; font: 400 var(--fs-ui)/1.5 var(--font-ui); color: var(--ink2); margin-top: 7px; }

/* --- Alphabet list (region page) ------------------------------------------
   One card, one row per letter: a fixed gutter label beside the names instead
   of a full-width heading band above them. The old band gave "A → Adelphi" the
   same 74px a 20-name letter got, which on a typical region (median 34 names)
   left the page mostly whitespace. */

.geo-az { display: flex; flex-direction: column; gap: var(--space-3); }
.geo-az__h { margin: 0 0 var(--space-1); }

.geo-az__row {
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr);
  gap: var(--space-4);
  align-items: start;
}

.geo-az__row + .geo-az__row { border-top: 1px solid var(--dv); padding-top: var(--space-3); }

/* Optically aligned to the first row of links, which sit on a 1.5 line box. */
.geo-az__letter { font: 800 var(--fs-body)/1.5 var(--font-dsp); letter-spacing: -.01em; color: var(--grnd); margin: 0; }

@media (max-width: 479px) {
  .geo-az__row { grid-template-columns: 20px minmax(0, 1fr); gap: var(--space-3); }
}

/* County headings: a name like "Santa Cruz de Tenerife" cannot live in the
   letter gutter, so it leads its block full width. */
.geo-az__group + .geo-az__group { border-top: 1px solid var(--dv); padding-top: var(--space-3); margin-top: var(--space-3); }
.geo-az__band { font: 800 var(--fs-body)/1.5 var(--font-dsp); letter-spacing: -.01em; color: var(--grnd); margin: 0 0 var(--space-2); }

/* Where the coast ends. A rule plus a label, not a heading: the sections below
   it are the same kind of thing, only further from the water. */
.geo-az__split {
  display: flex; align-items: center; gap: var(--space-3);
  font: 700 var(--fs-meta)/1.5 var(--font-ui); letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink3); margin: var(--space-5) 0 var(--space-3);
}
.geo-az__split::after { content: ""; flex: 1; height: 1px; background: var(--dv); }

/* --- Flat link grids ------------------------------------------------------ */

.geo-links { display: flex; flex-wrap: wrap; column-gap: 18px; row-gap: 7px; }
.geo-link { font: 500 var(--fs-ui)/1.5 var(--font-ui); color: var(--grnd); }
.geo-link:hover { text-decoration: underline; text-underline-offset: 3px; }

/* The region page's all-locations list: dense columns of plain anchors. */
.geo-names {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  column-gap: var(--space-5);
  row-gap: 7px;
}

.geo-names .geo-link { min-width: 0; overflow-wrap: anywhere; }

.geo-cross { display: flex; flex-wrap: wrap; gap: var(--space-5); margin-top: var(--space-8); }

/* --- Pagination ----------------------------------------------------------- */

.pager { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 7px; margin-top: var(--space-8); }

.pager__item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 28px;
  padding: 0 11px;
  border: 1px solid var(--bd);
  border-radius: var(--r-pill);
  font: 700 var(--fs-ui)/1 var(--font-ui);
  color: var(--ink2);
  background: var(--card);
}

.pager__item:hover { border-color: var(--grn); color: var(--grnd); text-decoration: none; }
.pager__item[aria-current="page"] { background: var(--grnd); border-color: transparent; color: var(--card); }
.pager__item--off { opacity: .4; pointer-events: none; }
.pager__gap { color: var(--ink3); font: 700 var(--fs-ui) var(--font-ui); }

:root[data-theme="dark"] .pager__item[aria-current="page"] { color: var(--bg); }

@media screen and (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .pager__item[aria-current="page"] { color: var(--bg); }
}

/* --- Station picker (/stations) ------------------------------------------- */

.picker {
  display: grid;
  grid-template-columns: 340px minmax(0, 1fr);
  /* The row is pinned to the container, not to the sidebar's content: an auto
     row grows to 200 result rows, which drags the map pane thousands of pixels
     tall and leaves the visible slice blank. */
  grid-template-rows: minmax(0, 1fr);
  height: calc(100vh - var(--header-h));
  min-height: 520px;
  border-bottom: 1px solid var(--bd);
}

.picker__side {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--card);
  border-right: 1px solid var(--bd);
}

.picker__head { padding: 14px 16px; min-width: 0; border-bottom: 1px solid var(--dv); }
.picker__title { font: 700 var(--fs-lede)/1.2 var(--font-dsp); letter-spacing: -.01em; color: var(--ink); margin-bottom: var(--space-3); }

.picker__search { position: relative; display: flex; align-items: center; }
.picker__search .station-search__icon { position: absolute; left: 13px; display: flex; color: var(--ink3); pointer-events: none; }
.picker__search input { height: 40px; width: 100%; min-width: 0; padding-left: 38px; padding-right: 38px; border-radius: var(--r-pill); }
.picker__clear { position: absolute; right: 4px; }

.picker__chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }

.fchip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-height: 26px;
  padding: 0 11px;
  border: 1px solid var(--bd);
  border-radius: var(--r-pill);
  background: var(--card);
  font: 700 var(--fs-data)/1 var(--font-ui);
  color: var(--ink2);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.fchip:hover { color: var(--grnd); border-color: var(--grn); }
.fchip[aria-pressed="true"] { background: var(--tint); border-color: transparent; color: var(--grnd); }

.picker__country { padding: 10px 16px 0; min-width: 0; }
.picker__country select { width: 100%; max-width: 100%; min-width: 0; padding-block: 8px; font-size: var(--fs-ui-lg); }

.picker__count { padding: 9px 16px; overflow-wrap: anywhere; }
.picker__list { flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.picker__foot { padding: 10px 16px 14px; overflow-wrap: anywhere; }

/* --- The shared result row ------------------------------------------------ */

.res {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  min-width: 0;
  padding: 11px 16px;
  border-top: 1px solid var(--dv);
  background: transparent;
  color: inherit;
}

.res:hover { background: var(--tint); }
.res[aria-current="true"] { background: var(--tint); }

.res__hit {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  flex: 1 1 auto;
  min-width: 0;
  padding: 0;
  border: 0;
  background: transparent;
  text-align: left;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

.res__hit:hover { text-decoration: none; }

.res__dot { width: 11px; height: 11px; border-radius: var(--r-pill); flex: none; margin-top: 3px; background: var(--grn); }
.res__dot--place { background: var(--card); border: 2px solid var(--grn); }
.res__dot--fav { background: var(--sun); border: 0; }

.res__body { flex: 1 1 auto; min-width: 0; }
.res__name { display: block; font: 700 var(--fs-ui-lg)/1.3 var(--font-ui); color: var(--ink); }
.res__sub { display: block; font: 400 var(--fs-data)/1.4 var(--font-ui); color: var(--ink2); margin-top: 1px; }
.res__meta { display: block; font-family: var(--font-mn); letter-spacing: -.03em; font-size: var(--fs-meta); color: var(--ink2); margin-top: 3px; }
.res__right { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; flex: none; }
.res__star { width: 28px; height: 28px; min-height: 28px; }
.res__star[aria-pressed="true"] { color: var(--sun); }

/* --- Map pane ------------------------------------------------------------- */

/* `isolation` keeps everything inside the map pane — the legend's z-500 and
   Leaflet's own 400–1000 panes and controls — in a private stacking context.
   Without it the legend paints over the sticky site header when the page is
   scrolled (verified in pixels; a hit test misses it, the legend is
   pointer-events:none). Inside the pane the ordering is unchanged. */
.picker__map { position: relative; min-width: 0; min-height: 0; background: var(--dv); isolation: isolate; }
.picker__canvas { position: absolute; inset: 0; }

.picker__legend {
  position: absolute;
  left: var(--space-4);
  top: var(--space-4);
  z-index: 500;
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  pointer-events: none;
}

.picker__legend .pill { background: var(--card); box-shadow: var(--shadow-sm); }

/* --- Marker popup --------------------------------------------------------- */

.mk-pop { min-width: 210px; font-family: var(--font-ui); }
.mk-pop__top { display: flex; align-items: flex-start; gap: var(--space-2); }
.mk-pop__name { flex: 1 1 auto; min-width: 0; font: 700 var(--fs-lede)/1.2 var(--font-dsp); letter-spacing: -.01em; color: var(--ink); }
.mk-pop__sub { font: 400 var(--fs-data)/1.4 var(--font-ui); color: var(--ink2); margin-top: 2px; }
.mk-pop__dist { font-family: var(--font-mn); letter-spacing: -.03em; font-size: var(--fs-data); color: var(--ink2); flex: none; }
.mk-pop__badge { display: flex; margin-top: 7px; }
.mk-pop__row { display: flex; gap: 7px; margin-top: 10px; }
.mk-pop__row .btn { flex: 1 1 auto; }
.mk-pop__row .btn--icon { flex: none; }
.mk-pop__link { display: block; text-align: center; font: 700 var(--fs-data) var(--font-ui); color: var(--grnd); margin-top: 8px; }

/* Leaflet paints its own popup chrome — retheme it rather than fight it.
   `.leaflet-container a` is (0,1,1), so every link inside a popup needs a
   selector at least that strong or Leaflet's default blue wins. */
.leaflet-popup-content-wrapper { background: var(--card); color: var(--ink); border-radius: var(--r-lg); box-shadow: var(--shadow-md); }
.leaflet-popup-content { margin: 12px 14px; color: var(--ink); }
.leaflet-container a.mk-pop__link,
.leaflet-container a.nearby-pop__link { color: var(--grnd); }
.leaflet-container a.btn--primary { color: var(--card); }
.leaflet-container .mk-pop .btn--icon { color: var(--ink2); }

:root[data-theme="dark"] .leaflet-container a.btn--primary { color: var(--bg); }

@media screen and (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .leaflet-container a.btn--primary { color: var(--bg); }
}
.leaflet-popup-tip { background: var(--card); }
.leaflet-container a.leaflet-popup-close-button { color: var(--ink3); }
/* Leaflet stacks its own furniture high — panes at 400, controls at 1000 — and
   `position: relative` alone creates no stacking context, so those numbers
   competed with the page's scale (header 20, rail 19) and the map painted over
   both. `isolation` contains them without inventing a z-index. */
.leaflet-container { background: var(--dv); font-family: var(--font-ui); isolation: isolate; }
.leaflet-bar a { background: var(--card); color: var(--ink); border-bottom-color: var(--bd); }
.leaflet-bar a:hover { background: var(--tint); color: var(--grnd); }
.leaflet-control-attribution { background: var(--frost); color: var(--ink3); font-family: var(--font-mn); font-size: var(--fs-micro); }
.leaflet-control-attribution a { color: var(--ink2); }

.marker-cluster-small div,
.marker-cluster-medium div,
.marker-cluster-large div {
  background: var(--grnd);
  color: var(--card);
  font-family: var(--font-mn);
  font-weight: 600;
}

.marker-cluster-small,
.marker-cluster-medium,
.marker-cluster-large { background: var(--tint); }

/* --- Browse by country (below the fold) ----------------------------------- */

.geo-continent { margin-top: var(--space-6); }

.geo-countries {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px var(--space-5);
}

@media (max-width: 1199px) { .geo-countries { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 920px) { .geo-countries { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 639px) { .geo-countries { grid-template-columns: minmax(0, 1fr); } }

.geo-country {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 5px 0;
  font: 700 var(--fs-ui-lg)/1.4 var(--font-ui);
  color: var(--ink);
}

.geo-country:hover { color: var(--grnd); text-decoration: underline; text-underline-offset: 3px; }
.geo-country__n { font-family: var(--font-mn); letter-spacing: -.03em; font-size: var(--fs-data); font-weight: 600; color: var(--ink3); }

/* --- Picker responsive ---------------------------------------------------- */

.picker__toggle { display: none; }

@media (max-width: 1199px) {
  .picker { grid-template-columns: 320px minmax(0, 1fr); }
}

/* Below 920px the two panes stack, so only one of them is shown at a time and a
   sticky Map · List control (under the sticky site header) switches between
   them. List wins the cold load — this product is search-first, not map-first. */
@media (max-width: 919px) {
  .picker { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto auto; height: auto; }
  .picker__side { border-right: 0; border-bottom: 1px solid var(--bd); }
  .picker__list { max-height: none; overflow: visible; }
  .picker__map { height: 60vh; }

  .picker__toggle {
    display: flex;
    justify-content: center;
    position: sticky;
    top: var(--header-h);
    z-index: calc(var(--z-sticky) - 1);
    padding: 10px 16px;
    background: var(--card);
    border-bottom: 1px solid var(--dv);
  }

  .picker__toggle .seg { width: 100%; max-width: 340px; }
  .picker__toggle .seg__opt { flex: 1 1 0; }

  .picker[data-pane="list"] .picker__map { display: none; }

  .picker[data-pane="map"] .picker__country,
  .picker[data-pane="map"] .picker__count,
  .picker[data-pane="map"] .picker__list,
  .picker[data-pane="map"] .picker__foot { display: none; }

  .picker[data-pane="map"] .picker__side { border-bottom: 0; }
}

@media (max-width: 639px) {
  .picker__map { height: 45vh; }
}

/* --- Browse page section rhythm ------------------------------------------- */

.geo-sec { margin-top: var(--space-8); }
.geo-sec--tight { margin-top: var(--space-6); }
.geo-sec__h { margin-bottom: var(--space-3); }
.geo-end { padding-bottom: var(--space-10); }

/* --- 5.16 Station landing (P1a/P1c) ----------------------------------------
   resources/views/public/partials/seo/{header-nav,hero,glance,datepicker,
   tide-curve}.blade.php + tides.blade.php. Task 1.2 follow-up (2026-08-28):
   these rules replace the 62 inline style="" attributes the original
   implementer left in place (see task-1.2-report.md) because tide.css shipped
   no layout classes for this page yet. Two bug fixes are folded in per the
   report: the .num-pair font-size placement (fixed at its actual source,
   .verdict in §6.4 — see the comment there, so it isn't duplicated here) and
   the missing [data-tint] rules for the 8 glance-tile accents below. A
   further handful of small rules — flagged by the report as needed but not
   written — were required to actually reach zero inline styles; each is
   marked "gap-fill" where it isn't a verbatim carry-over from the report. */

/* -- anchor rail (header-nav.blade.php) ---------------------------------- */
.anchor-rail__inner { display: flex; align-items: center; gap: var(--space-4); }
.anchor-rail__station { flex: 0 1 auto; min-width: 0; font-weight: 700; font-size: var(--fs-sub);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.anchor-rail__station .mn { font-size: var(--fs-data); color: var(--ink3); } /* gap-fill: month label */
.anchor-rail__nav { display: flex; align-items: center; gap: 5px; flex: 1 1 auto;
  min-width: 0; overflow-x: auto; scrollbar-width: none; } /* board W1 gap; 2px was tuned for the underlined tabs these replaced */
.anchor-rail__tools { display: flex; align-items: center; gap: 4px; flex: none; }

/* The picker wears the selected date, so it is a labelled button, not one of
   the icon squares beside it: rail-scale type, a tighter box and a gap that
   suits an 11-char label rather than a 14px word. */
.anchor-rail__date { height: 30px; padding: 0 9px; gap: 6px; font-size: var(--fs-ui); font-weight: 600;
  border: 1px solid var(--bd); }
.anchor-rail__date .mn { font-size: var(--fs-ui); letter-spacing: -.02em; }
/* The clock is a READOUT sitting in a row of controls, so it does not take the
   4px the three buttons share: +12px puts its text 16px off the date button's
   border — the board's own spacing between the clock and the icons beside it. */
.anchor-rail__clock { font-size: var(--fs-data); color: var(--ink2); white-space: nowrap;
  margin-right: var(--space-3); } /* gap-fill: base rule the report's own media query below assumed already existed */
@media (max-width: 767px) { .anchor-rail__station { display: none; } }

/* Between the board's 1280 and the phone the rail cannot hold everything: with
   the clock shown the anchors start scrolling at ~1029px. The nine anchors are
   the rail's job and the picker carries the selected date, so the clock goes
   first — the hero states the station's local time and zone anyway. */
@media (max-width: 1100px) { .anchor-rail__clock { display: none; } }

/* -- hero (hero.blade.php) ------------------------------------------------ */
.hero { display: flex; flex-wrap: wrap; gap: var(--space-6); align-items: flex-start;
  padding-block: var(--space-6) var(--space-5); }
.hero__main { flex: 1 1 460px; min-width: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.hero__eyebrow { display: flex; flex-wrap: wrap; gap: 6px; } /* gap-fill: country/region/coords eyebrow row */
.hero__rail { flex: 1 1 260px; max-width: 320px; display: flex; flex-wrap: wrap;
  gap: var(--space-2); align-content: flex-start; }
.hero__rail .btn { flex: 1 1 110px; }
.hero__rail .btn--primary, .hero__rail .card { flex: 1 1 100%; }
.hero__fact-pill { margin-top: var(--space-2); } /* gap-fill: pill under "Station facts" */
.hero__fact-text { margin-top: 9px; } /* gap-fill */
.hero__fact-note { font: 700 var(--fs-data)/1.4 var(--font-ui); color: var(--ink3); margin-top: 7px; } /* gap-fill */
.crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  }
.crumbs__sep { color: var(--ink3); }
.crumbs [aria-current="page"] { color: var(--ink); } /* gap-fill: current crumb, keyed off the a11y attribute already on the element */
.hero__lede { max-width: 640px; text-wrap: pretty; }

.verdict__count { font: 600 22px var(--font-mn); }
.verdict__next { font: 400 var(--fs-sub) var(--font-mn); color: var(--ink2); }
.verdict__last { font: 400 var(--fs-ui) var(--font-mn); color: var(--ink3); }
.verdict__meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4);
  padding-top: var(--space-3); border-top: 1px solid var(--dv); }
.verdict__meta-item { display: inline-flex; align-items: center; gap: 5px; } /* gap-fill: sunrise/sunset */
.verdict__fresh { font-size: var(--fs-data); color: var(--ink3); margin-left: auto; }

/* -- day-chip strip (datepicker.blade.php) -------------------------------- */
.day-chips { display: flex; align-items: center; gap: 5px; overflow-x: auto;
  scrollbar-width: none; padding-bottom: 2px; }
.day-chips__label { font-size: var(--fs-ui); font-weight: 600; color: var(--ink2); flex: none; padding-right: 3px; } /* gap-fill */
.day-chips .tc-day-chip { flex: none; border: 1px solid var(--bd); min-height: 28px; }
.day-chips .tc-day-chip[aria-pressed="true"] { border-color: transparent; }
/* "Today" must stay findable once the selection moves to another chip, and the
   cue has to survive a colour-blind reading: a dashed ring, not a hue swap. */
.day-chips .tc-day-chip.is-today:not([aria-pressed="true"]) {
  border-style: dashed;
  border-color: var(--grnd);
}

/* -- shared section rhythm (glance / datepicker / tide-curve outer sections) */
.tc-section--tight { padding-bottom: var(--space-4); } /* gap-fill */
.tc-section--loose { padding-bottom: var(--space-5); } /* gap-fill */

/* -- Tides section (tide-curve.blade.php) --------------------------------- */
.tides__head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--space-3); margin-bottom: var(--space-3); } /* gap-fill */
.tides__controls { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); } /* gap-fill */
.tides__summary { font-size: var(--fs-ui); color: var(--ink2); margin-top: var(--space-3); } /* gap-fill: compact H/L line */
.tides__narrative { margin-top: var(--space-3);
  max-width: 900px; text-wrap: pretty; } /* gap-fill: #tc-narrative */
.tides__hours { margin-top: var(--space-4); border-top: 1px solid var(--dv); padding-top: var(--space-4); } /* gap-fill */
.tides__hours-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-3);
  margin-bottom: var(--space-2); } /* gap-fill */

.chart__plot { background: var(--bg); border-radius: 10px; overflow: hidden; }
.chart__legend .swatch { width: 11px; height: 11px; border-radius: var(--r-pill); }
.chart__legend .swatch--high { background: var(--grn); }
.chart__legend .swatch--low { border: 2px solid var(--red); background: none; }
.chart__legend .swatch--day { width: 15px; border-radius: 3px; background: var(--sun); opacity: .3; }
.chart__legend .swatch--major { width: 15px; height: 8px; border-radius: 3px; background: var(--grn); }
.chart__legend .swatch--minor { width: 10px; height: 8px; border-radius: 3px; background: var(--minor); }

.hours { display: flex; flex-wrap: wrap; gap: 0 var(--space-6); }
.hours__col { flex: 1 1 280px; min-width: 0; }
.hours__head { display: grid; grid-template-columns: 64px 1fr 84px; gap: 8px; padding: 6px 10px; } /* gap-fill: column captions */
.hours__row { display: grid; grid-template-columns: 64px 1fr 84px; gap: 8px; align-items: center;
  padding: 5px 10px; border-radius: var(--r-sm); font-size: var(--fs-ui-lg); }
.hours__row:nth-child(odd) { background: var(--dv); }
.hours__row .js-tc-time { color: var(--ink2); } /* gap-fill */
.hours__height { display: flex; align-items: center; gap: 8px; min-width: 0; } /* gap-fill */
.hours__row .js-tc-h { font-weight: 600; min-width: 56px; } /* gap-fill */
.hours__bar { height: 6px; border-radius: 3px; background: var(--grn); opacity: .55; }
.hours__trend--up { color: var(--grnd); font-weight: 600; }
.hours__trend--down { color: color-mix(in srgb, var(--red) 85%, var(--ink)); font-weight: 600; }
.provenance { margin-top: var(--space-2); }

/* -- at-a-glance tile accents (glance.blade.php + components/tile.blade.php)
   Keys emitted by SeoPresenter::glance() via glance.blade.php's $tcTileMap:
   wind wave fish therm water press uv coef. wind/wave/press are the literal
   design-system metric accents (06-design-system-web.md §2.4); fish reuses
   the solunar-major green (same section: "Major → #3E8460"); coef reuses the
   product's single amber (design doc: "the single amber across the whole
   product", already how .pill--coef's default/avg band renders); uv reuses
   --sun since the tile's own icon glyph *is* a sun; therm/water take the two
   remaining warm/cool tokens (sunset / wave-deep) not already spoken for, so
   all eight read as visually distinct with zero new hex literals. */
.tile__ic[data-tint="wind"]  { background: color-mix(in srgb, var(--wind) 14%, transparent); color: var(--wind); }
.tile__ic[data-tint="wave"]  { background: color-mix(in srgb, var(--wave) 14%, transparent); color: var(--waved); }
.tile__ic[data-tint="fish"]  { background: var(--tint); color: var(--grnd); }
.tile__ic[data-tint="therm"] { background: color-mix(in srgb, var(--sunset) 14%, transparent); color: var(--sunset); }
.tile__ic[data-tint="water"] { background: color-mix(in srgb, var(--waved) 14%, transparent); color: var(--waved); }
.tile__ic[data-tint="press"] { background: var(--ambt); color: var(--amb); }
.tile__ic[data-tint="uv"]    { background: color-mix(in srgb, var(--sun) 16%, transparent); color: var(--sunset); }
.tile__ic[data-tint="coef"]  { background: var(--coef-avg-t); color: var(--amb); }
.tile__ic[data-tint="vis"]   { background: var(--dv); color: var(--ink2); }

/* =============================================================================
   6. CHARTS
   Inline SVG only. The container reserves its exact aspect ratio before data
   arrives, so a populating chart never shifts layout (the CLS budget).
   ========================================================================== */

/* A chart is a STACK: plot, then the legend under it, then whatever the slot
   adds ("Show as table", an axis note). Without this they touch — the button
   sat flush against the legend above it and the prose below. */
.chart { position: relative; width: 100%; touch-action: pan-y;
  display: flex; flex-direction: column; gap: var(--space-3); }

/* The plot, legend and table want the full width; a control does not. Without
   this a flex column stretches "Show as table" into a full-width bar. */
.chart > .btn { align-self: flex-start; }

/* The picture and its two absolutely-positioned overlays (badges, tooltip) — and
   nothing else. The aspect ratio AND the positioning context live here rather than
   on .chart, so a badge at top:20% is 20% of the PLOT: putting a legend, the data
   table or a "Show as table" button below the chart can no longer stretch the
   percentage box and drag every badge and tooltip off the curve. */
.chart__plot { position: relative; }

.chart__svg { display: block; width: 100%; height: 100%; overflow: visible; }
.chart:focus-visible { outline: 2px solid var(--grn); outline-offset: 2px; border-radius: var(--r-md); }

.chart--seo .chart__plot { aspect-ratio: 1000 / 230; }
.chart--tool .chart__plot { aspect-ratio: 1000 / 300; }
.chart--spark .chart__plot { aspect-ratio: auto; height: 44px; }

@media (max-width: 639px) {
  .chart--phone .chart__plot { aspect-ratio: 1000 / 260; }

  /* Board P1c: no badges on the curve at phone width - four 138px pills cannot
     share a ~360px plot without colliding. The extrema are already spelled out
     under the chart (the mono "H 04:30 0.7 m - L ..." line on the landing page,
     the High & Low card in the tool), so nothing is lost. */
  .chart__badge { display: none; }
}

/* The "Show as table" twin of every chart — same data, structurally accessible. */
.chart__table { width: 100%; border-collapse: collapse; }
.chart__table th { text-align: left; font: 700 var(--fs-micro) var(--font-ui); letter-spacing: .1em; text-transform: uppercase; color: var(--ink3); padding: 7px 9px; }
.chart__table td { padding: 7px 9px; font: 500 var(--fs-ui) var(--font-ui); border-top: 1px solid var(--dv); }

.chart__legend { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); font: 500 var(--fs-data) var(--font-ui); color: var(--ink2); }
.chart__legend > * { display: inline-flex; align-items: center; gap: 5px; }

.chart__axis,
.coef-strip__cap,
.sol__axis { font-family: var(--font-mn); font-size: var(--fs-meta); letter-spacing: -.03em; color: var(--ink3); fill: var(--ink3); }

/* Scrub tooltip — the strongest, closest float in the system. */
.chart__tip {
  position: absolute;
  z-index: 2;
  pointer-events: none;
  background: var(--inverted-bg);
  color: var(--inverted-text);
  border-radius: 12px;
  box-shadow: var(--shadow-tip);
  padding: 10px 12px;
  font: 500 var(--fs-ui)/1.5 var(--font-ui);
  white-space: nowrap;
  max-width: 260px;
}

/* Pre-printed high/low badges sitting directly on the curve. */
.chart__badge {
  position: absolute;
  transform: translate(-50%, -50%);
  font-family: var(--font-mn);
  letter-spacing: -.03em;
  font-size: var(--fs-meta);
  font-weight: 600;
  line-height: 1.2;
  padding: 4px 8px;
  border-radius: var(--r-pill);
  white-space: nowrap;
  border: 1px solid transparent;
}

/* Near the domain edge the badge anchors by its near side instead of its centre,
   so it stays inside the plot rather than being clipped by it. Both the SSR pass
   and tide-charts.js set [data-edge]; only the transform lives here. */
.chart__badge[data-edge="start"] { transform: translate(0, -50%); }
.chart__badge[data-edge="end"] { transform: translate(-100%, -50%); }

.chart__badge--high { background: var(--grnd); color: #FFFFFF; }
.chart__badge--low { background: var(--card); color: var(--ink); border-color: var(--bd); }

:root[data-theme="dark"] .chart__badge--high { color: var(--bg); }

@media screen and (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .chart__badge--high { color: var(--bg); }
}

/* --- 6.1 Coefficient strip (31-bar month heatmap) ------------------------- */

.coef-strip { display: flex; align-items: flex-end; gap: 3px; height: 64px; }

.coef-strip__bar {
  flex: 1 1 0;
  min-width: 4px;
  border-radius: 3px 3px 0 0;
  background: var(--coef-avg);
  transition: opacity var(--dur-fast) var(--ease);
}

.coef-strip__bar[data-band="low"] { background: var(--coef-low); }
.coef-strip__bar[data-band="avg"] { background: var(--coef-avg); }
.coef-strip__bar[data-band="high"] { background: var(--coef-high); }
.coef-strip__bar[data-band="vhigh"] { background: var(--coef-vhigh); }
.coef-strip__bar:hover,
.coef-strip__bar:focus-visible { opacity: .8; }


/* --- 6.2 Solunar timeline ------------------------------------------------- */

.timeline { position: relative; height: 26px; border-radius: var(--r-sm); background: var(--dv); overflow: hidden; }

.timeline__major { position: absolute; top: 0; bottom: 0; background: var(--grn); border-radius: 3px; }
.timeline__minor { position: absolute; top: 6px; bottom: 6px; background: var(--minor); border-radius: 3px; }
.timeline__tick { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--bd); }
.timeline__now { position: absolute; top: -3px; bottom: -3px; width: 2px; background: var(--ink); border-radius: 2px; }

/* --- 6.3 Moon card — always dark, in every theme -------------------------- */

.moon-card { background: var(--moon-bg); color: var(--moon-ink); border: 1px solid var(--moon-bd); border-radius: var(--r-lg); flex: none; }

.moon-card .eb { color: var(--moon-ink2); }
.moon-card .tbl td { color: var(--moon-ink); border-top-color: var(--moon-dv); }
.moon-card .tbl th { color: var(--moon-ink2); }

/* --- 6.4 Verdict (the answer card) ---------------------------------------- */

.verdict {
  background: var(--card);
  border: 1px solid var(--bd);
  border-radius: var(--r-lg);
  flex: none;
  max-width: 600px; /* Task 1.2 follow-up: the hero's verdict card wants a cap so the hero
                        numeral doesn't stretch full-width on wide screens. */
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* BUG FIX (Task 1.2 follow-up, 2026-08-28): font-size was set on .num-pair__val, but
   .num-pair__unit's `.36em` (§5.2) resolves against ITS OWN parent's font-size — the
   .num-pair flex row, not .num-pair__val — so the unit rendered at .36em of the
   inherited 15px body text (~5px) instead of .36em of the 64px numeral. Moved up. */
.verdict .num-pair { font-size: clamp(40px, 2rem + 3vw, 64px); }
.verdict .num-pair__val { font-size: 1em; }
.verdict p { font: 500 var(--fs-lede)/1.45 var(--font-ui); color: var(--grnd); }

/* =============================================================================
   7. STATES — empty, degraded, loading
   ========================================================================== */

.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-2);
  background: var(--card);
  border: 1px solid var(--bd);
  border-radius: var(--r-lg);
  flex: none;
  padding: var(--space-8) var(--space-5);
}

.empty__icon {
  width: 44px;
  height: 44px;
  border-radius: var(--r-pill);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  background: var(--tint);
  color: var(--grnd);
  margin-bottom: var(--space-1);
}

.empty__title { font: 700 18px/1.25 var(--font-dsp); letter-spacing: -.01em; color: var(--ink); }
.empty__body { font: 400 var(--fs-sub)/1.55 var(--font-ui); color: var(--ink2); max-width: 44ch; }
.empty__cta { margin-top: var(--space-3); }

.banner {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  border: 1px solid var(--bd);
  border-radius: var(--r-md);
  padding: 11px var(--space-4);
  font: 500 var(--fs-ui-lg)/1.45 var(--font-ui);
  background: var(--card);
  color: var(--ink);
}

.banner--offline { background: var(--ambt); border-color: transparent; color: var(--amb); }
.banner--error { background: var(--redt); border-color: transparent; color: var(--red); }
.banner--info { background: var(--tint); border-color: transparent; color: var(--grnd); }

/* Skeletons match the final layout exactly, so nothing shifts when data lands. */
.skeleton { display: block; background: var(--dv); border-radius: var(--r-md); position: relative; overflow: hidden; }

.skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, var(--bd), transparent);
  opacity: .55;
  transform: translateX(-100%);
  animation: skeleton-shimmer 1.4s var(--ease) infinite;
}

@keyframes skeleton-shimmer {
  to { transform: translateX(100%); }
}

@media (prefers-reduced-motion: reduce) {
  .skeleton::after { display: none; }
}

.skeleton__bar { display: block; height: 12px; border-radius: var(--r-sm); background: var(--dv); margin-bottom: var(--space-2); }
.skeleton__bar:last-child { margin-bottom: 0; }

/* =============================================================================
   8. PRINT
   Always the Light palette, charts as their table view, no chrome.
   ========================================================================== */

/* The print frame is markup every page carries and only paper ever shows. */
.print-mast,
.print-foot { display: none; }

@media print {
  /* Paper is always the Light palette. The explicit selector list is required:
     a bare :root (0,1,0) loses to :root[data-theme="dark"] (0,2,0), so a reader
     in dark mode would otherwise print white ink on a dark fill. Every
     prefers-color-scheme block in this file is scoped `screen and`, so the
     media-query half of the cascade cannot reach print at all. */
  :root,
  :root[data-theme="light"],
  :root[data-theme="dark"] {
    --bg: #FFFFFF;
    --card: #FFFFFF;
    --bd: #E7E1D2;
    --dv: #F0EBDE;
    --ink: #23301F;
    --ink2: #5D6B58;
    --ink3: #6E7466;
    --grn: #3E8460;
    --grnd: #2E6A4B;
    --tint: #EFF3E7;
    --gold: #96661C;
    --goldt: #F6EEDC;
    --red: #C6512F;
    --redt: #FDEFEA;
    --amb: #8D6916;
    --ambt: #FBF3DE;
    --inverted-bg: #23301F;
    --inverted-text: #FAF7F1;
    --shadow-rgb: 35, 48, 31;
    --coef-low-t: #F1EFE8;
    --coef-avg-t: #FBF3DE;
    --coef-high-t: #ECF2E0;
    --coef-vhigh-t: #E2EFDF;
    --moon-bg: #FFFFFF;
    --moon-ink: #23301F;
    --moon-ink2: #5D6B58;
    --moon-bd: #E7E1D2;
    --moon-dv: #F0EBDE;
    --font-ui: Helvetica, Arial, sans-serif;
    --font-dsp: 'Roboto Condensed', Helvetica, Arial, sans-serif;
    --font-mn: Consolas, Menlo, monospace;
  }

  @page { margin: 18mm; }

  html, body { background: #FFFFFF; color: #23301F; }
  body { font: 400 var(--fs-data)/1.45 var(--font-ui); }

  /* Board W21's "what print hides": the site chrome, the tool bar, every
     control, the cookie banner and the signup gate. `.btn` alone was not the
     whole set — the day-chip strip, the export menu and Leaflet's zoom pair are
     controls that never wear that class, and they printed. */
  .site-header,
  .site-footer,
  .rail,
  .strip,
  .anchor-rail,
  .tool-bar,
  .btn,
  .toast-host,
  .banner,
  .skip-link,
  .popover,
  .sheet,
  .sheet__scrim,
  .modal,
  .modal__scrim,
  .seg,
  .tabs,
  .day-chips,
  .month-export,
  .sec-actions,
  .hero__fact-note,
  .leaflet-control-container { display: none !important; }

  /* The two SVG ribbons have no table view to fall back on, so on paper they
     were a blank band. Both restate what the cards beside them already spell
     out (the major/minor windows), so paper drops the picture, not the data. */
  .chart--timeline,
  .bite-tl { display: none !important; }

  /* Charts print as their table view — a table prints reliably everywhere,
     an inline SVG does not. */
  .chart__svg,
  .chart__tip,
  .chart__badge,
  .chart__legend { display: none !important; }

  /* The ratio now lives on .chart__plot, so both boxes must collapse or paper
     keeps reserving a blank plot above the table. Two things decide this and
     only one of them is specificity: `.chart--baro/.chart--swell .chart__plot`
     size the same box in §9 and §12, FURTHER DOWN the file, so an
     equal-specificity rule up here loses on source order alone — which is
     exactly how the barometer and the swell chart came to print a 106px and a
     120px blank band above their tables. !important is what lets a block that
     must stay at the top of the file win; tests/js/css-print.test.mjs fails the
     day another sizing rule is appended below. */
  .chart,
  .chart .chart__plot,
  .chart--baro .chart__plot,
  .chart--swell .chart__plot,
  .chart--timeline .chart__plot,
  .bite-tl .chart__plot { aspect-ratio: auto !important; height: auto !important; }

  .chart__table,
  .chart__table[hidden] { display: table !important; width: 100%; }

  /* Nothing scrolls sideways on paper: a container left at overflow-x:auto
     simply cuts whatever sticks out past the sheet — that is how the month
     table lost its Activity column and the hourly weather strip lost its
     evening. (The scrollers that are chrome — the rails, the day chips — are
     hidden above instead.) */
  .tbl-wrap,
  .wx-hours,
  .water__strip,
  .swell-dirs,
  .bite__windows,
  .cond-hours,
  .cond-water__strip { overflow: visible !important; }

  /* The month table is the one block wider than a portrait sheet, so it asks
     for a landscape page of its own (CSS named pages; a browser without them
     just keeps the portrait sheet, which is why the type also tightens). The
     calendar grid is the same shape and gets the same treatment. */
  @page tide-wide { size: landscape; margin: 14mm; }

  #table { page: tide-wide; }

  /* The calendar page IS a month grid, so the whole sheet turns rather than the
     grid becoming a landscape island between two near-empty portrait pages —
     which is what a named page on .cal-card alone produced (4 sheets, 2 of them
     carrying only the masthead and the credits). */
  body:has(.cal-card) { page: tide-wide; }

  /* A month is taller than any sheet, so both tables have to be allowed to
     break — the `break-inside: avoid` every card carries would otherwise push
     the card off its first landscape page and leave that page blank. */
  #table .card,
  .cal-card { break-inside: auto; }

  .month-table th,
  .month-table td,
  .cal-table th,
  .cal-table td { font-size: var(--fs-micro); padding-inline: 4px; }

  /* A collapsed <details> prints as a row of empty headings — the six FAQ
     answers, the one block a printed page is most likely to be kept for. The
     content is inside the UA shadow slot, so only ::details-content reaches it. */
  details::details-content { content-visibility: visible !important; }
  details:not([open]) > summary { margin-bottom: 4px; }
  .faq__chev { display: none !important; }

  .card,
  .tile,
  .verdict,
  .empty,
  .moon-card { border: 1px solid var(--bd); box-shadow: none !important; break-inside: avoid; }

  .card--inverted,
  :root[data-theme="dark"] .card--inverted { background: #FFFFFF; color: #23301F; border: 1px solid var(--bd); }

  thead { display: table-header-group; }
  tfoot { display: table-footer-group; }
  tr, img, figure { break-inside: avoid; }
  /* A guide figure IS the argument of its section; splitting one across a
     sheet break leaves half a diagram above the fold and half below. */
  .fig { break-inside: avoid; }

  .tbl th,
  .chart__table th { border-bottom: 2px solid var(--ink); font-size: var(--fs-micro); color: var(--ink); }
  .tbl td,
  .chart__table td { border-top: 1px solid var(--bd); font-size: var(--fs-data); }
  .eb, .foot__link, .tile__sub { font-size: var(--fs-micro); }

  /* Rows become cards only on a narrow screen, never on paper. */
  .stack-cards,
  .stack-cards tbody,
  .stack-cards tr,
  .stack-cards td { display: revert; width: auto; }
  .stack-cards thead { display: table-header-group; position: static; width: auto; height: auto; clip: auto; clip-path: none; }
  .stack-cards td::before { content: none; }
  .stack-cards tr { border: 0; padding: 0; margin: 0; border-radius: 0; }

  /* Keep the colour that carries earned meaning. */
  .pill,
  .pill--coef,
  .pill--rising,
  .pill--falling,
  .pill--stale,
  .pro,
  .coef-strip__bar,
  .timeline__major,
  .timeline__minor,
  .fig,
  .fig svg,
  .bands__swatch { -webkit-print-color-adjust: exact; print-color-adjust: exact; }

  a[href]::after { content: ""; }

  /* ---- the print frame (partials/print-mast + print-foot, board W21) ------
     Paper loses the header and the footer link map, so the wordmark, the
     timestamp, the licence credits and the address the sheet came from are
     carried by markup of their own. Printed once, at the top and the bottom of
     the sheet — page numbers are the browser's own footer, which CSS cannot
     write in any shipping engine. */
  .print-mast { display: flex !important; flex-wrap: wrap; align-items: baseline; gap: 4px var(--space-3);
    border-bottom: 1.5px solid var(--ink); padding-bottom: 5px; margin-bottom: var(--space-4); }
  .print-mast__word { font: 800 13px/1.2 var(--font-dsp); letter-spacing: .1em;
    text-transform: uppercase; color: var(--ink); }
  .print-mast__domain { font: 400 var(--fs-micro)/1.2 var(--font-ui); color: var(--ink2); margin-left: auto; }
  .print-mast__meta { flex: 1 0 100%; font: 400 var(--fs-micro)/1.4 var(--font-ui); color: var(--ink2); }

  .print-foot { display: block !important; break-inside: avoid;
    border-top: 1px solid var(--bd); margin-top: var(--space-4); padding-top: 5px; }
  .print-foot p { margin: 0 0 2px; font: 400 var(--fs-micro)/1.45 var(--font-ui); color: var(--ink3); }
  /* Credit LINKS are links only on screen; on paper they are just the names the
     licences ask us to print, so they take the surrounding ink. */
  .print-foot a { color: inherit; text-decoration: none; }
  .print-foot__url { color: var(--ink2); }
}

/* =============================================================================
   9. CHARTS — ENGINE PAINT (a continuation of §6, appended after §8)
   Every node public/js/tide-charts.js draws. The engine sets only geometry plus
   a var(--token) fallback on the fill/stroke that would otherwise paint black,
   so a chart still reads if this block is ever missing. Colours: tokens only.
   ========================================================================== */

/* Curve + grid. Strokes are non-scaling (the viewBox is stretched, not the ink). */
.chart__area { fill: var(--grn); fill-opacity: .2; stroke: none; }
.chart__line { fill: none; stroke: var(--grn); stroke-width: 3; }
.chart__line--baro { stroke: var(--ink2); stroke-width: 2; }
.chart__area--baro { fill: var(--ink2); fill-opacity: .12; }
.chart__grid-line { stroke: var(--dv); stroke-width: 1; }
.chart__axis-line { stroke: var(--bd); stroke-width: 1; }

/* Daylight band + solunar strip. */
.chart__band--day { fill: var(--sun); opacity: .08; }
.chart__band--night { fill: none; }
.chart__period--major { fill: var(--grn); }
.chart__period--minor { fill: var(--minor); }

/* High/low markers: a hollow ring made of two stacked round caps. */
.chart__marker { stroke-width: 11; }
.chart__marker-core { stroke: var(--bg); stroke-width: 5.6; }

/* "Now": a dashed rule and a gently pulsing dot. */
.chart__now { stroke: var(--ink); stroke-width: 1.5; stroke-dasharray: 3 4; opacity: .5; }
.chart__now-dot { stroke: var(--grnd); stroke-width: 9; animation: tc-now-pulse 2.6s var(--ease) infinite; }

@keyframes tc-now-pulse {
  0%, 100% { stroke-width: 9; opacity: 1; }
  50% { stroke-width: 13; opacity: .55; }
}

/* Scrub crosshair. */
.chart__cross { stroke: var(--ink3); stroke-width: 1; }
.chart__cross-dot { stroke: var(--ink); stroke-width: 10; }

/* Tooltip: five stacked lines, flipped to the left half past 70% of the plot. */
.chart__tip { transform: translate(14px, -100%); }
.chart__tip--flip { transform: translate(calc(-100% - 14px), -100%); }

/* Not enough room above the anchor for the tip: hang it below instead, or
   .chart__plot's overflow clips it. Pairs with --flip, so all four corners. */
.chart__tip--below { transform: translate(14px, 10px); }
.chart__tip--flip.chart__tip--below { transform: translate(calc(-100% - 14px), 10px); }
.chart__tip-line { display: block; }
.chart__tip-line--1 { font-family: var(--font-mn); letter-spacing: -.03em; font-weight: 600; }
.chart__tip-line--4,
.chart__tip-line--5 { opacity: .72; }

/* Table mode: the picture and its furniture step aside for the data. */
.chart--table .chart__legend { display: none; }
/* Two classes, so this beats the per-KIND ratios (.chart--baro, --swell,
   --timeline) that are declared further down at equal specificity — with one
   class the plot kept its 1000/150 box and left an empty band above the
   table. Order-independent, so a new chart kind cannot re-break it. */
.chart.chart--table .chart__plot { aspect-ratio: auto; height: auto; }

/* Day-number captions under a coefficient strip (bars themselves are 6.1). */
.coef-strip__caps { display: flex; gap: 3px; }
.coef-strip__caps .coef-strip__cap { flex: 1 1 0; min-width: 4px; text-align: center; }

/* List/hub sparklines inherit the row's colour unless the caller passes an accent. */
.spark__line { fill: none; stroke: currentColor; stroke-width: 2; }

@media (prefers-reduced-motion: reduce) {
  .chart__now-dot { animation: none; }
}

/* --- 5.17 Station landing — P1b sections -----------------------------------
   resources/views/public/partials/seo/{weather-now,swell,solunar-moon,
   earth-lighting,astro-observation,planner,coefficient,month-table,sites,
   faq}.blade.php (Task 1.3). Everything those ten partials need, so they carry
   ZERO inline style="" beyond three genuinely runtime values: the moon-phase
   gradient, the swell arrow's bearing and the water/coefficient bar heights.
   Colour comes from tokens only; the section rhythm is one flex column. */

/* -- shared section furniture -------------------------------------------- */
.sec { display: flex; flex-direction: column; gap: var(--space-4); padding-block: var(--space-5); }

.sec__head { display: flex; flex-direction: column; gap: 4px; }

/* Chart controls that belong to a section HEADING, pinned right of it. Design
   system §5.1: "Every chart, tide curve included, carries a small text-button
   ('Show as table') in its section header" — six charts had drifted to a button
   under the plot, where the barometer's landed between its legend and the prose
   that follows and read as if it belonged to the paragraph. Works in both header
   shapes: .sec__head--row (a baseline row) and .cond-sechead (space-between).
   The tide sections keep their own richer clusters (.tides__controls,
   .tsec__actions) — zoom and units live there too. */
.sec-actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; margin-left: auto; }

.sec__head--row {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2) var(--space-4);
}

.sec__sub { font: 500 var(--fs-body)/1.5 var(--font-ui); color: var(--ink2); max-width: 70ch; text-wrap: pretty; }
.sec__body { font: 400 var(--fs-sub)/1.6 var(--font-ui); color: var(--ink2); max-width: 78ch; text-wrap: pretty; }
.sec__note { font: 400 var(--fs-data)/1.55 var(--font-ui); color: var(--ink3); }
.sec__note a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

/* A card inside a section lays its own children out on the same rhythm. */
/* Cards stack their children on the section rhythm. `.sec > .card` reaches only
   the cards a section owns DIRECTLY, so every nested one below had its children
   touching — the barometer's ladder ran into its heading and its "Show as table"
   into the prose under it. Listed rather than widened to `.card--pad`: two
   padded cards (station facts, the tides card) space themselves with margins on
   their own children, and a gap there would stack on top of those. */
.sec > .card,
.baro,
.uv,
.sol__main,
.moon-card,
.planner__main,
.planner__side { display: flex; flex-direction: column; gap: var(--space-3); }

/* -- weather: six condition cards ---------------------------------------- */
.wx-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3); }

/* -- weather: 13-slot hourly strip --------------------------------------- */
.wx-hours {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(64px, 1fr);
  gap: 4px;
  overflow-x: auto;
  scrollbar-width: thin;
}

.wx-hour {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: 8px 2px;
  border-radius: var(--r-md);
  text-align: center;
}

.wx-hour:nth-child(odd) { background: var(--dv); }

.wx-hour__t { font-size: var(--fs-data); color: var(--ink2); }
.wx-hour__ic { display: flex; height: 22px; align-items: center; color: var(--wx-overcast); }
:is(.wx-hour__ic, .outlook__ic)[data-wx-kind="clear"] { color: var(--wx-clear); }
:is(.wx-hour__ic, .outlook__ic)[data-wx-kind="partly"] { color: var(--wx-clear); }
:is(.wx-hour__ic, .outlook__ic)[data-wx-kind="overcast"] { color: var(--wx-overcast); }
:is(.wx-hour__ic, .outlook__ic)[data-wx-kind="fog"] { color: var(--ink3); }
:is(.wx-hour__ic, .outlook__ic)[data-wx-kind="rain"] { color: var(--wx-rain); }
:is(.wx-hour__ic, .outlook__ic)[data-wx-kind="snow"] { color: var(--wave); }
:is(.wx-hour__ic, .outlook__ic)[data-wx-kind="thunder"] { color: var(--amb); }

.wx-hour__temp { font-size: var(--fs-body); font-weight: 700; }
.wx-hour__wind { font: 500 var(--fs-data)/1.2 var(--font-ui); color: var(--ink2); }
.wx-hour__rain { font-size: var(--fs-data); font-weight: 600; color: var(--ink2); }
.wx-hour__rain[data-rain="mid"] { color: var(--ink); }
.wx-hour__rain[data-rain="hi"] { color: color-mix(in srgb, var(--wx-rain) 60%, var(--ink)); }
.wx-hour__mm,
.wx-hour__hum { font-size: var(--fs-data); color: var(--ink2); }

/* -- weather: barometer + UV ---------------------------------------------- */
.wx-split { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: var(--space-3); align-items: start; }

.baro__now { font-size: var(--fs-ui-lg); color: var(--ink2); }
.baro__now [data-trend="rising"] { color: var(--grnd); }
.baro__now [data-trend="falling"] { color: var(--red); }
.baro__now [data-trend="steady"] { color: var(--amb); }

.baro__ladder { display: flex; flex-direction: column; gap: 4px; }

.baro__row {
  display: grid;
  grid-template-columns: 18px 62px minmax(0, 130px) minmax(0, 1fr);
  align-items: baseline;
  gap: var(--space-2);
  padding: 7px 9px;
  border-radius: var(--r-md);
  font: 400 var(--fs-ui)/1.45 var(--font-ui);
  color: var(--ink2);
}

.baro__row--now { background: var(--tint); }
.baro__row--now .baro__quality { color: var(--grnd); font-weight: 700; }
.baro__arrow { display: flex; align-items: center; height: 14px; color: var(--ink3); }
.baro__row[data-trend="rising"] .baro__arrow { color: var(--grnd); }
.baro__row[data-trend="falling"] .baro__arrow { color: var(--red); }
.baro__dash { display: block; width: 14px; height: 2px; border-radius: 2px; background: var(--ink3); }
.baro__trend { font-weight: 700; color: var(--ink); }
.baro__quality { font-weight: 600; color: var(--ink); }
.baro__note { color: var(--ink2); }

.chart--baro .chart__plot { aspect-ratio: 1000 / 150; }
.chart--baro .chart__line,
.chart--baro .chart__line--baro { stroke: var(--amb); stroke-width: 2.4; }
.chart--baro .chart__area,
.chart--baro .chart__area--baro { fill: var(--amb); fill-opacity: .16; }

.uv__gauge { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 2px; }
.uv__dial { width: min(230px, 100%); height: auto; margin-bottom: -14px; }
.uv__val { font-family: var(--font-mn); letter-spacing: -.03em; font-size: 28px; font-weight: 700; line-height: 1; color: var(--ink); }
.uv__level { font: 700 var(--fs-ui-lg) var(--font-ui); }
.uv__advice { font: 400 var(--fs-ui)/1.45 var(--font-ui); color: var(--ink2); max-width: 34ch; }

.uv__scale { display: flex; flex-direction: column; gap: var(--space-2); }
.uv__scale dd { font: 400 var(--fs-ui)/1.45 var(--font-ui); color: var(--ink2); }

[data-uv="1"] { color: color-mix(in srgb, var(--uv-1) 50%, var(--ink)); }
[data-uv="2"] { color: color-mix(in srgb, var(--uv-2) 50%, var(--ink)); }
[data-uv="3"] { color: color-mix(in srgb, var(--uv-3) 50%, var(--ink)); }
[data-uv="4"] { color: color-mix(in srgb, var(--uv-4) 50%, var(--ink)); }
[data-uv="5"] { color: color-mix(in srgb, var(--uv-5) 50%, var(--ink)); }

/* -- weather: water temperature ------------------------------------------- */
.card.water { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: var(--space-5); align-items: center; }
.water__fig { display: flex; flex-direction: column; gap: 3px; }
.water__now { font-family: var(--font-mn); letter-spacing: -.03em; font-size: 38px; font-weight: 700; line-height: 1; color: var(--ink); }
.water__sub { font: 500 var(--fs-ui)/1.4 var(--font-ui); color: var(--ink2); }
.water__body { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }

.water__strip {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(48px, 1fr);
  gap: 6px;
  overflow-x: auto;
  scrollbar-width: thin;
}

.water__col { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.water__bar { width: 100%; height: 64px; border-radius: var(--r-sm); background: var(--dv); display: flex; align-items: flex-end; overflow: hidden; }

.water__fill {
  width: 100%;
  background: var(--grn);
  color: var(--card);
  font-family: var(--font-mn);
  letter-spacing: -.03em;
  font-size: var(--fs-data);
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 18px;
}

.water__t { font-size: var(--fs-data); color: var(--ink3); }

/* -- swell ---------------------------------------------------------------- */
.swell-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3); }
.swell-card { display: flex; flex-direction: column; gap: 3px; padding: var(--space-4); }
.swell-card__val { font-size: 28px; line-height: 1.1; }

.chart--swell .chart__plot { aspect-ratio: 1000 / 170; }
.chart--swell .chart__line { stroke: var(--wave); }
.chart--swell .chart__area { fill: var(--wave); fill-opacity: .16; }
.chart__legend .swatch--swell { width: 15px; height: 8px; border-radius: 3px; background: var(--wave); }

.swell-dirs {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(72px, 1fr);
  gap: 4px;
  overflow-x: auto;
  scrollbar-width: thin;
  border-top: 1px solid var(--dv);
  padding-top: var(--space-3);
}

.swell-dir { display: flex; flex-direction: column; align-items: center; gap: 4px; text-align: center; }
.swell-dir__t { font-size: var(--fs-data); color: var(--ink3); }
.swell-dir__arrow { display: block; color: var(--waved); }
.swell-dir__h { font-size: var(--fs-sub); font-weight: 600; }
.swell-dir__p { font-size: var(--fs-data); color: var(--ink3); }

/* -- solunar -------------------------------------------------------------- */
.sol { display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); gap: var(--space-4); align-items: start; }

.sol__rating { display: inline-flex; align-items: center; gap: 4px; }
/* <x-fish-rating>'s wrapper; .period__fish and .month-table__fish already
   declared this same row and now ride on the shared class. */
.fish-rating { display: inline-flex; align-items: center; gap: 3px; }
.sol__fish { display: inline-flex; color: var(--bd); }
.sol__fish[data-on="true"] { color: var(--grn); }
.sol__fish[data-on="true"][data-type="minor"] { color: var(--minor); }

.sol__periods { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); gap: var(--space-3); }

.period { display: flex; flex-direction: column; gap: 4px; padding: var(--space-4); }
.period__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.period__kind { color: var(--grnd); }
.period__kind[data-type="minor"] { color: var(--amb); }
.period__fish { display: inline-flex; gap: 3px; }
.period__range { font-size: 18px; font-weight: 600; color: var(--ink); }
.period .pill { align-self: flex-start; }

.sol__block { display: flex; flex-direction: column; gap: var(--space-3); }
.chart--timeline .chart__plot { aspect-ratio: auto; height: 34px; background: var(--dv); border-radius: var(--r-sm); }
.sol__axis { display: flex; justify-content: space-between; margin-top: 5px; }

/* -- moon card ------------------------------------------------------------ */
.moon-card .h3 { color: var(--moon-ink); }
.moon-card .sec__body,
.moon-card__line { color: var(--moon-ink2); }
.moon-card__top { display: flex; align-items: center; gap: var(--space-4); }

.moon-card__disc {
  width: 78px;
  height: 78px;
  border-radius: var(--r-pill);
  flex: none;
  box-shadow: inset 0 0 0 1px var(--moon-bd);
}

.moon-card__facts { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.moon-card__line { font: 400 var(--fs-ui)/1.45 var(--font-ui); }
.moon-card__arc { width: 100%; max-width: 320px; height: auto; }
.moon-card__phases { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-2); border-top: 1px solid var(--moon-dv); padding-top: var(--space-3); }

.phase { display: flex; flex-direction: column; align-items: center; gap: 2px; text-align: center; }
.phase__dom { font-size: 20px; font-weight: 700; color: var(--moon-ink); }
.phase__mon { color: var(--moon-ink2); }
.phase__name { font: 500 var(--fs-data)/1.3 var(--font-ui); color: var(--moon-ink); }
.phase__time { font-size: var(--fs-meta); color: var(--moon-ink2); }
.phase__rel { font: 700 var(--fs-meta) var(--font-ui); color: var(--moon-gold); }

/* -- lighting the Earth --------------------------------------------------- */
.earth__map { position: relative; border-radius: var(--r-md); overflow: hidden; background: var(--moon-bg); }
.earth__map img { width: 100%; display: block; filter: saturate(.85) brightness(.95); }
.earth__map svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.earth__night { fill: var(--moon-bg); fill-opacity: .72; }
.earth__label { font-size: 26px; font-weight: 800; text-transform: uppercase; fill: var(--inverted-text); }
/* var(--moon-ink2) is a mid-tone tuned for the moon card's near-black surface;
   the equirectangular map underneath this caption is a lit photo, not that
   flat dark fill, so the sub line needs the same light ink the label uses. */
.earth__sub { font: 400 var(--fs-ui-lg) var(--font-ui); fill: var(--inverted-text); opacity: .95; }

/* These two sit on a PHOTOGRAPH, not a token surface: on the lit hemisphere the
   light ink lands on bright ocean and falls under 4.5:1. A dark halo fixes it
   without changing the ink; --moon-bg is theme-constant, so both themes match. */
.earth__label,
.earth__sub { paint-order: stroke; stroke: var(--moon-bg); stroke-width: 3px; stroke-linejoin: round; stroke-opacity: .7; }
.earth__t { fill: var(--sun); font-weight: 700; }

/* -- astronomical observation --------------------------------------------- */
.card.astro { padding-bottom: 0; overflow: hidden; }
/* The 720px cap left the readouts hugging the left of a ~1140px card while the
   horizon below it ran edge to edge — an L-shaped void under the eyebrow. The
   pairs now span the card, so the Sun column sits under the date stamp. */
.astro__pairs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-5); }
.astro__pair { display: flex; flex-direction: column; gap: 3px; }
.astro__body { font-weight: 800; line-height: 1.1; letter-spacing: .04em; text-transform: uppercase; color: var(--ink2); margin-bottom: 4px; }
.astro__body[data-body="sun"] { color: var(--amb); }
.astro__val { font-size: 34px; color: var(--ink); }
.astro__ang { font-size: 17px; font-weight: 600; color: var(--ink); }
.astro__scene { width: 100%; height: 190px; display: block; }

/* -- outlook strip (board P1b) ---------------------------------------------- */
.outlook { display: flex; flex-direction: column; gap: var(--space-3); }

.outlook__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: 7px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.outlook__day {
  border: 1px solid var(--bd);
  border-radius: 9px;
  padding: 8px 6px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  text-align: center;
}

.outlook__day--today { background: var(--tint); }

.outlook__dow { font-size: var(--fs-meta); color: var(--ink2); }
.outlook__dow--today { font-weight: 700; color: var(--ink); }

.outlook__ic { display: inline-flex; color: var(--_wx, var(--ink2)); }

.outlook__t { font-size: var(--fs-ui); font-weight: 700; }

.outlook__wind { font-size: var(--fs-meta); color: var(--wind); }

/* -- air quality (landing) ------------------------------------------------- */
.aq { display: flex; flex-direction: column; gap: var(--space-3); }

.aq__pill {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  padding: 4px 11px;
  border-radius: var(--r-pill);
  font: 700 var(--fs-ui) var(--font-ui);
  color: #FFFFFF;
  background: var(--_aq, var(--aq-1));
}

/* The two light bands need dark ink on their fills; the rest stay white. */
.aq__pill[data-aq="2"],
.aq__pill[data-aq="3"] { color: var(--inverted-bg); }

.aq__advice { max-width: 72ch; }

/* Band hues, shared with the tool's Air & UV page (same tokens). */
.aq [data-aq="1"], .aq[data-aq="1"] { --_aq: var(--aq-1); }
.aq [data-aq="2"], .aq[data-aq="2"] { --_aq: var(--aq-2); }
.aq [data-aq="3"], .aq[data-aq="3"] { --_aq: var(--aq-3); }
.aq [data-aq="4"], .aq[data-aq="4"] { --_aq: var(--aq-4); }
.aq [data-aq="5"], .aq[data-aq="5"] { --_aq: var(--aq-5); }
.aq [data-aq="6"], .aq[data-aq="6"] { --_aq: var(--aq-6); }

/* -- datums mini-card -------------------------------------------------------- */
.datums { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-6); margin: var(--space-2) 0 0; }

.datums__row { display: flex; align-items: baseline; gap: 7px; }

.datums__row dd { margin: 0; font-weight: 600; color: var(--ink); }

.datums__what { font: 400 var(--fs-data) var(--font-ui); color: var(--ink3); }

/* -- daylight & twilight ---------------------------------------------------- */
.twilight { display: flex; flex-direction: column; gap: var(--space-3); }

/* Row heads are the light's NAME, not a column caption — plain case. */
.twilight__tbl th[scope="row"] { text-transform: none; letter-spacing: 0; font: 700 var(--fs-ui) var(--font-ui); color: var(--ink); }

.twilight__note { display: block; font: 400 var(--fs-data)/1.4 var(--font-ui); color: var(--ink3); letter-spacing: 0; }

@media (min-width: 640px) {
  .twilight__tbl .tbl__c { text-align: center; white-space: nowrap; }
}

/* -- planner -------------------------------------------------------------- */
/* `align-items: start` left the two cards ending at different heights (405px
   vs 517px on a typical station) — they read as one row, so they should end as
   one. Stretch is the grid default; the phone breakpoint stacks them anyway. */
.planner { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: var(--space-4); }
.plan-windows { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: var(--space-3); }
.plan-window { display: flex; flex-direction: column; gap: 3px; padding: 14px 16px; }
.plan-window--prime { background: var(--tint); border-color: transparent; }
.plan-window__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.plan-window--prime .plan-window__kind { color: var(--grnd); }
.plan-window__dot { width: 8px; height: 8px; border-radius: var(--r-pill); background: var(--grn); flex: none; }
.plan-window__dot[data-kind="low"] { background: var(--red); }
.plan-window__range { font-size: 18px; font-weight: 600; color: var(--ink); }
.plan-window__why,
.bestday__note,
.period__cause,
.swell-card__note { font: 400 var(--fs-ui)/1.45 var(--font-ui); color: var(--ink2); }

.bestdays { display: flex; flex-direction: column; gap: var(--space-2); }

.bestday {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: 44px;
  padding: 9px 13px;
  border: 1px solid var(--bd);
  border-radius: var(--r-md);
  background: var(--card);
  text-align: left;
  transition: border-color var(--dur-fast) var(--ease);
}

.bestday:hover { border-color: var(--grn); }
.bestday__date { font-size: var(--fs-sub); font-weight: 700; flex: none; min-width: 66px; }
.bestday__note { flex: 1 1 auto; min-width: 0; }

/* Selection (tide-seo.js's day-pick, distinct from .is-today) is never
   colour-only: a shape cue rides along with the tint everywhere it lands. */
.tc-bestday.is-selected {
  border-color: var(--grnd);
  background: var(--tint);
  font-weight: 700;
  box-shadow: inset 0 0 0 2px var(--grnd);
}

/* -- coefficient panel ---------------------------------------------------- */
.coef-panel__grid { display: grid; grid-template-columns: 210px minmax(0, 1fr); gap: var(--space-6); align-items: start; }
.coef-panel__fig { display: flex; flex-direction: column; gap: 4px; }
.coef-panel__val { font-size: clamp(42px, 2rem + 2.2vw, 52px); font-weight: 700; line-height: 1; color: var(--ink); }
.coef-panel__label { color: var(--amb); }
.coef-panel__label[data-band="low"] { color: var(--ink2); }
.coef-panel__label[data-band="avg"] { color: var(--amb); }
.coef-panel__label[data-band="high"],
.coef-panel__label[data-band="vhigh"] { color: var(--grnd); }
.coef-panel__month { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.coef-panel__readout { font-size: var(--fs-ui); font-weight: 600; color: var(--ink); min-height: 18px; }
.coef-panel__strip { min-width: 0; }
.coef-strip__bar.is-today { box-shadow: inset 0 0 0 2px var(--ink); }
.coef-strip__cap.is-today { color: var(--ink); font-weight: 700; }

/* .tc-coef-bar/.tc-coef-daynum (not the shared .coef-strip__* base — the
   app-tool coefficient strip reuses that with its own selection wiring) —
   a ring/outline plus a tint wash over the band colour, not a hue swap. */
.tc-coef-bar.is-selected {
  box-shadow:
    inset 0 0 0 999px color-mix(in srgb, var(--tint) 45%, transparent),
    inset 0 0 0 2px var(--grnd);
}
.tc-coef-daynum.is-selected { color: var(--ink); font-weight: 700; background: var(--tint); border-radius: var(--r-sm); }
.chart__legend .swatch--coef { width: 11px; height: 11px; border-radius: 3px; background: var(--coef-avg); }
.chart__legend .swatch--coef[data-band="low"] { background: var(--coef-low); }
.chart__legend .swatch--coef[data-band="avg"] { background: var(--coef-avg); }
.chart__legend .swatch--coef[data-band="high"] { background: var(--coef-high); }
.chart__legend .swatch--coef[data-band="vhigh"] { background: var(--coef-vhigh); }

/* -- month table ---------------------------------------------------------- */
.month-tools { display: flex; align-items: center; gap: var(--space-3); }
.month-export { position: relative; display: inline-flex; }
.month-export__menu { display: none; right: 0; top: calc(100% + 6px); min-width: 210px; flex-direction: column; gap: 2px; }
.month-export__menu.is-open { display: flex; }
.month-export__menu .btn { justify-content: flex-start; }

.month-table td { vertical-align: middle; }
.month-table__dom { font-size: 17px; font-weight: 700; }
.month-table__dow { font: 700 var(--fs-data) var(--font-ui); color: var(--ink3); margin-left: 5px; }
.month-table__moon { display: inline-block; width: 17px; height: 17px; border-radius: var(--r-pill); border: 1px solid var(--bd); vertical-align: middle; }
.month-table__sun { font-size: var(--fs-data); color: var(--ink2); white-space: nowrap; }
.month-table__none { color: var(--ink3); }
.month-table__tide { display: inline-flex; align-items: baseline; gap: 5px; white-space: nowrap; }
.month-table__dot { width: 7px; height: 7px; border-radius: var(--r-pill); background: var(--grn); flex: none; align-self: center; }
.month-table__dot[data-type="low"] { background: var(--red); }
.month-table__h { color: var(--ink2); font-size: var(--fs-data); }
.month-table__fish { display: inline-flex; gap: 3px; }
.tc-row.is-today { background: var(--tint); }
.tc-row.is-selected { background: var(--tint); font-weight: 600; box-shadow: inset 3px 0 0 var(--grnd); }

/* -- nearby --------------------------------------------------------------- */
.nearby__list { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 250px), 1fr)); gap: var(--space-2); }

.nearby-link {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: 10px 14px;
  color: var(--ink);
  transition: border-color var(--dur-fast) var(--ease);
}

.nearby-link:hover { border-color: var(--grn); text-decoration: none; }
.nearby-link__name { flex: 1 1 auto; font: 700 var(--fs-sub) var(--font-ui); }
.nearby-link__where { flex: 1 1 auto; min-width: 0; font: 400 var(--fs-data) var(--font-ui); color: var(--ink3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nearby-link__dist { font-size: var(--fs-ui); color: var(--ink2); flex: none; }

.nearby__map { border: 1px solid var(--bd); border-radius: var(--r-md); overflow: hidden; }
.nearby__map #tc-sites-map { width: 100%; height: 380px; }

.nearby-pop { display: block; min-width: 150px; font-family: var(--font-ui); }
.nearby-pop__name { display: block; font-weight: 700; font-size: var(--fs-ui); color: var(--ink); }
.nearby-pop__where { display: block; font-size: var(--fs-data); color: var(--ink3); margin-top: 2px; }
.nearby-pop__link { display: block; margin-top: 7px; font-size: var(--fs-ui); font-weight: 700; color: var(--grnd); }

.cloud { display: flex; flex-direction: column; gap: 6px; }
.cloud__links { font: 400 var(--fs-ui)/1.9 var(--font-ui); color: var(--ink2); max-width: 1100px; }
.cloud__link { font-weight: 600; color: var(--ink2); }
.cloud__link:hover { color: var(--grnd); }
.cloud__sep { color: var(--bd); }

/* -- FAQ ------------------------------------------------------------------ */
.faq { display: flex; flex-direction: column; gap: var(--space-2); }

.faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 16px 18px;
  cursor: pointer;
  list-style: none;
}

.faq__q::-webkit-details-marker { display: none; }
.faq__q:focus-visible { outline: 2px solid var(--grn); outline-offset: -2px; border-radius: var(--r-lg); }
.faq__qtext { font: 700 var(--fs-lede)/1.35 var(--font-dsp); letter-spacing: -.01em; color: var(--ink); }
.faq__chev { display: flex; color: var(--ink3); transform: rotate(90deg); transition: transform var(--dur-fast) var(--ease); }
.faq__item[open] .faq__chev { transform: rotate(-90deg); }
.faq__a { padding: 0 18px 16px; max-width: 78ch; text-wrap: pretty; }

/* -- responsive: 4-stop scale (1199 desktop, 920 tablet, 639 phone) -------- */
@media (max-width: 1199px) {
  .wx-split { grid-template-columns: minmax(0, 1fr); }
  .coef-panel__grid { grid-template-columns: 170px minmax(0, 1fr); gap: var(--space-4); }
}

@media (max-width: 920px) {
  .wx-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .swell-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sol,
  .planner { grid-template-columns: minmax(0, 1fr); }
  .card.water { grid-template-columns: minmax(0, 1fr); gap: var(--space-3); }
  .astro__pairs { gap: var(--space-4); }
  .nearby__map #tc-sites-map { height: 320px; }
}

@media (max-width: 639px) {
  .sec { gap: var(--space-3); padding-block: var(--space-4); }
  .wx-grid,
  .swell-stats { grid-template-columns: minmax(0, 1fr); }
  .coef-panel__grid { grid-template-columns: minmax(0, 1fr); }
  .coef-panel__val { font-size: 42px; }
  .astro__pairs { grid-template-columns: minmax(0, 1fr); }
  .astro__scene { height: 130px; }
  .moon-card__phases { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .baro__row { grid-template-columns: 18px 60px minmax(0, 1fr); row-gap: 2px; }
  .baro__note { grid-column: 2 / -1; }
  .nearby__map #tc-sites-map { height: 260px; }
  .faq__qtext { font-size: var(--fs-body); }

  /* A day with three tides must not print an empty "4th tide" row on a card.
     :has() is progressively enhanced — without it the row is blank, not wrong. */
  .month-table td[data-label]:not(:has(*)) { display: none; }
}

/* =============================================================================
   10. FISH / BITE TIMES (P2c, pages/04) — resources/views/app/sections/fish.blade.php

   One section's worth of rules, appended as a block so it can be lifted out
   whole. Everything here is layout, state and band colour; no chart geometry
   (that lives in the engine's own viewBox) and no new tokens.

   The ribbon reuses `.chart` + `.timeline`: `.timeline` paints the track,
   `.chart__plot` supplies the positioning context — with `overflow` opened up
   so the scrub tooltip can float clear of a 34px-tall track instead of being
   clipped by it.
   ========================================================================== */

.bite { display: flex; flex-direction: column; gap: var(--space-4); }

/* Both cards in a row share the row's height (the board's pairing) - `start`
   left the shorter card floating with a ragged hole under it. */
.bite__lede,
.bite__foot { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 420px); gap: var(--space-4); align-items: stretch; }
.bite__foot { grid-template-columns: minmax(0, 420px) minmax(0, 1fr); }

.bite__eb { margin-bottom: var(--space-2); }
.bite__best .bite__eb { color: var(--grnd); }

/* -- headline verdict ------------------------------------------------------ */
.bite__hero { margin: var(--space-2) 0 0; font-size: clamp(28px, 1.2rem + 2.6vw, 40px); font-weight: 700; line-height: 1.05; }

.bite__rate { display: flex; flex-wrap: wrap; align-items: center; gap: 9px; margin-top: 11px; }
.bite__rate-num { font-size: var(--fs-ui); color: var(--ink2); }

.bite-rate { display: inline-flex; align-items: center; gap: 3px; }
.bite-rate__fish { display: inline-flex; color: var(--ink3); opacity: .4; }
.bite-rate__fish[data-bite-fish="on"] { color: var(--grnd); opacity: 1; }

/* The 5-level band, worn by the label pill. The WORD carries the rating; the
   colour only repeats it. */
.pill--act { background: var(--tint); color: var(--grnd); }
.pill--act[data-score="1"] { background: var(--redt); color: var(--red); }
.pill--act[data-score="2"] { background: var(--ambt); color: var(--amb); }
.pill--act[data-score="3"] { background: var(--goldt); color: var(--gold); }
.pill--act[data-score="4"],
.pill--act[data-score="5"] { background: var(--grnd); color: #FFFFFF; }

.bite__reason { margin: 11px 0 0; font: 700 var(--fs-body)/1.45 var(--font-ui); }
.bite__calc { display: flex; align-items: center; gap: 6px; margin: 10px 0 0; font: 500 var(--fs-data) var(--font-ui); color: var(--ink3); }

/* -- solunar day: the 24 h ribbon ------------------------------------------ */
/* A 34px-tall ribbon cannot contain a ~38px tooltip, so both timeline variants
   let it escape and pin it to the top edge. `.chart--timeline` is the SEO
   copy of the same widget under a different class — it was clipping. */
.bite-tl .chart__plot,
.chart--timeline .chart__plot { height: 34px; overflow: visible; }
.bite-tl .chart__tip,
.chart--timeline .chart__tip { top: 0; }

.bite-tl__hl { position: absolute; top: 0; bottom: 0; border-radius: 4px; background: var(--gold); opacity: .3; pointer-events: none; }
.bite-tl__hl[hidden] { display: none; }

.bite-tl__axis { display: flex; justify-content: space-between; margin-top: 5px; font-size: var(--fs-micro); color: var(--ink3); }
.bite-tl__legend { margin-top: var(--space-2); font-size: var(--fs-data); gap: var(--space-3); }
/* The sun ticks are lines, not blocks — the legend swatch says so. */
.bite-tl__legend .swatch--tick { width: 3px; height: 12px; border-radius: 2px; background: var(--sun); }

/* -- solunar day: the period table ----------------------------------------- */
.bite__periods { margin-top: var(--space-3); }
.bite__periods td { padding: 6px 0; vertical-align: top; }
.bite__periods tr:first-child td { border-top: 0; }
.bite__periods-t { font-weight: 600; white-space: nowrap; }
.bite__periods-c { text-align: right; font-size: var(--fs-data); color: var(--ink2); }

.pill--period { font-size: var(--fs-meta); }
.pill--period[data-type="major"] { background: var(--tint); color: var(--grnd); }
.pill--period[data-type="minor"] { background: var(--ambt); color: var(--amb); }

.bite__bonus { display: block; margin-top: 2px; font-size: var(--fs-meta); font-weight: 700; color: var(--gold); }

/* -- tide-turn window cards ------------------------------------------------ */
.bite__windows { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--space-3); }

.bite-win {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: var(--card); border: 1px solid var(--bd); border-radius: var(--r-lg);
  padding: 11px 13px; font: inherit; color: inherit;
  transition: transform var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}
.bite-win:hover { transform: translateY(-2px); box-shadow: var(--shadow-sm); border-color: var(--grn); }
.bite-win[aria-pressed="true"] { border-color: var(--grn); box-shadow: 0 0 0 2px var(--tint); }
.bite-win--prime { background: var(--tint); border-color: var(--grn); }

.bite-win__t { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: var(--fs-ui-lg); font-weight: 700; }
.bite-win__label { display: block; margin-top: 3px; font: 500 var(--fs-data) var(--font-ui); color: var(--ink2); }
.bite-win__why { display: block; margin-top: 4px; font: 400 var(--fs-data)/1.4 var(--font-ui); color: var(--ink3); }
.bite-win--prime .bite-win__t,
.bite-win--prime .bite-win__label,
.bite-win--prime .bite-win__why { color: var(--grnd); }

.bite__note { margin: var(--space-2) 0 0; font: 400 var(--fs-data) var(--font-ui); color: var(--ink3); }

/* -- find a good day + the permanent honesty card -------------------------- */
.bite__finder-body { margin: 0 0 var(--space-3); font: 400 var(--fs-ui)/1.55 var(--font-ui); color: var(--ink2); }

.bite__honesty-row { display: flex; align-items: flex-start; gap: 10px; color: var(--ink3); }
.bite__honesty-h { margin: 0 0 4px; font: 700 var(--fs-ui) var(--font-ui); color: var(--ink); }
.bite__honesty-body { margin: 0; font: 400 var(--fs-ui)/1.5 var(--font-ui); color: var(--ink2); }
.bite__honesty-body a { color: var(--grnd); font-weight: 600; }

@media (max-width: 920px) {
  .bite__lede,
  .bite__foot { grid-template-columns: minmax(0, 1fr); }
}

/* Phone: the window row scrolls sideways, ~1.2 cards visible (pages/04 §5.3). */
@media (max-width: 639px) {
  .bite__windows {
    grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: 78%;
    overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none;
    padding-bottom: 2px;
  }
  .bite__windows::-webkit-scrollbar { display: none; }
  .bite-win { scroll-snap-align: start; }
  .bite__periods-c { text-align: left; }
}

/* =============================================================================
   11. FORECAST TOOL — NOW SECTION (P2a, pages/02 §5.3–5.8)
   The answer-first landing state: hero row (answer card + station-facts rail),
   the 8-tile grid (.tiles, §5.2), today's plan and the freshness ladder row.
   Colours are tokens only; nothing here paints a value it could inherit.
   ========================================================================== */

.now { display: flex; flex-direction: column; gap: var(--space-4); }

/* Hero row is a GRID, not a flex-wrap: the facts rail is a fixed 280px column
   so the answer card's right edge lands exactly on the tile grid's (P2a). The
   rail drops under the card at the tablet crossover, where 280px would eat the
   answer. */
.now__hero { display: grid; grid-template-columns: minmax(0, 1fr) 280px; gap: var(--space-4); align-items: start; }

@media (max-width: 920px) {
  .now__hero { grid-template-columns: minmax(0, 1fr); }
}

/* `.verdict` is declared after `.card--tint` (§6.4 vs §5.1), so the modifier
   loses the cascade on background. Restated at (0,2,0) — same two declarations,
   no new colour — so the tint fill works wherever the two classes meet. */
.verdict.card--tint { background: var(--tint); border-color: transparent; }

/* The 600px cap belongs to the PUBLIC hero, where the card floats in a
   full-width page. Here the grid column already caps it, and the cap would
   leave a dead gap between the card and the rail. */
.now__answer { max-width: none; }

.now__state { display: flex; align-items: center; gap: 10px; color: var(--grnd); }
.now__state-word { font: 700 17px var(--font-ui); }

.now__count { font-size: clamp(20px, 1rem + 1vw, 26px); font-weight: 600; }
.now__next { font-size: var(--fs-body); color: var(--ink2); }

.now__chip { display: flex; align-items: center; gap: 6px; color: var(--ink3); }
.now__chip span { font: 500 var(--fs-data) var(--font-ui); }

/* -- station-facts rail ---------------------------------------------------- */
.now__facts-pill { margin-top: 8px; }
.now__facts-src { margin: 9px 0 0; font: 400 var(--fs-data)/1.5 var(--font-ui); color: var(--ink2); }

.now__fresh { margin-top: 11px; padding-top: 10px; border-top: 1px solid var(--dv); }
.now__fresh p { margin: 5px 0 0; font: 400 var(--fs-data)/1.45 var(--font-ui); color: var(--ink2); }
.now__fresh strong { color: var(--grnd); }

/* -- today's plan ---------------------------------------------------------- */
.now__plan-title { margin-bottom: 10px; }

.now__windows { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 10px; }

.now__win {
  display: block;
  border: 1px solid var(--bd);
  border-radius: 11px;
  padding: 10px 12px;
  color: inherit;
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease);
}

a.now__win:hover { text-decoration: none; border-color: var(--grn); }

/* Prime is never colour alone — the card also carries a PRIME pill and the
   words "overlaps a major". */
.now__win--prime { border-color: var(--grn); background: var(--tint); }

.now__win-head { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.now__win-time { font-size: var(--fs-ui-lg); font-weight: 700; }
.now__win-label { display: block; margin-top: 3px; font: 500 var(--fs-data) var(--font-ui); color: var(--ink2); }

.now__win--prime .now__win-time,
.now__win--prime .now__win-label { color: var(--grnd); }

.now__narrative { margin: 11px 0 0; font: 400 var(--fs-ui-lg)/1.6 var(--font-ui); color: var(--ink2); text-wrap: pretty; }

/* -- freshness ladder row (§5.7) ------------------------------------------- */
.now__foot {
  display: flex; align-items: center; flex-wrap: wrap; gap: 7px;
  margin: 0; font: 500 var(--fs-data)/1.4 var(--font-ui); color: var(--ink2);
}

.now__foot--none { color: var(--ink3); }

/* =============================================================================
   12. CONDITIONS — hub + the six met sections (Task 2.5, P2d / pages/05)
   Appended as ONE block. Everything is namespaced `.cond*` so it cannot collide
   with the public station page's own section styles; colour comes only from the
   existing tokens, indexed through a private --_ac / --_aq / --_uv / --_wx
   custom property so a six-band ramp costs six one-line rules instead of six
   copies of every rule that uses it.
   ========================================================================== */

.cond { display: flex; flex-direction: column; gap: var(--space-4); }

/* The tool's page title is a notch quieter than the public pages' .h1 — P2d
   sets it at 28px so the six cards, not the word "Conditions", lead the page. */
.cond .h1 { font-size: clamp(26px, 1rem + 1.7vw, 34px); line-height: 1.05; }

/* Row headers in this section carry a sentence of context under the label, so
   they opt out of .tbl th's caption casing. */
.cond .tbl th[scope="row"] { text-transform: none; letter-spacing: 0;
  font: 700 var(--fs-ui)/1.35 var(--font-ui); color: var(--ink); }

/* Per-section accent: one lookup, used by the hub icon circles and sparklines. */
.cond [data-accent="astro"]   { --_ac: var(--grnd);  --_act: var(--tint); }
.cond [data-accent="weather"] { --_ac: var(--amb);   --_act: var(--ambt); }
.cond [data-accent="wind"]    { --_ac: var(--wind);  --_act: color-mix(in srgb, var(--wind) 14%, transparent); }
.cond [data-accent="wave"]    { --_ac: var(--waved); --_act: color-mix(in srgb, var(--wave) 14%, transparent); }
.cond [data-accent="baro"]    { --_ac: var(--amb);   --_act: var(--ambt); }
.cond [data-accent="air"]     { --_ac: var(--grnd);  --_act: var(--tint); }

/* -- shared page furniture ------------------------------------------------- */

.cond-lede { font: 400 var(--fs-ui-lg)/1.6 var(--font-ui); color: var(--ink2); max-width: 820px;
  margin: 0; text-wrap: pretty; }

.cond-back { display: inline-flex; align-items: center; gap: 4px; align-self: flex-start;
  font: 700 var(--fs-meta)/1.3 var(--font-ui); letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink3); text-decoration: none; padding: 4px 2px; min-height: 24px; }
.cond-back:hover { color: var(--grnd); }
.cond-back__ic { transform: rotate(180deg); }

.cond-title { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--space-3); }
.cond-title .h1 { margin: 0; }

.cond-sechead { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: var(--space-3); margin-bottom: var(--space-3); }
.cond-sechead .h3 { margin: 0; }

.cond-note { display: block; font: 400 var(--fs-data)/1.5 var(--font-ui); color: var(--ink3); margin: 0; }
.cond-note--block { margin-top: var(--space-3); }

.cond-attr { font-size: var(--fs-micro); line-height: 1.5; color: var(--ink3); margin: 0; }
.cond-attr a { color: inherit; }

/* Freshness marker. Astro is a chip, met is a dot; >30 min turns amber, and a
   section with nothing fetched at all goes grey and says so in words. */
.cond-fresh { display: inline-flex; align-items: center; gap: 5px;
  font: 500 var(--fs-meta) var(--font-ui); color: var(--ink3); }
.cond-fresh[data-state="calc"] { color: var(--grnd); }
.cond-fresh[data-state="stale"] { color: var(--amb); }
.cond-fresh[data-state="fresh"]::before,
.cond-fresh[data-state="stale"]::before,
.cond-fresh[data-state="none"]::before { content: ""; width: 7px; height: 7px; border-radius: var(--r-pill); flex: none; }
.cond-fresh[data-state="fresh"]::before { background: var(--grn); }
.cond-fresh[data-state="stale"]::before { background: var(--amb); }
.cond-fresh[data-state="none"]::before { background: var(--ink3); opacity: .5; }

/* -- 12.1 the hub ---------------------------------------------------------- */

.cond-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); }

.cond-card { display: flex; flex-direction: column; gap: 9px; padding: 14px 16px;
  text-decoration: none; color: inherit;
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease); }
.cond-card:hover { border-color: var(--grn); box-shadow: var(--shadow-md); }
.cond-card:hover .cond-card__chev { color: var(--grnd); transform: translateX(2px); }

.cond-card__top { display: flex; align-items: center; gap: 10px; }
.cond-card__ic { width: 34px; height: 34px; border-radius: var(--r-pill); flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--_act, var(--tint)); color: var(--_ac, var(--grnd)); }
.cond-card__title { font: 700 var(--fs-body) var(--font-ui); flex: 1; min-width: 0; }
.cond-card__fresh { display: inline-flex; align-items: center; gap: 5px; flex: none;
  font: 500 var(--fs-meta) var(--font-mn); letter-spacing: -.03em; color: var(--ink3); }
.cond-card__fresh[data-state="calc"] { color: var(--grnd); }
.cond-card__fresh[data-state="stale"] { color: var(--amb); }
.cond-card__fresh[data-state="fresh"]::before,
.cond-card__fresh[data-state="stale"]::before { content: ""; width: 7px; height: 7px;
  border-radius: var(--r-pill); flex: none; background: currentColor; }
.cond-card__fresh[data-state="fresh"]::before { background: var(--grn); }
.cond-card__chev { color: var(--ink3); flex: none; display: inline-flex;
  transition: transform var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease); }

.cond-card__body { display: flex; align-items: flex-end; gap: 12px; }
.cond-card__figures { flex: 1; min-width: 0; }
.cond-card__hero { display: block; font-size: 20px; font-weight: 700; line-height: 1.2; }
.cond-card__hero-alt { font-size: var(--fs-ui-lg); font-weight: 500; color: var(--ink2); }
.cond-card__sub { display: block; font: 400 var(--fs-ui)/1.4 var(--font-ui); color: var(--ink2); margin-top: 3px; }
.cond-card__sub.mn { font-size: var(--fs-data); }
.cond-card__none { font: 400 var(--fs-ui)/1.45 var(--font-ui); color: var(--ink3); }
.cond-card__trend { font-size: var(--fs-body); font-weight: 600; }
.cond-card__trend[data-trend="rising"] { color: var(--grnd); }
.cond-card__trend[data-trend="falling"] { color: var(--red); }
.cond-card__trend[data-trend="steady"] { color: var(--ink3); }
.cond-card__spark { width: 86px; height: 28px; flex: none; color: var(--_ac, var(--ink3)); }

@media (max-width: 639px) {
  .cond-grid { grid-template-columns: minmax(0, 1fr); }
}

/* -- 12.2 stat rows shared by the section pages ---------------------------- */

.cond-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr));
  gap: var(--space-3); }
.cond-stat { display: flex; flex-direction: column; gap: 6px; padding: 14px 16px; }
.cond-stat__val { font: 700 22px/1.1 var(--font-mn); letter-spacing: -.03em; }
.cond-stat__unit { font-size: var(--fs-ui); font-weight: 500; color: var(--ink2); letter-spacing: 0; }

.cond-hero { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr));
  gap: var(--space-4); margin-bottom: var(--space-4); }
.cond-hero__fig { display: flex; flex-direction: column; gap: 4px; }
.cond-hero__val { font-size: 26px; }
.cond-hero__note { font: 400 var(--fs-data)/1.4 var(--font-ui); color: var(--ink3); }

.cond-split { display: grid; grid-template-columns: minmax(0, 280px) minmax(0, 1fr);
  gap: var(--space-4); align-items: start; }
.cond-rose-side { display: flex; flex-direction: column; gap: var(--space-3); }

@media (max-width: 767px) {
  .cond-split { grid-template-columns: minmax(0, 1fr); }
}

/* -- 12.3 Sun & Moon ------------------------------------------------------- */

.cond-arc { margin: var(--space-2) 0 var(--space-4); }
.cond-arc svg { display: block; width: 100%; height: 210px; background: var(--bg); border-radius: 10px; }
.cond-arc__band[data-kind="golden"] { fill: var(--sun); fill-opacity: .22; }
.cond-arc__band[data-kind="blue"] { fill: var(--wave); fill-opacity: .3; }
.cond-arc__base { stroke: var(--bd); stroke-width: 1.5; }
.cond-arc__path { stroke: var(--sun); stroke-width: 2.6; stroke-dasharray: 2 7; stroke-linecap: round; }
.cond-arc__drop { stroke: var(--sun); stroke-width: 1.2; opacity: .5; }
.cond-arc__sun { fill: var(--sun); }
.cond-arc__axis { display: flex; justify-content: space-between; font-size: var(--fs-meta);
  color: var(--ink3); margin-top: 5px; }
.cond-arc__legend { margin-top: var(--space-2); }

.cond-swatch { display: inline-block; width: 15px; height: 8px; border-radius: 3px;
  vertical-align: middle; margin-right: 5px; }
.cond-swatch[data-kind="golden"] { background: var(--sun); opacity: .55; }
.cond-swatch[data-kind="blue"] { background: var(--wave); opacity: .7; }
.cond-swatch[data-kind="now"] { width: 10px; height: 10px; border-radius: var(--r-pill); background: var(--sun); }
.cond-swatch[data-kind="wind"] { background: var(--wind); }
.cond-swatch[data-kind="gust"] { background: none; border-top: 2px dashed var(--wind); height: 0; }

.cond-moon__top { display: flex; align-items: center; gap: var(--space-4); margin-bottom: var(--space-4); }
.cond-moon__disc { width: 96px; height: 96px; border-radius: var(--r-pill); flex: none;
  border: 1px solid var(--moon-bd); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12); }
.cond-moon__facts { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.cond-moon__name { font: 800 20px/1.15 var(--font-dsp); letter-spacing: -.01em; }
.cond-moon__line { font: 400 var(--fs-ui)/1.45 var(--font-ui); color: var(--moon-ink2); }
.cond-moon .cond-note { color: var(--moon-ink2); }
.cond-moon .tbl th[scope="row"] { color: var(--moon-ink); }

.cond-phases { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 130px), 1fr));
  gap: var(--space-2); margin-top: var(--space-4); }
.cond-phase { display: flex; flex-direction: column; gap: 2px; padding: 10px 12px;
  border: 1px solid var(--moon-bd); border-radius: var(--r-md); }
.cond-phase__dom { font-size: 20px; font-weight: 700; line-height: 1; }
.cond-phase__mon { color: var(--moon-ink2); }
.cond-phase__name { font: 700 var(--fs-ui) var(--font-ui); }
.cond-phase__time { font-size: var(--fs-data); color: var(--moon-ink2); }

.cond-astro__pairs { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
  gap: var(--space-4); }
.cond-astro__pair { display: flex; flex-direction: column; gap: 4px; padding: 14px 16px;
  border: 1px solid var(--bd); border-radius: var(--r-md); }
.cond-astro__body { font: 700 var(--fs-ui-lg) var(--font-ui); }
.cond-astro__body[data-body="moon"] { color: var(--ink2); }
.cond-astro__body[data-body="sun"] { color: var(--amb); }
.cond-astro__val { font-size: 24px; }
.cond-astro__ang { font-size: var(--fs-sub); font-weight: 600; }

/* -- 12.4 Weather ---------------------------------------------------------- */

.cond-wx-hero { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-5); }
.cond-wx-hero__ic { flex: none; display: inline-flex; color: var(--_wx, var(--ink2)); }
.cond-wx-hero__main { display: flex; flex-direction: column; gap: 2px; }
.cond-wx-hero__val { font-size: clamp(38px, 2rem + 2vw, 52px); }
.cond-wx-hero__cond { font: 700 var(--fs-sub) var(--font-ui); color: var(--ink2); }
.cond-wx-hero__feels { font: 400 var(--fs-ui) var(--font-ui); color: var(--ink3); }
.cond-wx-hero__hl { margin-left: auto; display: flex; flex-direction: column; gap: 4px; text-align: right; }
.cond-wx-hero__hlval { font-size: var(--fs-sub); font-weight: 700; }

/* WMO glyph families — the token is the ONLY colour source. Scoped under
   .cond: the public station page owns its own [data-wx-kind] copy
   (.wx-hour__ic[data-wx-kind=…], sets `color` directly) — this one must never
   reach past the tool. */
.cond [data-wx-kind="clear"]    { --_wx: var(--wx-clear); }
.cond [data-wx-kind="partly"]   { --_wx: var(--wx-clear); }
.cond [data-wx-kind="overcast"] { --_wx: var(--wx-overcast); }
.cond [data-wx-kind="fog"]      { --_wx: var(--wx-overcast); }
.cond [data-wx-kind="rain"]     { --_wx: var(--wx-rain); }
.cond [data-wx-kind="snow"]     { --_wx: var(--wx-overcast); }
.cond [data-wx-kind="thunder"]  { --_wx: var(--wx-thunder); }

.cond-hours { display: flex; gap: var(--space-2); overflow-x: auto; -webkit-overflow-scrolling: touch;
  padding-bottom: var(--space-2); scrollbar-width: thin; }
.cond-hour { display: flex; flex-direction: column; align-items: center; gap: 5px; flex: none;
  min-width: 62px; padding: 10px 6px; border: 1px solid var(--bd); border-radius: var(--r-md);
  background: var(--card); }
.cond-hour__t { font-size: var(--fs-data); color: var(--ink2); }
.cond-hour__ic { color: var(--_wx, var(--ink2)); display: inline-flex; }
.cond-hour__temp { font-size: var(--fs-ui-lg); font-weight: 700; }
.cond-hour__rain { font-size: var(--fs-data); color: var(--ink3); }
.cond-hour__rain[data-rain="mid"] { color: var(--wave); }
.cond-hour__rain[data-rain="hi"] { color: var(--waved); font-weight: 700; }
.cond-hour__hum { font-size: var(--fs-data); color: var(--ink3); }

.cond-prob { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.cond-prob__row { display: flex; align-items: center; gap: var(--space-4); padding: 7px 0; }
.cond-prob__label { width: 84px; flex: none; font: 700 var(--fs-ui) var(--font-ui); }
.cond-prob__track { flex: 1; height: 11px; border-radius: var(--r-sm); background: var(--dv); overflow: hidden; }
.cond-prob__fill { display: block; height: 100%; border-radius: var(--r-sm);
  background: var(--_wxp, var(--ink3)); }
.cond-prob__pct { width: 42px; flex: none; text-align: right; font-size: var(--fs-ui-lg); font-weight: 700; }
.cond-prob__fill[data-cond="clear"]    { --_wxp: var(--wx-clear); }
.cond-prob__fill[data-cond="overcast"] { --_wxp: var(--wx-overcast); }
.cond-prob__fill[data-cond="windy"]    { --_wxp: var(--wx-windy); }
.cond-prob__fill[data-cond="rain"]     { --_wxp: var(--wx-rain); }
.cond-prob__fill[data-cond="fog"]      { --_wxp: var(--wx-fog); }
.cond-prob__fill[data-cond="thunder"]  { --_wxp: var(--wx-thunder); }
.cond-prob__fill[data-cond="snow"]     { --_wxp: var(--wx-snow); }
.cond-prob__fill[data-cond="frost"]    { --_wxp: var(--wx-frost); }
/* --wx-snow and --wx-thunder sit within a hair of --bg / --ink respectively:
   a hairline keeps both readable in either theme. */
.cond-prob__fill[data-cond="snow"],
.cond-prob__fill[data-cond="thunder"] { box-shadow: inset 0 0 0 1px var(--bd); }

/* -- 12.5 Wind / Waves rose ------------------------------------------------ */

.cond-rose-card { display: flex; flex-direction: column; align-items: center; gap: var(--space-2); }
.cond-rose { width: 100%; max-width: 240px; height: auto; aspect-ratio: 1; }
.cond-rose__ring { stroke: var(--bd); stroke-width: 2; }
.cond-rose__ring--inner { stroke: var(--dv); stroke-width: 1.5; }
.cond-rose__tick { stroke: var(--bd); stroke-width: 1.5; }
.cond-rose__tick[data-major="true"] { stroke: var(--ink3); stroke-width: 2; }
.cond-rose__lbl { font: 700 var(--fs-ui-lg) var(--font-ui); fill: var(--ink3); }
.cond-rose__arrow { stroke: var(--wind); stroke-width: 5; }
.cond-rose__arrow--wave { stroke: var(--waved); }
.cond-rose__read { font-size: var(--fs-body); font-weight: 700; margin: 0; }

.cond-bf { display: flex; flex-direction: column; gap: 7px; }
.cond-bf__badge { align-self: flex-start; border-radius: var(--r-pill); padding: 5px 12px;
  font: 700 var(--fs-ui) var(--font-ui); background: var(--tint); color: var(--grnd); }
.cond-bf__badge[data-force="1"] { background: var(--coef-high-t); color: var(--grnd); }
.cond-bf__badge[data-force="2"] { background: var(--ambt); color: var(--amb); }
.cond-bf__badge[data-force="3"] { background: var(--redt); color: var(--red); }
.cond-bf__badge[data-force="4"] { background: var(--inverted-bg); color: var(--inverted-text); }
.cond-bf__feel { font: 400 var(--fs-ui-lg)/1.55 var(--font-ui); color: var(--ink2); margin: 0; }

.cond-explain__body { font: 400 var(--fs-ui)/1.6 var(--font-ui); color: var(--ink2); margin: 0; }

/* -- 12.6 Water temperature ------------------------------------------------ */

.cond-water__fig { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-4);
  margin-bottom: var(--space-4); }
.cond-water__now { font-size: 40px; color: var(--waved); }
.cond-water__range { font: 500 var(--fs-ui-lg) var(--font-ui); color: var(--ink2); }
.cond-water__strip { display: flex; gap: 5px; overflow-x: auto; padding-bottom: var(--space-2);
  margin-bottom: var(--space-4); }
.cond-water__col { display: flex; flex-direction: column; align-items: center; gap: 4px; flex: 1 1 0;
  min-width: 44px; }
.cond-water__bar { width: 100%; height: 86px; border-radius: var(--r-sm); background: var(--dv);
  display: flex; align-items: flex-end; overflow: hidden; }
.cond-water__fill { display: block; width: 100%; border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--waved) 70%, transparent); }
.cond-water__v { font-size: var(--fs-data); font-weight: 700; }
.cond-water__t { font-size: var(--fs-meta); color: var(--ink3); }

/* -- 12.7 Barometer -------------------------------------------------------- */

.cond-verdict { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-5); }
.cond-verdict__arrow { font-size: 44px; line-height: 1; color: var(--ink3); }
.cond-verdict[data-trend="rising"] .cond-verdict__arrow,
.cond-verdict[data-trend="rising"] .cond-verdict__word { color: var(--grnd); }
.cond-verdict[data-trend="falling"] .cond-verdict__arrow,
.cond-verdict[data-trend="falling"] .cond-verdict__word { color: var(--red); }
.cond-verdict__main { display: flex; flex-direction: column; gap: 3px; }
.cond-verdict__val { font-size: clamp(32px, 1.6rem + 2vw, 46px); }
.cond-verdict__word { font: 800 var(--fs-lede) var(--font-dsp); letter-spacing: .06em; color: var(--ink3); }
.cond-verdict__copy { flex: 1 1 260px; min-width: 0; }
.cond-verdict__sentence { font: 700 var(--fs-lede)/1.45 var(--font-ui); color: var(--ink); margin: 0 0 5px; }

.cond-ladder { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.cond-ladder__row { display: grid; grid-template-columns: 22px 74px minmax(0, 150px) minmax(0, 1fr);
  align-items: baseline; gap: var(--space-3); padding: 10px 12px; border-radius: var(--r-md);
  border: 1px solid var(--bd); }
.cond-ladder__row--now { background: var(--tint); border-color: var(--grn); }
.cond-ladder__arrow { display: inline-flex; color: var(--ink3); }
.cond-ladder__row[data-trend="rising"] .cond-ladder__arrow { color: var(--grnd); }
.cond-ladder__row[data-trend="falling"] .cond-ladder__arrow { color: var(--red); }
.cond-ladder__dash { display: block; width: 12px; height: 2px; background: currentColor; }
.cond-ladder__trend { font: 700 var(--fs-ui-lg) var(--font-ui); }
.cond-ladder__quality { font: 700 var(--fs-ui) var(--font-ui); color: var(--ink2); }
.cond-ladder__row--now .cond-ladder__quality { color: var(--grnd); }
.cond-ladder__note { font: 400 var(--fs-ui)/1.5 var(--font-ui); color: var(--ink3); }

.cond-details { margin-top: var(--space-4); border-top: 1px solid var(--dv); padding-top: var(--space-3); }
.cond-details summary { font: 700 var(--fs-ui-lg) var(--font-ui); cursor: pointer; padding: 4px 0; min-height: 24px; }
.cond-details p { font: 400 var(--fs-ui-lg)/1.6 var(--font-ui); color: var(--ink2); margin: var(--space-2) 0 0; max-width: 72ch; }

@media (max-width: 767px) {
  .cond-ladder__row { grid-template-columns: 22px minmax(0, 1fr); }
  .cond-ladder__note { grid-column: 1 / -1; }
}

/* -- 12.8 Air quality & UV ------------------------------------------------- */

.cond [data-aq="1"] { --_aq: var(--aq-1); }
.cond [data-aq="2"] { --_aq: var(--aq-2); }
.cond [data-aq="3"] { --_aq: var(--aq-3); }
.cond [data-aq="4"] { --_aq: var(--aq-4); }
.cond [data-aq="5"] { --_aq: var(--aq-5); }
.cond [data-aq="6"] { --_aq: var(--aq-6); }

.cond-aq__pill { display: inline-flex; align-items: center; gap: 6px; border-radius: var(--r-pill);
  padding: 5px 12px; font: 700 var(--fs-ui) var(--font-ui);
  background: color-mix(in srgb, var(--_aq, var(--aq-1)) 16%, transparent); color: var(--_aq, var(--aq-1)); }
.cond-aq__advice { font: 500 var(--fs-ui-lg)/1.6 var(--font-ui); margin: 0 0 var(--space-4);
  padding: 13px 15px; border-radius: var(--r-md);
  background: color-mix(in srgb, var(--_aq, var(--aq-1)) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--_aq, var(--aq-1)) 32%, transparent);
  color: var(--ink); }

/* The shared pollutant card (<x-pollutant-card>), used by this section AND the
   SEO station page — one markup, one band map (App\Support\AirQuality), one
   value size, so the µg/m³ unit can never again render at 5px off a 14px chip.
   Carries its own [data-aq] slots: the card must work in any scope. */
.poll-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  gap: var(--space-3); }
.poll-card { display: flex; flex-direction: column; gap: 7px; padding: 16px 18px; }
.poll-card__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.poll-card__name { font: 800 var(--fs-ui-lg) var(--font-ui); }
.poll-card__lvl { border-radius: var(--r-pill); padding: 3px 9px; font: 700 var(--fs-meta) var(--font-ui);
  background: color-mix(in srgb, var(--_aq, var(--aq-1)) 16%, transparent); color: var(--_aq, var(--aq-1)); }
.poll-card__val { font-size: 26px; }
.poll-card__track { height: 7px; border-radius: var(--r-sm); background: var(--dv); overflow: hidden; }
.poll-card__fill { display: block; height: 100%; border-radius: var(--r-sm); background: var(--_aq, var(--aq-1)); }
.poll-card__note { display: block; font: 400 var(--fs-data)/1.5 var(--font-ui); color: var(--ink3); }
.poll-card[data-aq="1"], .poll-card [data-aq="1"] { --_aq: var(--aq-1); }
.poll-card[data-aq="2"], .poll-card [data-aq="2"] { --_aq: var(--aq-2); }
.poll-card[data-aq="3"], .poll-card [data-aq="3"] { --_aq: var(--aq-3); }
.poll-card[data-aq="4"], .poll-card [data-aq="4"] { --_aq: var(--aq-4); }
.poll-card[data-aq="5"], .poll-card [data-aq="5"] { --_aq: var(--aq-5); }
.poll-card[data-aq="6"], .poll-card [data-aq="6"] { --_aq: var(--aq-6); }

.cond [data-uv="1"] { --_uv: var(--uv-1); }
.cond [data-uv="2"] { --_uv: var(--uv-2); }
.cond [data-uv="3"] { --_uv: var(--uv-3); }
.cond [data-uv="4"] { --_uv: var(--uv-4); }
.cond [data-uv="5"] { --_uv: var(--uv-5); }

.cond-uv__row { display: grid; grid-template-columns: minmax(0, 220px) minmax(0, 1fr);
  gap: var(--space-5); align-items: start; }
.cond-uv__gauge { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.cond-uv__dial { width: 100%; max-width: 200px; height: auto; }
.cond-uv__needle { stroke: var(--ink); stroke-width: 4; }
.cond-uv__hub { fill: var(--ink); }
.cond-uv__val { font-size: 24px; font-weight: 700; margin: 0; }
.cond-uv__level { font: 700 var(--fs-ui) var(--font-ui); margin: 0; color: var(--_uv, var(--uv-1)); }
.cond-uv__scale { margin: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.cond-uv__tier { padding: 10px 12px; border: 1px solid var(--bd); border-radius: var(--r-md); }
.cond-uv__tier--now { border-color: var(--_uv, var(--uv-1)); background: var(--dv); }
.cond-uv__tier dt { font: 700 var(--fs-ui) var(--font-ui); color: var(--_uv, var(--uv-1)); }
.cond-uv__tier dd { margin: 3px 0 0; font: 400 var(--fs-ui)/1.5 var(--font-ui); color: var(--ink2); }

@media (max-width: 767px) {
  .cond-uv__row { grid-template-columns: minmax(0, 1fr); }
}

/* -- 12.9 section chart accents -------------------------------------------- */

.cond-chart--weather .chart__line { stroke: var(--amb); }
.cond-chart--weather .chart__area { fill: var(--amb); }
.cond-chart--wind .chart__line { stroke: var(--wind); }
.cond-chart--wind .chart__area { fill: var(--wind); }
.cond-chart--wave .chart__line { stroke: var(--waved); }
.cond-chart--wave .chart__area { fill: var(--wave); }
.cond-chart--baro .chart__line { stroke: var(--amb); stroke-width: 2.4; }
.cond-chart--baro .chart__area { fill: var(--amb); fill-opacity: .14; }
.cond-chart__gust { stroke: var(--wind); stroke-width: 2; opacity: .75; }

/* =============================================================================
   13. TIDE SECTION (P2b, pages/03) — resources/views/app/sections/tide.blade.php

   Appended as ONE block so it can be lifted out whole. Everything is namespaced
   `.tsec*`; the chart, the coefficient strip, `.tbl` and `.card` do their own
   work and are only positioned here. Colour is tokens only — no value is
   painted that could be inherited.

   The 7-day zoom folds a week into the engine's 1440-minute box, which would
   stack 28 extrema badges on one plot: `[data-zoom="7d"]` hides them, and the
   day axis under the card carries the week's labels instead.
   ========================================================================== */

.tsec { display: flex; flex-direction: column; gap: var(--space-4); }

.tsec__head { display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3); flex-wrap: wrap; }
.tsec__actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

/* --- 13.1 chart + the day's facts ----------------------------------------- */

.tsec__main { display: grid; grid-template-columns: minmax(0, 1fr) 280px;
  gap: var(--space-4); align-items: start; }
.tsec__side { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.tsec__chart { min-width: 0; }

/* The skeleton covers the plot instead of replacing it, so the card keeps its
   height and nothing moves while the six extra days arrive. */
.tsec__plot { position: relative; }
.tsec__skel { position: absolute; inset: 0; z-index: 1; }
.tsec__skel .skeleton { height: 100%; }

.tsec__axis { display: flex; justify-content: space-between; gap: var(--space-2); margin-top: 6px; }
.tsec__axis-t { font-size: var(--fs-meta); color: var(--ink3); }

/* The clock axis labels ARE the tick positions (0 / 6 / 12 / 18 / 24 h), so
   space-between lands them exactly. A day label instead names a whole seventh
   of the plot, so it gets an equal cell and sits over the middle of its day. */
.tsec__axis[data-tide-axis="day"] .tsec__axis-t { flex: 1 1 0; text-align: center; }

/* At 7d the plot carries 28 turns, no sun band and no solunar strip: the
   extrema badges would stack into a smear, and three of the five legend keys
   would point at something that is not drawn. */
.chart[data-zoom="7d"] .chart__badge { display: none; }
.chart[data-zoom="7d"] .tsec-leg--day { display: none; }

/* --- 13.2 high & low ------------------------------------------------------ */

.tsec__hl { padding: 0; overflow: hidden; }
.tsec__cardhead { padding: 11px 13px 7px; }

.tsec__hl-type { color: var(--ink2); }
.tsec__hl-type[data-type="high"] { color: var(--grnd); font-weight: 700; }
.tsec__hl-t { text-align: right; }
.tsec__hl-h { text-align: right; font-weight: 700; }

/* --- 13.3 datums ---------------------------------------------------------- */

.tsec__datums { display: flex; flex-direction: column; gap: 4px; margin: var(--space-2) 0 0; }
.tsec__datum { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-2); }
.tsec__datum-key { font: 500 var(--fs-ui) var(--font-ui); color: var(--ink2); }
.tsec__datum-val { margin: 0; font-size: var(--fs-ui); }
.tsec__note { font: 400 var(--fs-meta)/1.45 var(--font-ui); color: var(--ink3); margin: var(--space-2) 0 0; }

/* --- 13.4 transparency ---------------------------------------------------- */

.tsec__row2 { display: grid; grid-template-columns: minmax(0, 1fr) 380px;
  gap: var(--space-4); align-items: start; }
.tsec__transp { min-width: 0; }
.tsec__transp-head { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.tsec__transp-src { font-size: var(--fs-data); color: var(--ink2); }
.tsec__transp-body { font: 400 var(--fs-ui)/1.5 var(--font-ui); color: var(--ink2); margin: 9px 0 0; }
.tsec__transp-body strong { color: var(--ink); font-weight: 700; }
.tsec__links { margin: 9px 0 0; font: 700 var(--fs-ui) var(--font-ui); }
.tsec__links a { color: var(--grnd); }

/* --- 13.5 coefficient ----------------------------------------------------- */

.tsec__coef { min-width: 0; }
.tsec__coef-top { display: flex; align-items: flex-end; gap: var(--space-3); }
.tsec__coef-val { font-size: 38px; font-weight: 700; line-height: .9; }
.tsec__coef-meta { flex: 1 1 auto; min-width: 0; padding-bottom: 3px; }
.tsec__coef-note { font: 400 var(--fs-data)/1.4 var(--font-ui); color: var(--ink2); margin: 3px 0 0; }

.tsec__readout { font: 700 var(--fs-data) var(--font-ui); color: var(--ink2); margin: var(--space-3) 0 5px; min-height: 16px; }

/* Selection on the shared `.coef-strip__*` base (§6.1 styles `.is-today` only,
   and §5.17's ring/wash belongs to the SEO page's own `.tc-coef-bar`). A tint
   wash plus a green ring — never a hue swap, which would move the bar to a
   different coefficient band. The two states COMBINE: on today's own bar the
   green ring reads as the selection and today's ink ring steps outside it. */
.tsec__strip .coef-strip__bar.is-selected {
  box-shadow:
    inset 0 0 0 999px color-mix(in srgb, var(--tint) 45%, transparent),
    inset 0 0 0 2px var(--grnd);
}
.tsec__strip .coef-strip__bar.is-today.is-selected { outline: 2px solid var(--ink); outline-offset: 1px; }
.tsec__strip { min-width: 0; }
.tsec__strip .coef-strip { height: 34px; }
.tsec__scale { display: flex; justify-content: space-between; gap: var(--space-2); margin-top: 5px; }
.tsec__scale span { font-size: var(--fs-micro); color: var(--ink3); }

/* --- 13.6 hour by hour ---------------------------------------------------- */

.tsec__hbh-card { padding: 0; overflow: hidden; }
.tsec__hbh { display: flex; border-top: 1px solid var(--dv); }
.tsec__hbh-col { flex: 1 1 0; min-width: 0; padding: 8px 0; text-align: center;
  border-left: 1px solid var(--dv); }
.tsec__hbh-col:first-child { border-left: 0; }
.tsec__hbh-col[data-current="true"] { background: var(--tint); }
.tsec__hbh-t { font-size: var(--fs-meta); color: var(--ink2); }
.tsec__hbh-col[data-current="true"] .tsec__hbh-t { color: var(--ink); font-weight: 700; }
.tsec__hbh-h { font-size: var(--fs-ui-lg); font-weight: 700; margin-top: 3px; }
.tsec__hbh-tr { display: inline-flex; margin-top: 2px; }
.tsec__hbh-tr--up { color: var(--grnd); }
.tsec__hbh-tr--down { color: var(--red); }

/* --- 13.7 responsive ------------------------------------------------------ */

@media (max-width: 1023px) {
  .tsec__main,
  .tsec__row2 { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 639px) {
  .tsec__hbh { flex-wrap: wrap; }
  .tsec__hbh-col { flex: 1 1 25%; border-top: 1px solid var(--dv); }
  .tsec__hbh-col:first-child { border-left: 1px solid var(--dv); }
  .tsec__coef-val { font-size: 32px; }
}

/* =============================================================================
   14. SETTINGS (W18, pages/18) — resources/views/app/settings.blade.php
   Appended as ONE block, everything namespaced `.set*`, so it lifts out whole.
   No new tokens and no new component: the unit rows are the shared `.seg`, the
   cards are `.card` + `.card__cap`. Only three things are genuinely new here —
   the theme swatch, the on/off switch and the two-column settings grid.
   ========================================================================== */

.set {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 360px;
  gap: var(--space-4);
  align-items: start;
}

.set__col,
.set__aside { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }

/* Cards whose body is a row list: the rows own the padding, not the card. */
.set__card { padding: 0; overflow: hidden; }

.set__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: 10px 16px;
  border-top: 1px solid var(--dv);
}

.set__label { font: 700 var(--fs-ui-lg)/1.35 var(--font-ui); color: var(--ink); }

/* --- 14.1 reset row -------------------------------------------------------
   A text button, not a `.btn`: it undoes a preference rather than performing
   an action, and the mock deliberately keeps it as quiet as a link. */

.set__row--action { padding: 0; }

.set__reset {
  flex: 1 1 auto;
  padding: 11px 16px;
  text-align: left;
  background: transparent;
  border: 0;
  border-radius: 0 0 var(--r-lg) var(--r-lg);
  cursor: pointer;
  font: 700 var(--fs-ui)/1.4 var(--font-ui);
  color: var(--grnd);
}

.set__reset:hover { background: var(--tint); }

/* --- 14.2 appearance swatches ---------------------------------------------
   The previews are LITERAL light/dark, never tokens: a "Light" chip that goes
   dark with the page stops being a preview. Pressed state is an inset ring so
   the border never changes width and the three chips cannot jitter. */

.set__appearance { display: flex; flex-direction: column; gap: var(--space-2); }

.set__themes { display: flex; gap: var(--space-2); }

.set__theme {
  flex: 1 1 0;
  min-width: 0;
  padding: 9px;
  text-align: center;
  background: transparent;
  border: 1px solid var(--bd);
  border-radius: 10px;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}

.set__theme:hover { border-color: var(--grn); }

.set__theme[aria-pressed="true"] {
  border-color: var(--grn);
  background: var(--tint);
  box-shadow: inset 0 0 0 1px var(--grn);
}

/* One neutral hairline for all three: a literal-dark preview on a dark card is
   otherwise invisible, and no token works on both halves of the split swatch. */
.set__swatch { display: block; height: 32px; border-radius: 6px; border: 1px solid rgba(138, 146, 128, .55); }

.set__swatch--light { background: #FAF7F1; }
.set__swatch--dark { background: #141C12; }
.set__swatch--system { background: linear-gradient(90deg, #FAF7F1 0 50%, #141C12 50% 100%); }

.set__theme-name {
  display: block;
  margin-top: 6px;
  font: 700 var(--fs-data)/1.3 var(--font-ui);
  color: var(--ink2);
}

.set__theme[aria-pressed="true"] .set__theme-name { font-weight: 700; color: var(--grnd); }

/* --- 14.3 section-layout switches ----------------------------------------- */

.set__row--toggle { justify-content: flex-start; gap: var(--space-3); flex-wrap: nowrap; padding: 9px 16px; }

.set__row-ic { display: inline-flex; flex: none; color: var(--ink3); }

.set__label--section { flex: 1 1 auto; min-width: 0; font-weight: 500; font-size: var(--fs-ui-lg); }

.set__switch {
  flex: none;
  width: 40px;
  height: 24px;
  padding: 3px;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  background: var(--bd);
  border: 0;
  border-radius: var(--r-pill);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
}

.set__switch[aria-checked="true"] { justify-content: flex-end; background: var(--grnd); }

.set__knob {
  width: 18px;
  height: 18px;
  border-radius: var(--r-pill);
  background: var(--card);
  box-shadow: 0 1px 2px rgba(var(--shadow-rgb), .28);
}

/* Off, the knob has to read against `--bd`, which in dark mode is only a shade
   off `--card` — the shadow that carries it in light mode disappears there. */
.set__switch[aria-checked="false"] .set__knob { background: var(--ink3); box-shadow: none; }

/* Off-state rows read as off at a glance, not only from the switch. */
.set__row--toggle:has(.set__switch[aria-checked="false"]) .set__label--section { color: var(--ink3); }

/* --- 14.4 responsive ------------------------------------------------------ */

@media (max-width: 1023px) {
  .set { grid-template-columns: minmax(0, 1fr); }
  /* The aside is full-width here; unbounded swatches stretch into letterboxes. */
  .set__themes { max-width: 420px; }
}

@media (max-width: 639px) {
  /* A four-option seg (wind, pressure) gets its own full line rather than
     shrinking "km/h" and "mmHg" into abbreviations nobody asked for. */
  .set__row .seg { flex: 1 1 100%; justify-content: space-between; }
  .set__row .seg__opt { flex: 1 1 0; min-height: 36px; padding: 0 8px; }
}

/* =============================================================================
   15. MONTH CALENDAR (P3a, pages/06) — Task 3.1
   -----------------------------------------------------------------------------
   Namespace `.cal-*`, all of it new (grepped: `.cal-` was unused before this
   block). The grid REUSES `.tbl.stack-cards` and the bars REUSE `.coef-strip`
   rather than redefining either — the only additions here are the month pager,
   the export menu's open state, the row/day chrome and the two-card footer.
   ========================================================================== */

.cal { display: flex; flex-direction: column; gap: var(--space-4); }

/* --- 15.1 month head: pager · export · jump-to-today ---------------------- */

.cal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
}

.cal-pager { display: flex; align-items: center; gap: var(--space-4); min-width: 0; }

.cal-pager__month { margin: 0; font-weight: 800; font-size: 26px; line-height: 1.1; color: var(--ink); }

.cal-pager__step {
  font: 500 var(--fs-ui-lg) var(--font-ui);
  color: var(--ink2);
  white-space: nowrap;
  padding: 2px 4px;          /* keeps the hit box at the 24px floor */
  min-height: 24px;
  display: inline-flex;
  align-items: center;
}

.cal-pager__step:hover { color: var(--ink); }

.cal-actions { display: flex; align-items: center; gap: var(--space-2); }

/* The export popover: `hidden` must win, so the flex display is scoped to the
   not-hidden state (TC.popover toggles the attribute, never a style). */
.cal-menu { top: calc(100% + 6px); right: 0; min-width: 190px; }
.cal-menu:not([hidden]) { display: flex; flex-direction: column; gap: 2px; }
.cal-menu__item { justify-content: flex-start; width: 100%; }

/* "Jump to today" is the one inverted control on the page (board P3a). */
.cal-jump,
.cal-jump:hover { background: var(--ink); border-color: var(--ink); color: var(--bg); }

/* --- 15.2 the month grid -------------------------------------------------- */

.cal-card { padding: 0; overflow: hidden; }

.cal-table th,
.cal-table td { padding: 8px 10px; }

.cal-table__right { text-align: right; }

.cal-row { cursor: pointer; }
.cal-row[data-cal-today] { background: var(--goldt); }
.cal-row.is-selected td:first-child { box-shadow: inset 3px 0 0 var(--grn); }

.cal-row__day { font: 700 var(--fs-ui) var(--font-ui); color: var(--ink); white-space: nowrap; }
.cal-row__day:hover { text-decoration: underline; text-underline-offset: 3px; }

/* Moon: one two-stop gradient disc, not an eight-icon set. */
.cal-moon {
  display: inline-block;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  border: 1px solid var(--bd);
  vertical-align: middle;
}

.cal-sun { display: inline-flex; align-items: center; gap: 4px; font-size: var(--fs-data); color: var(--ink2); white-space: nowrap; }

.cal-ev { display: inline-flex; align-items: baseline; font-size: var(--fs-data); white-space: nowrap; }
.cal-ev[data-type="low"] { color: var(--ink2); }
.cal-ev__sep { margin: 0 4px; color: var(--ink3); }

.cal-fish { display: inline-flex; align-items: center; gap: 2px; }

.cal-none { font-size: var(--fs-data); color: var(--ink3); }

/* --- 15.3 coefficient strip + best days ---------------------------------- */

.cal-bottom { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: var(--space-3); align-items: start; }

.cal-strip { height: 56px; margin-top: var(--space-2); }

/* The bars are real links (one per day, keyboard-reachable, crawl-free inside
   the noindexed tool) — anchors need the underline and the min-height off. */
.cal-strip__bar { display: block; text-decoration: none; }
.cal-strip__bar:hover { text-decoration: none; }

.cal-strip__caps {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  margin-top: 5px;
  font-size: var(--fs-micro);
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink3);
}

.cal-strip__range { text-align: center; }

.cal-best { list-style: none; margin: var(--space-2) 0 0; padding: 0; }

.cal-best__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: 6px 0;
  border-top: 1px solid var(--dv);
}

.cal-best__row:first-child { border-top: 0; }
.cal-best__link { font: 700 var(--fs-ui) var(--font-ui); color: var(--ink); }

.cal-best__note {
  margin: var(--space-2) 0 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--dv);
  font: 400 var(--fs-data)/1.45 var(--font-ui);
  color: var(--ink3);
}

.cal-note { margin: 0; font: 400 var(--fs-data)/1.5 var(--font-ui); color: var(--ink3); }

/* --- 15.4 responsive ------------------------------------------------------ */

@media (max-width: 1023px) {
  .cal-bottom { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 639px) {
  .cal-head { align-items: flex-start; flex-direction: column; }
  .cal-pager { width: 100%; justify-content: space-between; }
  .cal-pager__month { font-size: 22px; }
  .cal-pager__step { min-width: 44px; min-height: 44px; }
  .cal-actions { width: 100%; }

  /* Stacked day cards (.stack-cards) — the tint and the accent have to move
     from the cell to the card, since the row IS the card below this stop. */
  .cal-row.is-selected td:first-child { box-shadow: none; }
  .cal-row.is-selected { box-shadow: inset 3px 0 0 var(--grn); }
  .cal-table td { padding: 4px 0; }
}

@media print {
  /* Paper gets the grid and nothing that only works on screen. */
  .cal-actions,
  .cal-pager__step { display: none !important; }
  .cal-card { overflow: visible; }
  .cal-bottom { grid-template-columns: minmax(0, 1fr); }
}

/* =============================================================================
   16. PLAN — BEST DAY FINDER + REPORTS (Task 3.2, W15/W20, pages/15 + pages/20)
   resources/views/app/{seaquery,reports}.blade.php

   Appended as ONE block so it lifts out whole. Two namespaces, `.bdf*` and
   `.rpt*`; `.card`, `.seg`, `.pill`, `.tbl`, `.chart` and `.empty` do their own
   work and are only positioned here. Colour is tokens only.

   Both pages are FREE surfaces (config('tides.pro_gating') === false), so there
   are no lock/blur/teaser rules here at all — the markup those would style is
   never rendered.
   ========================================================================== */

/* --- 16.1 Best Day Finder — the two-column search ------------------------- */

.bdf {
  display: grid;
  grid-template-columns: 300px minmax(0, 1fr);
  gap: var(--space-5);
  align-items: start;
}

.bdf__panel { display: flex; flex-direction: column; gap: var(--space-3); }

.bdf__title { margin: 0; }

.bdf__lede { margin: 0; font: 400 var(--fs-ui-lg)/1.45 var(--font-ui); color: var(--ink2); }

.bdf__card { display: flex; flex-direction: column; gap: var(--space-3); }

/* The horizon control has exactly one reachable option while the product is
   free, so the button is disabled rather than dressed to look pressable. */
.bdf__horizon { display: flex; width: 100%; }
/* While Pro gating is off there is exactly ONE horizon, so this control states a
   fact rather than offering a choice: it drops the segmented track and reads as
   a tinted chip instead of an empty-looking field. */
.bdf__horizon { background: transparent; padding: 0; }
.bdf__horizon .seg__opt { flex: 1; min-height: 30px; }
.bdf__horizon .seg__opt[aria-pressed="true"] { background: var(--tint); color: var(--grnd); }
.bdf__horizon .seg__opt[disabled] { cursor: default; opacity: 1; }

.bdf__group-label { margin-top: var(--space-2); }

.bdf-filter { border: 1px solid var(--bd); border-radius: var(--r-md); padding: 10px 12px; margin: 0; }

/* The active filter is outlined, not just tinted: at the one-filter-at-a-time
   scale the border is what tells you which control the result list came from. */
.bdf-filter--on { border-color: var(--grn); background: var(--tint); }

.bdf-filter__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); cursor: pointer; }

.bdf-filter__name { font: 700 var(--fs-ui) var(--font-ui); color: var(--grnd); padding: 0; }

.bdf-filter__value { font-size: var(--fs-data); font-weight: 600; color: var(--grnd); }

.bdf-filter__hint { margin: 6px 0 0; font: 400 var(--fs-data)/1.4 var(--font-ui); color: var(--ink2); }

/* A <fieldset> ignores the flex/grid it sits in unless min-inline-size is reset. */
fieldset.bdf-filter { min-inline-size: 0; }
fieldset.bdf-filter .bdf-filter__name { color: var(--ink); }

/* Range input: one track rule per engine, because no shorthand reaches both. */
.bdf-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  margin: 10px 0 0;
  height: 16px;
  background: transparent;
  cursor: pointer;
}

/* WebKit has no ::-*-progress pseudo, so the filled part of the track is a hard
   gradient stop driven by --bdf-fill, which the page sets from the input value. */
.bdf-slider::-webkit-slider-runnable-track {
  height: 5px;
  border-radius: 3px;
  background: linear-gradient(to right, var(--grnd) 0 var(--bdf-fill, 0%), var(--dv) var(--bdf-fill, 0%) 100%);
}

.bdf-slider::-moz-range-track {
  height: 5px;
  border-radius: 3px;
  background: linear-gradient(to right, var(--grnd) 0 var(--bdf-fill, 0%), var(--dv) var(--bdf-fill, 0%) 100%);
}

.bdf-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 15px;
  height: 15px;
  margin-top: -5px;
  border-radius: var(--r-pill);
  background: var(--grnd);
  border: 2px solid var(--card);
}

.bdf-slider::-moz-range-thumb {
  width: 15px;
  height: 15px;
  border-radius: var(--r-pill);
  background: var(--grnd);
  border: 2px solid var(--card);
}

.bdf-slider:focus-visible { outline: 2px solid var(--grn); outline-offset: 4px; }

.bdf-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }

.bdf-chip { display: inline-flex; }

/* The radio stays in the accessibility tree and keeps the keyboard behaviour of
   a real radio group — only its painted box is replaced. */
.bdf-chip input { position: absolute; width: 1px; height: 1px; opacity: 0; }

.bdf-chip span {
  display: inline-flex;
  align-items: center;
  min-height: 34px;
  padding: 0 13px;
  border: 1px solid var(--bd);
  border-radius: var(--r-pill);
  font: 700 var(--fs-ui) var(--font-ui);
  color: var(--ink2);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.bdf-chip span:hover { border-color: var(--grn); color: var(--grnd); }
.bdf-chip input:checked + span { border-color: var(--grn); background: var(--tint); color: var(--grnd); }
.bdf-chip input:focus-visible + span { outline: 2px solid var(--grn); outline-offset: 2px; }

.bdf__submit { width: 100%; margin-top: var(--space-2); }

/* --- 16.2 Best Day Finder — the ranked result list ------------------------ */

.bdf__results { display: flex; flex-direction: column; gap: var(--space-3); }

.bdf__count { margin: 0; font: 700 var(--fs-ui-lg) var(--font-ui); color: var(--ink2); }

.bdf-hit {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: 14px 16px;
  background: var(--card);
  border: 1px solid var(--bd);
  border-radius: var(--r-lg);
  color: inherit;
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}

.bdf-hit:hover { border-color: var(--grn); background: var(--tint); }

/* #1 is outlined so the ranking is visible before any number is read. */
.bdf-hit--top { border-color: var(--grn); }
.bdf-hit--top .bdf-hit__rank { color: var(--grnd); }

.bdf-hit__rank { flex: none; font-size: var(--fs-body); font-weight: 700; color: var(--ink3); }

.bdf-hit__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }

.bdf-hit__date { font: 700 var(--fs-lede) var(--font-ui); color: var(--ink); }

.bdf-hit__window { font-size: var(--fs-ui); color: var(--ink2); }

.bdf-hit__why { font: 400 var(--fs-ui)/1.4 var(--font-ui); color: var(--ink2); }

.bdf-hit__scores { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }

.bdf-hit__rating { font-size: var(--fs-data); font-weight: 600; color: var(--ink); }

.bdf-hit__chev { flex: none; color: var(--ink3); }

/* --- 16.3 Reports --------------------------------------------------------- */

.rpt { display: flex; flex-direction: column; gap: var(--space-4); }

.rpt__head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap; }

.rpt__head h1 { margin: 0; }

.rpt__sub { margin: 4px 0 0; font: 500 var(--fs-ui-lg) var(--font-ui); color: var(--ink2); }

/* The period control is real navigation, so its options are links; `.seg__opt`'s
   pressed rule keys off aria-pressed, which an <a> may not carry. */
.rpt-seg .seg__opt { text-decoration: none; }
.rpt-seg .seg__opt[aria-current="page"] { background: var(--card); color: var(--ink); }

:root[data-theme="dark"] .rpt-seg .seg__opt[aria-current="page"] { background: var(--bd); }

@media screen and (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .rpt-seg .seg__opt[aria-current="page"] { background: var(--bd); }
}

.rpt__grid { display: grid; grid-template-columns: minmax(0, 1fr) 330px; gap: var(--space-4); align-items: start; }

.rpt__summary { display: flex; flex-direction: column; gap: var(--space-3); }

.rpt-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-3); }

.rpt-stat { min-width: 0; }

.rpt-stat__val { margin-top: 4px; font-size: 24px; }

/* .num-pair's default .36em unit disappears at this size — a "m" that reads as
   a stray dot is worse than no unit at all. */
.rpt-stat__val .num-pair__unit { font-size: .5em; }

.rpt-stat__val--none { font: 500 var(--fs-ui-lg) var(--font-ui); color: var(--ink3); }

.rpt-stat__link { font-weight: 700; color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--bd); }
.rpt-stat__link:hover { color: var(--grnd); border-bottom-color: var(--grn); }

.rpt-stat__sub { margin-top: 2px; font-size: var(--fs-meta); color: var(--ink3); }

.rpt-chart .chart__legend { justify-content: space-between; width: 100%; }

.rpt-chart__axis { font-size: var(--fs-micro); color: var(--ink3); }

.rpt-chart__bar { transition: fill-opacity var(--dur-fast) var(--ease); }

/* --- 16.4 Reports — export card ------------------------------------------ */

.rpt-export { display: flex; flex-direction: column; }

.rpt-export .eb { margin-bottom: 8px; }

.rpt-export__row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 11px 0;
  border-top: 1px solid var(--dv);
}

.rpt-export__text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }

.rpt-export__name { font: 700 var(--fs-ui) var(--font-ui); color: var(--ink); }

.rpt-export__hint { font: 400 var(--fs-meta)/1.35 var(--font-ui); color: var(--ink3); }

/* --- 16.5 Reports — the table -------------------------------------------- */

.rpt-table-card { padding: 4px 6px; }

.rpt-table th[scope="row"] {
  font: 700 var(--fs-ui) var(--font-ui);
  letter-spacing: normal;
  text-transform: none;
  color: var(--ink);
  white-space: nowrap;
}

.rpt-table__v { display: inline-flex; align-items: baseline; gap: 4px; }

.rpt-table th[scope="row"] a { color: inherit; text-decoration: none; }
.rpt-table th[scope="row"] a:hover { color: var(--grnd); }

.rpt-table tbody tr { cursor: pointer; }

.rpt-table__today { background: var(--tint); }

.rpt-table__now {
  margin-left: 7px;
  font: 700 var(--fs-micro) var(--font-ui);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--grnd);
}

/* --- 16.6 responsive ------------------------------------------------------ */

@media (max-width: 1023px) {
  .rpt__grid { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 920px) {
  .bdf { grid-template-columns: minmax(0, 1fr); }
  .bdf__panel { position: static; }
}

@media (max-width: 639px) {
  .rpt-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .rpt-stat__val { font-size: 20px; }

  /* The card stacks: rank + date first, then the cause, then the scores. */
  .bdf-hit { flex-wrap: wrap; gap: var(--space-2) var(--space-3); }
  .bdf-hit__body { flex: 1 1 100%; order: 2; }
  .bdf-hit__rank { order: 1; }
  .bdf-hit__chev { order: 1; margin-left: auto; }
  .bdf-hit__scores { flex: 1 1 100%; order: 3; flex-direction: row; align-items: center; justify-content: flex-start; gap: var(--space-3); }

  .rpt-table__today { background: none; box-shadow: inset 3px 0 0 var(--grn); }
}

@media print {
  /* Paper keeps the numbers and the table; the search form and the export rows
     are screen-only controls with nothing to say on a printed page. */
  .bdf__panel,
  .rpt-export { display: none !important; }

  .bdf { grid-template-columns: minmax(0, 1fr); }
  .rpt__grid { grid-template-columns: minmax(0, 1fr); }
  .rpt-table tbody tr { cursor: auto; }
}

/* =============================================================================
   17. ALERTS (W14) + FAVORITES (W13) — Task 3.3
   resources/views/app/{events,alerts,favorites}.blade.php

   Appended as ONE block so it can be lifted out whole. Two namespaces:
   `.alr*` for the station alert editor and the cross-station hub, `.fav*` for
   the saved-spots dashboard. Nothing here introduces a token or a colour
   literal — every accent is an existing var().

   The switch and the chips are NATIVE controls wearing a face: the switch is a
   submit button with role="switch", each chip is a real radio/checkbox with an
   .sr-only input and a styled sibling. That is what lets both work with
   JavaScript off, and it is why the states below key off :checked and
   [aria-checked] rather than off a class a script would have to maintain.
   ========================================================================== */

/* --- 17.1 ALERTS (W14) ---------------------------------------------------- */

.alr { display: grid; gap: var(--space-6); grid-template-columns: minmax(0, 1fr); align-items: start; }
/* pages/14 §5: the rule column is held to a readable measure rather than
   stretched across the tool's full 1320px. */
.alr:not(.alr--split) { max-width: 900px; }
.alr__col { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
.alr__aside { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }

/* The aside only exists for a signed-in visitor (nothing has ever fired for a
   guest), so the second column is opt-in — never a reserved empty gutter. */
@media (min-width: 921px) {
  .alr--split { grid-template-columns: minmax(0, 1fr) 340px; }
}

/* The counter belongs TO the heading ("Alerts - 5 alerts"), so it sits snug after
   it on the same baseline; only the primary action is pushed to the far edge. A
   space-between row would strand the counter in the middle of the gap. */
.alr__head { display: flex; align-items: baseline; justify-content: flex-start;
  gap: var(--space-2) var(--space-3); flex-wrap: wrap; }
.alr__head > .btn { margin-left: auto; align-self: center; }
.alr__lede { margin-top: calc(var(--space-2) * -1); }

.alr__banner { flex-wrap: wrap; }
.alr__banner-txt { flex: 1 1 22ch; }

/* -- rule rows ------------------------------------------------------------- */

.alr__rules { display: flex; flex-direction: column; }

.alr__rule { display: flex; align-items: center; gap: 11px; padding: 13px 15px;
  border-top: 1px solid var(--dv); }
.alr__rule:first-child { border-top: 0; }

.alr__rule-ic { width: 32px; height: 32px; border-radius: var(--r-pill); flex: none;
  display: flex; align-items: center; justify-content: center;
  background: var(--dv); color: var(--ink3); }

/* Colour follows the event, but the row always spells the event out as well. */
[data-ev="high"] .alr__rule-ic,
[data-ev="high"] .alr__fired-ic { background: var(--tint); color: var(--grnd); }
[data-ev="low"] .alr__rule-ic,
[data-ev="low"] .alr__fired-ic { background: var(--redt); color: var(--red); }
[data-ev="sun"] .alr__rule-ic,
[data-ev="sun"] .alr__fired-ic { background: var(--goldt); color: var(--sun); }
[data-ev="moon"] .alr__rule-ic,
[data-ev="moon"] .alr__fired-ic { background: var(--dv); color: var(--ink2); }

.alr__rule-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.alr__rule-name { font: 700 var(--fs-sub)/1.3 var(--font-ui); color: var(--ink); }
.alr__rule-sub { font: 400 var(--fs-data)/1.4 var(--font-ui); color: var(--ink2); }

.alr__toggle { flex: none; display: flex; }

/* The switch. 42×25 face, ≥24px hit area, knob driven by [aria-checked] so the
   server-rendered state and the fetch-flipped state are the same one. */
.alr__switch { width: 42px; height: 25px; padding: 2px; flex: none; cursor: pointer;
  border: 0; border-radius: var(--r-pill); background: var(--dv);
  display: flex; align-items: center; justify-content: flex-start;
  transition: background var(--dur-fast) var(--ease); }
.alr__switch[aria-checked="true"] { background: var(--grnd); justify-content: flex-end; }
.alr__knob { width: 21px; height: 21px; border-radius: var(--r-pill); background: var(--card);
  box-shadow: 0 1px 2px rgba(var(--shadow-rgb), .35); }
.alr__toggle[data-busy] { opacity: .55; }

/* -- the builder ----------------------------------------------------------- */

.alr__new-sum { display: flex; align-items: center; gap: 7px; cursor: pointer;
  font: 700 var(--fs-ui-lg)/1.3 var(--font-ui); color: var(--grnd); list-style: none; }
.alr__new-sum::-webkit-details-marker { display: none; }
.alr__new[open] .alr__new-sum { margin-bottom: var(--space-4);
  padding-bottom: var(--space-3); border-bottom: 1px solid var(--dv); }

.alr__form { display: flex; flex-direction: column; gap: var(--space-4); align-items: stretch; }
/* The submit sits at its own width — a stretched primary button in a column
   flex form reads as a banner, not a control. */
.alr__form > .btn { align-self: flex-start; }
.alr__err { margin: 0; }

.alr__field { border: 0; padding: 0; margin: 0; min-width: 0; }
.alr__field legend { padding: 0; margin-bottom: var(--space-2); }
.alr__field .copy { margin-top: var(--space-2); }

.alr__chips { display: flex; flex-wrap: wrap; gap: 7px; }
.alr__chip { display: inline-flex; }
.alr__chip-face { display: inline-flex; align-items: center; min-height: 32px;
  padding: 6px 13px; border: 1px solid var(--bd); border-radius: var(--r-md);
  background: var(--card); color: var(--ink2);
  font: 700 var(--fs-ui)/1.3 var(--font-ui); cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease); }

.alr__chip input:checked + .alr__chip-face { border-color: var(--grn); background: var(--tint); color: var(--grnd); }
.alr__chip input:focus-visible + .alr__chip-face { outline: 2px solid var(--grn); outline-offset: 2px; }

.alr__lead { display: flex; align-items: center; gap: 8px; margin: 0;
  padding: 11px 14px; border-radius: var(--r-md); background: var(--bg);
  font: 700 var(--fs-ui-lg)/1.3 var(--font-ui); color: var(--ink2); }

/* -- delivery channels ----------------------------------------------------- */

.alr__channels { display: flex; flex-wrap: wrap; gap: 9px; padding: 0 15px 14px; }
.alr__channel { flex: 1 1 190px; display: flex; flex-direction: column; gap: 2px;
  padding: 10px 12px; border: 1px solid var(--bd); border-radius: var(--r-md); }
.alr__channel[data-on="true"] { border-color: var(--grn); background: var(--tint); }
.alr__channel[data-on="true"] .alr__channel-name,
.alr__channel[data-on="true"] .alr__channel-sub { color: var(--grnd); }
.alr__channel-name { font: 700 var(--fs-ui)/1.3 var(--font-ui); color: var(--ink); }
.alr__channel-sub { font: 400 var(--fs-data)/1.4 var(--font-ui); color: var(--ink2); }

/* -- recently fired -------------------------------------------------------- */

.alr__fired { display: flex; flex-direction: column; gap: var(--space-3); }
.alr__fired-row { display: flex; gap: 9px; align-items: flex-start;
  padding-top: var(--space-3); border-top: 1px solid var(--dv); }
.alr__fired-row:first-of-type { padding-top: 0; border-top: 0; }
.alr__fired-ic { width: 22px; height: 22px; border-radius: var(--r-pill); flex: none;
  display: flex; align-items: center; justify-content: center;
  background: var(--dv); color: var(--ink3); }
.alr__fired-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.alr__fired-when { font-size: var(--fs-data); font-weight: 600; color: var(--ink); }
.alr__fired-what { font: 400 var(--fs-data)/1.4 var(--font-ui); color: var(--ink2); }

/* -- the cross-station hub ------------------------------------------------- */

.alr-hub { display: flex; flex-direction: column; gap: var(--space-4); max-width: 900px; }
.alr-hub__count { font-size: var(--fs-ui); font-weight: 600; color: var(--ink2); }

.alr-hub__group { display: flex; flex-direction: column; }
.alr-hub__station { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.alr-hub__station-name { font: 700 var(--fs-sub)/1.3 var(--font-ui); color: var(--ink); }
.alr-hub__station-region { font: 400 var(--fs-data)/1.3 var(--font-ui); color: var(--ink2); }

.alr-hub__rule { display: flex; align-items: baseline; gap: var(--space-3);
  padding: 11px 16px; border-top: 1px solid var(--dv); flex-wrap: wrap; }
.alr-hub__rule-name { font: 700 var(--fs-ui-lg)/1.3 var(--font-ui); color: var(--ink); }
.alr-hub__rule-sub { flex: 1 1 16ch; font: 400 var(--fs-data)/1.4 var(--font-ui); color: var(--ink2); }
.alr-hub__rule-state { font: 700 var(--fs-meta)/1.3 var(--font-ui); letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink3); }
.alr-hub__rule-state[data-on="true"] { color: var(--grnd); }

.alr-hub__foot { padding: 10px 12px; border-top: 1px solid var(--dv); }

/* =============================================================================
   17.2 FAVORITES (W13)
   ========================================================================== */

.fav { display: flex; flex-direction: column; gap: var(--space-4); }
.fav__head { display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3); flex-wrap: wrap; }
.fav__count { font-size: var(--fs-ui); font-weight: 600; color: var(--ink2); }

.fav__grid { display: grid; gap: var(--space-4); grid-template-columns: minmax(0, 1fr); }

@media (min-width: 921px) {
  .fav__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.fav__card { display: flex; flex-direction: column; gap: var(--space-3); }
.fav__card-top { display: flex; align-items: flex-start; gap: 10px; }

/* Gold, and OUTLINED: #icStar is drawn `fill="none" stroke="currentColor"`, so
   a filled star would need a second sprite glyph (the sprite is shared). Gold
   against the tool's greens still reads as "saved", and the button's
   aria-pressed + label carry the state for anyone not seeing colour. */
.fav__star { flex: none; width: 28px; height: 28px; padding: 0; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  border: 0; background: none; color: var(--gold); }
[data-busy] .fav__star { opacity: .5; }

.fav__card-id { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.fav__name { font: 700 var(--fs-body)/1.25 var(--font-ui); color: var(--ink); }
.fav__region { font: 400 var(--fs-data)/1.35 var(--font-ui); color: var(--ink2); }

/* Today-so-far tide curve, one glance per card. Only drawn when a cached row's
   series is at hand (AppController::sparklineValues) — no row, no placeholder. */
.fav__spark { display: block; width: 100%; height: 32px; color: var(--grn); }

.fav__next { display: flex; align-items: center; gap: 6px; margin: 0; flex-wrap: wrap;
  font: 700 var(--fs-ui)/1.4 var(--font-ui); }
.fav__next[data-state="rising"] { color: var(--grnd); }
.fav__next[data-state="falling"] { color: var(--red); }
.fav__next-txt { font-weight: 400; color: var(--ink2); }

.fav__actions { display: flex; gap: var(--space-2); margin-top: auto; }

/* The add tile matches a card's geometry so the grid keeps its rhythm. */
.fav__add { display: flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 104px; padding: var(--space-4);
  border: 1px dashed var(--bd); border-radius: var(--r-lg);
  font: 700 var(--fs-sub)/1.3 var(--font-ui); color: var(--grnd); }
.fav__add:hover { border-color: var(--grn); background: var(--tint); }

@media (max-width: 639px) {
  .alr__channels { padding: 0 var(--space-3) var(--space-3); }
  .alr__channel { flex-basis: 100%; }
  .alr__rule { padding: 12px var(--space-3); }
  .alr-hub__rule { padding: 11px var(--space-3); }
}

/* =============================================================================
   18. EDITORIAL — guide article (W23) + legal rail (W24)
   resources/views/public/{guide,static,contact}.blade.php

   Appended as ONE block so it lifts out whole. Two boards, one idea: long-form
   copy in a measured column with navigation beside it rather than stacked on
   top of it. The horizontal .anchor-rail these pages used to wear stays where
   it belongs — in-page anchors on the station landing (§5.9).
   ========================================================================== */

/* --- 18.1 headline sizes --------------------------------------------------
   .h1 tops out at 62px, which is right over a full-bleed hero and far too big
   over a 72ch reading column. The boards set 44px for an article and 38px for
   a legal page; these clamp to exactly that. */

.h1--article { font-size: clamp(30px, 1rem + 2.4vw, 44px); line-height: 1.05; }
.h1--legal { font-size: clamp(28px, 1rem + 1.6vw, 38px); line-height: 1.05; }

/* The site breadcrumb was born on the country/region pages, which open straight
   onto it with no .page wrapper; inside one, its own top step doubles up. */
.geo-crumbs--flush { margin-top: 0; }

/* --- 18.2 article head ----------------------------------------------------- */

/* Pill, headline, stamp, lede — the board's order; .page__head's own step is
   already the board's, so the head needs no rule of its own. */
.article__stamp { font-size: var(--fs-data); color: var(--ink3); }

/* The category label over an article headline. Its own variant, not a borrowed
   status pill: the neutral .pill paints no ground, and at this size a bare word
   over a 44px headline reads as a stray caption rather than a label. */
.pill--cat { background: var(--tint); color: var(--grnd); }
.article__stamp--caps { text-transform: uppercase; letter-spacing: .06em; }

/* --- 18.3 table of contents ------------------------------------------------
   Board W23's right column. The active row is marked by a green rule down its
   left edge AND by weight — colour is never the only signal. tide-core.js's
   scrollspy sets aria-current/.is-current; the transparent border on every row
   is what stops the active one nudging its neighbours sideways. */

.toc { display: flex; flex-direction: column; gap: var(--space-1); }
.toc .eb { margin-bottom: var(--space-1); }

.toc__link {
  padding: 3px 0 3px 11px;
  border-left: 2px solid transparent;
  font: 500 var(--fs-ui)/1.4 var(--font-ui);
  color: var(--ink2);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}

.toc__link:hover { color: var(--grnd); text-decoration: none; }

.toc__link.is-current,
.toc__link[aria-current="true"] { border-left-color: var(--grnd); color: var(--grnd); font-weight: 700; }

/* --- 18.4 link rows -------------------------------------------------------
   "Related guides": a hairline-separated list where the whole row is the link,
   so the target is the row and not four words of it. */

.link-rows { display: flex; flex-direction: column; }

.link-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 7px 0;
  border-top: 1px solid var(--dv);
  text-decoration: none;
}

.link-rows > .link-row:first-child { border-top: 0; padding-top: 0; }

.link-row__label { flex: 1 1 auto; font: 500 var(--fs-ui)/1.35 var(--font-ui); color: var(--grnd); }
.link-row__chev { flex: none; color: var(--ink3); }
.link-row:hover .link-row__label { text-decoration: underline; text-underline-offset: 3px; }

/* --- 18.5 the aside -------------------------------------------------------- */

@media (min-width: 921px) {
  /* Short enough to sit still while the article scrolls past it; `align-items:
     start` on .split is what makes sticky possible at all. */
  .article-aside { position: sticky; top: calc(var(--header-h) + var(--space-4)); }
}

/* --- 18.6 legal rail (W24) ------------------------------------------------
   A vertical list of the legal set beside the content, not a pill row above it.
   Narrow screens have no column to spare, so it folds into a wrapping row —
   still the same rows, just laid out sideways. */

.side-rail__inner { display: flex; flex-wrap: wrap; gap: var(--space-1); align-items: center; }

.side-rail__head { flex: 1 0 100%; margin-bottom: var(--space-1); }

.side-rail__link {
  display: flex;
  align-items: center;
  padding: 8px 10px;
  border-radius: 9px;
  font: 600 var(--fs-ui-lg)/1.25 var(--font-ui);
  color: var(--ink2);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}

.side-rail__link:hover { background: var(--tint); color: var(--grnd); text-decoration: none; }

.side-rail__link[aria-current="page"] { background: var(--tint); color: var(--grnd); font-weight: 700; }

@media (min-width: 921px) {
  /* The nav stretches the grid row so the divider runs the full height of the
     page, the way the board draws it; the inner block is what sticks. */
  .side-rail {
    align-self: stretch;
    padding-right: var(--space-4);
    border-right: 1px solid var(--bd);
  }

  .side-rail__inner {
    position: sticky;
    top: calc(var(--header-h) + var(--space-4));
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
  }

  .side-rail__head { padding: 0 10px; }
}

/* --- 18.7 the three-track split -------------------------------------------
   .split's body + aside, with the legal rail added as a leading track. The
   aside only earns its own column once all three fit; between the breakpoints
   it drops under the content it annotates rather than squeezing it. */

@media (min-width: 921px) {
  .split--rail { grid-template-columns: 200px minmax(0, 1fr); }
  .split--rail > aside { grid-column: 2; }
}

@media (min-width: 1180px) {
  .split--rail { grid-template-columns: 210px minmax(0, 1fr) 300px; }
  .split--rail > aside { grid-column: 3; }
}

/* --- 18b guide article body vocabulary ------------------------------------
   `.prose` styles nothing but the measure on purpose (the reset strips list
   markers and margins), so an article's lists, pull quotes, figures and the
   coefficient band table carry explicit classes. Everything here is written
   by a guide BODY view — resources/views/public/guides/{slug}.blade.php and
   its figure partials — and by nothing else. */

.list { display: flex; flex-direction: column; gap: var(--space-2); padding-left: 1.3em; }
.list > li { list-style: disc; font: 400 var(--fs-body)/1.6 var(--font-ui); color: var(--ink2); }
.list--num > li { list-style: decimal; }
.list > li::marker { color: var(--grnd); }

/* A pull quote is a left accent rule and a heavier line, never a box: a
   bordered card mid-article reads as a different kind of content. */
.pull {
  border-left: 3px solid var(--grnd);
  padding: 2px 0 2px var(--space-4);
  margin: var(--space-2) 0;
  font: 500 var(--fs-lede)/1.55 var(--font-ui);
  color: var(--ink);
  max-width: 60ch;
  text-wrap: pretty;
}

/* A figure is an inline SVG that plots something true, plus its caption. No
   raster, no photographs — design system §42. */
.fig { display: flex; flex-direction: column; gap: var(--space-2); margin: var(--space-2) 0; }
.fig__svg { display: block; width: 100%; height: auto; }
.fig__cap { font: 400 var(--fs-data)/1.5 var(--font-ui); color: var(--ink3); max-width: 70ch; }
.fig--panels .fig__panels { display: grid; gap: var(--space-4); }

@media (min-width: 640px) {
  .fig--panels .fig__panels { grid-template-columns: 1fr 1fr; }
}

/* Labels sit ON the drawing, so they carry a background-coloured halo rather
   than a plate: `paint-order: stroke` paints the stroke first and the glyph
   over it, which keeps the letterforms intact. */
.fig__label {
  font: 700 var(--fs-ui) var(--font-dsp);
  fill: var(--ink);
  paint-order: stroke;
  stroke: var(--bg);
  stroke-width: 3px;
  stroke-linejoin: round;
}

.fig__label--muted { fill: var(--ink3); }
.fig__mono { font: 500 var(--fs-data) var(--font-mn); fill: var(--ink2); }

/* SVG type scales with its viewBox, so the same declaration that reads at
   12.5px in a 560px-wide figure reads at 8px when the column is a 358px phone.
   That is under --fs-micro (9.5px), the smallest type the system allows, and
   no `px` value can fix it because the px here are USER units. Raising the
   user-unit size is the lever: the drawing keeps its proportions and the
   glyphs come back over the floor (≈10.2px and ≈9.6px at 358px). */
@media (max-width: 640px) {
  .fig__label { font-size: 16px; }
  .fig__mono { font-size: 15px; }

  /* .fig--panels figures (e.g. syzygy.blade.php) use a 280-unit viewBox, not
     560 — at a 358px stacked column that is already scale 1.28, so the bump
     above would render labels at ~20.5px, bigger than body text. Their own
     base sizes clear the floor at this scale without help, so panels are
     excluded from the bump; the panel is also capped so a wide phone/tablet
     column cannot blow a 280-unit box up past ~1.36x its drawn proportions. */
  .fig--panels .fig__label { font-size: 12.5px; }
  .fig--panels .fig__mono { font-size: 11px; }
  .fig--panels .fig__panels > * { max-width: 380px; margin-inline: auto; width: 100%; }
}

/* The coefficient bands, board W23's one drawn figure: swatch, name, range and
   what it buys you, one hairline-separated row each. */
.bands { display: flex; flex-direction: column; }
.bands__row { display: flex; align-items: center; gap: 11px; padding: 7px 0; border-top: 1px solid var(--dv); }
.bands__row:first-child { border-top: 0; padding-top: 0; }
.bands__swatch { flex: none; width: 34px; height: 14px; border-radius: 4px; }
.bands__name { width: 66px; font: 700 var(--fs-ui)/1.3 var(--font-ui); color: var(--ink); }
.bands__range { width: 60px; font: 500 var(--fs-data) var(--font-mn); color: var(--ink2); }
.bands__what { flex: 1; font: 400 var(--fs-ui)/1.5 var(--font-ui); color: var(--ink2); }

/* ==========================================================================
   19. EMBED — hub (/embed) and the per-station configurator (W22)
   ==========================================================================
   Only the two OUR-SITE pages live here. The widget document itself carries
   its own ~7 KB stylesheet inlined (resources/views/embed/widget-css.blade.php)
   and never loads this file: it promises to be under 20 KB, and this one is
   215 KB. That is the single sanctioned exception to "all CSS lives here".
   -------------------------------------------------------------------------- */

/* --- 19.1 hub ------------------------------------------------------------- */

.embed-hero { display: grid; gap: var(--space-6); align-items: start; }

@media (min-width: 861px) {
  .embed-hero { grid-template-columns: minmax(0, 380px) minmax(0, 1fr); gap: var(--space-8); }
}

.embed-hero__demo { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); }

/* The demo sits straight on the page, and the widget's own attribution bar is
   painted in --bg — the same colour — so without a border the frame bleeds into
   the page and the bar reads as page furniture. */
.embed-hero__demo iframe {
  border-radius: var(--r-md); background: var(--card);
  box-shadow: 0 1px 3px rgba(var(--shadow-rgb), .10);
}

/* A stack stretches its children; the call to action is a button, not a bar. */
.embed-hero__copy .btn { align-self: flex-start; }

.embed-popular { display: flex; flex-wrap: wrap; gap: var(--space-2); }

.embed-popular__link {
  border: 1px solid var(--bd); border-radius: 99px;
  padding: 5px 12px; background: var(--card);
  font: 500 var(--fs-ui)/1 var(--font-ui); color: var(--ink2); text-decoration: none;
}
.embed-popular__link:hover { border-color: var(--grn); color: var(--grnd); }

.embed-steps {
  margin: 0; padding-left: 1.2em;
  display: flex; flex-direction: column; gap: var(--space-2);
  font: 400 var(--fs-ui-lg)/1.6 var(--font-ui); color: var(--ink2);
}
.embed-steps strong { color: var(--ink); }

/* --- 19.2 configurator layout --------------------------------------------- */

.embed-layout { display: grid; gap: var(--space-6); align-items: start; }

@media (min-width: 921px) {
  .embed-layout { grid-template-columns: 330px minmax(0, 1fr); gap: var(--space-8); }
  /* The controls follow the preview down a long page rather than stranding it. */
  .embed-controls { position: sticky; top: calc(var(--header-h, 60px) + var(--space-4)); }
}

.embed-preview-col { display: flex; flex-direction: column; gap: var(--space-3); }

/* The preview is a REAL widget in a real frame, so it gets a neutral bed
   rather than a card: a card border around a bordered widget reads as two. */
.embed-preview {
  display: flex; justify-content: center;
  padding: var(--space-5); border-radius: var(--r-lg);
  background: var(--dv);
}
.embed-preview iframe { border-radius: var(--r-md); background: var(--card); box-shadow: 0 1px 3px rgba(var(--shadow-rgb), .10); }

/* --- 19.3 configurator controls ------------------------------------------- */

.embed-field { border: 0; margin: 0; padding: 0; }
.embed-field + .embed-field { margin-top: var(--space-5); }
.embed-field > legend { padding: 0; margin-bottom: var(--space-2); }
.embed-field__note { margin-top: var(--space-2); }

.embed-opts { display: flex; flex-wrap: wrap; gap: 6px; }
.embed-opts--tight { margin-top: 6px; }

/* Real radios, styled as pills: the form has to work with JavaScript off, so
   the segmented-button component (which renders <button>s) cannot serve here. */
.embed-opt { position: relative; display: inline-flex; cursor: pointer; }
.embed-opt input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  margin: 0; opacity: 0; cursor: pointer;
}
.embed-opt > span {
  border: 1px solid var(--bd); border-radius: 99px;
  padding: 5px 12px; background: var(--card);
  font: 500 var(--fs-ui)/1 var(--font-ui); color: var(--ink2);
}
.embed-opt input:checked + span { background: var(--grnd); border-color: var(--grnd); color: #fff; }
.embed-opt input:focus-visible + span { outline: 2px solid var(--grn); outline-offset: 2px; }

.embed-checks { display: flex; flex-direction: column; gap: var(--space-2); }

.embed-check {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  font: 500 var(--fs-ui)/1.3 var(--font-ui); cursor: pointer;
}
.embed-check input { position: absolute; opacity: 0; width: 0; height: 0; }

.embed-check__switch {
  flex: none; position: relative; width: 38px; height: 22px; border-radius: 99px;
  background: var(--dv); border: 1px solid var(--bd); transition: background .12s ease;
}
.embed-check__switch::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 16px; height: 16px; border-radius: 99px; background: var(--card);
  box-shadow: 0 1px 2px rgba(var(--shadow-rgb), .25);
  transition: transform .12s ease;
}
.embed-check input:checked + .embed-check__switch { background: var(--grnd); border-color: var(--grnd); }
.embed-check input:checked + .embed-check__switch::after { transform: translateX(16px); }
.embed-check input:focus-visible + .embed-check__switch { outline: 2px solid var(--grn); outline-offset: 2px; }

/* Shown only while the enhancement is NOT running — tide-embed.js adds the
   modifier once it has taken over, and removes it again if a fetch fails. */
.embed-controls__go { margin-top: var(--space-5); }
.embed-controls--live .embed-controls__go { display: none; }

/* --- 19.4 the snippet ----------------------------------------------------- */

.embed-snippet__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); }
.embed-snippet__dims { font-size: var(--fs-meta); color: var(--ink3); }

.embed-snippet {
  margin: var(--space-2) 0 var(--space-3);
  padding: 11px; border: 1px solid var(--bd); border-radius: var(--r-md);
  background: var(--bg); color: var(--ink2);
  font-size: var(--fs-meta); line-height: 1.7;
  white-space: pre-wrap; word-break: break-all;
  max-height: 190px; overflow: auto;
}

@media print {
  /* The snippet is code someone might reasonably want on paper, and a scroller
     leaves whatever is past its edge off the sheet. */
  .embed-snippet { max-height: none; overflow: visible; }
}
