/* ════════════════════════════════════════════════════════════════════════
   WHERE-PORT — the Schengen Forecaster's face (ticket 05)

   BRAND: the Family Calendar skin (brand-registry.md:28, formalised
   2026-08-04, `.fc-` prefix, tokens in
   projects/studio-tool-kit/brand-foundation/calendar.tokens.json).
   This is a family-department app in that lineage, so the token NAMES are
   kept verbatim (`--fc-*`) and the values are mirrored by hand below — the
   same no-build-step mirror the sibling app uses. Nothing here is `.bf-*`
   or `.ds-*`; two skins never meet in one artefact.

   ISOLATION (RAID R2): this file is a COPY, not a link. Nothing in the app
   loads a stylesheet, script or asset from the sibling calendar project at
   runtime. The paths below are provenance for a human reader only.

   DESIGN PROVENANCE — every block cites where it came from:
     · family-department/projects/family-calendar/css/calendar.css
         :40–:230   the token block (colour, radius, shadow)
         :265–:298  the type scale
         :548–:567  .fc-eu-chip — the header chip
         :1358–:1450 .fc-leg — the card/row component
     · family-department/projects/family-calendar/css/where.css
         :129–:133  the big-numeral type tokens
         :653–:804  SECTION 4 — the Schengen half's visual language
     · family-department/projects/family-calendar/design-export-2026-08-03/
       'Family Calendar Views.dc.html'
         :446–:479  the rolling-window timeline + its legend
         :657       "LIVE TRIPS — STILL COUNTING"
         :666       the roll-off copy
         :1005–:1011 the window band, today marker and block fills
     · family-department/projects/family-calendar/index.html:126
         the header chip's markup contract

   FOUR TOKENS ADDED beyond calendar.tokens.json, each lifted from a design
   source rather than invented — they exist because the sibling app renders
   an UNAVAILABLE Schengen state and therefore never needed a live status
   palette. Named in the build report so the token file can absorb them:
     --fc-status-ok-ground     #E6F7EF   Views.dc.html statusStyle, ok branch
     --fc-status-alert-ground  #FFEBF0   Views.dc.html statusStyle, not-ok
     --fc-tentative-ground     #FFF3DB   Views.dc.html tentative chip ground
     --fc-warn-ink             #B77400   Views.dc.html tentative chip ink

   ACCESSIBILITY SUBSTITUTIONS are named at each site, in the house habit of
   the source files ("design value X (2.54:1). Substituted for AA."). Every
   ratio quoted below was computed, not estimated.

   REGISTER: clarity-first. This is a legal-cap instrument, so the motion
   budget is state + focus transitions only — no reveal, no scroll effect,
   nothing that moves on load.
   ════════════════════════════════════════════════════════════════════════ */

/* ════════════════════════════════════════════════════════════════════════
   1 — TOKENS (mirrored from the Family Calendar skin)
   ════════════════════════════════════════════════════════════════════════ */
:root {
  /* type — calendar.css:265–:298 */
  --fc-font: 'Plus Jakarta Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --fc-size-screen-title: 26px;
  --fc-size-section:      19px;
  --fc-size-body:         12.5px;
  --fc-size-qualifier:    12px;
  --fc-size-pill:         11.5px;
  --fc-size-meta:         11px;
  --fc-size-chip:         10.5px;
  --fc-size-chip-line2:   9.5px;
  --fc-size-badge:        9px;
  --fc-m-size-eu:         13px;   /* BUILD-ANSWERS C8: the chip figure */
  --fc-weight-regular:  500;
  --fc-weight-medium:   600;
  --fc-weight-semibold: 700;
  --fc-weight-bold:     800;
  --fc-track-title: -0.03em;
  --fc-track-tight: -0.02em;
  --fc-track-caps:   0.1em;
  --fc-leading-body: 1.6;
  --fc-leading-flat: 1;

  /* the big numeral — where.css:129–:133 */
  --fc-w-size-count:    46px;
  --fc-w-size-of:       13px;
  --fc-w-size-caps:     10px;
  --fc-w-track-count:   -0.04em;
  --fc-w-leading-count: 0.9;   /* FONTS §3: deliberate — do not normalise */

  /* surfaces + hairlines — calendar.css:114–:162 */
  --fc-surface-page:  #F1F4FA;
  --fc-surface-card:  #FFFFFF;
  --fc-surface-rail:  #F7F9FD;
  --fc-hairline-card: #EEF1F7;
  --fc-hairline-row:  #F0F2F7;
  --fc-hairline-leg:  #F2F4F9;
  --fc-cell-vertical: #F3F5F9;
  --fc-neutral-chip:  #F1F4FA;
  --fc-weekend-chip:  #E7EDF9;
  --fc-skeleton-bar:  #E9EDF4;

  /* text — calendar.css:104–:113 */
  --fc-text-ink:       #1A2233;
  --fc-text-secondary: #5C6679;
  --fc-text-on-fill:   #FFFFFF;

  /* Schengen green — reserved for the count, never for a trip */
  --fc-eu-ink:     #0E7A55;
  --fc-eu-fill:    #10B981;
  --fc-eu-fill-2:  #34D399;
  --fc-eu-track:   #D9EEE4;
  --fc-eu-chip:    #ECF9F2;
  --fc-eu-micro:   #7FB79E;
  --fc-eu-plan-a:  #93C5FD;
  --fc-eu-plan-b:  #DCE9FD;

  /* today / plan blue */
  --fc-today:         #4F7DF3;
  --fc-today-numeral: #2F5BD0;

  /* rolled-off — Views.dc.html:1010 fill + :479 legend, ink from the same map */
  --fc-eu-rolled:     #CFE0F6;
  --fc-eu-rolled-ink: #41608F;

  /* status escalation */
  --fc-warn-ink:            #B77400;
  --fc-tentative-ground:    #FFF3DB;
  --fc-status-ok-ground:    #E6F7EF;
  --fc-status-alert-ground: #FFEBF0;
  --fc-breach:              #E5123F;
  --fc-breach-figure:       #D3123C;
  --fc-breach-ground:       #FFF0F3;

  /* geometry — calendar.css:209–:229 */
  --fc-shadow-card:   0 18px 44px rgba(38, 50, 84, 0.09);
  --fc-radius-card:   22px;
  --fc-radius-panel:  18px;
  --fc-radius-pill:   11px;
  --fc-radius-row:    10px;
  --fc-radius-chip:   7px;
  --fc-radius-badge:  5px;

  /* targets — calendar.css:261: 44 is the rule, 24 the fine-pointer exception */
  --fc-tap: 44px;

  /* motion — state + focus only */
  --fc-duration-state: 120ms;
  --fc-ease-standard:  cubic-bezier(0.2, 0, 0.38, 0.9);
  --fc-hover-lift:     -1px;

  /* component-scoped proportions (calendar.css:1358 keeps these off :root
     in the source too — they are proportions, not brand values) */
  --sf-tile: 22px;
  --sf-gap:  14px;
}

/* ════════════════════════════════════════════════════════════════════════
   2 — CHASSIS
   ════════════════════════════════════════════════════════════════════════ */
*, *::before, *::after { box-sizing: border-box; }

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

body {
  margin: 0;
  /* dvh, never vh — a mobile URL bar makes vh taller than the viewport */
  min-height: 100dvh;
  padding: 22px 16px 56px;
  background: var(--fc-surface-page);
  color: var(--fc-text-ink);
  font-family: var(--fc-font);
  font-size: var(--fc-size-body);
  line-height: var(--fc-leading-body);
}

.sf-shell {
  max-width: 1080px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--sf-gap);
}

/* the one vertical rhythm on the page */
.sf-stack { display: flex; flex-direction: column; gap: var(--sf-gap); }

/* `hidden` must win over any display a component class sets, or the error
   and boot states leak onto the page before there is anything to show */
[hidden] { display: none !important; }

/* every figure on this page is a number that must line up column-to-column */
.sf-num { font-variant-numeric: tabular-nums; }

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

/* One visible focus treatment for the whole page (AA 2.4.7). */
:where(a, button, input, select, [tabindex]):focus-visible {
  outline: 2px solid var(--fc-today);
  outline-offset: 2px;
  border-radius: var(--fc-radius-badge);
}

.sf-caps {
  display: block;
  /* --fc-text-secondary, not --fc-text-muted: where.css:690 measured muted
     at 2.78:1 on this ground and substituted for the same reason. */
  font: var(--fc-weight-bold) var(--fc-w-size-caps) / var(--fc-leading-flat) var(--fc-font);
  letter-spacing: var(--fc-track-caps);
  color: var(--fc-text-secondary);
  text-transform: uppercase;
}

/* ── section heading — where.css:661–:671 ─────────────────────────────── */
.sf-head { display: flex; align-items: baseline; gap: 12px; margin: 0 0 2px; }
.sf-head__title {
  margin: 0;
  font: var(--fc-weight-bold) var(--fc-size-section) / var(--fc-leading-flat) var(--fc-font);
  letter-spacing: var(--fc-track-tight);
  color: var(--fc-text-ink);
}
.sf-head__sub {
  font: var(--fc-weight-medium) var(--fc-size-qualifier) / var(--fc-leading-flat) var(--fc-font);
  /* design value --fc-text-muted-3 #9AA3B4 (2.6:1 here). Substituted for AA. */
  color: var(--fc-text-secondary);
}
.sf-head__rule { flex: 1 1 auto; height: 1px; background: var(--fc-hairline-card); }

.sf-card {
  background: var(--fc-surface-card);
  border-radius: var(--fc-radius-card);
  box-shadow: var(--fc-shadow-card);
  padding: 22px 24px 24px;
}

/* ════════════════════════════════════════════════════════════════════════
   3 — HEADER + THE EU CHIP
   markup contract: family-calendar/index.html:126 · css calendar.css:548
   ════════════════════════════════════════════════════════════════════════ */
.sf-topbar {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 0 4px;
}
.sf-topbar__title {
  margin: 0;
  font: var(--fc-weight-bold) var(--fc-size-screen-title) / var(--fc-leading-flat) var(--fc-font);
  letter-spacing: var(--fc-track-title);
}
.sf-topbar__sub {
  font: var(--fc-weight-medium) var(--fc-size-qualifier) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-secondary);
}

.sf-eu-chip {
  margin-left: auto;
  display: inline-flex;
  align-items: baseline;
  /* gap:0 — the source sets "EU {n}" as ONE run with only "/90" recoloured */
  gap: 0;
  padding: 7px 13px;
  border-radius: var(--fc-radius-pill);
  background: var(--sf-state-chip-ground, var(--fc-eu-chip));
}
.sf-eu-chip__label,
.sf-eu-chip__figure,
.sf-eu-chip__of,
.sf-eu-chip__state {
  font-size: var(--fc-m-size-eu);
  font-weight: var(--fc-weight-bold);
  line-height: var(--fc-leading-flat);
}
.sf-eu-chip__label { color: var(--fc-text-secondary); }
/* calendar.css:566 greys this figure BECAUSE it is an absent em-dash
   ("the em-dash of an ABSENT figure is grey, never the green of a real
   reading"). Here the figure is real, so it takes the state ink — which is
   what that comment says a real count would do. eu-ink on eu-chip = 4.93:1. */
.sf-eu-chip__figure { color: var(--sf-state-text); margin-left: 6px; }
.sf-eu-chip__of {
  /* design value --fc-text-dow #AEB6C4 (1.9:1). Substituted for AA. */
  color: var(--fc-text-secondary);
  margin-left: 2px;
}
.sf-eu-chip__state {
  margin-left: 9px;
  padding-left: 9px;
  border-left: 1px solid var(--fc-hairline-card);
  color: var(--sf-state-text);
  font-size: var(--fc-size-chip);
}

/* ════════════════════════════════════════════════════════════════════════
   4 — STATE PALETTE
   Four states from the engine (schengen.js:288 stateFor + :332 STATE_META).
   Each carries an ICON and a WORD as well as a hue — spec 06 rule 6: never
   a hue alone, so the escalation survives greyscale and colour-blindness.
   ════════════════════════════════════════════════════════════════════════ */
.sf-state--ok {
  --sf-state-text:        var(--fc-eu-ink);          /* 5.06:1 on rail */
  --sf-state-spine:       var(--fc-eu-ink);
  --sf-state-ground:      var(--fc-status-ok-ground);
  --sf-state-chip-ground: var(--fc-eu-chip);
}
.sf-state--warn {
  /* No AA-passing amber exists anywhere in the skin: the design's own
     #E08A00 on #FFF7E8 measures 2.53:1 and #B77400 on #FFF3DB is 3.46:1.
     So the WORD takes ink (14.46:1) and the amber carries the signal as a
     non-text spine, where 3.46:1 clears the 3:1 bar of AA 1.4.11.
     The 46px numeral keeps the amber directly — 3.61:1, over the 3:1
     large-text bar. */
  --sf-state-text:        var(--fc-text-ink);
  --sf-state-numeral:     var(--fc-warn-ink);
  --sf-state-spine:       var(--fc-warn-ink);
  --sf-state-ground:      var(--fc-tentative-ground);
  --sf-state-chip-ground: var(--fc-tentative-ground);
}
.sf-state--alert {
  /* alert (85–90) is still legal, breach (>90) is not, so they must not look
     the same. Red ink on the AMBER ground separates it from breach's red-on-
     red without inventing a fifth hue. 5.03:1. */
  --sf-state-text:        var(--fc-breach-figure);
  --sf-state-spine:       var(--fc-breach-figure);
  --sf-state-ground:      var(--fc-tentative-ground);
  --sf-state-chip-ground: var(--fc-tentative-ground);
}
.sf-state--breach {
  --sf-state-text:        var(--fc-breach-figure);   /* 5.08:1 on rail */
  --sf-state-spine:       var(--fc-breach);
  --sf-state-ground:      var(--fc-status-alert-ground);
  --sf-state-chip-ground: var(--fc-status-alert-ground);
}

.sf-status {
  align-self: flex-start;
  display: flex;                 /* flex, not inline-flex: a two-line verdict
                                    should indent under itself, not orphan */
  align-items: baseline;
  gap: 7px;
  line-height: 1.45;
  padding: 6px 12px 7px 9px;
  border-left: 3px solid var(--sf-state-spine);
  border-radius: var(--fc-radius-badge) var(--fc-radius-row) var(--fc-radius-row) var(--fc-radius-badge);
  background: var(--sf-state-ground);
  color: var(--sf-state-text);
  font: var(--fc-weight-semibold) var(--fc-size-pill) / var(--fc-leading-flat) var(--fc-font);
}
.sf-status__icon { font-weight: var(--fc-weight-bold); }

/* ════════════════════════════════════════════════════════════════════════
   5 — THE PICTURE: figure panel + breakdown
   where.css:672–:762 · Views.dc.html:446–:461
   ════════════════════════════════════════════════════════════════════════ */
.sf-picture {
  display: grid;
  grid-template-columns: minmax(280px, 320px) minmax(0, 1fr);
  gap: var(--sf-gap);
  align-items: start; /* was stretch — the Today card grew to the list beside it and left a dead band (visual pass 2026-09-07; the bug Oli pointed at) */
}

.sf-figure,
.sf-breakdown {
  padding: 18px 20px;
  border-radius: var(--fc-radius-panel);
  background: var(--fc-surface-rail);
}
/* Views.dc.html:447 — the figure panel is the one gradient on the page */
.sf-figure {
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: linear-gradient(140deg, var(--fc-eu-chip) 0%, var(--fc-surface-rail) 100%);
}

.sf-figure__count { display: flex; align-items: baseline; gap: 8px; margin: 10px 0 2px; }
.sf-figure__big {
  font: var(--fc-weight-bold) var(--fc-w-size-count) / var(--fc-w-leading-count) var(--fc-font);
  letter-spacing: var(--fc-w-track-count);
  font-variant-numeric: tabular-nums;
  color: var(--sf-state-numeral, var(--sf-state-text));
}
.sf-figure__of {
  font: var(--fc-weight-semibold) var(--fc-w-size-of) / var(--fc-leading-flat) var(--fc-font);
  /* design value --fc-text-muted #8A94A6 (2.9:1). Substituted for AA. */
  color: var(--fc-text-secondary);
}

/* the 90-day track — Views.dc.html:452, medFill + medPlan */
.sf-figure__track {
  position: relative;
  height: 9px;
  border-radius: 5px;
  background: var(--fc-eu-track);
  overflow: hidden;
}
.sf-figure__fill {
  position: absolute; inset: 0 auto 0 0;
  border-radius: 5px;
  background: linear-gradient(90deg, var(--fc-eu-fill), var(--fc-eu-fill-2));
}
/* a PLANNED day is hatched, never solid — the house convention (medPlan,
   Views.dc.html:454). Here it is the climb from today's count to the worst
   window the plan reaches, which is the only future figure that belongs on a
   90-day scale. */
.sf-figure__plan {
  position: absolute; top: 0; bottom: 0;
  background: repeating-linear-gradient(115deg,
    var(--fc-eu-plan-a) 0 4px, var(--fc-eu-plan-b) 4px 8px);
}
/* the peak itself, ticked. Full-height, not overhanging: the track clips to
   its own radius, so an overhang would simply be cut off. The white halo is
   horizontal and survives that clip, which is where it is needed — the tick
   has to read against both the solid fill and the hatch. */
.sf-figure__peak {
  position: absolute; top: 0; bottom: 0;
  width: 3px;
  margin-left: -1.5px;
  background: var(--fc-text-ink);
  box-shadow: 0 0 0 1.5px var(--fc-surface-card);
}
.sf-figure__line {
  font: var(--fc-weight-medium) var(--fc-size-qualifier) / 1.45 var(--fc-font);
  /* design value #3E4757; secondary is the nearest token and clears AA. */
  color: var(--fc-text-secondary);
  margin: 6px 0 0;
}
/* pinned to the foot of the panel (the design's `margin-top:auto`), with a
   hairline so the space above reads as a footer rule rather than a gap */
.sf-figure__rel {
  margin: auto 0 0;
  padding-top: 10px;
  border-top: 1px solid var(--fc-hairline-card);
  font: var(--fc-weight-medium) var(--fc-size-meta) / 1.4 var(--fc-font);
  color: var(--fc-text-secondary);
}

/* ── still counting / rolls off ───────────────────────────────────────── */
.sf-breakdown { display: flex; flex-direction: column; gap: 12px; }
.sf-breakdown__cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 4px 26px;
}
.sf-list { list-style: none; margin: 8px 0 0; padding: 0; }
.sf-list__item {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  border-top: 1px solid var(--fc-hairline-row);
}
.sf-list__item:first-child { border-top: 0; }
/* In the sibling app this chip is neutral because it shows a trip's own
   LENGTH and must not be misread as a Schengen count (where.css:735).
   Here it IS the engine's Schengen count for that trip, so the inversion is
   deliberate: green, because green means counted. 4.93:1. */
.sf-list__n {
  justify-self: start;
  min-width: 30px;
  padding: 3px 0;
  text-align: center;
  border-radius: var(--fc-radius-chip);
  background: var(--fc-eu-chip);
  color: var(--fc-eu-ink);
  font: var(--fc-weight-bold) var(--fc-size-pill) / var(--fc-leading-flat) var(--fc-font);
  font-variant-numeric: tabular-nums;
}
.sf-list__n--off {
  background: var(--fc-neutral-chip);
  color: var(--fc-text-secondary);
}
.sf-list__name {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font: var(--fc-weight-semibold) var(--fc-size-body) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-ink);
}
.sf-list__when {
  font: var(--fc-weight-medium) var(--fc-size-meta) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-secondary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.sf-note {
  margin: 0;
  font: var(--fc-weight-medium) var(--fc-size-meta) / 1.5 var(--fc-font);
  color: var(--fc-text-secondary);
  max-width: 62ch;
  text-wrap: pretty;
}

/* ════════════════════════════════════════════════════════════════════════
   5b — THE TAB SHELL (ticket 18)
   WAI-ARIA tabs pattern. Reuses §1's palette and §2's focus ring — no new
   hex anywhere in this block.
   ════════════════════════════════════════════════════════════════════════ */
.sf-tabs {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  padding: 4px;
  border-radius: var(--fc-radius-pill);
  background: var(--fc-surface-rail);
}
.sf-tab {
  flex: 1 1 auto;
  min-height: var(--fc-tap);
  padding: 8px 18px;
  border: 1px solid transparent;
  border-radius: var(--fc-radius-pill);
  background: transparent;
  color: var(--fc-text-secondary);
  font: var(--fc-weight-semibold) var(--fc-size-body) / var(--fc-leading-flat) var(--fc-font);
  cursor: pointer;
  transition: background var(--fc-duration-state) var(--fc-ease-standard),
              color var(--fc-duration-state) var(--fc-ease-standard);
}
.sf-tab:hover { background: var(--fc-surface-card); }
.sf-tab[aria-selected="true"] {
  background: var(--fc-surface-card);
  color: var(--fc-eu-ink);
  box-shadow: var(--fc-shadow-card);
}
/* .sf-tabpanel is tabindex="0" (WAI-ARIA tabs pattern) so a keyboard user
   tabbing past the active tab lands on the panel itself when it holds no
   focusable child yet (e.g. the empty Leave placeholder) — the page's one
   shared focus-visible ring (§2) already covers it, no new ring needed.
   /visual-qa static-review fix (2026-09-04): the class itself carried no
   declaration (only this comment) — real now, even though minimal.
   min-width: 0 is the load-bearing part: #main is a flex column, and a
   flex item's default min-width is `auto` (its content's intrinsic width),
   which — for a panel holding a scrollable table (9b, below) — could in
   principle force the panel, and so #main, wider than the viewport. This
   was already proven NOT to be happening (page-level scrollWidth ===
   innerWidth at 390px, measured) because .sf-headroom-table-host's own
   overflow:auto already caps its contribution; this rule makes that safe
   by construction rather than by an emergent property of a sibling class. */
.sf-tabpanel {
  display: block;
  min-width: 0;
}

/* ════════════════════════════════════════════════════════════════════════
   6 — THE ROLLING-WINDOW TIMELINE
   Views.dc.html:461–:479 (head, track, axis, legend) and :1005–:1011
   (band, today marker, block fills).

   Built as a CSS grid with ONE COLUMN PER DAY. That is not decoration: it
   means every position on this bar is a day index produced by walking dates,
   never a date subtraction — the day-arithmetic ban (RAID R1) holds in the
   layout as well as in the maths.
   ════════════════════════════════════════════════════════════════════════ */
.sf-tl { display: flex; flex-direction: column; gap: 10px; }
.sf-tl__head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  font: var(--fc-weight-bold) var(--fc-w-size-caps) / var(--fc-leading-flat) var(--fc-font);
  letter-spacing: 0.06em;
  color: var(--fc-text-secondary);
  text-transform: uppercase;
}
.sf-tl__head-note {
  font-weight: var(--fc-weight-semibold);
  color: var(--fc-eu-ink);
  font-variant-numeric: tabular-nums;
}
.sf-tl__head-fc {
  margin-left: auto;
  font-weight: var(--fc-weight-semibold);
  color: var(--fc-today-numeral);
  font-variant-numeric: tabular-nums;
}

/* Below ~560px the bar cannot resolve a 3-day trip, so it scrolls rather
   than lying about the shape. A scrollable region is focusable and named,
   so a keyboard can reach it (AA 2.1.1). */
.sf-tl__scroll { overflow-x: auto; overscroll-behavior-x: contain; }
.sf-tl__inner { min-width: 560px; }

.sf-tl__track {
  position: relative;
  display: grid;
  /* ONE row, full height. Every item is placed on it, so the band and the
     today marker can stretch to the full 104px while the blocks centre
     inside it — the layering is z-index, not rows. */
  grid-template-rows: minmax(0, 1fr);
  height: 104px;
  border-radius: 14px;
  background: var(--fc-surface-rail);
  overflow: hidden;
}
.sf-tl__track > * { grid-row: 1; }

/* the shaded band = the 180 days currently in scope (legend, :479).
   The design's #A9D9C2 edge measures 2.17:1 against the track ground, under
   the 3:1 non-text bar, so the edge is the full-strength eu-ink (5.06:1) and
   DASHED — dashed so a scope boundary can never be read as a counted block,
   which is solid. */
.sf-tl__band {
  align-self: stretch;
  background: var(--fc-eu-chip);
  border-left: 1.5px dashed var(--fc-eu-ink);
  border-right: 1.5px dashed var(--fc-eu-ink);
  z-index: 1;
}
.sf-tl__month {
  align-self: stretch;
  border-left: 1px solid var(--fc-cell-vertical);
  z-index: 2;
}

.sf-tl__block {
  align-self: center;
  height: 42px;
  min-width: 4px;       /* a 1-day trip stays visible at any track width */
  border-radius: 9px;
  z-index: 3;
}
.sf-tl__block--counted { background: var(--fc-eu-ink); }
/* the design's flat #CFE0F6 measures 1.27:1 against the track ground, so it
   gains a 1px edge in its own ink (6.05:1). Fill unchanged. */
.sf-tl__block--rolled {
  background: var(--fc-eu-rolled);
  border: 1px solid var(--fc-eu-rolled-ink);
}
/* forecast: hatched AND dash-outlined. Two channels, both of which survive
   greyscale — texture against the counted block's flat fill, dashes against
   the rolled-off block's solid edge. */
.sf-tl__block--forecast {
  background: repeating-linear-gradient(115deg,
    var(--fc-eu-plan-a) 0 4px, var(--fc-eu-plan-b) 4px 8px);
  border: 1.5px dashed var(--fc-today);
}
.sf-tl__today {
  align-self: stretch;
  width: 2px;
  background: var(--fc-today);
  z-index: 6;
}
/* Ticket 18 rule 8 — the headroom marker (opts.overlay). Must read as
   distinct from BOTH the band (dashed eu-ink edge, translucent chip-green
   fill) and a forecast block (blue hatch, blue dash) by PATTERN, not hue
   alone — so this is the one place on the timeline that borrows the warn
   palette (§4 STATE PALETTE), on a cross-hatch angled the other way from
   the forecast block's. No new hex: --fc-warn-ink and --fc-tentative-ground
   are already declared in §1. */
.sf-tl__overlay {
  align-self: stretch;
  border-radius: 9px;
  border: 2px dashed var(--fc-warn-ink);
  background: repeating-linear-gradient(-115deg,
    transparent 0 4px, var(--fc-tentative-ground) 4px 8px);
  z-index: 4;
  pointer-events: none;
}

.sf-tl__axis { display: grid; height: 15px; }
.sf-tl__tick {
  grid-row: 1;
  padding-left: 5px;
  border-left: 1px solid var(--fc-hairline-card);
  font: var(--fc-weight-semibold) var(--fc-size-badge) / var(--fc-leading-flat) var(--fc-font);
  /* design value --fc-text-dow #AEB6C4 (2.04:1). Substituted for AA. */
  color: var(--fc-text-secondary);
  overflow: hidden;
  white-space: nowrap;
}

.sf-legend {
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
  font: var(--fc-weight-medium) var(--fc-size-meta) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-secondary);
}
.sf-legend__item { display: inline-flex; align-items: center; gap: 7px; }
.sf-legend__swatch {
  width: 16px; height: 10px;
  border-radius: 3px;
  flex: 0 0 auto;
}
.sf-legend__swatch--counted  { background: var(--fc-eu-ink); }
.sf-legend__swatch--rolled   { background: var(--fc-eu-rolled); border: 1px solid var(--fc-eu-rolled-ink); }
.sf-legend__swatch--forecast {
  background: repeating-linear-gradient(115deg,
    var(--fc-eu-plan-a) 0 4px, var(--fc-eu-plan-b) 4px 8px);
  border: 1.5px dashed var(--fc-today);
}
.sf-legend__swatch--today { width: 2px; height: 13px; border-radius: 0; background: var(--fc-today); }
.sf-legend__swatch--band  { background: var(--fc-eu-chip); border: 1.5px dashed var(--fc-eu-ink); }
.sf-legend__swatch--overlay {
  background: repeating-linear-gradient(-115deg,
    transparent 0 4px, var(--fc-tentative-ground) 4px 8px);
  border: 1.5px dashed var(--fc-warn-ink);
}

/* ════════════════════════════════════════════════════════════════════════
   7 — SCENARIO PANEL
   ════════════════════════════════════════════════════════════════════════ */
.sf-toggles {
  display: flex;
  gap: 10px 22px;
  flex-wrap: wrap;
  align-items: center;
  margin: 0;
  padding: 14px 18px;
  border: 1px solid var(--fc-hairline-card);
  border-radius: var(--fc-radius-panel);
  background: var(--fc-surface-rail);
}
.sf-toggles legend {
  padding: 0 4px;
  font: var(--fc-weight-bold) var(--fc-w-size-caps) / var(--fc-leading-flat) var(--fc-font);
  letter-spacing: var(--fc-track-caps);
  color: var(--fc-text-secondary);
  text-transform: uppercase;
}
.sf-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* 44px is the rule, not the AA floor of 24 (calendar.css:261) */
  min-height: var(--fc-tap);
  font: var(--fc-weight-semibold) var(--fc-size-body) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-ink);
  cursor: pointer;
}
.sf-toggle input[type="checkbox"] {
  inline-size: 18px; block-size: 18px;
  accent-color: var(--fc-eu-ink);
  cursor: pointer;
}
.sf-toggle select {
  /* The wrapping label is already --fc-tap tall, but clicking a label does
     not reliably OPEN a native select — the dropdown's real hit area is the
     select itself, which measured 47×34 (/visual-qa, 2026-08-25). The token,
     not 34px: this page is opened on a phone. */
  min-height: var(--fc-tap);
  padding: 4px 8px;
  border: 1px solid var(--fc-hairline-card);
  border-radius: var(--fc-radius-chip);
  background: var(--fc-surface-card);
  color: var(--fc-text-ink);
  font: var(--fc-weight-semibold) var(--fc-size-body) / var(--fc-leading-flat) var(--fc-font);
  cursor: pointer;
}
.sf-saved {
  margin-left: 6px;
  color: var(--fc-eu-ink);
  font: var(--fc-weight-semibold) var(--fc-size-badge) / var(--fc-leading-flat) var(--fc-font);
  letter-spacing: 0;
  text-transform: none;
  opacity: 0;
  transition: opacity var(--fc-duration-state) var(--fc-ease-standard);
}
.sf-saved.is-on { opacity: 1; }

/* ── custom scenario rows (ticket 16) — reuses §7's tokens, no new hex ──── */
.sf-scenarios { display: flex; flex-direction: column; gap: 10px; }
.sf-scenario-form { display: flex; flex-direction: column; gap: 6px; }
.sf-scenario-form__label {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font: var(--fc-weight-bold) var(--fc-w-size-caps) / var(--fc-leading-flat) var(--fc-font);
  letter-spacing: var(--fc-track-caps);
  color: var(--fc-text-secondary);
  text-transform: uppercase;
}
.sf-scenario-form__hint {
  font: var(--fc-weight-medium) var(--fc-size-meta) / var(--fc-leading-flat) var(--fc-font);
  letter-spacing: 0;
  text-transform: none;
}
.sf-scenario-form__row { display: flex; gap: 8px; flex-wrap: wrap; }
.sf-scenario-form__input {
  flex: 1 1 220px;
  min-height: var(--fc-tap);
  padding: 4px 10px;
  border: 1px solid var(--fc-hairline-card);
  border-radius: var(--fc-radius-chip);
  background: var(--fc-surface-card);
  color: var(--fc-text-ink);
  font: var(--fc-weight-semibold) var(--fc-size-body) / var(--fc-leading-flat) var(--fc-font);
}
.sf-scenario-form__add {
  min-height: var(--fc-tap);
  padding: 8px 16px;
  border: 1px solid transparent;
  border-radius: var(--fc-radius-pill);
  background: var(--fc-eu-ink);
  color: var(--fc-text-on-fill);
  font: var(--fc-weight-semibold) var(--fc-size-body) / var(--fc-leading-flat) var(--fc-font);
  cursor: pointer;
  transition: background var(--fc-duration-state) var(--fc-ease-standard);
}
.sf-scenario-form__add:hover { background: var(--fc-eu-fill); }

.sf-scenario-rows {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.sf-scenario-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 8px 12px;
  border: 1px solid var(--fc-hairline-leg);
  border-radius: var(--fc-radius-row);
  background: var(--fc-surface-card);
}
.sf-scenario-row__label {
  font: var(--fc-weight-semibold) var(--fc-size-body) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-ink);
}
.sf-scenario-row__dates {
  font: var(--fc-weight-medium) var(--fc-size-meta) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-secondary);
  font-variant-numeric: tabular-nums;
}
.sf-scenario-row__badges { display: flex; gap: 6px; flex-wrap: wrap; }
.sf-scenario-row__spacer { flex: 1 1 auto; }
.sf-scenario-row__toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font: var(--fc-weight-medium) var(--fc-size-meta) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-secondary);
  cursor: pointer;
}
.sf-scenario-row__remove {
  min-height: 32px;
  padding: 4px 10px;
  border: 1px solid var(--fc-hairline-card);
  border-radius: var(--fc-radius-chip);
  background: var(--fc-surface-rail);
  color: var(--fc-text-secondary);
  font: var(--fc-weight-semibold) var(--fc-size-meta) / var(--fc-leading-flat) var(--fc-font);
  cursor: pointer;
}
.sf-scenario-row__remove:hover { background: var(--fc-neutral-chip); }

.sf-outcome {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px 18px;
  border-radius: var(--fc-radius-panel);
  background: var(--fc-surface-rail);
}
.sf-outcome__line {
  margin: 0;
  font: var(--fc-weight-medium) var(--fc-size-qualifier) / 1.5 var(--fc-font);
  color: var(--fc-text-secondary);
  font-variant-numeric: tabular-nums;
  max-width: 72ch;
  text-wrap: pretty;
}
.sf-outcome__line strong { color: var(--fc-text-ink); font-weight: var(--fc-weight-bold); }
.sf-deltas { display: flex; gap: 8px; flex-wrap: wrap; }
.sf-delta {
  padding: 4px 10px;
  border-radius: var(--fc-radius-badge);
  background: var(--fc-weekend-chip);
  color: var(--fc-text-secondary);
  font: var(--fc-weight-semibold) var(--fc-size-meta) / var(--fc-leading-flat) var(--fc-font);
  font-variant-numeric: tabular-nums;
}

/* ════════════════════════════════════════════════════════════════════════
   8 — TRIP CARDS (Unit A)
   Adapted from .fc-leg (calendar.css:1358–:1450) — the 22px icon tile, the
   `tile / 1fr / auto` top row, the 31px-indented sub line — laid onto the
   panel surface of where.css SECTION 4 so each trip is a CARD, not a row.
   ════════════════════════════════════════════════════════════════════════ */
.sf-trips {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.sf-trip {
  padding: 13px 16px 14px;
  border: 1px solid var(--fc-hairline-leg);
  border-left: 3px solid var(--sf-trip-spine, var(--fc-neutral-chip));
  border-radius: var(--fc-radius-panel);
  background: var(--fc-surface-card);
  transition:
    background var(--fc-duration-state) var(--fc-ease-standard),
    transform   var(--fc-duration-state) var(--fc-ease-standard);
}
.sf-trip:hover { background: var(--fc-surface-rail); transform: translateY(var(--fc-hover-lift)); }
.sf-trip--counts { --sf-trip-spine: var(--fc-eu-ink); }
.sf-trip--off    { --sf-trip-spine: var(--fc-weekend-chip); }

.sf-trip__top {
  display: grid;
  grid-template-columns: var(--sf-tile) minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 9px;
}
.sf-trip__tile {
  inline-size: var(--sf-tile);
  block-size: var(--sf-tile);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--fc-radius-chip);
  background: var(--fc-neutral-chip);
  color: var(--fc-text-secondary);
  font-size: 11px;
  line-height: 1;
}
.sf-trip--counts .sf-trip__tile { background: var(--fc-eu-chip); color: var(--fc-eu-ink); }

.sf-trip__title { display: flex; align-items: center; gap: 8px; min-width: 0; }
.sf-trip__name {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font: var(--fc-weight-semibold) var(--fc-size-body) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-ink);
}
.sf-trip__flag {
  flex: 0 0 auto;
  padding: 3px 7px;
  border-radius: var(--fc-radius-badge);
  background: var(--fc-weekend-chip);
  color: var(--fc-text-secondary);
  font: var(--fc-weight-semibold) var(--fc-size-chip-line2) / var(--fc-leading-flat) var(--fc-font);
  letter-spacing: 0.03em;
}
/* the spine writes tentative 2027 phases with a trailing "?"; that mark is
   promoted to a labelled chip rather than left as punctuation nobody reads */
.sf-trip__flag--forecast {
  background: var(--fc-eu-plan-b);
  color: var(--fc-today-numeral);   /* 4.85:1 */
}
.sf-trip__days {
  font: var(--fc-weight-semibold) var(--fc-size-meta) / var(--fc-leading-flat) var(--fc-font);
  /* design value --fc-text-muted (3.06:1). Substituted for AA. */
  color: var(--fc-text-secondary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.sf-trip--counts .sf-trip__days { color: var(--fc-eu-ink); font-weight: var(--fc-weight-bold); }

.sf-trip__sub {
  margin: 6px 0 0;
  padding-left: calc(var(--sf-tile) + 9px);
  font: var(--fc-weight-regular) var(--fc-size-meta) / 1.45 var(--fc-font);
  color: var(--fc-text-secondary);
  font-variant-numeric: tabular-nums;
  text-wrap: pretty;
}
.sf-trip__badges {
  margin: 7px 0 0;
  padding-left: calc(var(--sf-tile) + 9px);
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}
.sf-badge {
  padding: 3px 8px;
  border-radius: var(--fc-radius-badge);
  font: var(--fc-weight-semibold) var(--fc-size-badge) / var(--fc-leading-flat) var(--fc-font);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.sf-badge--counts { background: var(--fc-eu-chip);      color: var(--fc-eu-ink); }
.sf-badge--off    { background: var(--fc-neutral-chip); color: var(--fc-text-secondary); }
.sf-badge--live   { background: var(--fc-status-ok-ground); color: var(--fc-eu-ink); }
.sf-badge--gone   { background: var(--fc-eu-rolled);    color: var(--fc-eu-rolled-ink); }
.sf-badge--what   { background: var(--fc-tentative-ground); color: var(--fc-text-ink); }

/* ════════════════════════════════════════════════════════════════════════
   9 — EMPTY / ERROR
   The glyph tiles of state-card.js: empty `＋` on #EEF3FE in #4F7DF3,
   error `!` on the breach ground in the breach ink.
   ════════════════════════════════════════════════════════════════════════ */
.sf-state {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  padding: 26px 28px;
  border-radius: var(--fc-radius-card);
  background: var(--fc-surface-card);
  box-shadow: var(--fc-shadow-card);
}
.sf-state__glyph {
  inline-size: 38px; block-size: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--fc-radius-row);
  font-size: 18px;
  font-weight: var(--fc-weight-bold);
}
.sf-state__glyph--error { background: var(--fc-breach-ground); color: var(--fc-breach-figure); }
.sf-state__glyph--empty { background: var(--fc-eu-plan-b);     color: var(--fc-today-numeral); }
.sf-state__title {
  margin: 0;
  font: var(--fc-weight-bold) 17px / 1.25 var(--fc-font);
  letter-spacing: var(--fc-track-tight);
  color: var(--fc-text-ink);
}
.sf-state__body {
  margin: 0;
  max-width: 68ch;
  font: var(--fc-weight-medium) var(--fc-size-qualifier) / var(--fc-leading-body) var(--fc-font);
  color: var(--fc-text-secondary);
  text-wrap: pretty;
}
.sf-state__detail {
  margin: 0;
  max-width: 68ch;
  padding: 10px 12px;
  border-radius: var(--fc-radius-row);
  background: var(--fc-surface-rail);
  font: var(--fc-weight-regular) var(--fc-size-meta) / 1.5 var(--fc-font);
  color: var(--fc-text-secondary);
  word-break: break-word;
}

/* The state card's one action — the sign-in button (ticket 10). Forked from
   calendar.css:1224 `.fc-state__btn` + `.fc-state__btn--ink` (shape, tap
   target, transition), with one deliberate divergence: the fill is this app's
   own accent (--fc-eu-ink, the Schengen green the numeral and the counted
   timeline blocks already use) rather than the sibling's near-black ink. One
   accent per artefact, and this page's accent is the green. No new colour
   enters the file: both fills are existing tokens.
   Full-width on the narrow breakpoint, because a 44px control floated left in
   a 26px-padded card is a thumb-miss waiting to happen. */
.sf-state__action {
  min-height: var(--fc-tap);
  margin-top: 6px;
  padding: 10px 18px;
  border: 1px solid transparent;
  border-radius: var(--fc-radius-pill);
  background: var(--fc-eu-ink);
  color: var(--fc-text-on-fill);
  font: var(--fc-weight-semibold) var(--fc-size-body) / var(--fc-leading-flat) var(--fc-font);
  letter-spacing: var(--fc-track-tight);
  cursor: pointer;
  transition: background var(--fc-duration-state) var(--fc-ease-standard);
}
.sf-state__action:hover { background: var(--fc-eu-fill); }
/* A button with no handler wired is UNAVAILABLE, not broken: quiet ground,
   secondary ink, no hover response, so it reads as a control that cannot act
   rather than one that failed. */
.sf-state__action[disabled] {
  background: var(--fc-neutral-chip);
  border-color: var(--fc-hairline-card);
  color: var(--fc-text-secondary);
  cursor: not-allowed;
}
.sf-state__action[disabled]:hover { background: var(--fc-neutral-chip); }

.sf-meta {
  margin: 0;
  padding: 0 4px;
  font: var(--fc-weight-medium) var(--fc-size-meta) / 1.5 var(--fc-font);
  color: var(--fc-text-secondary);
  max-width: 88ch;
  text-wrap: pretty;
}

/* Ticket 11 — the token-expiry bar (spec 11 rule 10). Same amber ink as
   .sf-state--warn (§4 STATE PALETTE), reused rather than a new hue: this is
   the same "still true, ageing" signal, on a banner instead of a card. */
.sf-expiry-bar {
  margin: 0 0 12px;
  padding: 10px 14px;
  border-radius: var(--fc-radius-panel);
  background: var(--fc-tentative-ground);
  color: var(--fc-warn-ink);
  font-weight: var(--fc-weight-semibold);
  max-width: none;
}

/* ════════════════════════════════════════════════════════════════════════
   9b — THE HEADROOM VIEW (ticket 18)
   Reuses §5/§7's card/panel/note tokens throughout — no new hex.
   ════════════════════════════════════════════════════════════════════════ */
.sf-headroom-view { display: flex; flex-direction: column; gap: var(--sf-gap); }

.sf-headroom-controls { display: flex; align-items: center; gap: 10px; }
.sf-headroom-controls__label {
  font: var(--fc-weight-bold) var(--fc-w-size-caps) / var(--fc-leading-flat) var(--fc-font);
  letter-spacing: var(--fc-track-caps);
  color: var(--fc-text-secondary);
  text-transform: uppercase;
}
.sf-headroom-controls__select {
  min-height: var(--fc-tap);
  padding: 4px 8px;
  border: 1px solid var(--fc-hairline-card);
  border-radius: var(--fc-radius-chip);
  background: var(--fc-surface-card);
  color: var(--fc-text-ink);
  font: var(--fc-weight-semibold) var(--fc-size-body) / var(--fc-leading-flat) var(--fc-font);
  cursor: pointer;
}

/* orchestrator P1 fix (2026-09-04): the full descriptive sentence now
   lives in .sf-headroom__lede, a plain paragraph ABOVE this scroll
   wrapper — it wraps at the page's width. This wrapper is a named,
   focusable a11y region (headroom-view.js sets role="region" +
   aria-label) with a visible scroll-fade affordance (below) for whenever
   it genuinely still scrolls — never a substitute for the ≤600px column
   hide (breakpoint raised from 480, see the comment on that rule below),
   only a backstop for the width band above it. */
.sf-headroom-table-host {
  position: relative;
  overflow-x: auto;
}
.sf-headroom-table-host.is-scrollable::after {
  content: '';
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: 28px;
  background: linear-gradient(to right, transparent, var(--fc-surface-card));
  pointer-events: none;
}
.sf-headroom__lede {
  margin: 0;
}
.sf-headroom {
  width: 100%;
  min-width: 560px;
  border-collapse: collapse;
  font: var(--fc-weight-medium) var(--fc-size-body) / var(--fc-leading-flat) var(--fc-font);
}
.sf-headroom caption {
  /* SHORT a11y-only caption now (orchestrator P1 fix a) — visually
     collapsed rather than display:none, so it still announces to a
     screen reader entering the table without duplicating .sf-headroom__lede
     on screen for a sighted reader. Same technique as .sf-vh (§2). */
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.sf-headroom th {
  padding: 8px 10px;
  border-bottom: 1px solid var(--fc-hairline-card);
  text-align: left;
  font: var(--fc-weight-bold) var(--fc-size-meta) / var(--fc-leading-flat) var(--fc-font);
  letter-spacing: var(--fc-track-caps);
  color: var(--fc-text-secondary);
  text-transform: uppercase;
}
.sf-headroom td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--fc-hairline-row);
  color: var(--fc-text-ink);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.sf-headroom tbody tr:hover { background: var(--fc-surface-rail); }
/* orchestrator P1 fix (b) — the "Capped by"/"Shortest" data, duplicated
   into each row's own detail line (below), hidden on desktop where the
   real columns already show it. */
.sf-headroom__row-detail {
  display: none;
}
/* /visual-qa P2 fix (2026-09-04): measured hit area was 62.7×12.5px — well
   under even WCAG AA's 24px floor (--fc-tap's own comment names 24 as that
   floor, 44 as the house's stricter target). The button now fills its
   whole <td> (negative margin cancels the cell's own padding, then re-adds
   it as the button's own) so the tappable area is the full row height a
   reader already sees as "the Month column", not just the underlined text. */
.sf-headroom__month-btn {
  display: block;
  width: 100%;
  margin: -8px -10px;
  padding: 8px 10px;
  border: 0;
  background: none;
  color: var(--fc-eu-ink);
  font: var(--fc-weight-bold) var(--fc-size-body) / var(--fc-leading-flat) var(--fc-font);
  text-align: left;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.sf-headroom__month-btn:hover { color: var(--fc-eu-fill); }
/* the headline figure per row — same emphasis language as the "still
   counting" list's own count chip (§5 .sf-list__n): bold, tabular, ink that
   means "counted". */
.sf-headroom__best {
  color: var(--fc-eu-ink);
  font-weight: var(--fc-weight-bold);
}
/* /visual-qa P1 fix (2026-09-04): --fc-warn-ink on white measures 3.81:1 —
   below the 4.5:1 AA body-text bar (§4's own comment on .sf-state--warn
   already names this exact trap: "no AA-passing amber exists… so the WORD
   takes ink, and the amber carries the signal as a non-text [element]").
   The readable word takes ink here too; the ⚠ glyph carries its own native
   emoji colour regardless of this rule, so the amber signal survives. */
.sf-headroom__flag {
  color: var(--fc-text-ink);
  font-weight: var(--fc-weight-semibold);
}
/* renamed from .sf-headroom__caption (orchestrator fix) — this paragraph
   is the audited-history CAVEAT, distinct from the table's own real
   <caption> element above; the old name read as if it styled that. */
.sf-headroom__caveat { margin-top: 10px; }
.sf-headroom__timeline { margin-top: 4px; }

/* orchestrator P1 fix (b) — below the breakpoint, the "Capped by"/
   "Shortest" columns move into each row's own detail line instead of
   needing horizontal scroll to reach; min-width also drops so the
   remaining three columns fit the viewport without scrolling at all in
   the normal case (measured: 390px, and 520px after the breakpoint raise
   below). The scroll-fade affordance above is the backstop for any WIDTH
   in between where scrolling still genuinely happens — this breakpoint is
   not that backstop, it removes the need.
   Breakpoint raised 480 → 600 by the orchestrator's /visual-qa re-check
   (2026-09-04): at 520px (inside the old 480px cutoff, so this whole block
   didn't apply yet) the five-column table measured 560px inside a 488px
   host, pushing "Shortest" behind the side-scroll with only the fade to
   signal it. The table's natural width is ~560px, so the stacked layout
   has to own every viewport under ~600px, not just phones — re-verified
   at 520px after the raise: host.scrollWidth === host.clientWidth, no
   scroll needed (see t18-headroom-cdp.mjs's dedicated 520px leg). */
@media (max-width: 600px) {
  .sf-headroom { min-width: 0; width: 100%; table-layout: fixed; }
  .sf-headroom__th-capped,
  .sf-headroom__th-shortest {
    display: none;
  }
  /* Found while MEASURING the fix, not assumed: .sf-headroom td's own
     white-space: nowrap (desktop rule, above) is inherited by this span,
     so "Capped by a later trip · shortest 44 days" rendered as one long
     unbroken line regardless of `display: block` — the table's
     scrollWidth measured 393px against a 366px host at 390px, 27px of
     residual scroll the min-width:0 change above was supposed to remove.
     white-space: normal here (and on the Best-start cell, which now also
     carries the audited-history flag text) lets both actually wrap. */
  .sf-headroom__row-detail {
    display: block;
    margin-top: 2px;
    color: var(--fc-text-secondary);
    font: var(--fc-weight-medium) var(--fc-size-meta) / 1.4 var(--fc-font);
    white-space: normal;
  }
  .sf-headroom td {
    white-space: normal;
  }
}

/* ════════════════════════════════════════════════════════════════════════
   10 — RESPONSIVE
   ════════════════════════════════════════════════════════════════════════ */
@media (max-width: 860px) {
  .sf-picture { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 430px) {
  body { padding: 16px 12px 44px; }
  .sf-card { padding: 18px 16px 20px; border-radius: var(--fc-radius-panel); }
  .sf-topbar__title { font-size: 21px; }
  .sf-eu-chip { margin-left: 0; }
  .sf-figure, .sf-breakdown { padding: 16px; }
  /* a title and its qualifier both wrapping side-by-side is two ragged
     columns; stacked, each gets the full measure */
  .sf-head { flex-direction: column; align-items: stretch; gap: 3px; }
  .sf-head__rule { display: none; }
  .sf-tl__head-fc { margin-left: 0; }
  .sf-state { padding: 22px 18px; }
  .sf-state__action { align-self: stretch; }
}

/* ════════════════════════════════════════════════════════════════════════
   11 — REDUCED MOTION
   Every transition on this page is a state or focus change of 120–160ms;
   there is no travel, no reveal and no scroll effect to disable. Honouring
   the preference therefore means going instant, with nothing lost.
   ════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .sf-trip:hover { transform: none; }
  .sf-saved { transition: none; }
}

/* ── drag the rolling window (2026-09-07) ─────────────────────────────── */
.sf-tl__scrub {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 10px; font-size: var(--fc-size-meta); color: var(--fc-text-secondary);
}
.sf-tl__scrub-label { text-transform: uppercase; letter-spacing: var(--fc-track-caps); font-weight: var(--fc-weight-semibold); }
.sf-tl__scrub input[type="range"] { flex: 1 1 200px; min-height: var(--fc-tap); accent-color: var(--fc-today); }
.sf-tl__scrub-val { font-weight: var(--fc-weight-semibold); color: var(--fc-text-ink); font-variant-numeric: tabular-nums; }
.sf-tl__scrub-reset {
  min-height: 32px; padding: 4px 10px; cursor: pointer; font-family: var(--fc-font);
  font-size: var(--fc-size-chip); font-weight: var(--fc-weight-semibold); color: var(--fc-today-numeral);
  background: var(--fc-surface-card); border: 1px solid var(--fc-today); border-radius: var(--fc-radius-pill);
}
/* the real today while scrubbed: same mark, dashed, so it reads as "reference" not "now" */
.sf-tl__today--real { opacity: 0.45; border-left-style: dashed; }

/* ── what-if → calendar (2026-09-07) ─────────────────────────────────── */
.sf-head__link { color: var(--fc-today-numeral); font-weight: var(--fc-weight-semibold); text-decoration: none; }
.sf-head__link:hover { text-decoration: underline; }
.sf-scenario-row__pencil, .sf-scenario-row__cal {
  min-height: 32px; padding: 4px 10px; font-family: var(--fc-font); font-size: var(--fc-size-chip);
  font-weight: var(--fc-weight-semibold); border-radius: var(--fc-radius-pill); text-decoration: none;
  display: inline-flex; align-items: center;
}
.sf-scenario-row__pencil { cursor: pointer; color: var(--fc-today-numeral); background: var(--fc-surface-card); border: 1px solid var(--fc-today); }
.sf-scenario-row__pencil:disabled { opacity: 0.55; cursor: progress; }
.sf-scenario-row__cal { color: var(--fc-eu-ink); background: var(--fc-eu-chip); border: 1px solid var(--fc-eu-micro); }
