/* ============================================================================
   ASTRAMIR — SITE SHELL

   Consumes the brand layer and adds only what a multi-page site needs:
   header, footer, page furniture, and the handful of components every page
   repeats. Nothing here redefines a brand decision; if a value is missing,
   it belongs in the token layer, not in this file.

     ../brand/tokens/astramir.tokens.css      primitives + both modes
     ../brand/backgrounds/backgrounds.css     the nine fields
     ./site.css                               this file

   MODE IS ASSIGNED PER PAGE, on <html data-mode>. Pages about operating run
   Operate; pages about accountability, people and paperwork run Publish. That
   is the mode contract doing real work across a site instead of being asserted
   in a style guide. The header toggle lets a visitor override it.

   PORTING TO ASTRO: the header and footer are the layout, everything between
   <main> tags is the page. Each page's <html data-mode> becomes a layout prop.
   Nothing here depends on being static.
   ========================================================================= */

*, *::before, *::after { box-sizing: border-box; }
html { scroll-padding-top: 6.5rem; }

body {
  margin: 0;
  background: var(--color-ground);
  color: var(--color-ink);
  font-family: var(--font-sans);
  font-size: var(--text-body);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
}

/* ---- The two runs ------------------------------------------------------
   The page is built from two regions rather than one document that changes
   its mind. The dark run carries Operate, the light run carries Publish, each
   declares its own mode on the wrapper, and the line between them is a real
   edge in the layout.

   That replaces a scroll-timed flip of the whole document. However the timing
   was tuned there was always a moment where part of one region wore the
   other's colours, and no easing fixes a boundary in the wrong place.

   The dark run gradates as it descends — void, then violet-tinted, then
   toward indigo — so the eye is prepared for light before it arrives. The
   stops are palette mixes rather than invented blues: the ground warms toward
   the band's own short end.

   The light run does not gradate. It is simply light, which is what makes the
   frontier read as intentional.
   ------------------------------------------------------------------------ */
/* Each run sets ink as well as ground. Without this, anything inheriting
   colour from <body> — which sits outside both wrappers — resolves against the
   ROOT mode, while elements setting their own colour inside a run resolve
   against the run. Headings went near-white on the light ground while body
   copy stayed dark, in the same paragraph block. */
.dark-run, .light-run { color: var(--color-ink); }

.dark-run {
  background: linear-gradient(180deg,
    var(--p-void-900) 0%,
    color-mix(in oklab, var(--p-violet) 14%, var(--p-void-900)) 30%,
    color-mix(in oklab, var(--p-violet) 30%, var(--p-void-900)) 58%,
    color-mix(in oklab, var(--p-indigo) 38%, var(--p-void-900)) 82%,
    color-mix(in oklab, var(--p-indigo) 50%, var(--p-void-900)) 100%);
}
/* Fields sit ON the gradient rather than covering it with a flat ground. */
.dark-run .bg { background-color: transparent; }

.light-run { background: var(--p-sheet-050); }
.light-run .bg { background-color: transparent; }

/* The header is fixed at Operate and never changes. It spans both runs, and a
   bar that restyles as you scroll past a boundary reads as instability rather
   than as response. The lockup already sits on its own void plate, so a dark
   bar is the state the mark expects.

   Nothing on the page now changes colour on scroll: the runs are static and
   the header is static. There is no crossover left to time. */
.site-header { border-block-end-color: color-mix(in oklab, var(--p-void-050) 16%, transparent); }
h1, h2, h3, h4 { margin: 0; font-weight: 500; }
p { margin: 0; }
img { max-inline-size: 100%; }

/* ---- Type roles -------------------------------------------------------- */
.d1 { font-size: var(--text-display-1); line-height: var(--leading-display); letter-spacing: var(--track-display); font-variation-settings: "wdth" var(--wdth-wide), "wght" 500; text-wrap: balance; }
.d2 { font-size: var(--text-display-2); line-height: 1.04; letter-spacing: var(--track-display); font-variation-settings: "wdth" var(--wdth-wide), "wght" 500; text-wrap: balance; }
.d3 { font-size: var(--text-display-3); line-height: 1.05; letter-spacing: var(--track-title); font-variation-settings: "wdth" 112, "wght" 500; text-wrap: balance; }
.title { font-size: var(--text-title); line-height: var(--leading-title); letter-spacing: var(--track-title); font-variation-settings: "wdth" 106, "wght" 500; text-wrap: balance; }
.heading { font-size: var(--text-heading); line-height: 1.3; font-variation-settings: "wdth" var(--wdth-normal), "wght" 600; }

.prose { max-width: var(--measure); color: var(--color-ink-2); }
.prose.lead { font-size: var(--text-body-lg); color: var(--color-ink); }
.prose.small { font-size: var(--text-small); }

/* Data label: legends, table headers, captions. NEVER above a heading. */
.label { font-family: var(--font-mono); font-size: var(--text-micro); letter-spacing: var(--track-label); text-transform: uppercase; font-variation-settings: "wdth" 87, "wght" 500; color: var(--color-ink-3); line-height: 1.4; }
.notation { font-family: var(--font-mono); font-size: var(--text-notation); letter-spacing: var(--track-notation); font-variation-settings: "wdth" 87, "wght" 400; font-variant-numeric: tabular-nums; }
/* Interface text. Archivo narrow, not the mono: nav, buttons, toggles. */
.ui-label { font-family: var(--font-sans); font-size: var(--text-micro); letter-spacing: 0.14em; text-transform: uppercase; font-variation-settings: "wdth" var(--wdth-narrow), "wght" 600; }
/* Capability names only — Astramir, Verity, Commander. Never body copy. */
.capability { font-variation-settings: "wdth" 118, "wght" 200; letter-spacing: 0.04em; }

code { font-family: var(--font-mono); font-size: 0.9em; font-variation-settings: "wdth" 87; }

/* Available to assistive technology, absent from the visual layout. Used where
   a visual convention (an asterisk, an icon) carries meaning that a screen
   reader would otherwise miss. */
.visually-hidden {
  position: absolute; inline-size: 1px; block-size: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

a { color: var(--color-accent); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--color-accent-hover); }
:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; border-radius: var(--radius-edge); }

/* ---- Layout ------------------------------------------------------------ */
.wrap { width: min(1240px, 100% - 3rem); margin-inline: auto; }

/* A narrow column shares the page's LEFT EDGE rather than centring itself.
   Centring a 760px block inside a 1240px page gives it a different left edge
   from every other section, and the page visibly loses its spine. So .narrow
   takes the same outer width as .wrap and constrains its children instead. */
.narrow { width: min(1240px, 100% - 3rem); margin-inline: auto; }
.narrow > * { max-inline-size: 760px; }

@media (max-width: 640px) { .wrap, .narrow { width: min(1240px, 100% - 2rem); } }

.stack { display: grid; gap: var(--space-3); align-content: start; }
.stack > h1 + *, .stack > h2 + *, .stack > h3 + * { margin-block-start: calc(var(--space-1) * -1); }
.stack.wide { gap: var(--space-6); }

.section { position: relative; padding-block: var(--space-24); overflow: hidden; }
.section.tight { padding-block: var(--space-16); }
.section.quiet { padding-block: var(--space-32); }
@media (max-width: 860px) {
  .section { padding-block: var(--space-16); }
  .section.quiet { padding-block: var(--space-24); }
}
.section + .section { border-block-start: var(--hairline) solid var(--color-rule); }
.section-head { display: grid; gap: var(--space-4); margin-block-end: var(--space-12); }

.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); gap: var(--space-8); }
.grid-3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); gap: var(--space-8); }
/* auto-fit fills whatever room it finds, so a six-card set laid itself out as
   4 + 2. Where the row count is part of the design, state it. */
.grid-3.of-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 980px) { .grid-3.of-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) { .grid-3.of-3 { grid-template-columns: 1fr; } }

.split  { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--space-12); align-items: start; }
@media (max-width: 900px) { .split { grid-template-columns: 1fr; gap: var(--space-8); } }

/* ---- Skip link (WCAG 2.4.1) -------------------------------------------- */
.skip {
  position: fixed; inset-inline-start: var(--space-4); inset-block-start: var(--space-2); z-index: 90;
  transform: translateY(-250%);
  background: var(--color-ground-raised); color: var(--color-accent);
  border: var(--hairline) solid var(--color-ink-3); border-radius: var(--radius-edge);
  padding: var(--space-2) var(--space-4); min-block-size: 44px;
  display: inline-flex; align-items: center;
  transition: transform var(--dur-fast) var(--ease-focus);
}
.skip:focus-visible { transform: translateY(0); }

/* ---- Header ------------------------------------------------------------ */
.site-header {
  position: sticky; inset-block-start: 0; z-index: 50;
  background: color-mix(in oklab, var(--color-ground) 88%, transparent);
  backdrop-filter: blur(14px);
  border-block-end: var(--hairline) solid var(--color-rule);
}
.site-header-in { display: flex; align-items: center; gap: var(--space-4); padding-block: var(--space-3); flex-wrap: wrap; }

/* The lockup is the no-background export (white strokes, transparent ground),
   so it sits directly on the header's Operate ground with no plate of its own.
   Never inverted: inversion turns the brand cyan orange. Never cropped. */
.brand-plate {
  display: inline-flex; align-items: center; flex: none;
  padding: var(--space-1) 0;
}
.brand-plate img { display: block; block-size: 34px; inline-size: auto; }

/* Light and dark lockups.
   BOTH slots currently point at the same dark asset, because only the dark
   lockup exists. Every placement sits in the Operate (dark) run, so that is
   fine today; a Publish-mode placement would need the light lockup first. */
/* Specificity matters here: `.brand-plate img` and `.brand-hero img` are
   (0,1,1) and would beat a bare `.brand-light` (0,1,0), putting the hidden
   variant back on screen — which is exactly what happened, rendering the
   lockup twice in the header. Match on img so the comparison is fair. */
/* Specificity, decisively. These compete with layout rules on the same
   elements — .brand-plate img and .brand-hero img are both (0,1,1), so a bare
   img.brand-light ties with them and loses on source order, which rendered the
   lockup twice. Twice. Qualifying by container puts these at (0,2,1) so they
   win regardless of where they sit in the file or what gets added later. */
.brand-plate img.brand-light,
.brand-hero  img.brand-light { display: none; }
[data-mode="publish"] .brand-plate img.brand-dark,
[data-mode="publish"] .brand-hero  img.brand-dark  { display: none; }
[data-mode="publish"] .brand-plate img.brand-light,
[data-mode="publish"] .brand-hero  img.brand-light { display: block; }

/* The mark leads on the landing page. Sized against the viewport so it stays
   dominant on a laptop without overwhelming a phone. */
.brand-hero {
  /* The lockup spans the full content width on the landing page and zooms
     out as the hero scrolls away: site.js sets --mark-scale from 1 down to
     0.5 over the first 60% of the hero. A transform, so the text below never
     reflows; the space it leaves is in the hero, which is leaving anyway.
     Paused motion and reduced motion hold it at full size. */
  display: block; inline-size: 100%;
  padding: 0;                     /* the no-bg asset needs no box around it */
  margin-block-end: var(--space-4);
}
.brand-hero img {
  /* Capped at 60% of the viewport from tablet up, as on the coming-soon page;
     on a phone 60% is too small to read, so there it keeps the content width. */
  display: block; inline-size: min(100%, 60vw); block-size: auto;
  transform: scale(var(--mark-scale, 1)); transform-origin: 0 50%;
}
@media (max-width: 640px) { .brand-hero img { inline-size: 100%; } }

/* Trademark mark. Superscript without disturbing the line box, and never
   inheriting a display weight — at wght 200 the glyph disappears. */
.tm {
  /* 0.42em superscript rendered as a 5px dash on a 12px nav label. Sized to
     roughly x-height and raised proportionally instead, so it reads as a
     trademark at every size the word appears in. */
  font-size: 0.62em; vertical-align: 0.34em; line-height: 0;
  letter-spacing: 0; margin-inline-start: 0.06em;
  font-variation-settings: "wdth" 100, "wght" 500;
}
/* Uppercase interface labels are already small; the mark needs more of the
   cap height there or it disappears into the tracking. */
.ui-label .tm, .label .tm { font-size: 0.72em; vertical-align: 0.26em; }
/* Display settings run at wght 200, where the glyph thins to nothing. */
.d1 .tm, .d2 .tm, .d3 .tm, .capability .tm { font-variation-settings: "wdth" 100, "wght" 400; }

.site-nav { display: flex; gap: var(--space-5, 1.25rem); margin-inline-start: auto; flex-wrap: wrap; }
.site-nav a {
  color: var(--color-ink-3); text-decoration: none; white-space: nowrap;
  display: inline-flex; align-items: center; min-block-size: 44px;   /* 2.5.8 */
  border-block-end: var(--beam) solid transparent;
  transition: color var(--dur-fast) var(--ease-focus);
}
.site-nav a:hover { color: var(--color-ink); }
.site-nav a[aria-current="page"] {
  color: var(--color-ink);
  background-image: var(--band); background-repeat: no-repeat;
  background-position: 0 100%; background-size: 100% var(--beam);
}
.header-tools { display: flex; gap: var(--space-2); align-items: center; }
/* Below 1040px the nav collapses behind a Menu control (a .ctl, like the
   footer's motion control). Scoped to .js: without the script the nav stays
   a wrapped row, as before, and the control never appears. */
.header-tools .nav-toggle { display: none; }   /* outranks .ctl, which also sets display */
.nav-toggle .i-close { display: none; }
.nav-toggle[aria-expanded="true"] .i-menu { display: none; }
.nav-toggle[aria-expanded="true"] .i-close { display: block; }
@media (max-width: 1040px) {
  .header-tools { margin-inline-start: auto; }
  .js .header-tools .nav-toggle { display: inline-flex; }
  .site-nav { order: 3; flex-basis: 100%; margin-inline-start: 0; }
  .js .site-nav {
    display: none; flex-direction: column; gap: 0;
    padding-block: var(--space-2) var(--space-4);
    border-block-start: var(--hairline) solid var(--color-rule);
    max-block-size: calc(100dvh - 88px); overflow-y: auto;
  }
  .js .site-header[data-nav="open"] .site-nav { display: flex; }
  .js .site-nav a {
    min-block-size: 48px; padding-inline: var(--space-3);
    border-block-end: var(--hairline) solid var(--color-rule);
  }
  /* In a column the band moves from under the word to the row's leading edge. */
  .js .site-nav a[aria-current="page"] {
    background-image: none; color: var(--color-ink);
    border-inline-start: var(--beam) solid var(--color-accent);
  }
}
@media (max-width: 640px) {
  /* Brand and Menu only. Contact is in the menu and the hero carries the briefing button. */
  .header-tools .btn { display: none; }
}

/* ---- Controls ---------------------------------------------------------- */
.btn {
  appearance: none; cursor: pointer; display: inline-flex; align-items: center; gap: var(--space-2);
  min-block-size: 44px; padding: var(--space-3) var(--space-5, 1.25rem);
  border-radius: var(--radius-edge); border: var(--hairline) solid transparent;
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease-focus), color var(--dur-fast) var(--ease-focus), border-color var(--dur-fast) var(--ease-focus);
}
.btn-primary { background: var(--color-accent); color: var(--color-accent-ink); }
.btn-primary:hover { background: var(--color-accent-hover); color: var(--color-accent-ink); }
/* Boundary clears 3:1 (WCAG 1.4.11) — ink-3, not a hairline rule. */
.btn-secondary { background: transparent; color: var(--color-accent); border-color: var(--color-ink-3); }
.btn-secondary:hover { border-color: var(--color-accent); }
.btn-quiet { background: transparent; color: var(--color-ink-2); }
.btn-quiet:hover { color: var(--color-ink); }
.btn[disabled], .btn[aria-disabled="true"] { cursor: not-allowed; opacity: 0.45; }

.ctl {
  appearance: none; cursor: pointer; display: inline-flex; align-items: center; gap: var(--space-2);
  min-block-size: 44px; padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-edge); border: var(--hairline) solid var(--color-ink-3);
  background: transparent; color: var(--color-ink-3);
  transition: color var(--dur-fast) var(--ease-focus), border-color var(--dur-fast) var(--ease-focus);
}
.ctl:hover { color: var(--color-ink); border-color: var(--color-accent); }
.ctl[aria-pressed="true"] { color: var(--color-accent); border-color: var(--color-accent); }

/* ---- Plates ------------------------------------------------------------
   In-flow panels are separated by hairlines and take NO shadow: an instrument
   panel is machined flush. Shadows belong to surfaces that genuinely float. */
.plate {
  border: var(--hairline) solid var(--color-rule);
  border-radius: var(--radius-plate);
  background: var(--color-ground-raised);
  padding: var(--space-6);
  display: grid; gap: var(--space-3); align-content: start;
}
.plate .icon { inline-size: 28px; block-size: 28px; color: var(--color-accent); }
.plate-band { padding-block-start: var(--space-5, 1.25rem); border-block-start: var(--beam) solid transparent; border-image: var(--band) 1; background: none; border-inline: 0; border-block-end: 0; border-radius: 0; padding-inline: 0; }

/* ---- Hero -------------------------------------------------------------- */
.hero { position: relative; padding-block: var(--space-32) var(--space-24); overflow: hidden; }
.hero-in { display: grid; gap: var(--space-6); position: relative; z-index: 1; }
.hero .d1 { max-width: 17ch; }
.hero-actions { display: flex; flex-wrap: wrap; gap: var(--space-4); margin-block-start: var(--space-4); }
@media (max-width: 860px) { .hero { padding-block: var(--space-24) var(--space-16); } }

/* ---- Statement --------------------------------------------------------
   A verbatim, attested passage. Uses the band's vertical spine, which the
   system reserves for text carrying provenance — the correct form for a
   sentence that is quoted rather than written fresh each time.
   Drawn as a pseudo-element rather than border-image: reliable across
   browsers, and it keeps the band a true edge. */
.statement { position: relative; padding-inline-start: var(--space-6); display: grid; gap: var(--space-5, 1.25rem); }
.statement::before {
  content: ""; position: absolute; inset-block: 0; inset-inline-start: 0;
  inline-size: var(--beam); background: var(--band-vertical);
}
.statement p { max-width: var(--measure); color: var(--color-ink); font-size: var(--text-body-lg); }
.statement p.opening { font-size: var(--text-title); line-height: var(--leading-title); letter-spacing: var(--track-title); font-variation-settings: "wdth" 106, "wght" 500; text-wrap: balance; }

/* ---- Formula ----------------------------------------------------------
   Set as real notation rather than as a sentence. Each term stacks over two
   lines, the operators sit on the optical centre between them, and the
   parentheses scale to enclose the whole group — which is what a parenthesis
   is for and what a same-size one fails to do.

   Terms run at wdth 115 / wght 300: lighter and wider than the display voice,
   so the strokes stay open and the heartbeat behind them reads through the
   gaps instead of being blocked by them.

   Trust sits above the closing parenthesis, the position an exponent occupies.
   Raising it is the claim, so it is raised rather than written with a caret.

   The visual is aria-hidden and paired with a spoken line: a screen reader
   meeting stacked fragments, bare parentheses and a superscript announces
   punctuation soup.
   ------------------------------------------------------------------------ */
.formula {
  display: flex; align-items: center; flex-wrap: wrap;
  justify-content: center;          /* a statement, so it sits on its own axis */
  text-align: center;
  gap: 0.28em 0.34em;
  font-size: var(--text-display-3);
  line-height: 1.04;
  letter-spacing: -0.01em;
  font-variation-settings: "wdth" 115, "wght" 300;
  color: var(--color-ink);
}
/* Two words, one term. Centred on each other so the stack has an axis. */
.formula .paren + .term, .formula .term + .paren { margin-inline-start: -0.24em; }
.formula .term { display: grid; justify-items: center; gap: 0.04em; }
.formula .term b { font-weight: inherit; display: block; white-space: nowrap; }

.formula .op {
  font-style: normal;
  color: var(--color-accent);
  font-variation-settings: "wdth" 100, "wght" 400;
  font-size: 0.98em;
}

/* Enclosing, not decorative: scaled to span both lines of the terms. */
.formula .paren {
  position: relative;
  font-size: 2.45em;         /* tall enough to enclose both lines */
  line-height: 0.68;
  color: var(--color-ink);   /* structure, and the enclosure is the point */
  font-variation-settings: "wdth" 100, "wght" 200;
  margin-inline: -0.02em;
}
.formula .paren sup {
  /* Relative, not absolute. Absolute positioning took the exponent out of
     flow, so it contributed no width — and once the equation was centred the
     centring maths ignored it and Trust ran off the right edge. Relative keeps
     it in flow, occupying space, while still sitting raised. */
  position: relative;
  top: -0.66em;
  font-size: 0.28em;
  line-height: 1;
  color: var(--color-accent);
  font-variation-settings: "wdth" 118, "wght" 500;
  letter-spacing: 0.05em;
  margin-inline-start: 0.06em;
}

/* Narrow: the equation wraps to two rows, which is how a long expression
   breaks on paper. The row gap has to grow with it, or the two halves read as
   one tangle rather than as consecutive lines. */
@media (max-width: 720px) {
  .formula { font-size: var(--text-title); gap: 0.85em 0.3em; }
  .formula .paren { font-size: 2.2em; }
  .formula .paren sup { font-size: 0.32em; top: -0.6em; }
}

.formula-note {
  max-inline-size: 68ch;
  margin-inline: auto;
  text-align: left;
}

/* ---- Facts row --------------------------------------------------------- */
.facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr)); gap: var(--space-6); }
.fact { display: grid; gap: var(--space-2); padding-block-start: var(--space-4); border-block-start: var(--hairline) solid var(--color-rule); }
.fact b { font-size: var(--text-title); font-variation-settings: "wdth" 106, "wght" 500; letter-spacing: var(--track-title); }

/* ---- Reveal (FOCUS, one grammar sitewide) ------------------------------
   Two forms, and the distinction is not cosmetic.

   .reveal-load  — above the fold. Plays on first paint from CSS alone, with
                   no observer and no dependency on the deferred script. Hero
                   content must never wait for JavaScript to become visible.
   .reveal       — below the fold. Gated on an IntersectionObserver, which is
                   correct there because the visitor has to scroll to it
                   anyway.

   The first draft used .reveal in the heroes, which left every page with an
   invisible headline until the script ran. */
.js .reveal-load { animation: resolve-in var(--dur-slow) var(--ease-focus) both; }
.js .reveal-load.dly-1 { animation-delay: 90ms; }
.js .reveal-load.dly-2 { animation-delay: 180ms; }
.js .reveal-load.dly-3 { animation-delay: 270ms; }
@keyframes resolve-in {
  from { opacity: 0; filter: blur(6px); transform: translateY(10px); }
  to   { opacity: 1; filter: blur(0);   transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion="full"]) .reveal-load { animation: none; }
}
:root[data-motion="reduced"] .reveal-load { animation: none; }

.js .reveal { opacity: 0; filter: blur(6px); transform: translateY(10px); }
.js .reveal.in { opacity: 1; filter: blur(0); transform: none; transition: opacity var(--dur-slow) var(--ease-focus), filter var(--dur-slow) var(--ease-focus), transform var(--dur-slow) var(--ease-focus); }
.js .reveal.dly-1 { transition-delay: 80ms; }
.js .reveal.dly-2 { transition-delay: 160ms; }
.js .reveal.dly-3 { transition-delay: 240ms; }
@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion="full"]) .reveal { opacity: 1; filter: none; transform: none; transition: none; }
}
:root[data-motion="reduced"] .reveal { opacity: 1; filter: none; transform: none; transition: none; }

/* ---- Placeholder ------------------------------------------------------
   A visible, deliberately unlovely marker for content still to be written.
   The point is that it CANNOT be shipped by accident: a shell whose blanks
   look like design is how lorem reaches production. Every instance also goes
   on the replacement list in the page footer. */
.todo {
  border: 2px dashed var(--color-accent-quiet);
  border-radius: var(--radius-plate);
  padding: var(--space-5, 1.25rem);
  display: grid; gap: var(--space-2);
  background: color-mix(in oklab, var(--color-accent) 4%, transparent);
}
.todo > .label { color: var(--color-accent); }

/* ---- Footer ------------------------------------------------------------ */
.site-footer { padding-block: var(--space-16); border-block-start: var(--hairline) solid var(--color-rule); }
.footer-grid { display: grid; grid-template-columns: minmax(0, 2fr) repeat(auto-fit, minmax(150px, 1fr)); gap: var(--space-8); }
.footer-col { display: grid; gap: var(--space-2); align-content: start; }
.footer-col a { color: var(--color-ink-2); text-decoration: none; display: inline-flex; align-items: center; min-block-size: 32px; }
.footer-col a:hover { color: var(--color-ink); text-decoration: underline; }
.footer-legal {
  margin-block-start: var(--space-12); padding-block-start: var(--space-6);
  border-block-start: var(--hairline) solid var(--color-rule);
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--space-4) var(--space-8);
}
.footer-fine { display: grid; gap: var(--space-1); }
/* Phones: the statement takes the full width, the link groups sit two-up
   beneath it, and every link is a 44px target. The legal row stacks. */
@media (max-width: 640px) {
  .site-footer { padding-block: var(--space-12); }
  .footer-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-8) var(--space-6); }
  .footer-grid .footer-col:first-child { grid-column: 1 / -1; }
  .footer-col a { min-block-size: 44px; }
  .footer-legal { flex-direction: column; align-items: flex-start; }
}

/* ---- Forced colors ----------------------------------------------------- */
@media (forced-colors: active) {
  .plate, .todo, .site-header, .site-footer { border-color: CanvasText; }
  .site-nav a[aria-current="page"] { background-image: none; text-decoration: underline; }
}

/* ---- Print ------------------------------------------------------------- */
@media print {
  .site-header, .header-tools, .skip, .hero-actions { display: none !important; }
  .section { padding-block: 1.5rem; }
}
