/* ATLAS shared shell: tokens, theming, nav, common components.
   Light is the base. Dark is applied under prefers-color-scheme and overridden by an
   explicit data-theme on the root, so the toggle wins in both directions.

   Type: one sans for everything — headings, running text and figures — with weight and
   negative tracking carrying the hierarchy. The serif is spent in exactly one place, the
   wordmark, because the stack available offline lands on Georgia or Palatino, and a
   Georgia heading over a system-sans body reads as an unstyled document rather than as a
   product. Figures are always tabular. No web fonts: the prototype must open offline from
   one file, so the type system is built from stacks every machine already has. */

:root {
  --ink: #0f1f27;
  --muted: #556973;
  --faint: #6b7f89;
  --line: #dde5e9;
  --control-line: #b3c2c9;
  --bg: #ffffff;
  /* Cool, faintly blue-green neutrals rather than the warm cream this used to carry.
     Cream-and-serif is the register of a printed report; ATLAS is an instrument, and the
     panels now sit as quiet surfaces under the content instead of colouring it. */
  --panel: #f2f6f7;
  --sunk: #e9eff1;
  --raised: #ffffff;
  --accent: #12694a;
  --accent-ink: #ffffff;
  --accent-soft: #e3f1e9;
  --accent-line: #bfe0ce;
  --warn: #7a5c1e;
  --warn-bg: #fdf6e3;
  --good: #146c4a;
  --good-bg: #e6f4ec;
  --stop: #7a2f2f;
  --stop-bg: #f8ecec;

  /* The eight evidence classes from docs/RESPONSIBLE_AI.md §1 (also prototype/labels.mjs),
     one hue each, spaced 45 degrees apart around the wheel so no two are adjacent -- the
     one slot a hue would otherwise take is spent on "unknown" instead, desaturated to
     grey, matching the no-data grey already used on the choropleth ramp rather than
     competing with the other seven for wheel space. Every pairing here clears 4.5:1 text
     contrast against its own background (checked with the WCAG relative-luminance
     formula, not by eye) and the hue step is chosen to stay distinguishable under
     deuteranopia and protanopia; the class name is always printed as well, so colour is
     reinforcement, never the only carrier. */
  --ev-verified-bg: #cae7d1;
  --ev-verified-ink: #196b2e;
  --ev-calculated-bg: #cad1e7;
  --ev-calculated-ink: #1f3884;
  --ev-inference-bg: #d9cae7;
  --ev-inference-ink: #521f84;
  --ev-scenario-bg: #cae7e7;
  --ev-scenario-ink: #196b6b;
  --ev-forecast-bg: #d9e7ca;
  --ev-forecast-ink: #426b19;
  --ev-assumption-bg: #e7e0ca;
  --ev-assumption-ink: #705a1a;
  --ev-unknown-bg: #d9d9d9;
  --ev-unknown-ink: #525252;
  --ev-judgement-bg: #e7cae0;
  --ev-judgement-ink: #841f6b;

  /* The masthead is the one dark surface in the light theme: it frames every page the
     same way and matches the dark decision cards on the Evidence Gate page. */
  --masthead-bg: #17262e;
  --masthead-ink: #f2f5f7;
  --masthead-muted: #a9b8c2;
  --masthead-line: #2b3a44;

  /* Sequential choropleth ramp, light theme: pale gold through green to deep teal.
     Multi-hue for visual interest, but lightness decreases monotonically, which is what
     actually makes a ramp colour-blind safe. Deliberately never red-green opposition. */
  --ramp-0: #f4f3d4;
  --ramp-1: #c7e09a;
  --ramp-2: #79c58a;
  --ramp-3: #2f9470;
  --ramp-4: #14574f;
  --no-data: #b9b4ab;
  --map-bg: #fbfbf6;
  --map-stroke: #ffffff;

  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, "Cascadia Mono", Consolas, "Liberation Mono", monospace;

  /* Elevation. Everything used to be a 1px border on a flat ground, which reads as an
     unstyled document: nothing is above anything else, so nothing draws the eye. Depth is
     spent by role — a resting surface, a thing that can be picked up, a thing floating
     over the page — and most surfaces still take none of it. */
  --shadow-sm: 0 1px 2px rgba(15, 31, 39, .05), 0 1px 3px rgba(15, 31, 39, .04);
  --shadow-md: 0 2px 4px rgba(15, 31, 39, .05), 0 8px 20px -8px rgba(15, 31, 39, .16);
  --shadow-lg: 0 4px 8px rgba(15, 31, 39, .06), 0 20px 44px -16px rgba(15, 31, 39, .26);
  --ring: 0 0 0 3px rgba(18, 105, 74, .18);

  /* One radius scale, applied by size of surface rather than uniformly. */
  --r-sm: 6px;
  --r-md: 10px;
  --r-lg: 14px;

  /* A spacing scale, so section rhythm is a decision rather than whatever the last
     margin happened to be. */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;
  --s-8: 72px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ink: #e8eef2;
    --muted: #9db0bb;
    --faint: #8496a1;
    --line: #26333c;
    --control-line: #3d4d58;
    --bg: #0d1418;
    --panel: #151f26;
    --sunk: #101920;
    --raised: #1a252d;
    --accent: #5fc78d;
    --accent-ink: #06120c;
    --accent-soft: #14301f;
    --accent-line: #24503a;
    --warn: #e0bd6a;
    --warn-bg: #2a2415;
    --good: #7fd6a6;
    --good-bg: #12291f;
    --stop: #e2918f;
    --stop-bg: #2a1717;

    /* Dark pairing for the eight evidence classes: deep tint background, light saturated
       text, mirroring how --good/--warn/--stop already invert above. Same hue per class as
       the light theme, same 4.5:1 floor checked the same way. */
    --ev-verified-bg: #1b3722;
    --ev-verified-ink: #81da97;
    --ev-calculated-bg: #1b2237;
    --ev-calculated-ink: #8197da;
    --ev-inference-bg: #291b37;
    --ev-inference-ink: #ad81da;
    --ev-scenario-bg: #1b3737;
    --ev-scenario-ink: #81dada;
    --ev-forecast-bg: #29371b;
    --ev-forecast-ink: #adda81;
    --ev-assumption-bg: #37301b;
    --ev-assumption-ink: #dac481;
    --ev-unknown-bg: #292929;
    --ev-unknown-ink: #adadad;
    --ev-judgement-bg: #371b30;
    --ev-judgement-ink: #da81c4;
    --masthead-bg: #0b1114;
    --masthead-ink: #eef2f5;
    --masthead-muted: #92a3ad;
    --masthead-line: #1f2b33;

    /* Dark ramp runs the other way: deep to bright, so higher stays visually heavier
       against a dark ground rather than disappearing into it. */
    --ramp-0: #152a22;
    --ramp-1: #1f4a37;
    --ramp-2: #2f8059;
    --ramp-3: #5cb783;
    --ramp-4: #b6e8a4;
    --no-data: #4a5550;
    --map-bg: #0a1210;
    --map-stroke: #0c1317;
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
    --shadow-md: 0 2px 4px rgba(0, 0, 0, .4), 0 8px 20px -8px rgba(0, 0, 0, .6);
    --shadow-lg: 0 4px 8px rgba(0, 0, 0, .45), 0 20px 44px -16px rgba(0, 0, 0, .75);
    --ring: 0 0 0 3px rgba(95, 199, 141, .22);
  }
}

:root[data-theme="dark"] {
  --ink: #e8eef2;
  --muted: #9db0bb;
  --faint: #8496a1;
  --line: #26333c;
  --control-line: #3d4d58;
  --bg: #0d1418;
  --panel: #151f26;
  --sunk: #101920;
  --raised: #1a252d;
  --accent: #5fc78d;
  --accent-ink: #06120c;
  --accent-soft: #14301f;
  --accent-line: #24503a;
  --warn: #e0bd6a;
  --warn-bg: #2a2415;
  --good: #7fd6a6;
  --good-bg: #12291f;
  --stop: #e2918f;
  --stop-bg: #2a1717;
  --ev-verified-bg: #1b3722;
  --ev-verified-ink: #81da97;
  --ev-calculated-bg: #1b2237;
  --ev-calculated-ink: #8197da;
  --ev-inference-bg: #291b37;
  --ev-inference-ink: #ad81da;
  --ev-scenario-bg: #1b3737;
  --ev-scenario-ink: #81dada;
  --ev-forecast-bg: #29371b;
  --ev-forecast-ink: #adda81;
  --ev-assumption-bg: #37301b;
  --ev-assumption-ink: #dac481;
  --ev-unknown-bg: #292929;
  --ev-unknown-ink: #adadad;
  --ev-judgement-bg: #371b30;
  --ev-judgement-ink: #da81c4;
  --masthead-bg: #0b1114;
  --masthead-ink: #eef2f5;
  --masthead-muted: #92a3ad;
  --masthead-line: #1f2b33;
  --ramp-0: #152a22;
  --ramp-1: #1f4a37;
  --ramp-2: #2f8059;
  --ramp-3: #5cb783;
  --ramp-4: #b6e8a4;
  --no-data: #4a5550;
  --map-bg: #0a1210;
  --map-stroke: #0c1317;
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
  --shadow-md: 0 2px 4px rgba(0, 0, 0, .4), 0 8px 20px -8px rgba(0, 0, 0, .6);
  --shadow-lg: 0 4px 8px rgba(0, 0, 0, .45), 0 20px 44px -16px rgba(0, 0, 0, .75);
  --ring: 0 0 0 3px rgba(95, 199, 141, .22);
}

* { box-sizing: border-box; }

html { color-scheme: light dark; }

body {
  margin: 0;
  font: 16px/1.55 var(--sans);
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

.wrap { max-width: 1180px; margin: 0 auto; padding: 0 20px 56px; }

/* ---- Masthead and navigation ------------------------------------------- */

.masthead {
  background: var(--masthead-bg);
  color: var(--masthead-ink);
  border-bottom: 1px solid var(--masthead-line);
  margin-bottom: 26px;
}
.masthead .inner {
  max-width: 1180px; margin: 0 auto; padding: var(--s-3) 20px;
  display: flex; align-items: center; gap: var(--s-3) var(--s-5); flex-wrap: wrap;
}
/* The one deliberate use of the serif: a wordmark. Against a sans interface it reads as
   identity; used for headings it read as an unstyled document. */
.brand {
  font-family: var(--serif); font-size: 1.4rem; font-weight: 600; letter-spacing: 0.08em;
  text-decoration: none; color: var(--masthead-ink);
  display: inline-flex; align-items: center; gap: var(--s-3);
}
.brand svg { color: var(--accent); flex: none; }
.brand span {
  font-family: var(--sans); color: var(--masthead-muted); font-weight: 400;
  font-size: 0.82rem; letter-spacing: 0; margin-left: 6px;
}
nav.main { display: flex; gap: 2px; flex-wrap: wrap; margin-left: auto; }
nav.main a {
  color: var(--masthead-muted); text-decoration: none; padding: 6px 12px;
  border-radius: 4px; font-size: 0.92rem;
}
nav.main a:hover { background: var(--masthead-line); color: var(--masthead-ink); }
nav.main a[aria-current="page"] { background: var(--accent); color: var(--accent-ink); }
.masthead nav.main a.section-active { color: var(--masthead-ink); font-weight: 600; }
.masthead nav.main a.section-active[aria-current="page"] { color: var(--accent-ink); }

.theme-toggle {
  font: inherit; font-size: 0.82rem; cursor: pointer;
  background: transparent; color: var(--masthead-ink);
  border: 1px solid var(--masthead-line); border-radius: 4px; padding: 5px 10px;
}
.theme-toggle:hover { border-color: var(--masthead-muted); }

/* Sub-navigation for the active section. Visually secondary to the masthead but a
   real landmark, so a screen reader can jump straight to it. */
.subnav {
  border-bottom: 1px solid var(--line);
  background: var(--panel);
  margin: -26px 0 26px;
}
.subnav .inner {
  max-width: 1180px; margin: 0 auto; padding: 8px 20px;
  display: flex; align-items: center; gap: 4px 18px; flex-wrap: wrap;
}
.subnav-blurb { color: var(--muted); font-size: 0.82rem; margin-right: 4px; }
/* The unreviewed-translation notice. Its own bar so it appears on every page, including
   the sections that have no sub-navigation to hang it from. The negative margin above is
   collapsed when it follows a .subnav, which already supplies the gap. */
.langnotice {
  border-bottom: 1px solid var(--line); background: var(--panel); margin: -26px 0 26px;
}
.subnav + .langnotice { margin-top: -26px; }
.langnotice .inner { max-width: 1180px; margin: 0 auto; padding: 8px 20px; }
.langnotice[hidden] { display: none; }
.subnav a {
  color: var(--muted); text-decoration: none; font-size: 0.92rem;
  padding: 6px 2px; border-bottom: 2px solid transparent;
}
.subnav a:hover { color: var(--ink); }
.subnav a[aria-current="page"] { color: var(--ink); font-weight: 600; border-bottom-color: var(--accent); }

/* Time-group headings inside the sub-navigation. Not links, so they read as labels
   rather than as disabled options. */
.subnav-group {
  font-size: 0.76rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--faint);
  padding-left: 14px; border-left: 2px solid var(--line); margin-left: 6px;
}

/* ---- Footer -------------------------------------------------------------- */

.site-footer {
  border-top: 1px solid var(--line);
  background: var(--panel);
  color: var(--muted);
  font-size: 0.82rem;
  margin-top: 40px;
}
.site-footer .inner {
  max-width: 1180px; margin: 0 auto; padding: 22px 20px 28px;
  display: grid; grid-template-columns: 1.4fr 1fr; gap: 14px 40px;
}
.site-footer p { margin: 0 0 6px; }
.site-footer strong { color: var(--ink); font-weight: 600; }
.site-footer nav { display: flex; flex-wrap: wrap; gap: 4px 16px; }
.site-footer a { color: var(--ink); }
@media (max-width: 720px) { .site-footer .inner { grid-template-columns: 1fr; } }

/* ---- Typography and blocks --------------------------------------------- */

/* Headings sit in the sans, not the serif.
   The serif here is a system fallback stack that lands on Georgia or Palatino on most
   machines, and a Georgia heading over a system-sans body is the typographic signature of
   an unstyled document rather than a product. Weight and negative tracking do the work
   instead. The serif is kept as a token, and spent in exactly one place — the wordmark —
   where a contrasting face reads as identity rather than as a default. */
h1, h2, h3 { font-family: var(--sans); font-weight: 700; letter-spacing: -0.021em; text-wrap: balance; }
h1 { font-size: 2.1rem; line-height: 1.1; margin: 0 0 var(--s-3); }
h2 { font-size: 1.3rem; line-height: 1.25; margin: var(--s-7) 0 var(--s-3); letter-spacing: -0.015em; }
h3 { font-size: 1.02rem; line-height: 1.3; margin: var(--s-5) 0 var(--s-2); letter-spacing: -0.01em; }
.eyebrow {
  font-family: var(--sans); font-size: 0.7rem; font-weight: 700; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--accent); margin: 0 0 var(--s-2);
}
.lede { color: var(--muted); margin: 0 0 var(--s-5); max-width: 64ch; font-size: 1.05rem; line-height: 1.6; }
/* The rest of a lede that ran to more than one sentence (KAN-73), moved into the page
   body rather than dropped. Same size and colour as .lede itself, not .note's smaller
   caption type -- review round 1 found several of these sentences were responsible-AI
   statements (what a page will not show, that no number is model-written, that a chain
   never becomes a statement of need) demoted to caption weight, which reads as though the
   sentence mattered less than the one before it. It is the same sentence, continued. */
.lede-continued { color: var(--muted); margin: 0 0 var(--s-5); max-width: 64ch; font-size: 1.05rem; line-height: 1.6; }

/* ---- Page header (KAN-73) -------------------------------------------------
   Eyebrow, h1, one lede sentence, in that order, on every page that shares this
   stylesheet. Not a layout component in its own right -- .eyebrow, h1 and .lede already
   carry their own spacing -- but a single named wrapper, so a page cannot quietly grow a
   second way of introducing itself, and prototype/test/design-system.test.mjs has one
   thing to find on every page rather than a different shape each time. A second lede
   sentence is never dropped to fit this; it moves into the page body as its own
   paragraph instead. */
.page-header { margin: 0 0 var(--s-2); }
.page-header .lede { margin-bottom: var(--s-5); }
.note { color: var(--muted); font-size: 0.85rem; }
.faint { color: var(--faint); }
.num, .tabular { font-variant-numeric: tabular-nums; }

/* A figure is data, so it is set in the sans with tabular figures and a tight fit —
   never in the serif, which spaced digits unevenly and read as a pull-quote. */
.figure, .stat b, .horizon .figure {
  font-family: var(--sans); font-variant-numeric: tabular-nums lining-nums;
  font-weight: 700; letter-spacing: -0.028em;
}

.panel {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: var(--s-4) var(--s-4);
}
.callout { border-left: 4px solid var(--warn); background: var(--warn-bg); padding: 10px 14px; margin: 14px 0; font-size: 0.9rem; border-radius: 0 4px 4px 0; }
.callout.good { border-left-color: var(--good); background: var(--good-bg); }
.callout.stop { border-left-color: var(--stop); background: var(--stop-bg); }

.grid { display: grid; gap: 20px; }
.grid.two { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid.three { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.controls { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; }
/* An explicit display beats the [hidden] default, so a hidden control row kept showing
   and the Past and Present rows appeared together. */
.controls[hidden] { display: none; }

label {
  font-weight: 600; display: block; margin-bottom: var(--s-2);
  font-size: 0.78rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted);
}
select, input {
  font: inherit; font-size: 0.95rem; padding: 0 var(--s-3); height: 40px; width: 100%;
  border: 1px solid var(--control-line); border-radius: var(--r-sm);
  background: var(--raised); color: var(--ink);
  transition: border-color .12s ease, box-shadow .12s ease;
}
/* A native select arrow is the most recognisable piece of unstyled browser chrome on a
   page, and it differs on every platform. This one is drawn as an inline SVG data URI so
   it stays identical everywhere and still opens from one offline file. The colour is
   baked per theme rather than tokenised because a data URI cannot read a custom property. */
select {
  appearance: none; -webkit-appearance: none;
  padding-right: var(--s-6);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%23556973' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--s-3) center;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) select {
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%239db0bb' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  }
}
:root[data-theme="dark"] select {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%239db0bb' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
select:hover, input:hover { border-color: var(--faint); }
select:focus-visible, input:focus-visible {
  outline: none; border-color: var(--accent); box-shadow: var(--ring);
}

table { border-collapse: collapse; width: 100%; font-size: 0.88rem; }
th, td { text-align: left; padding: var(--s-2) var(--s-3); border-bottom: 1px solid var(--line); vertical-align: top; }
tbody tr:last-child td { border-bottom: 0; }
th {
  background: var(--sunk); position: sticky; top: 0; font-weight: 600;
  font-size: 0.78rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted);
}
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
.scroll {
  max-height: 400px; overflow: auto; border: 1px solid var(--line);
  border-radius: var(--r-md); background: var(--raised);
  /* KAN-79: a scrollable box needs an affordance visible at rest, not only on hover --
     Chromium/WebKit draw overlay scrollbars that show nothing until the pointer arrives, and
     a table clipped mid-digit reads as a different, wrong figure rather than a cut-off one.
     A styled, always-drawn scrollbar (Firefox via scrollbar-width/color, WebKit via the
     pseudo-elements below) plus a right-edge shadow fixed to the box -- not to its scrolling
     content, since box-shadow is painted relative to the border box -- give that signal in
     both languages and both themes without any new copy to translate. */
  scrollbar-width: thin; scrollbar-color: var(--faint) var(--sunk);
  box-shadow: inset -16px 0 12px -10px rgba(0, 0, 0, 0.3);
}
.scroll::-webkit-scrollbar { height: 12px; width: 12px; }
.scroll::-webkit-scrollbar-track { background: var(--sunk); }
.scroll::-webkit-scrollbar-thumb {
  background: var(--faint); border-radius: var(--r-sm); border: 2px solid var(--sunk);
  transition: background-color 0.15s ease;
}
.scroll::-webkit-scrollbar-thumb:hover { background: var(--muted); }
@media (prefers-reduced-motion: reduce) {
  .scroll::-webkit-scrollbar-thumb { transition: none; }
}
/* KAN-79 round 5 (design-audit ERROR): iOS Safari never honours ::-webkit-scrollbar or
   scrollbar-width -- documented WebKit behaviour, not directly observed here, since no iOS
   runtime is available in this sandbox -- so the styled scrollbar above is invisible there and
   this inset shadow is the ONLY affordance that browser will ever show. Figures below are
   computed from the rules' declared alpha values (alpha * 255), not measured pixels. The
   light theme's rgba(0, 0, 0, .3) darkens by ~76 of 255 (255 -> ~179). Fading dark-theme's
   near-black --raised (#1a252d) by that same dark shadow would be a much smaller, largely
   imperceptible edge, since there is little room left to darken. A light-coloured shadow at
   rgba(255, 255, 255, .22) lightens the edge instead, by ~56 of 255 -- a smaller swing than
   the light theme's ~76, but a highly visible one against a near-black background, so the
   cue is comparably visible in both themes. Same shape and position as the light-theme rule,
   only the colour changes -- not a colour-only signal on its own, since the styled scrollbar
   (where it renders) is the primary cue throughout. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .scroll {
    box-shadow: inset -16px 0 12px -10px rgba(255, 255, 255, 0.22);
  }
}
:root[data-theme="dark"] .scroll {
  box-shadow: inset -16px 0 12px -10px rgba(255, 255, 255, 0.22);
}
.tablewrap { overflow-x: auto; }

dl.meta { display: grid; grid-template-columns: auto 1fr; gap: 3px 14px; margin: 0; font-size: 0.85rem; }
dl.meta dt { color: var(--muted); }
dl.meta dd { margin: 0; font-variant-numeric: tabular-nums; }

/* ---- Empty and refused states (KAN-73) ------------------------------------
   One component for every "there is nothing to show here" a reader can reach: a question
   Ask declines to answer, the consultations tracker with no source connected, a list this
   copy was built without. Calm rather than alarming -- this is not an error state, it is
   the honest, normal shape of a tool that refuses to fabricate what it does not have --
   and always two things: the reason, truthfully told apart from its neighbours (nothing
   was published; this build does not carry it; the question was refused), and the next
   step. Never the only thing standing in for absent data without saying so (rule 6). */
.empty {
  padding: var(--s-4) var(--s-5); border: 1px dashed var(--control-line);
  border-radius: var(--r-lg); background: var(--panel); color: var(--muted);
}
.empty strong { color: var(--ink); }
.empty p { margin: 0 0 var(--s-2); }
.empty p:last-child { margin-bottom: 0; }
.empty .next-step { color: var(--ink); font-weight: 600; }

.pill {
  display: inline-block; font-size: 0.7rem; font-weight: 600; letter-spacing: 0.02em;
  padding: 3px 9px; border-radius: 999px;
  border: 1px solid var(--line); color: var(--muted); background: var(--raised);
  white-space: nowrap;
}
.pill.live { border-color: var(--good); color: var(--good); background: var(--good-bg); }
.pill.partial { border-color: var(--accent); color: var(--accent); }
.pill.catalogued { border-color: var(--warn); color: var(--warn); background: var(--warn-bg); }
.pill.candidate { border-color: var(--line); color: var(--faint); }
.pill.blocked { border-color: var(--stop); color: var(--stop); background: var(--stop-bg); }

/* ---- Evidence-class badge (KAN-73) ---------------------------------------
   One component for the eight classes docs/RESPONSIBLE_AI.md §1 and prototype/labels.mjs
   already name -- verified fact, calculated indicator, inference, scenario,
   forecast/projection, assumption, unknown, judgement required. Replaces the ad-hoc
   eyebrows and chips that used to say the same thing with a different look on every page
   (the lead-time chain, the forecast verdict, the map profile sections, the retired
   Evidence Gate's .kind chips). The class name is always the element's text -- colour is
   reinforcement, never the only carrier, so this reads the same with the ramp switched
   off. See prototype/test/design-system.test.mjs for the contrast and coverage checks. */
.evidence {
  display: inline-flex; align-items: center;
  font-family: var(--sans); font-size: 0.7rem; font-weight: 700; letter-spacing: 0.03em;
  text-transform: uppercase; padding: 3px 9px; border-radius: 999px; white-space: nowrap;
}
.evidence.verified { background: var(--ev-verified-bg); color: var(--ev-verified-ink); }
.evidence.calculated { background: var(--ev-calculated-bg); color: var(--ev-calculated-ink); }
.evidence.inference { background: var(--ev-inference-bg); color: var(--ev-inference-ink); }
.evidence.scenario { background: var(--ev-scenario-bg); color: var(--ev-scenario-ink); }
.evidence.forecast { background: var(--ev-forecast-bg); color: var(--ev-forecast-ink); }
.evidence.assumption { background: var(--ev-assumption-bg); color: var(--ev-assumption-ink); }
.evidence.unknown { background: var(--ev-unknown-bg); color: var(--ev-unknown-ink); }
.evidence.judgement { background: var(--ev-judgement-bg); color: var(--ev-judgement-ink); }

.bar { background: var(--accent); height: 11px; display: inline-block; vertical-align: middle; border-radius: 2px; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}
a { color: var(--accent); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Buttons that act, as opposed to the mode toggles below. */
.button {
  font: inherit; font-size: 0.92rem; font-weight: 600; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  height: 42px; padding: 0 var(--s-5); border-radius: var(--r-sm); text-decoration: none;
  background: var(--accent); color: var(--accent-ink); border: 1px solid var(--accent);
  box-shadow: var(--shadow-sm);
  transition: transform .1s ease, box-shadow .12s ease, background-color .12s ease, border-color .12s ease;
}
.button.quiet {
  background: var(--raised); color: var(--ink); border-color: var(--control-line);
  box-shadow: none;
}
.button:hover { box-shadow: var(--shadow-md); transform: translateY(-1px); }
.button.quiet:hover { border-color: var(--accent); color: var(--accent); transform: none; box-shadow: var(--shadow-sm); }
.button:active { transform: translateY(0); box-shadow: var(--shadow-sm); }
.button:focus-visible { outline: none; box-shadow: var(--ring), var(--shadow-sm); }
@media (prefers-reduced-motion: reduce) { .button:hover, .button:active { transform: none; } }

/* ---- Stat tiles ---------------------------------------------------------- */

/* At-a-glance figures. One bordered container holding hairline-separated cells rather
   than four separate boxes: the figures are one reading of the same system, and four
   equally-weighted tiles floating apart read as a debug panel. */
.stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1px; background: var(--line);
  border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden;
  box-shadow: var(--shadow-sm);
}
.stat { padding: var(--s-4) var(--s-4) var(--s-4); background: var(--raised); }
.stat b {
  display: block; font-size: 1.75rem; line-height: 1.05; color: var(--ink);
}
.stat b small { font-size: 0.55em; font-weight: 600; color: var(--muted); letter-spacing: 0; }
.stat span {
  display: block; color: var(--muted); font-size: 0.78rem; line-height: 1.35;
  margin-top: var(--s-2);
}

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

svg.map {
  width: 100%; height: auto; display: block;
  background: var(--map-bg); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
}
svg.map path { stroke: var(--map-stroke); stroke-width: 0.3; }
svg.map path:hover { stroke: var(--ink); stroke-width: 1.2; }
svg.map path:focus-visible { stroke: var(--ink); stroke-width: 2.5; outline: 3px solid var(--accent); outline-offset: 1px; }

.legend { display: flex; flex-wrap: wrap; gap: 8px 12px; margin-top: 10px; font-size: 0.85rem; }
.legend span { display: inline-flex; align-items: center; gap: 6px; }
.sw { width: 18px; height: 12px; border: 1px solid var(--line); display: inline-block; }

/* Map beside a rail on a wide screen; stacked on a narrow one. The rail holds the
   readout, legend, area finder and the profile, so choosing an area never means
   scrolling away from the map to see what was chosen. */
/* The rail is narrower than it was: it holds a search box and a profile, and every pixel
   it gives back goes to the map, which is the page. */
.map-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: var(--s-5); align-items: start; }
.map-frame { position: relative; }
.map-rail { position: sticky; top: 12px; display: flex; flex-direction: column; gap: 12px; max-height: calc(100vh - 24px); overflow: auto; }
@media (max-width: 1000px) {
  .map-layout { grid-template-columns: 1fr; }
  .map-rail { position: static; max-height: none; overflow: visible; }
}

/* Cursor tooltip. Kept as text in the DOM as well (the readout and the title element),
   so nothing is available only on hover. */
.tip {
  position: absolute; pointer-events: none; z-index: 5;
  background: var(--ink); color: var(--bg);
  font-size: 0.8rem; line-height: 1.35; padding: 6px 9px; border-radius: 4px;
  max-width: 260px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
}
.tip[hidden] { display: none; }
.tip b { display: block; }

/* Skip link: hidden until focused, so a keyboard user can jump past the masthead
   and, on the map page, past the map itself. */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: var(--accent-ink);
  padding: 10px 16px; border-radius: 0 0 4px 0; text-decoration: none; font-weight: 600;
}
.skip-link:focus { left: 0; }

/* Respect a reduced-motion preference. There is little animation, but scroll
   behaviour and any future transition should honour it. */
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important; scroll-behavior: auto !important; }
}

/* Past / Present / Future. Three modes, because each rests on data published at a
   different geography and blending them would spread a county figure across divisions. */
/* One segmented control, not three separate buttons. Three bordered boxes side by side
   read as three unrelated choices; a single track with a moving selection reads as one
   setting with three positions, which is what it is. */
.modes {
  display: inline-flex; gap: 2px; margin: 0; flex-wrap: wrap;
  background: var(--sunk); padding: 3px; border-radius: var(--r-sm);
  border: 1px solid var(--line);
}
.modes button {
  font: inherit; font-size: 0.9rem; font-weight: 600; cursor: pointer;
  padding: 0 var(--s-5); height: 34px; border: 1px solid transparent;
  background: transparent; color: var(--muted); border-radius: 4px;
  transition: background-color .12s ease, color .12s ease, box-shadow .12s ease;
}
.modes button[aria-pressed="true"] {
  background: var(--raised); color: var(--ink); border-color: var(--line);
  box-shadow: var(--shadow-sm);
}
.modes button:hover { color: var(--ink); }
.modes button[aria-pressed="true"]:hover { color: var(--ink); }
.modes button:focus-visible { outline: none; box-shadow: var(--ring); }

/* The map is the point of the page, so it gets the full width. */
/* The map is the point of the page, so it takes the width it is given rather than
   being capped well short of it. */
svg.map.wide { max-width: 100%; margin: 0 auto; }

/* Area profile. Open by default in the rail; the summary still names the selection. */
details.profile { margin: 0; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--raised); overflow: hidden; }
details.profile > summary {
  cursor: pointer; padding: 11px 14px; font-weight: 600; list-style: none; font-size: 0.95rem;
}
details.profile > summary::-webkit-details-marker { display: none; }
details.profile > summary::before { content: "\25B8 "; color: var(--muted); }
details.profile[open] > summary::before { content: "\25BE "; }
details.profile > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
details.profile .panel { border: 0; border-top: 1px solid var(--line); border-radius: 0; }

/* Sections under the map, collapsed by default: the map is the answer, these are the
   working behind it and should be there when wanted rather than always in the way. */
details.section { margin: var(--s-3) 0; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--raised); }
details.section > summary {
  cursor: pointer; padding: var(--s-3) var(--s-4); font-weight: 600; list-style: none;
  background: var(--panel); border-radius: var(--r-md);
}
details.section > summary:hover { background: var(--sunk); }
details.section[open] > summary { border-radius: var(--r-md) var(--r-md) 0 0; border-bottom: 1px solid var(--line); }
details.section > summary::-webkit-details-marker { display: none; }
details.section > summary::before { content: "\25B8 "; color: var(--muted); }
details.section[open] > summary::before { content: "\25BE "; }
details.section > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
details.section > *:not(summary) { margin-left: 16px; margin-right: 16px; }
details.section > *:last-child { margin-bottom: 14px; }
input[type="search"] {
  font: inherit; padding: 6px 8px; border: 1px solid var(--control-line);
  border-radius: 4px; background: var(--raised); color: var(--ink); width: 100%;
}

/* Zoom controls. Buttons first: wheel and pinch are conveniences, not the interface. */
.zoombar { display: flex; align-items: center; gap: var(--s-2); margin: 0 0 var(--s-2); flex-wrap: wrap; }
.zoombar button {
  font: inherit; cursor: pointer; min-width: 38px; min-height: 38px; padding: 0 var(--s-3);
  border: 1px solid var(--control-line); background: var(--raised); color: var(--ink);
  border-radius: var(--r-sm); line-height: 1; font-weight: 600;
  transition: border-color .12s ease, color .12s ease, box-shadow .12s ease;
  display: inline-flex; align-items: center; justify-content: center;
}
/* KAN-94: #zoom-reset holds an icon rather than translated text (see map.html) so its
   width never depends on which language is active; the icon carries no accessible name
   of its own (aria-hidden), that lives in aria-label/title. */
.zoombar button svg { display: block; }
.zoombar button:hover { border-color: var(--accent); color: var(--accent); box-shadow: var(--shadow-sm); }
.zoombar button:focus-visible { outline: none; box-shadow: var(--ring); border-color: var(--accent); }
.zoombar .note { margin: 0 0 0 var(--s-1); }

/* On a wide screen the zoom controls sit ON the map, the way every map product puts them,
   instead of floating above it as three loose boxes in the page flow. They stay first in
   the DOM so the keyboard still reaches them before the map itself, and they are only
   lifted out of the flow where there is room — on a narrow screen they would cover the
   thing they control. */
@media (min-width: 760px) {
  .map-frame > .zoombar {
    position: absolute; top: var(--s-3); left: var(--s-3); z-index: 3; margin: 0;
    background: var(--raised); border: 1px solid var(--line);
    border-radius: var(--r-md); padding: var(--s-2);
    box-shadow: var(--shadow-md);
  }
  .map-frame > .zoombar button { background: transparent; border-color: transparent; }
  .map-frame > .zoombar button:hover { background: var(--panel); border-color: var(--accent-line); }
  .map-frame > .zoombar .note { padding-right: var(--s-2); }
}
svg.map { touch-action: none; }
svg.map:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* ---- Charts ------------------------------------------------------------- */

svg.chart {
  width: 100%; height: auto; display: block; background: var(--raised);
  border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--shadow-sm);
}
svg.chart .axis { stroke: var(--line); }
svg.chart .grid-line { stroke: var(--line); stroke-dasharray: 2 3; }
svg.chart .label { fill: var(--muted); font-size: 11px; font-family: var(--sans); }
svg.chart .history { stroke: var(--ink); fill: none; stroke-width: 2; stroke-linejoin: round; }
svg.chart .model { stroke: var(--accent); fill: none; stroke-width: 2; stroke-dasharray: 5 4; }
svg.chart .baseline { stroke: var(--faint); fill: none; stroke-width: 1.5; stroke-dasharray: 2 3; }
svg.chart .band { fill: var(--accent); opacity: 0.14; }
svg.chart .divider { stroke: var(--control-line); stroke-dasharray: 1 3; }
svg.chart .end { fill: var(--ink); }
svg.chart .candidate { stroke: var(--accent); fill: none; stroke-width: 1.1; stroke-dasharray: 3 3; opacity: 0.35; }
svg.chart .candidate.hover { opacity: 1; stroke-width: 2.5; }
svg.chart .baseline.hover { opacity: 1; stroke-width: 2.5; }
svg.chart .model.hover { stroke-width: 3.2; }

/* ---- Loading skeleton (KAN-73) -------------------------------------------
   One placeholder for the map panel and every chart, present in the served markup so it
   is what a reader's first paint actually shows -- not swapped in after a round trip. Its
   own aspect-ratio holds the footprint the real svg.chart/svg.map will take, so revealing
   the real content does not jump the page. Text names the file shell.mjs's loadJson is
   fetching, and the byte count only once loadJson has actually read one from the
   response -- see watchLoading() in shell.mjs. A guess would be a number this ticket's own
   rule 1 forbids; an honest "no size yet" is what a reader sees until one exists. */
.loading {
  display: flex; align-items: center; justify-content: center; text-align: center;
  aspect-ratio: 31 / 15; min-height: 160px; padding: var(--s-4);
  border: 1px dashed var(--control-line); border-radius: var(--r-lg);
  background: var(--panel); color: var(--muted); font-size: 0.85rem;
  animation: loading-pulse 1.6s ease-in-out infinite;
}
/* The map is portrait (viewBox 620x780) where a chart is landscape; matching the
   skeleton's own aspect-ratio to it is what stops the reveal from jumping the page. */
.loading.map { aspect-ratio: 31 / 39; min-height: 320px; }
/* An explicit display beats the [hidden] default, so a hidden skeleton kept showing --
   the same pitfall already guarded at .controls[hidden] and .tip[hidden] below. */
.loading[hidden] { display: none; }
@keyframes loading-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.6; } }
@media (prefers-reduced-motion: reduce) { .loading { animation: none; } }

/* ---- Landing page --------------------------------------------------------- */

/* The hero used to align its two columns at the bottom, which left the figures floating
   at the top of a tall empty column — the single strongest "unfinished" signal on the
   page. Both columns now start at the top and the figure panel is a self-contained block,
   so the column ends where its content ends instead of trailing off. */
/* One column, with the figures as a full-width strip beneath the statement.
   This used to be a two-column grid, and the right column held four small tiles at the
   top of a column as tall as the headline beside it — so the page opened on a large void,
   which is the most reliable way to look unfinished. The figures now run the full measure
   as a metrics strip, the statement gets the whole width to be set large, and there is no
   empty region at any breakpoint. */
/* The hero sits on a full-bleed tinted band.
   Left-aligned type in a 1180px column leaves a wide margin on the right whatever is done
   with it, and on a white page that margin reads as an unfinished column. Against a band
   that runs the full width of the window it reads as framing, which is what it is — so
   the asymmetry becomes deliberate instead of looking like missing content. The band is
   drawn with a pseudo-element rather than a wrapper so no page markup has to change. */
.hero {
  padding: var(--s-7) var(--s-7) var(--s-6);
  margin: 0 0 var(--s-7);
  background: linear-gradient(160deg, var(--panel) 0%, var(--bg) 62%);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.hero-statement { max-width: 68ch; }
@media (max-width: 700px) { .hero { padding: var(--s-5) var(--s-4) var(--s-5); } }
.hero h1 { font-size: clamp(2.2rem, 4.4vw, 3.4rem); max-width: 18ch; letter-spacing: -0.032em; line-height: 1.03; }
.hero .lede { margin-bottom: var(--s-5); font-size: 1.08rem; max-width: 58ch; }
.hero .actions { display: flex; gap: var(--s-3); flex-wrap: wrap; }
.hero-figures { margin-top: var(--s-7); }
.hero .stats { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
/* A quiet label above the figures, so the strip reads as a summary of the system rather
   than as four numbers that happen to be there. */
.hero .stats-head {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--faint); margin: 0 0 var(--s-3);
}
@media (max-width: 860px) { .hero-figures { margin-top: var(--s-6); } }

.horizons { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px; }
/* The three horizons are the page's primary route in, so they are the one place that
   spends real elevation: a card that lifts under the cursor and shows its accent edge. */
.horizon {
  display: flex; flex-direction: column; gap: var(--s-2);
  padding: var(--s-5) var(--s-5) var(--s-4);
  border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--raised);
  color: inherit; text-decoration: none; position: relative; overflow: hidden;
  box-shadow: var(--shadow-sm);
  transition: transform .14s ease, box-shadow .14s ease, border-color .14s ease;
}
/* A hairline of accent along the top edge, revealed on hover — the card's own signal
   that it is a way in, without adding another border to the resting state. */
.horizon::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: var(--accent); transform: scaleX(0); transform-origin: left;
  transition: transform .18s ease;
}
.horizon:hover { border-color: var(--accent-line); box-shadow: var(--shadow-md); transform: translateY(-2px); }
.horizon:hover::before { transform: scaleX(1); }
.horizon:focus-visible { outline: none; box-shadow: var(--ring), var(--shadow-md); }
.horizon .eyebrow { margin: 0; }
.horizon h3 { margin: 0; font-size: 1.2rem; }
.horizon p { margin: 0; color: var(--muted); font-size: 0.9rem; }
.horizon .figure { font-size: 1.5rem; color: var(--ink); margin-top: var(--s-2); line-height: 1.1; }
.horizon .figure small {
  display: block; font-weight: 400; font-size: 0.76rem; letter-spacing: 0;
  color: var(--muted); margin-top: var(--s-1); font-variant-numeric: normal;
}
.horizon .go { margin-top: auto; padding-top: var(--s-3); color: var(--accent); font-weight: 600; font-size: 0.88rem; }
@media (prefers-reduced-motion: reduce) { .horizon:hover { transform: none; } }

.entries { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: var(--s-3); }
.entry {
  display: block; padding: var(--s-4); border: 1px solid var(--line);
  border-radius: var(--r-md); text-decoration: none; color: inherit; background: var(--raised);
  transition: border-color .12s ease, box-shadow .12s ease, background-color .12s ease;
}
.entry:hover { border-color: var(--accent-line); background: var(--panel); box-shadow: var(--shadow-sm); }
.entry:focus-visible { outline: none; box-shadow: var(--ring); border-color: var(--accent); }
.entry b { display: block; font-weight: 650; letter-spacing: -0.008em; }
.entry span { display: block; color: var(--muted); font-size: 0.85rem; margin-top: var(--s-1); line-height: 1.45; }

.labels { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: 10px 24px; margin: 0; }
.labels div { display: grid; grid-template-columns: auto 1fr; gap: 8px; align-items: start; font-size: 0.88rem; }
.labels dt { font-weight: 600; white-space: nowrap; }
.labels dd { margin: 0; color: var(--muted); }

/* ---- Year scrubber's play toggle ---------------------------------------- */

/* A pressed-state toggle rather than an action, so it takes the segmented-control
   treatment rather than .button: it stays put and reports whether it is running. */
.yearplay {
  font: inherit; font-size: 0.85rem; font-weight: 600; cursor: pointer;
  height: 32px; padding: 0 var(--s-4);
  border: 1px solid var(--control-line); border-radius: var(--r-sm);
  background: var(--raised); color: var(--ink);
  transition: background-color .12s ease, border-color .12s ease, color .12s ease;
}
.yearplay:hover { border-color: var(--accent); color: var(--accent); }
.yearplay[aria-pressed="true"] {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink);
}
.yearplay:focus-visible { outline: none; box-shadow: var(--ring); border-color: var(--accent); }

/* ---- Hero jump links ------------------------------------------------------ */

/* Three destinations in the first screen, so the page can be used before it is read.
   Sized for a thumb first: full-width rows on a phone, an inline row once there is
   space. 44px minimum height is the touch target Apple and Google both publish. */
.hero-jump {
  list-style: none; margin: var(--s-4) 0 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--s-2);
}
.hero-jump a {
  display: flex; align-items: center; gap: var(--s-2);
  min-height: 44px; padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--raised); color: var(--ink); text-decoration: none;
  font-weight: 550; font-size: 0.95rem;
  transition: border-color .12s ease, color .12s ease;
}
.hero-jump a::after { content: "→"; margin-left: auto; color: var(--muted); }
.hero-jump a:hover { border-color: var(--accent); color: var(--accent); }
.hero-jump a:hover::after { color: var(--accent); }
.hero-jump a:focus-visible { outline: none; box-shadow: var(--ring); border-color: var(--accent); }

@media (min-width: 640px) {
  .hero-jump { flex-direction: row; flex-wrap: wrap; }
  .hero-jump a { flex: 1 1 auto; }
}

/* ---- Collapsible primary navigation, below 640px -------------------------- */

/* Hidden above 640px: the row layout works there and a menu button would only add a
   click. Below it the six section links wrapped to three rows and pushed the first
   usable control 62% down a 390px screen. */
.masthead .menu-toggle { display: none; }

@media (max-width: 639px) {
  .masthead .inner { gap: var(--s-2) var(--s-3); }
  .masthead .brand { flex: 1 1 auto; }

  .masthead .menu-toggle {
    display: inline-flex; align-items: center; gap: 7px;
    font: inherit; font-size: 0.85rem; font-weight: 600; cursor: pointer;
    min-height: 40px; padding: 0 var(--s-3);
    background: transparent; color: var(--masthead-ink);
    border: 1px solid var(--masthead-line); border-radius: var(--r-sm);
  }
  /* Drawn in CSS rather than shipped as an icon: three lines need no font, no file and
     no translation, and it becomes a cross when open so the control states what it does. */
  .masthead .menu-toggle::before {
    content: ""; width: 14px; height: 10px;
    border-top: 2px solid currentColor; border-bottom: 2px solid currentColor;
    box-sizing: border-box; position: relative;
  }
  .masthead.nav-open .menu-toggle::before {
    height: 14px; border: 0;
    background: linear-gradient(currentColor, currentColor), linear-gradient(currentColor, currentColor);
    background-size: 100% 2px, 100% 2px; background-position: center, center;
    background-repeat: no-repeat; transform: rotate(45deg);
  }
  .masthead .menu-toggle:focus-visible { outline: none; box-shadow: var(--ring); }

  /* The nav itself: a full-width column under the brand row, only when opened. Each row
     is a 44px touch target, the size Apple and Google both publish as the minimum. */
  .masthead nav.main { display: none; width: 100%; flex-direction: column; gap: 0; }
  .masthead.nav-open nav.main { display: flex; }
  .masthead nav.main a {
    display: flex; align-items: center; min-height: 44px;
    padding: 0 var(--s-2); border-top: 1px solid var(--masthead-line);
  }

  /* The theme control folds into the menu; the language control does not. Irish parity is
     a rule this project holds itself to, and a language switch a reader has to go hunting
     for is one most readers never find. It also keeps the automated bilingual check able
     to reach it at 390px, which is how this distinction was noticed. */
  .masthead .theme-toggle:not(.lang-toggle) { display: none; }
  .masthead.nav-open .theme-toggle:not(.lang-toggle) { display: inline-flex; margin-top: var(--s-2); }
  .masthead .lang-toggle { min-height: 40px; }
}

/* The door to the phone build. Quiet on a desk, where it is information; the phone
   layout gives it room because that is where it is an offer. */
.phone-app { margin-top: var(--s-3); }
.phone-app a { color: var(--accent); text-decoration: none; font-weight: 550; }
.phone-app a:hover { text-decoration: underline; }
@media (max-width: 639px) {
  .phone-app { margin-top: var(--s-4); font-size: 0.95rem; }
}
