/* ── 00-layers.css ───────────────────────────────────────── */
/* RelevantSec design system — see DESIGN.md at the repo root.
 *
 * Hallmark · genre: editorial+technical · macrostructure: Split Diptych (home) /
 * Long Document (research) · nav: N6 masthead + scroll-morph · footer: Ft4
 * colophon · anchor hue 62 · design-system: DESIGN.md · designed-as-app
 *
 * Authored as styles/src/*.css and served as one hashed bundle (lib/assets.js).
 * Filename order is the concatenation order.
 *
 * Layer order is declared once, here, and is the whole cascade contract:
 * anything unlayered beats everything below, so `overrides` stays empty in
 * steady state and is only a break-glass escape hatch.
 */
@layer tokens, reset, base, layout, register, chrome, controls, components, prose, code, utilities, overrides;


/* ── 01-fonts.css ───────────────────────────────────────── */
/* Self-hosted, same-origin. The CSP is `font-src 'self'` — there is no
 * third-party font request on any page, which is also why style-src can stay
 * clean of fonts.googleapis.com.
 *
 * Only the faces a page actually paints get downloaded, so broadcast surfaces
 * never fetch the serif.
 */

/* ── Cabinet Grotesk · UI, chrome, broadcast headings ─────────────────── */
@font-face {
  font-family: "Cabinet Grotesk";
  src: url("/fonts/cabinet-grotesk-400-normal.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Cabinet Grotesk";
  src: url("/fonts/cabinet-grotesk-500-normal.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Cabinet Grotesk";
  src: url("/fonts/cabinet-grotesk-800-normal.woff2") format("woff2");
  font-weight: 800; font-style: normal; font-display: swap;
}

/* ── Newsreader · document register reading face ──────────────────────── */
@font-face {
  font-family: "Newsreader";
  src: url("/fonts/newsreader-400-normal-latin.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Newsreader";
  src: url("/fonts/newsreader-400-normal-latin-ext.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Newsreader";
  src: url("/fonts/newsreader-600-normal-latin.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Newsreader";
  src: url("/fonts/newsreader-600-normal-latin-ext.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Newsreader";
  src: url("/fonts/newsreader-400-italic-latin.woff2") format("woff2");
  font-weight: 400; font-style: italic; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Newsreader";
  src: url("/fonts/newsreader-400-italic-latin-ext.woff2") format("woff2");
  font-weight: 400; font-style: italic; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ── JetBrains Mono · the outlier register: code, metadata, labels ────── */
@font-face {
  font-family: "JetBrains Mono";
  src: url("/fonts/jetbrains-mono-400-normal-latin.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("/fonts/jetbrains-mono-400-normal-latin-ext.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("/fonts/jetbrains-mono-500-normal-latin.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("/fonts/jetbrains-mono-700-normal-latin.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("/fonts/jetbrains-mono-400-italic-latin.woff2") format("woff2");
  font-weight: 400; font-style: italic; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ── metric-matched fallbacks ─────────────────────────────────────────
 * Derived by measuring each webfont against its fallback in-browser
 * (advance width of a fixed string, plus fontBoundingBox ascent/descent):
 *
 *   size-adjust   = webfont advance / fallback advance
 *   ascent/descent-override = webfont metric / size-adjust
 *
 * Numbers come from the Arial / Liberation metric family, which covers the
 * common Linux and Windows stacks. Re-derive with scripts/verify-visual.mjs's
 * probe if a face is ever swapped — wrong overrides are worse than none.
 *
 * With these in place `font-display: swap` costs almost no visible reflow,
 * which is why it is used everywhere including the hero.
 */
@font-face {
  font-family: "Cabinet Grotesk Fallback";
  src: local("Arial"), local("Liberation Sans"), local("Helvetica Neue"), local("Helvetica");
  size-adjust: 95.11%;
  ascent-override: 91.48%;
  descent-override: 29.44%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Newsreader Fallback";
  src: local("Times New Roman"), local("Liberation Serif"), local("Times");
  size-adjust: 106.71%;
  ascent-override: 69.34%;
  descent-override: 25.30%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "JetBrains Mono Fallback";
  src: local("Liberation Mono"), local("DejaVu Sans Mono"), local("Menlo"), local("Consolas");
  size-adjust: 99.98%;
  ascent-override: 102.02%;
  descent-override: 30.00%;
  line-gap-override: 0%;
}


/* ── 10-tokens.css ───────────────────────────────────────── */
@layer tokens {
  :root {
    /* ── type families ───────────────────────────────────────────────────
     * Three, which is the ceiling. Display/UI grotesque, a reading serif for
     * the document register, and mono as the outlier register that tags
     * machine-adjacent content (code, dates, counts, tags, captions).
     */
    --font-ui: "Cabinet Grotesk", "Cabinet Grotesk Fallback", ui-sans-serif, system-ui, sans-serif;
    --font-serif: "Newsreader", "Newsreader Fallback", ui-serif, Georgia, serif;
    --font-mono: "JetBrains Mono", "JetBrains Mono Fallback", ui-monospace, monospace;

    /* ── type scale · 1.25 major third off a 16px base ───────────────── */
    --text-2xs: 0.64rem;
    --text-xs: 0.8rem;
    --text-sm: 0.875rem;
    --text-base: 1rem;
    --text-md: 1.125rem;
    --text-lg: 1.25rem;
    --text-xl: 1.5625rem;
    --text-2xl: 1.953rem;
    --text-3xl: clamp(1.953rem, 1.62rem + 1.55vw, 2.441rem);
    --text-4xl: clamp(2.2rem, 1.72rem + 2.3vw, 3.052rem);
    /* Display caps at 4.6rem (73.6px) — well under the 5.5rem ceiling where
     * hero headlines start crowding themselves at 1280–1440px. */
    --text-display: clamp(2.6rem, 1.86rem + 3.4vw, 4.6rem);

    --lh-display: 1.04;
    --lh-tight: 1.18;
    --lh-snug: 1.35;
    --lh-normal: 1.55;
    --lh-prose: 1.72;

    --track-display: -0.028em;
    --track-tight: -0.015em;
    --track-label: 0.14em;

    /* ── spacing · 4pt, named by role, no raw values anywhere else ────── */
    --space-3xs: 2px;
    --space-2xs: 4px;
    --space-xs: 8px;
    --space-sm: 12px;
    --space-md: 16px;
    --space-lg: 24px;
    --space-xl: 40px;
    --space-2xl: 64px;
    --space-3xl: 96px;
    --space-4xl: 144px;

    /* ── measure + shell ─────────────────────────────────────────────── */
    --wrap: 1180px;
    --gutter: clamp(20px, 5vw, 40px);

    /* ── radius ──────────────────────────────────────────────────────── */
    --radius-0: 0;
    --radius-1: 2px;
    --radius-2: 4px;
    --radius-3: 8px;
    --radius-pill: 999px;

    /* ── motion · three buckets, three easings ───────────────────────── */
    --dur-micro: 120ms;
    --dur-short: 220ms;
    --dur-long: 420ms;
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    --ease-in: cubic-bezier(0.7, 0, 0.84, 0);
    --ease-inout: cubic-bezier(0.65, 0, 0.35, 1);

    /* ── z-index · six named levels, never a freestyle integer ───────── */
    --z-base: 1;
    --z-raised: 10;
    --z-dropdown: 100;
    --z-sticky: 200;
    --z-modal: 400;
    --z-toast: 500;

    /* ── colour ──────────────────────────────────────────────────────
     * OKLCH throughout, anchor hue 62 (signal amber) held across BOTH
     * schemes — only lightness and chroma move. Every neutral carries a
     * trace of the anchor's chroma; there are no flat greys and no pure
     * #000/#fff anywhere in the system.
     *
     * `color-scheme` is what actually flips the palette, so native
     * scrollbars, form controls and video chrome follow along for free.
     */
    color-scheme: light dark;

    /* ---- light ---- */
    --color-paper: oklch(97% 0.008 62);
    --color-paper-2: oklch(94.5% 0.01 62);
    --color-paper-3: oklch(91% 0.011 62);
    --color-ink: oklch(18% 0.012 62);
    --color-ink-2: oklch(35% 0.012 62);
    --color-muted: oklch(48% 0.014 62);
    --color-rule: oklch(85% 0.012 62);
    --color-rule-strong: oklch(76% 0.013 62);
    --color-edge: oklch(60% 0.015 62);
    --color-accent: oklch(45% 0.12 62);
    --color-accent-ink: oklch(98% 0.005 62);
    --color-focus: oklch(45% 0.13 62);
    --grain-blend: multiply;
    --grain-opacity: 0.035;
    --thumb-filter: grayscale(1) contrast(1.08);
  }

  /* ---- dark ----
   * Two selectors, because the scheme has three states: an explicit choice
   * stamps data-theme on <html> server-side (see server.js), and the default
   * "auto" stamps nothing and resolves here, before first paint.
   */
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
      color-scheme: dark;
      --color-paper: oklch(11% 0.008 62);
      --color-paper-2: oklch(14.5% 0.009 62);
      --color-paper-3: oklch(18% 0.01 62);
      --color-ink: oklch(94% 0.006 66);
      --color-ink-2: oklch(80% 0.01 64);
      --color-muted: oklch(64% 0.012 62);
      --color-rule: oklch(30% 0.01 62);
      --color-rule-strong: oklch(40% 0.011 62);
      --color-edge: oklch(53% 0.013 62);
      --color-accent: oklch(78% 0.15 62);
      --color-accent-ink: oklch(16% 0.02 62);
      --color-focus: oklch(82% 0.155 62);
      --grain-blend: screen;
      --grain-opacity: 0.05;
      --thumb-filter: grayscale(1) contrast(0.95) brightness(1.05);
    }
  }

  :root[data-theme="dark"] {
    color-scheme: dark;
    --color-paper: oklch(11% 0.008 62);
    --color-paper-2: oklch(14.5% 0.009 62);
    --color-paper-3: oklch(18% 0.01 62);
    --color-ink: oklch(94% 0.006 66);
    --color-ink-2: oklch(80% 0.01 64);
    --color-muted: oklch(64% 0.012 62);
    --color-rule: oklch(30% 0.01 62);
    --color-rule-strong: oklch(40% 0.011 62);
    --color-edge: oklch(53% 0.013 62);
    --color-accent: oklch(78% 0.15 62);
    --color-accent-ink: oklch(16% 0.02 62);
    --color-focus: oklch(82% 0.155 62);
    --grain-blend: screen;
    --grain-opacity: 0.05;
    --thumb-filter: grayscale(1) contrast(0.95) brightness(1.05);
  }

  :root[data-theme="light"] { color-scheme: light; }

  /* Depth is weight, scale and rule. These two exist for the rare case where
   * a surface must lift; they are never stacked, and never used on dark
   * (a shadow on a dark surface is a glow, which reads as cheap). */
  :root {
    --shadow-whisper: 0 1px 2px color-mix(in oklab, var(--color-ink) 8%, transparent);
    --shadow-hairline: 0 0 0 1px color-mix(in oklab, var(--color-ink) 9%, transparent);
    --wash: color-mix(in oklab, var(--color-accent) 7%, var(--color-paper));
  }
}


/* ── 20-reset.css ───────────────────────────────────────── */
@layer reset {
  *, *::before, *::after { box-sizing: border-box; }

  /* clip, not hidden: clip keeps position:sticky and position:fixed working on
   * descendants, where hidden silently creates a scroll container and breaks
   * the sticky masthead and the sticky table of contents. */
  html, body { overflow-x: clip; }

  html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    scroll-padding-top: calc(var(--masthead-h, 76px) + var(--space-lg));
  }

  body, h1, h2, h3, h4, p, figure, blockquote, dl, dd, ol, ul {
    margin: 0;
  }
  ol[class], ul[class] { list-style: none; padding: 0; }

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

  input, button, textarea, select { font: inherit; color: inherit; }
  button { background: none; border: 0; padding: 0; cursor: pointer; }

  a { color: inherit; }

  /* ── focus ──────────────────────────────────────────────────────────
   * There was not one focus rule in the previous stylesheet. This is the
   * single largest accessibility fix in the redesign.
   *
   * :focus-visible, so it is keyboard-only; instant, never transitioned —
   * a ring that fades in is useless to the person who needs it; and paired
   * with a soft halo so 2px survives on both the near-black and the paper
   * surface, and on top of a hovered row.
   */
  :where(a, button, summary, input, select, textarea, [tabindex]):focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
    box-shadow: 0 0 0 5px color-mix(in oklab, var(--color-focus) 24%, transparent);
    border-radius: var(--radius-2);
    transition: none;
  }
  :where(a, button):focus:not(:focus-visible) { outline: none; }

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

  :target { scroll-margin-top: calc(var(--masthead-h, 76px) + var(--space-lg)); }

  [hidden] { display: none !important; }
  [inert] { pointer-events: none; }
}


/* ── 30-base.css ───────────────────────────────────────── */
@layer base {
  body {
    background: var(--color-paper);
    color: var(--color-ink);
    font-family: var(--font-ui);
    font-size: var(--text-base);
    font-weight: 400;
    line-height: var(--lh-normal);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    font-synthesis-weight: none;   /* never fake a weight we did not load */
    min-height: 100vh;
  }

  h1, h2, h3, h4 {
    font-family: var(--font-heading);
    font-weight: var(--heading-weight);
    line-height: var(--lh-tight);
    letter-spacing: var(--track-tight);
    text-wrap: balance;
    overflow-wrap: anywhere;
  }

  p { text-wrap: pretty; }

  /* Machine-adjacent content — dates, counts, tags, code, captions, labels.
   * Once the outlier tags a role it tags every instance of that role. */
  .mono {
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0;
  }

  /* Film grain. Sits at the base level BEHIND content — the old one was at
   * z-index 2000, on top of literally everything including focus rings. It
   * also blends differently per scheme, because a light-on-dark speckle reads
   * as dirt on paper. */
  .grain {
    position: fixed;
    inset: 0;
    z-index: var(--z-base);
    pointer-events: none;
    opacity: var(--grain-opacity);
    mix-blend-mode: var(--grain-blend);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
  }

  /* The sprite carrying the ribbon mark; present in the DOM, never painted. */
  .sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

  .mark { fill: currentColor; }

  .skip-link {
    position: absolute;
    left: var(--space-md);
    top: var(--space-md);
    z-index: var(--z-toast);
    transform: translateY(-200%);
    background: var(--color-accent);
    color: var(--color-accent-ink);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    text-decoration: none;
    padding: var(--space-sm) var(--space-md);
    border-radius: var(--radius-2);
  }
  .skip-link:focus-visible { transform: translateY(0); }

  main:focus { outline: none; }
}


/* ── 40-layout.css ───────────────────────────────────────── */
@layer layout {
  .wrap {
    width: 100%;
    max-width: var(--wrap);
    margin-inline: auto;
    padding-inline: var(--gutter);
  }

  .section { padding-block: var(--section-gap); }

  /* Rhythm is deliberately uneven — equal padding on every section is the
   * flattest, most templated thing a page can do. */
  .section--alt {
    background: var(--color-paper-2);
    border-block: 1px solid var(--color-rule);
    padding-block: calc(var(--section-gap) * 1.15) var(--section-gap);
  }
  .section--contact {
    padding-block: calc(var(--section-gap) * 0.85) calc(var(--section-gap) * 1.2);
    border-top: 1px solid var(--color-rule);
    background: var(--wash);
  }
  .section--index { padding-block: var(--space-xl) var(--section-gap); }

  .section-head {
    margin-bottom: var(--space-xl);
    padding-bottom: var(--space-md);
    border-bottom: 1px solid var(--color-rule-strong);
  }
  .section-head--split {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-lg);
    flex-wrap: wrap;
  }

  .kicker {
    font-size: var(--text-xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--color-accent);
    margin-bottom: var(--space-sm);
  }

  .section-title {
    font-size: var(--text-3xl);
    letter-spacing: var(--track-display);
  }

  .empty-note {
    color: var(--color-muted);
    font-size: var(--text-sm);
    padding-block: var(--space-xl);
  }
}


/* ── 45-register.css ───────────────────────────────────────── */
@layer register {
  /* The register axis rewrites shape, density, type and rhythm — never colour.
   * The scheme axis rewrites colour — never shape. Keeping them disjoint is
   * what stops a 2x2 combinatorial explosion in every component rule.
   *
   * `--font-reading` and `--font-heading` are the only type knobs a component
   * ever touches; the chrome always uses --font-ui explicitly. */

  :root[data-register="document"] {
    --font-reading: var(--font-serif);
    --font-heading: var(--font-serif);
    --heading-weight: 600;
    --measure: 68ch;
    --section-gap: clamp(3.5rem, 7vw, 5.5rem);
    --reading-size: var(--text-md);
    --reading-lh: var(--lh-prose);
    --card-radius: var(--radius-0);
  }

  :root[data-register="broadcast"] {
    --font-reading: var(--font-ui);
    --font-heading: var(--font-ui);
    --heading-weight: 800;
    --measure: 60ch;
    --section-gap: clamp(4.5rem, 9vw, 7.5rem);
    --reading-size: var(--text-base);
    --reading-lh: var(--lh-normal);
    --card-radius: var(--radius-3);
  }

  /* A section can opt into the other register locally — inheritance handles
   * it, no cascade gymnastics required. */
  [data-register] { }
}


/* ── 50-chrome.css ───────────────────────────────────────── */
@layer chrome {
  /* ── N6 newspaper masthead, morphing to a compact bar on scroll ───────
   * Deliberately not the wordmark-left / links-right / CTA-right bar every
   * generated page ships. The top plate carries the brand and live issue
   * metadata; past the fold it collapses (grid-template-rows 1fr -> 0fr, which
   * animates without touching layout properties) and a compact lockup fades
   * into the link bar.
   */
  .masthead {
    position: sticky;
    top: 0;
    z-index: var(--z-sticky);
    background: color-mix(in oklab, var(--color-paper) 86%, transparent);
    backdrop-filter: blur(14px) saturate(1.4);
    border-bottom: 1px solid var(--color-rule);
  }

  .masthead-collapse {
    display: grid;
    grid-template-rows: 1fr;
    transition: grid-template-rows var(--dur-long) var(--ease-inout);
  }
  /* min-height:0 is required — without it the grid item refuses to shrink
   below its content and the 1fr->0fr collapse silently does nothing. */
  .masthead-collapse-inner { overflow: clip; min-height: 0; }
  .masthead.is-compact .masthead-collapse { grid-template-rows: 0fr; }

  .masthead-top {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: var(--space-md);
    padding-block: var(--space-lg) var(--space-md);
  }

  .masthead-est,
  .masthead-issue {
    font-size: var(--text-2xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--color-muted);
  }
  .masthead-issue { text-align: right; }

  .masthead-brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-sm);
    text-decoration: none;
    color: var(--color-ink);
  }
  .masthead-mark {
    width: 38px;
    height: auto;
    transform-origin: 50% 55%;
    transition: color var(--dur-short) var(--ease-out),
                transform var(--dur-short) var(--ease-out);
  }
  .masthead-word {
    font-family: var(--font-ui);
    font-weight: 800;
    font-size: var(--text-2xl);
    letter-spacing: -0.03em;
    line-height: 1;
  }
  .masthead-word-thin {
    font-weight: 400;
    color: var(--color-muted);
    transition: color var(--dur-short) var(--ease-out);
  }
  /* The brand is the primary "click here for home" affordance. On hover the
   * ribbon mark lights to the accent and tips slightly, and the thin "Sec"
   * warms to match — a clear, on-brand signal it is interactive. Keyboard
   * users get the global focus ring instead. */
  @media (hover: hover) {
    .masthead-brand:hover .masthead-mark {
      color: var(--color-accent);
      transform: scale(1.08) rotate(-4deg);
    }
    .masthead-brand:hover .masthead-word-thin { color: var(--color-accent); }
    .masthead-compact:hover .masthead-compact-mark { color: var(--color-accent); }
    .masthead-compact:hover .masthead-word-thin { color: var(--color-accent); }
  }

  /* the double rule that says "masthead" */
  .masthead-bar { border-top: 3px double var(--color-rule-strong); }

  .masthead-bar-inner {
    display: flex;
    align-items: center;
    gap: var(--space-lg);
    min-height: 46px;
  }

  .masthead-compact {
    display: inline-flex;
    align-items: center;
    gap: var(--space-xs);
    text-decoration: none;
    color: var(--color-ink);
    font-weight: 800;
    font-size: var(--text-sm);
    letter-spacing: -0.02em;
    opacity: 0;
    transform: translateX(-6px);
    transition: opacity var(--dur-short) var(--ease-out), transform var(--dur-short) var(--ease-out);
    width: 0;
    overflow: clip;
    white-space: nowrap;
  }
  .masthead.is-compact .masthead-compact {
    opacity: 1;
    transform: none;
    width: auto;
    margin-inline-end: var(--space-md);
  }
  .masthead-compact-mark {
    width: 20px;
    transition: color var(--dur-short) var(--ease-out);
  }

  .masthead-nav {
    display: flex;
    align-items: center;
    gap: var(--space-lg);
    margin-inline-end: auto;
  }
  .masthead-link {
    position: relative;
    font-size: var(--text-sm);
    font-weight: 500;
    letter-spacing: 0.01em;
    color: var(--color-ink-2);
    text-decoration: none;
    padding-block: var(--space-sm);
    transition: color var(--dur-micro) var(--ease-out);
  }
  .masthead-link::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    bottom: 2px;
    height: 2px;
    background: var(--color-accent);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--dur-short) var(--ease-out);
  }
  .masthead-link.is-current { color: var(--color-ink); }
  .masthead-link.is-current::after { transform: scaleX(1); }
  @media (hover: hover) {
    .masthead-link:hover { color: var(--color-ink); }
    .masthead-link:hover::after { transform: scaleX(1); }
  }

  .masthead-utils {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
  }
  .util-icon {
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    color: var(--color-muted);
    border-radius: var(--radius-2);
    transition: color var(--dur-micro) var(--ease-out);
  }
  @media (hover: hover) { .util-icon:hover { color: var(--color-accent); } }

  /* ── burger + mobile menu ────────────────────────────────────────── */
  .burger {
    display: none;
    width: 44px;
    height: 44px;
    position: relative;
    margin-inline-start: auto;
  }
  .burger span {
    position: absolute;
    left: 11px;
    width: 22px;
    height: 2px;
    background: var(--color-ink);
    transition: transform var(--dur-short) var(--ease-out), opacity var(--dur-micro) linear;
  }
  .burger span:nth-child(1) { top: 18px; }
  .burger span:nth-child(2) { top: 25px; }
  .burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(3.5px) rotate(45deg); }
  .burger[aria-expanded="true"] span:nth-child(2) { transform: translateY(-3.5px) rotate(-45deg); }

  .mobile-menu {
    position: fixed;
    inset: 0;
    z-index: var(--z-modal);
    background: var(--color-paper);
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: var(--space-xl);
    padding: var(--space-3xl) var(--gutter);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-8px);
    transition: opacity var(--dur-short) var(--ease-out), transform var(--dur-short) var(--ease-out), visibility 0s linear var(--dur-short);
  }
  .mobile-menu.is-open {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition-delay: 0s;
  }
  .mobile-menu nav { display: flex; flex-direction: column; gap: var(--space-md); }
  .mobile-menu nav a {
    font-family: var(--font-ui);
    font-weight: 800;
    font-size: var(--text-3xl);
    letter-spacing: var(--track-display);
    text-decoration: none;
    color: var(--color-ink);
    opacity: 0;
    transform: translateY(10px);
    transition: opacity var(--dur-short) var(--ease-out), transform var(--dur-short) var(--ease-out);
  }
  .mobile-menu.is-open nav a { opacity: 1; transform: none; }
  /* Stagger without an inline style — the CSP blocks style="". Written out to
   * eight so the menu can grow without this silently capping. */
  .mobile-menu.is-open nav a:nth-child(1) { transition-delay: 40ms; }
  .mobile-menu.is-open nav a:nth-child(2) { transition-delay: 80ms; }
  .mobile-menu.is-open nav a:nth-child(3) { transition-delay: 120ms; }
  .mobile-menu.is-open nav a:nth-child(4) { transition-delay: 160ms; }
  .mobile-menu.is-open nav a:nth-child(5) { transition-delay: 200ms; }
  .mobile-menu.is-open nav a:nth-child(6) { transition-delay: 240ms; }
  .mobile-menu.is-open nav a:nth-child(7) { transition-delay: 280ms; }
  .mobile-menu.is-open nav a:nth-child(8) { transition-delay: 320ms; }
  .mobile-menu-utils { display: flex; align-items: center; gap: var(--space-md); }

  /* ── Ft4 dense typographic colophon ──────────────────────────────────
   * A log-style block of credits and machine paths, not the four columns of
   * Product / Company / Resources / Legal that every generated footer ships.
   */
  .colophon {
    border-top: 3px double var(--color-rule-strong);
    background: var(--color-paper-2);
    padding-block: var(--space-2xl) var(--space-xl);
    margin-top: auto;
  }
  .colophon-inner {
    display: grid;
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr) auto;
    gap: var(--space-xl);
    align-items: start;
  }
  .colophon-wordmark {
    display: flex;
    align-items: center;
    gap: var(--space-xs);
    font-weight: 800;
    font-size: var(--text-lg);
    letter-spacing: -0.03em;
  }
  .colophon-mark { width: 26px; }
  .colophon-tag {
    margin-top: var(--space-sm);
    font-size: var(--text-2xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--color-muted);
  }
  .colophon-log { font-size: var(--text-xs); color: var(--color-muted); }
  .colophon-log > div {
    display: grid;
    grid-template-columns: 4.5rem 1fr;
    gap: var(--space-sm);
    padding-block: var(--space-3xs);
    border-bottom: 1px solid var(--color-rule);
  }
  .colophon-log dt { color: var(--color-accent); }
  .colophon-log dd { margin: 0; overflow-wrap: anywhere; }
  .colophon-log a { text-decoration: none; }
  @media (hover: hover) { .colophon-log a:hover { color: var(--color-ink); } }

  .colophon-nav { display: flex; flex-direction: column; gap: var(--space-xs); }
  .colophon-nav a {
    font-size: var(--text-sm);
    color: var(--color-ink-2);
    text-decoration: none;
    transition: color var(--dur-micro) var(--ease-out);
  }
  @media (hover: hover) { .colophon-nav a:hover { color: var(--color-accent); } }

  .colophon-rights {
    margin-top: var(--space-xl);
    padding-top: var(--space-md);
    border-top: 1px solid var(--color-rule);
    font-size: var(--text-2xs);
    color: var(--color-muted);
  }

  @media (max-width: 60rem) {
    .masthead-nav, .masthead-utils { display: none; }
    .burger { display: block; }
    .masthead-top { grid-template-columns: 1fr; justify-items: center; text-align: center; }
    .masthead-est, .masthead-issue { text-align: center; }
    .colophon-inner { grid-template-columns: 1fr; gap: var(--space-lg); }
  }
  @media (max-width: 40rem) {
    .masthead-word { font-size: var(--text-xl); }
    .masthead-mark { width: 30px; }
    .mobile-menu nav a { font-size: var(--text-2xl); }
  }
}


/* ── 55-controls.css ───────────────────────────────────────── */
@layer controls {
  /* One CTA voice across every surface: squared-off, mono label, tracked
   * uppercase. Eight states, because default+hover is two and an element with
   * only two is not finished. */
  .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-xs);
    min-height: 44px;                       /* touch floor, never below */
    padding: var(--space-sm) var(--space-lg);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    font-weight: 500;
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    text-decoration: none;
    line-height: 1;
    border: 1px solid transparent;
    border-radius: var(--radius-2);
    transition: transform var(--dur-micro) var(--ease-out),
                background-color var(--dur-micro) var(--ease-out),
                border-color var(--dur-micro) var(--ease-out),
                color var(--dur-micro) var(--ease-out);
  }
  .btn svg { transition: transform var(--dur-short) var(--ease-out); }
  @media (hover: hover) {
    .btn:hover { transform: translateY(-1px); }
    .btn:hover svg { transform: translateX(3px); }
  }
  .btn:active { transform: translateY(1px); }
  .btn[aria-disabled="true"], .btn:disabled {
    opacity: 0.55;
    cursor: not-allowed;
    transform: none;
  }

  .btn--solid {
    background: var(--color-ink);
    color: var(--color-paper);
  }
  @media (hover: hover) {
    .btn--solid:hover { background: var(--color-accent); color: var(--color-accent-ink); }
  }

  .btn--ghost {
    border-color: var(--color-edge);
    color: var(--color-ink);
  }
  @media (hover: hover) {
    .btn--ghost:hover { border-color: var(--color-accent); color: var(--color-accent); }
  }

  .btn--lg { min-height: 52px; padding-inline: var(--space-xl); font-size: var(--text-sm); }

  .cta-row { display: flex; flex-wrap: wrap; gap: var(--space-sm); align-items: center; }
  .cta-row--center { justify-content: center; }

  .link-more {
    display: inline-flex;
    align-items: center;
    gap: var(--space-xs);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    text-decoration: none;
    color: var(--color-accent);
    padding-block: var(--space-2xs);
  }
  .link-more svg { transition: transform var(--dur-short) var(--ease-out); }
  @media (hover: hover) { .link-more:hover svg { transform: translateX(4px); } }

  /* ── tags ────────────────────────────────────────────────────────── */
  .tags { display: flex; flex-wrap: wrap; gap: var(--space-2xs); }
  .tag {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2xs);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    letter-spacing: 0.06em;
    text-transform: lowercase;
    text-decoration: none;
    color: var(--color-muted);
    padding: var(--space-3xs) var(--space-xs);
    border: 1px solid var(--color-rule-strong);
    border-radius: var(--radius-1);
    transition: color var(--dur-micro) var(--ease-out), border-color var(--dur-micro) var(--ease-out);
  }
  a.tag { min-height: 26px; }
  @media (hover: hover) {
    a.tag:hover { color: var(--color-accent); border-color: var(--color-accent); }
  }

  .tag-filter {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2xs);
    margin-top: var(--space-lg);
  }
  .tag--filter { min-height: 32px; padding-inline: var(--space-sm); }
  .tag--filter.is-active {
    background: var(--color-ink);
    color: var(--color-paper);
    border-color: var(--color-ink);
  }
  .tag-count { color: currentColor; opacity: 0.6; }

  /* ── theme toggle + language switch ──────────────────────────────── */
  .theme-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2xs);
    min-height: 34px;
    padding-inline: var(--space-xs);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    text-decoration: none;
    color: var(--color-muted);
    border: 1px solid var(--color-rule-strong);
    border-radius: var(--radius-pill);
    transition: color var(--dur-micro) var(--ease-out), border-color var(--dur-micro) var(--ease-out);
  }
  .theme-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    border: 1px solid currentColor;
    background: linear-gradient(to right, currentColor 50%, transparent 50%);
  }
  @media (hover: hover) {
    .theme-toggle:hover { color: var(--color-accent); border-color: var(--color-accent); }
  }

  .lang-switch { display: inline-flex; align-items: center; }
  .lang-opt {
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    letter-spacing: var(--track-label);
    text-decoration: none;
    color: var(--color-muted);
    padding: var(--space-xs) var(--space-2xs);
    transition: color var(--dur-micro) var(--ease-out);
  }
  .lang-opt.is-active { color: var(--color-ink); }
  .lang-opt + .lang-opt { border-left: 1px solid var(--color-rule-strong); }
  @media (hover: hover) { .lang-opt:hover { color: var(--color-accent); } }

  .theme-toggle--mobile, .lang-switch--mobile { transform: scale(1.15); transform-origin: left center; }
}


/* ── 60-home.css ───────────────────────────────────────── */
@layer components {
  /* ── H2 split diptych hero ───────────────────────────────────────────
   * Statement left, live proof column right. Deliberately NOT a
   * min-height:100vh block with everything stacked on one centred axis —
   * that shape is the single most reliable generated-page tell, and it
   * wastes the first screen on a site whose credibility is its output.
   *
   * Bottom padding is heavier than top so the hero sits into the page
   * rather than floating above it.
   */
  .hero {
    position: relative;
    padding-block: clamp(2.5rem, 5vw, 4rem) clamp(4rem, 9vw, 7rem);
    border-bottom: 1px solid var(--color-rule);
    overflow: clip;
  }

  /* The one piece of hero decoration, and it is motivated: an engineering
   * grid field, the substrate the research is drawn on. Fades out so it never
   * competes with the type. */

  .hero-grid {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1.45fr) minmax(0, 0.85fr);
    gap: clamp(var(--space-xl), 6vw, var(--space-3xl));
    align-items: start;
  }

  .hero-eyebrow {
    font-size: var(--text-xs);
    letter-spacing: 0.1em;
    color: var(--color-accent);
    margin-bottom: var(--space-lg);
    min-height: 1.4em;
  }
  /* The caret is the one infinite animation kept, and only because it is
   * semantic: it sits at the end of a typed command and signals "you would
   * type next". It stops entirely under reduced motion. */
  .typed-caret {
    display: inline-block;
    width: 0.55em;
    height: 1em;
    margin-left: 0.15em;
    vertical-align: -0.14em;
    background: currentColor;
    animation: caret 1.05s steps(2, start) infinite;
  }
  @keyframes caret { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }

  .hero-title {
    font-family: var(--font-ui);
    font-weight: 800;
    font-size: var(--text-display);
    line-height: var(--lh-display);
    letter-spacing: var(--track-display);
    margin-bottom: var(--space-lg);
  }
  .hero-title .line { display: block; }

  /* Emphasis by accent + a drawn rule, not by italic (italic display type is
   * a top AI tell) and not by -webkit-text-stroke on transparent text, which
   * makes the word vanish outright in forced-colors mode. */
  .hero-em {
    color: var(--color-accent);
    box-shadow: inset 0 -0.12em 0 color-mix(in oklab, var(--color-accent) 30%, transparent);
  }

  .hero-lede {
    font-family: var(--font-reading);
    font-size: var(--text-md);
    line-height: var(--lh-normal);
    color: var(--color-ink-2);
    max-width: 52ch;
    margin-bottom: var(--space-xl);
  }

  /* the proof column — the page earns its claim in the first screen */
  .hero-proof {
    border: 1px solid var(--color-rule-strong);
    border-radius: var(--card-radius);
    background: var(--color-paper-2);
    padding: var(--space-lg);
  }
  .proof-label {
    font-size: var(--text-2xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--color-accent);
    padding-bottom: var(--space-sm);
    border-bottom: 1px solid var(--color-rule);
  }
  .proof-item + .proof-item { border-top: 1px solid var(--color-rule); }
  .proof-item a {
    display: grid;
    gap: var(--space-3xs);
    padding-block: var(--space-sm);
    text-decoration: none;
  }
  .proof-date { font-size: var(--text-2xs); color: var(--color-muted); }
  .proof-title {
    font-size: var(--text-sm);
    font-weight: 500;
    line-height: var(--lh-snug);
    color: var(--color-ink-2);
    transition: color var(--dur-micro) var(--ease-out);
  }
  @media (hover: hover) { .proof-item a:hover .proof-title { color: var(--color-accent); } }

  .proof-stats {
    display: flex;
    gap: var(--space-lg);
    margin-top: var(--space-md);
    padding-top: var(--space-md);
    border-top: 1px solid var(--color-rule-strong);
  }
  .proof-stats dt {
    font-size: var(--text-2xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--color-muted);
  }
  .proof-stats dd {
    margin: 0;
    font-size: var(--text-xl);
    font-weight: 700;
    line-height: 1.1;
    color: var(--color-ink);
    font-variant-numeric: tabular-nums;
  }

  /* ── ruled numbered index (replaces the icon-tile feature grid) ─────
   * Four identical bordered cards with an icon above a heading above two
   * lines of copy is *the* generated feature grid. This is a hanging-numeral
   * ruled list instead: denser, more editorial, and it carries four items
   * without pretending they are equal-weight tiles.
   */
  .index-list { border-top: 1px solid var(--color-rule-strong); }
  .index-item {
    display: grid;
    grid-template-columns: 4.5rem minmax(0, 1fr);
    gap: var(--space-lg);
    padding-block: var(--space-lg);
    border-bottom: 1px solid var(--color-rule);
    transition: background-color var(--dur-short) var(--ease-out);
  }
  .index-num {
    font-size: var(--text-sm);
    color: var(--color-accent);
    padding-top: var(--space-3xs);
  }
  .index-body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr);
    gap: var(--space-lg);
    align-items: baseline;
  }
  .index-title { font-size: var(--text-lg); letter-spacing: var(--track-tight); }
  .index-desc {
    font-family: var(--font-reading);
    font-size: var(--text-sm);
    color: var(--color-muted);
    line-height: var(--lh-normal);
  }

  /* ── card grid, with a lead item that breaks the equal-column rhythm ── */
  .card-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1px;
    background: var(--color-rule);
    border: 1px solid var(--color-rule);
  }
  .card {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
    background: var(--color-paper);
    padding: var(--space-lg);
    text-decoration: none;
    transition: background-color var(--dur-short) var(--ease-out);
  }
  .card--lead { grid-column: 1 / -1; }
  .card--lead .card-title { font-size: var(--text-2xl); }
  @media (hover: hover) { .card:hover { background: var(--wash); } }

  .meta-row {
    display: flex;
    align-items: center;
    gap: var(--space-xs);
    font-size: var(--text-2xs);
    letter-spacing: 0.06em;
    color: var(--color-muted);
  }
  .meta-sep { color: var(--color-rule-strong); }

  .card-title {
    font-size: var(--text-lg);
    letter-spacing: var(--track-tight);
    transition: color var(--dur-micro) var(--ease-out);
  }
  @media (hover: hover) { .card:hover .card-title { color: var(--color-accent); } }
  .card-desc {
    font-family: var(--font-reading);
    font-size: var(--text-sm);
    color: var(--color-muted);
    display: -webkit-box;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .card-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-sm);
    flex-wrap: wrap;
    margin-top: auto;
    padding-top: var(--space-sm);
  }
  .card-arrow {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2xs);
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--color-accent);
  }
  .card-arrow svg { transition: transform var(--dur-short) var(--ease-out); }
  @media (hover: hover) { .card:hover .card-arrow svg { transform: translateX(4px); } }

  /* ── about ───────────────────────────────────────────────────────── */
  .about-grid {
    display: grid;
    grid-template-columns: minmax(0, 0.72fr) minmax(0, 1.28fr);
    gap: clamp(var(--space-xl), 6vw, var(--space-3xl));
    align-items: start;
  }
  .about-frame {
    position: relative;
    border: 1px solid var(--color-rule-strong);
  }
  .about-frame::after {
    content: "";
    position: absolute;
    inset: var(--space-sm) calc(var(--space-sm) * -1) calc(var(--space-sm) * -1) var(--space-sm);
    border: 1px solid var(--color-accent);
    opacity: 0.35;
    pointer-events: none;
  }
  .about-frame {
    display: grid;
    place-items: center;
    aspect-ratio: 1;
    background:
      repeating-linear-gradient(135deg, transparent 0 11px, color-mix(in oklab, var(--color-ink) 4%, transparent) 11px 12px),
      var(--color-paper-2);
  }
  .about-mark { width: 48%; color: var(--color-ink); }
  .about-body p {
    font-family: var(--font-reading);
    font-size: var(--text-base);
    line-height: var(--lh-normal);
    color: var(--color-ink-2);
    max-width: var(--measure);
  }
  .about-body p + p { margin-top: var(--space-md); }
  .about-list {
    margin-top: var(--space-lg);
    padding-top: var(--space-md);
    border-top: 1px solid var(--color-rule);
    font-size: var(--text-xs);
    color: var(--color-muted);
  }
  .about-list li {
    padding-block: var(--space-2xs);
    padding-left: var(--space-md);
    position: relative;
  }
  .about-list li::before {
    content: "\2192";
    position: absolute;
    left: 0;
    color: var(--color-accent);
  }

  /* ── contact ─────────────────────────────────────────────────────── */
  /* Left-flush, not auto-centred. `.wrap` supplies margin-inline:auto, so a
   * max-width on the same element floats a narrow block over left-flush
   * content above it — the accidental-mismatch tell. The page has one primary
   * axis and the closing CTA stays on it. */
  .contact-inner { max-width: 62ch; margin-inline: 0; }
  .contact-title {
    font-size: var(--text-3xl);
    letter-spacing: var(--track-display);
    margin-bottom: var(--space-md);
  }
  .contact-sub {
    font-family: var(--font-reading);
    color: var(--color-ink-2);
    margin-bottom: var(--space-xl);
  }

  @media (max-width: 60rem) {
    .hero-grid { grid-template-columns: minmax(0, 1fr); }
    .hero-proof { order: 2; }
    .index-body { grid-template-columns: minmax(0, 1fr); gap: var(--space-xs); }
    .about-grid { grid-template-columns: minmax(0, 1fr); }
    .about-media { max-width: 320px; }
    .card-grid { grid-template-columns: minmax(0, 1fr); }
  }
  @media (max-width: 40rem) {
    .index-item { grid-template-columns: minmax(0, 1fr); gap: var(--space-xs); }
    .proof-stats { gap: var(--space-md); }
  }
}


/* ── 65-index.css ───────────────────────────────────────── */
@layer components {
  /* ── page head, shared by research / 404 / stub surfaces ─────────── */
  .page-head {
    padding-block: clamp(2.5rem, 5vw, 4rem) var(--space-xl);
    border-bottom: 1px solid var(--color-rule);
    background: linear-gradient(to bottom, var(--wash), var(--color-paper));
  }
  .page-title {
    font-size: var(--text-4xl);
    letter-spacing: var(--track-display);
  }
  .page-sub, .page-lede {
    margin-top: var(--space-sm);
    font-family: var(--font-reading);
    color: var(--color-ink-2);
    max-width: var(--measure);
  }
  .page-lede { font-size: var(--text-md); }
  .page-sub code {
    font-family: var(--font-mono);
    font-size: 0.9em;
    background: var(--color-paper-3);
    padding: 0.1em 0.35em;
    border-radius: var(--radius-1);
    overflow-wrap: anywhere;
  }

  /* ── ruled research index (Long Document, not a card wall) ──────────
   * Rows, not cards: a research index is a table of contents for the lab's
   * output, and a hairline-ruled list reads faster and denser than tiles.
   */
  .row-list { border-top: 1px solid var(--color-rule-strong); }
  .row-item { border-bottom: 1px solid var(--color-rule); }
  .row {
    display: grid;
    grid-template-columns: 148px minmax(0, 1fr) 40px;
    gap: var(--space-lg);
    align-items: center;
    padding: var(--space-lg) var(--space-sm);
    text-decoration: none;
    transition: background-color var(--dur-short) var(--ease-out),
                padding-left var(--dur-short) var(--ease-out);
  }
  @media (hover: hover) {
    .row:hover { background: var(--wash); }
    .row:hover .row-title { color: var(--color-accent); }
    .row:hover .row-thumb img { filter: none; }
    .row:hover .row-arrow { transform: translateX(4px); color: var(--color-accent); }
  }

  .row-thumb {
    display: block;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    border: 1px solid var(--color-rule-strong);
    border-radius: var(--radius-1);
    background: var(--color-paper-3);
  }
  .row-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    filter: var(--thumb-filter);
    transition: filter var(--dur-long) var(--ease-out);
  }

  /* A post with no image gets a branded tile whose hatch angle comes from its
   * slug, so fallbacks differ from one another but never change when the list
   * is filtered. Six discrete angles, keyed by data-attribute because the CSP
   * forbids the inline custom property this used to be. */
  .row-thumb--fallback {
    display: grid;
    place-items: center;
    background-image: repeating-linear-gradient(
      var(--hatch, 45deg),
      transparent 0 7px,
      color-mix(in oklab, var(--color-ink) 5%, transparent) 7px 8px
    );
  }
  .row-thumb--fallback[data-hatch="0"] { --hatch: 0deg; }
  .row-thumb--fallback[data-hatch="1"] { --hatch: 30deg; }
  .row-thumb--fallback[data-hatch="2"] { --hatch: 60deg; }
  .row-thumb--fallback[data-hatch="3"] { --hatch: 90deg; }
  .row-thumb--fallback[data-hatch="4"] { --hatch: 120deg; }
  .row-thumb--fallback[data-hatch="5"] { --hatch: 150deg; }
  .row-thumb-mark { width: 34px; color: var(--color-rule-strong); }

  .row-main { display: grid; gap: var(--space-2xs); }
  .row-title {
    font-family: var(--font-heading);
    font-weight: var(--heading-weight);
    font-size: var(--text-lg);
    line-height: var(--lh-tight);
    letter-spacing: var(--track-tight);
    color: var(--color-ink);
    transition: color var(--dur-micro) var(--ease-out);
  }
  .row-desc {
    font-family: var(--font-reading);
    font-size: var(--text-sm);
    line-height: var(--lh-normal);
    color: var(--color-muted);
    max-width: 76ch;
  }
  .row-tags { display: flex; flex-wrap: wrap; gap: var(--space-2xs); margin-top: var(--space-2xs); }
  .row-arrow {
    display: grid;
    place-items: center;
    color: var(--color-rule-strong);
    transition: transform var(--dur-short) var(--ease-out), color var(--dur-micro) var(--ease-out);
  }

  /* ── 404 ─────────────────────────────────────────────────────────── */
  .page-404 { text-align: center; padding-block: clamp(4rem, 12vw, 8rem); border-bottom: 0; }
  .page-404 .page-sub, .page-404 .cta-row { margin-inline: auto; }
  .page-404 .page-sub { max-width: 46ch; }
  /* Give the actions clear air beneath the copy — they were crowding it. */
  .page-404 .cta-row { margin-top: var(--space-2xl); }
  .code-404 {
    font-size: clamp(4rem, 18vw, 9rem);
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.04em;
    color: var(--color-accent);
    margin-block: var(--space-md) var(--space-lg);
  }

  @media (max-width: 60rem) {
    .row { grid-template-columns: 104px minmax(0, 1fr); }
    .row-arrow { display: none; }
  }
  @media (max-width: 40rem) {
    .row { grid-template-columns: 72px minmax(0, 1fr); gap: var(--space-md); padding-inline: 0; }
    .row-desc { display: none; }
  }
}


/* ── 70-post.css ───────────────────────────────────────── */
@layer components {
  /* ── article header ──────────────────────────────────────────────── */
  .post-head {
    padding-block: clamp(2rem, 4vw, 3rem) var(--space-xl);
    border-bottom: 1px solid var(--color-rule-strong);
    background: linear-gradient(to bottom, var(--wash), var(--color-paper));
  }
  /* Same grid as .post-body, so the title sits exactly over the measure column.
   * A narrow auto-centred header floating above left-flush body copy is the
   * classic accidental-mismatch tell. */
  .post-head-inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) min(var(--measure), 100%) minmax(0, 1fr);
    column-gap: clamp(var(--space-lg), 4vw, var(--space-2xl));
  }
  .post-head-inner > * { grid-column: 2; }

  .back-link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2xs);
    font-size: var(--text-2xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    text-decoration: none;
    color: var(--color-muted);
    padding-block: var(--space-xs);
    transition: color var(--dur-micro) var(--ease-out);
  }
  .back-link svg { transition: transform var(--dur-short) var(--ease-out); }
  @media (hover: hover) {
    .back-link:hover { color: var(--color-accent); }
    .back-link:hover svg { transform: translateX(-3px); }
  }

  .post-kicker {
    margin-top: var(--space-md);
    font-size: var(--text-2xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--color-accent);
  }
  .post-title {
    margin-top: var(--space-sm);
    font-size: var(--text-4xl);
    line-height: var(--lh-display);
    letter-spacing: var(--track-display);
  }
  .post-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-xs);
    margin-top: var(--space-md);
    font-size: var(--text-2xs);
    letter-spacing: 0.06em;
    color: var(--color-muted);
  }
  .post-head-tags { margin-top: var(--space-md); }

  /* ── body: measure + sticky outline gutter ───────────────────────────
   * The outline is server-rendered (lib/posts.js) and positioned with
   * position:sticky in a grid track. The previous build measured and wrote
   * `top`/`maxHeight` from JS on every frame, hard-coupled to the nav height,
   * the header's bounding box and a decorative footer rule — change any one
   * of those and the outline landed in the gutter or collapsed to zero.
   */
  .post-body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) min(var(--measure), 100%) minmax(0, 1fr);
    column-gap: clamp(var(--space-lg), 4vw, var(--space-2xl));
    align-items: start;
    padding-block: var(--space-2xl);
  }
  .post-measure { grid-column: 2; min-width: 0; }

  /* Marginal metadata rail (DESIGN.md: "single measure, marginal metadata").
   * Lives in the left gutter, right-aligned toward the measure like a margin
   * note, sticky beside the reading column. Fills the left symmetrically with
   * the TOC on the right, and it is genuinely useful while scrolling. */
  .post-rail {
    grid-column: 1;
    justify-self: end;
    width: min(13rem, 100%);
    align-self: start;
    position: sticky;
    top: calc(var(--masthead-h, 76px) + var(--space-lg));
    display: grid;
    gap: var(--space-lg);
    padding-right: var(--space-lg);
    border-right: 1px solid var(--color-rule);
    text-align: right;
  }
  .rail-block { display: grid; gap: var(--space-3xs); }
  .rail-label {
    font-size: var(--text-2xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--color-muted);
  }
  .rail-value { font-size: var(--text-xs); color: var(--color-ink-2); }
  .rail-value time { font-variant-numeric: tabular-nums; }
  .rail-tags { display: flex; flex-wrap: wrap; gap: var(--space-2xs); justify-content: flex-end; }

  .post-toc {
    grid-column: 3;
    justify-self: start;
    width: min(15rem, 100%);
    position: sticky;
    top: calc(var(--masthead-h, 76px) + var(--space-lg));
    max-height: calc(100vh - var(--masthead-h, 76px) - var(--space-2xl));
    overflow-y: auto;
    border-left: 1px solid var(--color-rule);
    padding-left: var(--space-md);
    overscroll-behavior: contain;
  }
  .toc-head {
    font-size: var(--text-2xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--color-muted);
    margin-bottom: var(--space-sm);
  }
  .toc-item--h3 { padding-left: var(--space-md); }
  .toc-link {
    display: block;
    font-size: var(--text-xs);
    line-height: var(--lh-snug);
    color: var(--color-muted);
    text-decoration: none;
    padding-block: var(--space-2xs);
    border-left: 2px solid transparent;
    margin-left: calc(var(--space-md) * -1 - 1px);
    padding-left: var(--space-md);
    transition: color var(--dur-micro) var(--ease-out), border-color var(--dur-micro) var(--ease-out);
  }
  @media (hover: hover) { .toc-link:hover { color: var(--color-ink); } }
  .toc-link.is-active { color: var(--color-accent); border-left-color: var(--color-accent); }

  .post-langnote {
    font-size: var(--text-xs);
    color: var(--color-muted);
    background: var(--color-paper-2);
    border-left: 2px solid var(--color-accent);
    padding: var(--space-sm) var(--space-md);
    margin-bottom: var(--space-xl);
  }

  /* ── article footer ──────────────────────────────────────────────── */
  .post-foot { padding-bottom: var(--space-3xl); }
  .post-foot-rule {
    display: flex;
    align-items: center;
    gap: var(--space-md);
    color: var(--color-rule-strong);
    margin-bottom: var(--space-xl);
  }
  .post-foot-rule::before, .post-foot-rule::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--color-rule);
  }
  .post-foot-mark { width: 26px; }

  .post-nav {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1px;
    background: var(--color-rule);
    border: 1px solid var(--color-rule);
  }
  .post-nav-item {
    display: grid;
    gap: var(--space-2xs);
    background: var(--color-paper);
    padding: var(--space-lg);
    text-decoration: none;
    transition: background-color var(--dur-short) var(--ease-out);
  }
  .post-nav-item--next { text-align: right; }
  @media (hover: hover) {
    a.post-nav-item:hover { background: var(--wash); }
    a.post-nav-item:hover .post-nav-title { color: var(--color-accent); }
  }
  .post-nav-label {
    font-size: var(--text-2xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--color-muted);
  }
  .post-nav-title {
    font-family: var(--font-heading);
    font-weight: var(--heading-weight);
    font-size: var(--text-base);
    line-height: var(--lh-snug);
    transition: color var(--dur-micro) var(--ease-out);
  }

  @media (max-width: 78rem) {
    .post-body, .post-head-inner {
      grid-template-columns: minmax(0, var(--measure));
      justify-content: center;
      column-gap: 0;
    }
    .post-head-inner > *, .post-measure, .post-rail { grid-column: 1; }
    .post-toc { display: none; }
    /* rail becomes a plain meta strip above the article on narrow screens */
    .post-rail {
      position: static;
      width: auto;
      text-align: left;
      border-right: 0;
      border-bottom: 1px solid var(--color-rule);
      padding: 0 0 var(--space-lg);
      margin-bottom: var(--space-lg);
      grid-auto-flow: row;
    }
    .rail-tags { justify-content: flex-start; }
  }
  @media (max-width: 40rem) {
    .post-nav { grid-template-columns: minmax(0, 1fr); }
    .post-nav-item--next { text-align: left; }
  }
}

@layer prose {
  /* Document register: serif, 68ch, generous leading. This is the surface the
   * whole site exists to serve, so it gets the most typographic care. */
  .prose {
    font-family: var(--font-reading);
    font-size: var(--reading-size);
    line-height: var(--reading-lh);
    color: var(--color-ink-2);
    font-variant-numeric: oldstyle-nums proportional-nums;
  }

  .prose > * + * { margin-top: var(--space-md); }
  .prose p { max-width: var(--measure); }

  .prose h2, .prose h3, .prose h4 {
    font-family: var(--font-heading);
    color: var(--color-ink);
    letter-spacing: var(--track-tight);
    scroll-margin-top: calc(var(--masthead-h, 76px) + var(--space-lg));
  }
  .prose h2 {
    font-size: var(--text-2xl);
    margin-top: var(--space-2xl);
    padding-top: var(--space-md);
    border-top: 1px solid var(--color-rule);
  }
  .prose h3 { font-size: var(--text-lg); margin-top: var(--space-xl); }
  .prose h4 { font-size: var(--text-md); margin-top: var(--space-lg); }

  .h-anchor {
    margin-left: 0.4em;
    color: var(--color-rule-strong);
    text-decoration: none;
    opacity: 0;
    transition: opacity var(--dur-micro) var(--ease-out);
  }
  .prose :is(h2, h3, h4):hover .h-anchor,
  .h-anchor:focus-visible { opacity: 1; }

  .prose a {
    color: var(--color-accent);
    text-decoration-line: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
    text-decoration-color: color-mix(in oklab, var(--color-accent) 45%, transparent);
    transition: text-decoration-color var(--dur-micro) var(--ease-out);
  }
  @media (hover: hover) { .prose a:hover { text-decoration-color: currentColor; } }

  .prose strong { color: var(--color-ink); font-weight: 600; }
  .prose em { font-style: italic; }

  .prose ul, .prose ol { padding-left: var(--space-lg); max-width: var(--measure); }
  .prose li + li { margin-top: var(--space-2xs); }
  .prose li::marker { color: var(--color-accent); }

  .prose blockquote {
    border-left: 2px solid var(--color-accent);
    padding-left: var(--space-md);
    color: var(--color-muted);
    max-width: var(--measure);
  }

  /* A markdown blockquote opening with "**Label:** …" is upgraded to a
   * callout by the renderer, so existing posts gain the treatment without a
   * single markdown edit. */
  .callout {
    border: 1px solid var(--color-rule-strong);
    border-left: 3px solid var(--color-accent);
    background: var(--color-paper-2);
    padding: var(--space-md) var(--space-lg);
    max-width: var(--measure);
  }
  .callout--warn { border-left-color: var(--color-accent); }
  .callout-label {
    font-size: var(--text-2xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--color-accent);
    margin-bottom: var(--space-xs);
  }
  .callout > p + p { margin-top: var(--space-sm); }

  .prose hr {
    border: 0;
    height: 1px;
    background: var(--color-rule);
    margin-block: var(--space-2xl);
  }

  .prose code:not(.hljs) {
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-size: 0.86em;
    background: var(--color-paper-3);
    border: 1px solid var(--color-rule);
    border-radius: var(--radius-1);
    padding: 0.1em 0.35em;
    overflow-wrap: anywhere;
  }

  .prose table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm);
    font-variant-numeric: tabular-nums;
  }
  .prose :is(th, td) {
    text-align: left;
    padding: var(--space-xs) var(--space-sm);
    border-bottom: 1px solid var(--color-rule);
  }
  .prose th {
    font-family: var(--font-ui);
    font-weight: 500;
    font-size: var(--text-xs);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-muted);
    background: var(--color-paper-2);
  }

  /* figures break the measure deliberately — the one grid-break on the page */
  .post-figure {
    margin-block: var(--space-xl);
    width: 100%;
    margin-inline: 0;
  }
  .post-figure :is(img, video) {
    width: 100%;
    border: 1px solid var(--color-rule);
    border-radius: var(--radius-2);
    background: var(--color-paper-2);
  }
  /* .post-video is the class the desmume post writes by hand in raw HTML and
   * which had no rule at all before; it is treated exactly like a rendered
   * video figure. Capped so a tall clip cannot swallow the article. */
  .post-figure--video video, .post-video video {
    max-height: 78vh;
    object-fit: contain;
    background: var(--color-paper-3);
  }
  .post-figure figcaption {
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    line-height: var(--lh-snug);
    color: var(--color-muted);
    margin-top: var(--space-xs);
    padding-left: var(--space-xs);
    border-left: 2px solid var(--color-rule-strong);
  }

  @media (max-width: 60rem) {
    .post-figure { width: 100%; margin-inline: 0; }
  }
}


/* ── 75-code.css ───────────────────────────────────────── */
@layer code {
  /* Code is a first-class surface on a vulnerability-research site: an article
   * is mostly disassembly, hex and shellcode. Two things the previous build
   * got wrong and this fixes — the block could not be scrolled from the
   * keyboard at all (WCAG 2.1.1) despite being horizontally overflowing, and
   * the syntax theme existed only for dark.
   */
  .code-block {
    margin-block: var(--space-xl);
    width: 100%;
    margin-inline: 0;
    border: 1px solid var(--color-rule-strong);
    border-radius: var(--radius-2);
    background: var(--color-paper-2);
    overflow: hidden;
  }
  .code-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-sm);
    padding: var(--space-2xs) var(--space-sm);
    border-bottom: 1px solid var(--color-rule);
    background: var(--color-paper-3);
  }
  .code-lang {
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--color-muted);
  }
  .code-copy {
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--color-muted);
    min-height: 32px;
    padding-inline: var(--space-xs);
    border: 1px solid transparent;
    border-radius: var(--radius-1);
    transition: color var(--dur-micro) var(--ease-out), border-color var(--dur-micro) var(--ease-out);
  }
  @media (hover: hover) {
    .code-copy:hover { color: var(--color-ink); border-color: var(--color-edge); }
  }
  .code-copy.is-copied { color: var(--color-accent); border-color: var(--color-accent); }

  .code-block pre {
    margin: 0;
    padding: var(--space-md);
    overflow-x: auto;
    overscroll-behavior-x: contain;
  }
  .code-block code.hljs {
    font-family: var(--font-mono);
    /* Ligatures OFF. A ligated -> or != is actively hostile in a post whose
     * readers byte-compare the listing against their own output. */
    font-variant-ligatures: none;
    font-size: var(--text-sm);
    line-height: 1.65;
    color: var(--syn-base);
    background: none;
    tab-size: 4;
  }

  /* ── syntax palette ────────────────────────────────────────────────
   * Near-monochrome by design: the accent carries strings, everything else is
   * a low-chroma hue shift off the ink. All values verified ≥4.5:1 on the
   * code surface in both schemes by scripts/check-contrast.mjs.
   */
  :root {
    --syn-base: var(--color-ink-2);
    --syn-comment: oklch(50% 0.02 62);
    --syn-keyword: oklch(43% 0.115 300);
    --syn-string: oklch(43% 0.10 150);
    --syn-number: oklch(46% 0.11 40);
    --syn-title: oklch(43% 0.115 250);
    --syn-type: oklch(44% 0.09 195);
    --syn-attr: oklch(45% 0.10 80);
    --syn-meta: oklch(52% 0.04 62);
    --syn-del: oklch(47% 0.15 25);
    --syn-add: oklch(44% 0.12 150);
  }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
      --syn-comment: oklch(66% 0.02 62);
      --syn-keyword: oklch(80% 0.12 300);
      --syn-string: oklch(82% 0.14 145);
      --syn-number: oklch(82% 0.13 45);
      --syn-title: oklch(80% 0.11 250);
      --syn-type: oklch(83% 0.11 195);
      --syn-attr: oklch(85% 0.12 95);
      --syn-meta: oklch(70% 0.04 62);
      --syn-del: oklch(75% 0.15 25);
      --syn-add: oklch(82% 0.14 150);
    }
  }
  :root[data-theme="dark"] {
    --syn-comment: oklch(66% 0.02 62);
    --syn-keyword: oklch(80% 0.12 300);
    --syn-string: oklch(82% 0.14 145);
    --syn-number: oklch(82% 0.13 45);
    --syn-title: oklch(80% 0.11 250);
    --syn-type: oklch(83% 0.11 195);
    --syn-attr: oklch(85% 0.12 95);
    --syn-meta: oklch(70% 0.04 62);
    --syn-del: oklch(75% 0.15 25);
    --syn-add: oklch(82% 0.14 150);
  }

  .hljs-comment, .hljs-quote { color: var(--syn-comment); font-style: italic; }
  .hljs-keyword, .hljs-selector-tag, .hljs-literal, .hljs-doctag { color: var(--syn-keyword); font-weight: 500; }
  .hljs-string, .hljs-regexp { color: var(--syn-string); }
  .hljs-number, .hljs-symbol, .hljs-bullet, .hljs-link { color: var(--syn-number); }
  .hljs-title, .hljs-name, .hljs-section, .hljs-title\.function_ { color: var(--syn-title); }
  .hljs-type, .hljs-built_in, .hljs-class .hljs-title { color: var(--syn-type); }
  .hljs-attr, .hljs-attribute, .hljs-variable, .hljs-template-variable, .hljs-selector-attr, .hljs-selector-class, .hljs-selector-id { color: var(--syn-attr); }
  .hljs-meta, .hljs-meta .hljs-keyword { color: var(--syn-meta); }
  .hljs-params { color: var(--syn-base); }
  .hljs-emphasis { font-style: italic; }
  .hljs-strong { font-weight: 700; }
  .hljs-deletion { color: var(--syn-del); background: color-mix(in oklab, var(--syn-del) 12%, transparent); }
  .hljs-addition { color: var(--syn-add); background: color-mix(in oklab, var(--syn-add) 12%, transparent); }

  @media (max-width: 60rem) {
    .code-block { width: 100%; margin-inline: 0; }
  }
}


/* ── 80-broadcast.css ───────────────────────────────────────── */
@layer components {
  /* ── broadcast surfaces: Academy / Courses / Videos ──────────────────
   * These carry placeholder copy and are not linked from the nav. They exist
   * so the second register has real components — media tiles, a curriculum
   * list, state chips — that can be built and reviewed before any course or
   * video actually exists.
   */
  .page-head--broadcast { padding-block: clamp(2.5rem, 5vw, 4rem) var(--space-xl); }
  .page-academy .section, .page-courses .section, .page-videos .section { padding-block: var(--space-2xl); }

  /* Says plainly, on the page, that nothing here is real yet. */
  .placeholder-note {
    display: inline-block;
    margin-top: var(--space-lg);
    font-size: var(--text-2xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--color-accent);
    border: 1px dashed var(--color-accent);
    border-radius: var(--radius-2);
    padding: var(--space-xs) var(--space-sm);
  }

  .stub-body {
    font-family: var(--font-reading);
    color: var(--color-ink-2);
    max-width: var(--measure);
    margin-bottom: var(--space-2xl);
  }

  /* curriculum list — asymmetric, ruled, numbered */
  .track-list { border-top: 1px solid var(--color-rule-strong); }
  .track {
    display: grid;
    grid-template-columns: 7rem minmax(0, 1fr) minmax(0, 1.5fr) auto;
    gap: var(--space-lg);
    align-items: baseline;
    padding-block: var(--space-lg);
    border-bottom: 1px solid var(--color-rule);
  }
  .track-label {
    font-size: var(--text-2xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--color-accent);
  }
  .track-title { font-size: var(--text-xl); letter-spacing: var(--track-tight); }
  .track-desc { font-family: var(--font-reading); font-size: var(--text-sm); color: var(--color-muted); }
  .track-state, .media-state {
    font-size: var(--text-2xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--color-muted);
    border: 1px solid var(--color-rule-strong);
    border-radius: var(--radius-pill);
    padding: var(--space-3xs) var(--space-xs);
    white-space: nowrap;
    justify-self: start;
  }

  /* media grid — 16:9 tiles, the shape YouTube/Udemy content will want */
  .media-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
    gap: var(--space-lg);
  }
  .media-grid--wide { grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); }

  .media-card {
    display: grid;
    gap: var(--space-xs);
    align-content: start;
    padding: var(--space-md);
    border: 1px solid var(--color-rule);
    border-radius: var(--card-radius);
    background: var(--color-paper-2);
  }
  /* first tile spans two columns so the grid is not four identical boxes */
  .media-grid > .media-card:first-child { grid-column: span 2; }
  @media (max-width: 60rem) {
    .media-grid > .media-card:first-child { grid-column: auto; }
    .track { grid-template-columns: minmax(0, 1fr); gap: var(--space-xs); }
  }

  .media-thumb {
    display: grid;
    place-items: center;
    aspect-ratio: 4 / 3;
    border-radius: var(--radius-2);
    background:
      repeating-linear-gradient(135deg, transparent 0 9px, color-mix(in oklab, var(--color-ink) 4%, transparent) 9px 10px),
      var(--color-paper-3);
  }
  .media-thumb--wide { aspect-ratio: 16 / 9; }
  .media-thumb-mark { width: 44px; color: var(--color-rule-strong); }
  .media-title { font-size: var(--text-md); letter-spacing: var(--track-tight); }
  .media-desc { font-family: var(--font-reading); font-size: var(--text-sm); color: var(--color-muted); }

  /* ── internal admin preview (/admin, password-gated) ─────────────── */
  .admin-grid {
    display: grid;
    gap: var(--space-lg);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
    margin-bottom: var(--space-2xl);
  }
  .admin-card {
    display: grid;
    grid-template-columns: 4.5rem minmax(0, 1fr);
    gap: var(--space-md);
    padding: var(--space-md);
    border: 1px solid var(--color-rule);
    border-radius: var(--card-radius);
    background: var(--color-paper-2);
  }
  .admin-thumb {
    display: grid; place-items: center; aspect-ratio: 1;
    border-radius: var(--radius-2);
    background:
      repeating-linear-gradient(135deg, transparent 0 9px, color-mix(in oklab, var(--color-ink) 4%, transparent) 9px 10px),
      var(--color-paper-3);
  }
  .admin-thumb-mark { width: 28px; color: var(--color-rule-strong); }
  .admin-card-body { display: grid; gap: var(--space-2xs); align-content: start; }
  .admin-card-head { display: flex; align-items: center; gap: var(--space-sm); justify-content: space-between; }
  .admin-card-title { font-size: var(--text-lg); letter-spacing: var(--track-tight); }
  .admin-pill {
    font-size: var(--text-2xs); letter-spacing: var(--track-label); text-transform: uppercase;
    color: var(--color-accent); border: 1px solid var(--color-accent);
    border-radius: var(--radius-pill); padding: var(--space-3xs) var(--space-xs); white-space: nowrap;
  }
  .admin-card-macro { font-size: var(--text-2xs); color: var(--color-muted); }
  .admin-card-note { font-family: var(--font-reading); font-size: var(--text-sm); color: var(--color-ink-2); }
  .admin-card .link-more { margin-top: var(--space-2xs); }
  .admin-note {
    border-left: 3px solid var(--color-accent); background: var(--color-paper-2);
    padding: var(--space-md) var(--space-lg); max-width: 72ch;
  }
  .admin-note-label {
    font-size: var(--text-2xs); letter-spacing: var(--track-label); text-transform: uppercase;
    color: var(--color-accent); margin-bottom: var(--space-2xs);
  }
  .admin-note p:last-child { font-family: var(--font-reading); color: var(--color-ink-2); }
  .admin-note code, .page-lede code {
    font-family: var(--font-mono); font-size: 0.9em;
    background: var(--color-paper-3); padding: 0.1em 0.35em; border-radius: var(--radius-1);
  }
}


/* ── 90-utilities.css ───────────────────────────────────────── */
@layer utilities {
  .visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  /* ── the page-load reveal ─────────────────────────────────────────────
   * One orchestrated entrance, driven entirely by CSS.
   *
   * Deliberately NOT scroll-triggered. Fading every block in as it enters the
   * viewport is an anti-pattern in its own right, and the previous build tied
   * it to an IntersectionObserver — including on `.prose`, which meant the
   * entire article body was `opacity: 0` until JavaScript ran. Between that
   * and a `body.preload` gate cleared only by JS, there were two independent
   * paths to a blank article if /js/main.js ever failed to load. Primary
   * content is never gated on script now.
   */
  @keyframes reveal {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
  }
  [data-reveal] {
    animation: reveal var(--dur-long) var(--ease-out) both;
  }
  /* Stagger by position, capped at 400ms total — past ~500ms a page reads as
   * slow to settle. */
  [data-reveal]:nth-of-type(2) { animation-delay: 80ms; }
  [data-reveal]:nth-of-type(3) { animation-delay: 160ms; }
  [data-reveal]:nth-of-type(4) { animation-delay: 240ms; }
  [data-reveal]:nth-of-type(5) { animation-delay: 320ms; }
  [data-reveal]:nth-of-type(n + 6) { animation-delay: 400ms; }

  .index-item:nth-child(2) { animation-delay: 70ms; }
  .index-item:nth-child(3) { animation-delay: 140ms; }
  .index-item:nth-child(4) { animation-delay: 210ms; }
  .row-item:nth-child(2) { animation-delay: 60ms; }
  .row-item:nth-child(3) { animation-delay: 120ms; }
  .row-item:nth-child(4) { animation-delay: 180ms; }
  .row-item:nth-child(n + 5) { animation-delay: 240ms; }
}

@layer utilities {
  /* ── reduced motion ──────────────────────────────────────────────────
   * Every animation is switched OFF, not merely shortened. The previous build
   * only crushed durations, which left infinite transform animations parked
   * on their final frame — the marquee ended up permanently translated -50%.
   */
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
      animation-duration: 1ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 1ms !important;
      scroll-behavior: auto !important;
    }
    [data-reveal] { animation: none; opacity: 1; transform: none; }
    .typed-caret { animation: none; opacity: 1; }
    .masthead-collapse { transition: none; }
    .btn:hover, .row:hover, .card:hover { transform: none; }
  }

  /* ── forced colors ───────────────────────────────────────────────────
   * The old hero relied on -webkit-text-stroke over transparent text, so the
   * emphasised word disappeared entirely wherever the stroke did not paint.
   * Nothing in this system depends on a paint effect to be legible, but the
   * decorative layers are still dropped and the rules made explicit.
   */
  @media (forced-colors: active) {
    .grain { display: none; }
    .hero-em { color: LinkText; box-shadow: none; text-decoration: underline; }
    .btn, .tag, .card, .row, .post-nav-item, .media-card, .hero-proof {
      border: 1px solid ButtonBorder;
    }
    :focus-visible { outline: 3px solid Highlight; }
    .masthead-link.is-current::after { background: Highlight; }
  }

  /* ── print ───────────────────────────────────────────────────────── */
  @media print {
    .masthead, .colophon, .mobile-menu, .grain, .post-toc, .skip-link, .cta-row { display: none !important; }
    body { background: #fff; color: #000; }
    .prose a::after { content: " (" attr(href) ")"; font-size: 0.8em; word-break: break-all; }
    .code-block, .post-figure { break-inside: avoid; width: 100%; margin-inline: 0; }
  }
}
