/* ═══════════════════════════════════════════════════════════════════════════
   The Trace — shared stylesheet
   Contender D, oxblood. Tokens and components lifted from
   contender-D-oxblood.template.html; the prototype's view-switcher chrome
   (.proto-bar / .view) is deliberately absent.

   Two rules that govern the whole sheet, both hard-won:

   1. FONT FAMILY NAMES ARE ALWAYS QUOTED. An unquoted CSS family name may
      not contain a component beginning with a digit, so bare Source Sans 3
      is invalid and every browser silently falls back to the default serif.
      Anton and JetBrains Mono are unaffected, which is why this bug lived
      in the diagram toolkit for months without being spotted.

   2. REVEALS NEVER GATE VISIBILITY. Every reveal in this sheet animates
      transform only, never opacity, so an element whose scroll observer
      never fires (headless render, hidden tab, JS failure) is still fully
      readable. This is the fix for the three invisible article cards.
   ═══════════════════════════════════════════════════════════════════════ */

/* ─────────── faces: self-hosted, subset, woff2 ─────────── */
@font-face { font-family: "Anton"; src: url("../fonts/anton-400.woff2") format("woff2");
             font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Barlow Condensed"; src: url("../fonts/barlowcondensed-600.woff2") format("woff2");
             font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: "Barlow Condensed"; src: url("../fonts/barlowcondensed-700.woff2") format("woff2");
             font-weight: 700; font-style: normal; font-display: swap; }
@font-face { font-family: "Source Sans 3"; src: url("../fonts/sourcesans3-400.woff2") format("woff2");
             font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Source Sans 3"; src: url("../fonts/sourcesans3-600.woff2") format("woff2");
             font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: "Source Sans 3"; src: url("../fonts/sourcesans3-700.woff2") format("woff2");
             font-weight: 700; font-style: normal; font-display: swap; }
@font-face { font-family: "JetBrains Mono"; src: url("../fonts/jetbrainsmono-500.woff2") format("woff2");
             font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: "JetBrains Mono"; src: url("../fonts/jetbrainsmono-700.woff2") format("woff2");
             font-weight: 700; font-style: normal; font-display: swap; }

:root {
  /* grounds: warm neutral, chroma kept low so it never reads as cream */
  /* FRAME WEIGHT.
     A 1px border does not survive fractional display scaling. On a Mac at 1.3x
     it renders at 1.3 device pixels, so it anti-aliases across two rows at part
     strength and the frame reads as incomplete: measured, a 1px edge washes from
     71 to 119 in brightness at 1.3x, while 2px stays at 71 at every ratio from
     1.0 to 2.0. Quiet hairline DIVIDERS can live with that, because a slightly
     lighter hairline still reads as a hairline. A FRAME that is supposed to close
     a rectangle cannot, because a washed-out edge reads as a rendering fault.
     So anything drawing a full rectangle in ink or oxblood uses this. */
  --frame: 2px;

  /* EVERY COLOUR TOKEN NOW LIVES IN theme.css, which loads before this file.
     Nothing in this sheet may name a colour: a hex here is a hex that cannot
     follow the theme, and dark mode is one file or it is a hundred overrides.
     The non-colour tokens below stay, because they are not themed.

     The literal #fff values still in this sheet are all deliberate. Each sits
     on a ground that is dark in BOTH themes — the --ox-deep drench, the
     --ink-deep footer, the lightbox scrim — or on a logo chip that stays white
     on purpose so a brand mark never fights the tint. Before adding another,
     check the ground it lands on: --ox takes --on-ox, --ink takes --on-ink,
     and --ink INVERTS between themes. */

  --display: "Anton", "Arial Narrow", sans-serif;
  --label: "Barlow Condensed", "Arial Narrow", sans-serif;
  --text: "Source Sans 3", system-ui, -apple-system, sans-serif;
  --code: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;

  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --ease-expo: cubic-bezier(0.16, 1, 0.3, 1);
  --z-sticky: 20; --z-overlay: 60; --z-lb: 70;
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
body { font-family: var(--text); font-size: 17px; line-height: 1.65;
       background: var(--bg); color: var(--body); -webkit-font-smoothing: antialiased;
       text-rendering: optimizeLegibility; }
::selection { background: var(--ox); color: var(--on-ox); }
a { color: inherit; text-decoration: none; }
img, svg, video { max-width: 100%; }
button { font: inherit; }
.mono { font-family: var(--code); }
.wrap { max-width: 1260px; margin: 0 auto; padding: 0 clamp(20px, 4vw, 56px); }
.vh { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
      overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* every arrow, chevron and control glyph on this site is an SVG symbol, never
   a text character. Barlow Condensed ships no U+2192, so a text arrow inside a
   button falls back to a system face at a visibly different weight and width;
   the expand/collapse pair (U+2922/U+2921) is missing from all four faces. */
svg.i { width: 1em; height: 1em; fill: none; stroke: currentColor; stroke-width: 2;
        stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; }
svg.i.solid { fill: currentColor; stroke: none; }
svg.li { width: 16px; height: 16px; flex-shrink: 0; fill: currentColor; stroke: none;
        border-radius: 3px; }

/* focus is visible everywhere, and never only as a colour change */
:focus-visible { outline: 3px solid var(--ox); outline-offset: 2px; }
.sec.drench :focus-visible, footer :focus-visible,
.nav-overlay :focus-visible, .card--latest :focus-visible,
.next-card :focus-visible { outline-color: #fff; }
.skip { position: absolute; left: 16px; top: -60px; z-index: 100;
        background: var(--ox); color: var(--on-ox); padding: 12px 18px; font-family: var(--code);
        font-size: 12px; font-weight: 700; letter-spacing: 0.08em; transition: top 160ms var(--ease-out); }
.skip:focus { top: 12px; }

/* ═══════ brand: one dot, pulsing on hover, and nothing else on the site ═══════ */
.brand { display: inline-flex; align-items: center; gap: 10px; }
.brand .dot { position: relative; width: 9px; height: 9px; border-radius: 50%;
              background: var(--ox); flex-shrink: 0; }
.brand .dot::before, .brand .dot::after { content: ''; position: absolute; inset: -1px;
              border-radius: 50%; border: 1.5px solid var(--ox); opacity: 0; pointer-events: none; }
.brand span { font-family: var(--code); font-weight: 500; font-size: 14.5px;
              letter-spacing: 0.12em; color: var(--ink); }
@keyframes dot-pulse { 0%,100% { transform: scale(1); } 50% { transform: scale(1.35); } }
@keyframes dot-ring { 0% { opacity: 0.75; transform: scale(1); } 100% { opacity: 0; transform: scale(3.4); } }
@media (hover: hover) and (pointer: fine) {
  .brand:hover .dot { animation: dot-pulse 1400ms var(--ease-in-out) infinite; }
  .brand:hover .dot::before { animation: dot-ring 1400ms var(--ease-out) infinite; }
  .brand:hover .dot::after { animation: dot-ring 1400ms var(--ease-out) 700ms infinite; }
}
footer .brand span { color: #fff; }

/* ═══════ header ═══════ */
header { position: sticky; top: 0; z-index: var(--z-sticky); background: var(--header-veil);
         border-bottom: 1px solid var(--hair); backdrop-filter: saturate(1.4) blur(6px); }
.head-inner { display: flex; align-items: center; justify-content: space-between;
              gap: 20px; padding: 15px 0; }
nav.desk { display: flex; gap: clamp(14px, 2vw, 28px); }
nav.desk a { font-family: var(--label); text-transform: uppercase; font-weight: 600;
             font-size: 17.5px; letter-spacing: 0.05em; color: var(--ink); position: relative; padding: 4px 0; }
nav.desk a::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
             background: var(--ox); transform: scaleX(0); transform-origin: left;
             transition: transform 260ms var(--ease-out); }
nav.desk a:hover::after, nav.desk a[aria-current]::after { transform: scaleX(1); }
.nav-toggle { display: none; background: none; border: none; cursor: pointer;
              font-family: var(--code); font-size: 12px; letter-spacing: 0.08em;
              color: var(--ink); min-height: 44px; align-items: center; gap: 8px; }

/* ═══════ the theme control ═══════
   Three states, not two: AUTO has to stay reachable or a visitor on a dark
   machine can never get back to following it. Moved here from the preview
   harness — WITHOUT its `margin-left: auto`, because the preview's .head-inner
   is a plain flex row that needed the push, and this one is space-between,
   which already puts the control at the end. */
.seg { display: inline-flex; border: 1px solid var(--hair-2); }
.seg button { display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px;
       font-family: var(--code); font-weight: 500; font-size: 10.5px; letter-spacing: 0.09em;
       color: var(--meta); min-height: 34px; background: none; border: none; cursor: pointer;
       transition: color 160ms var(--ease-out), background 160ms var(--ease-out); }
.seg button + button { border-left: 1px solid var(--hair-2); }
.seg button:hover { color: var(--ink); }
.seg button[aria-pressed="true"] { background: var(--ox); color: var(--on-ox); }
.seg svg.i { width: 13px; height: 13px; }
@media (max-width: 900px) { .seg button span:not(.vh) { display: none; } }
/* At 390 the wordmark, the control and MENU do not all fit on one line, and the
   wordmark is what gives: it wraps to two lines and the header grows by 24px.
   Measured — brand 212 + control 103 + menu 52 + gaps is over the 350 available.
   Tightening the control to three 26px targets brings the row back under it.
   The buttons stay ≥30px tall, which is still a comfortable touch target given
   they sit in a 44px-tall header row. */
@media (max-width: 560px) {
  .seg button { padding: 6px 7px; min-height: 30px; }
  .seg svg.i { width: 12px; height: 12px; }
}

.nav-overlay { position: fixed; inset: 0; z-index: var(--z-overlay); background: var(--ox-deep);
              padding: 76px clamp(20px,6vw,48px) 40px; opacity: 0; visibility: hidden;
              transition: opacity 300ms var(--ease-out), visibility 0s linear 300ms; overflow-y: auto; }
.nav-overlay.open { opacity: 1; visibility: visible; transition: opacity 300ms var(--ease-out); }
.nav-overlay a { display: block; font-family: var(--display); text-transform: uppercase;
              font-size: clamp(28px, 8.5vw, 42px); color: #fff; padding: 12px 0;
              border-bottom: 1px solid rgba(255,255,255,0.18); letter-spacing: 0.012em;
              transform: translateY(14px); }
.nav-overlay.open a { animation: rise 460ms var(--ease-expo) forwards; }
.nav-overlay.open a:nth-child(1){animation-delay:60ms}.nav-overlay.open a:nth-child(2){animation-delay:110ms}
.nav-overlay.open a:nth-child(3){animation-delay:160ms}.nav-overlay.open a:nth-child(4){animation-delay:210ms}
.nav-overlay.open a:nth-child(5){animation-delay:260ms}.nav-overlay.open a:nth-child(6){animation-delay:310ms}
.nav-overlay.open a:nth-child(7){animation-delay:360ms}
@keyframes rise { to { transform: none; } }
.nav-close { position: absolute; top: 18px; right: clamp(20px,6vw,48px); background: none;
             border: none; color: var(--ox-on-dark); font-family: var(--code); font-size: 12px;
             letter-spacing: 0.08em; cursor: pointer; min-height: 44px; display: inline-flex;
             align-items: center; gap: 8px; }
.nav-close:hover { color: #fff; }

/* ═══════ buttons ═══════ */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 9px;
       font-family: var(--label); text-transform: uppercase; font-weight: 700; font-size: 18px;
       letter-spacing: 0.06em; padding: 12px 24px; min-height: 46px; background: var(--ox);
       color: var(--on-ox); border: none; cursor: pointer; text-align: center;
       transition: background 200ms var(--ease-out), color 200ms var(--ease-out), transform 200ms var(--ease-out); }
.btn:hover { background: var(--ink); color: var(--on-ink); transform: translateY(-2px); }
.btn .ar { transition: transform 240ms var(--ease-out); }
.btn:hover .ar { transform: translateX(5px); }
.btn.ghost { background: transparent; color: var(--ink); box-shadow: inset 0 0 0 1.5px var(--hair-2); }
.btn.ghost:hover { background: var(--ink); color: var(--on-ink); box-shadow: inset 0 0 0 1.5px var(--ink); }
.btn.on-dark { background: #fff; color: var(--ox-fixed); }
.btn.on-dark:hover { background: var(--ox-on-dark); color: var(--ox-deep); }
.btn.sm { font-size: 15px; padding: 9px 15px; min-height: 38px; }
.btn[disabled] { opacity: 0.55; cursor: not-allowed; transform: none; }

/* ═══════ topic pills: real marks only, never a logo plus its own word ═══════ */
.pill { display: inline-flex; align-items: center; gap: 7px; font-family: var(--code);
        font-size: 10.5px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
        padding: 4px 10px 4px 5px; border-radius: 999px; border: 1px solid var(--hair-2);
        background: var(--surface); color: var(--ink); white-space: nowrap;
        transition: border-color 240ms var(--ease-out), background 240ms var(--ease-out); }
/* the logo always sits on its own white chip, so when a row flips to oxblood
   on hover the brand colour is never lost. Only border and text change. */
.pill .chip { display: inline-flex; align-items: center; justify-content: center;
        background: #fff; border-radius: 999px; padding: 3px 7px; height: 20px; }
.pill .chip img { display: block; height: 12px; width: auto; }
.pill .chip + .chip { margin-left: -3px; }
.pill.text-only { padding-left: 12px; }

/* ═══════ plates ═══════ */
.plate { position: relative; display: block; background: var(--surface); border: var(--frame) solid var(--ink);
         padding: clamp(8px,1.1vw,13px); overflow: hidden; }
.plate::before { content: ''; position: absolute; inset: 5px; border: 1px solid var(--hair);
         pointer-events: none; z-index: 4; transition: border-color 320ms var(--ease-out); }
.plate:hover::before, .plate:focus-visible::before { border-color: var(--ox); }
.plate .win { position: relative; display: block; overflow: hidden; }
.plate img.still { display: block; width: 100%; height: auto; transform: scale(1.004);
         transition: transform 900ms var(--ease-expo), filter 700ms var(--ease-out); }
.plate:hover img.still { transform: scale(1.05); filter: saturate(1.08); }
.plate.dwell img.still { animation: kenburns 9000ms var(--ease-in-out) infinite alternate; }
@keyframes kenburns { 0% { transform: scale(1.05); } 50% { transform: scale(1.14) translate3d(-2.2%,-1.6%,0); }
         100% { transform: scale(1.09) translate3d(1.8%,-2.4%,0); } }
/* motion assets drop in later by adding one <video class="motion"> per plate and
   touching no CSS. The still stays the poster and the reduced-motion fallback. */
.plate video.motion { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
         opacity: 0; transition: opacity 700ms var(--ease-out); z-index: 2; }
.plate.dwell video.motion { opacity: 1; }
.plate .sweep { position: absolute; inset: 0; z-index: 3; pointer-events: none;
         background: linear-gradient(102deg, transparent 40%, var(--ox-sheen) 50%, transparent 60%);
         transform: translateX(-115%); transition: transform 1100ms var(--ease-expo); }
.plate:hover .sweep { transform: translateX(115%); }
.plate-cap { display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap;
         padding-top: 11px; font-family: var(--code); font-size: 10.5px; letter-spacing: 0.07em;
         color: var(--meta); }
.plate-cap b { color: var(--ox); }
/* the designed placeholder for a portrait that does not exist yet: the plate
   frame and a caption, so the layout is genuinely reviewable and a real
   photograph drops straight in. Not a grey box, not a hidden slot. */
.plate.portrait .win { aspect-ratio: 4 / 5; background: var(--sunk);
         display: grid; place-items: center; }
.plate.portrait .ph { display: grid; place-items: center; gap: 14px; text-align: center;
         padding: 24px; color: var(--meta); font-family: var(--code); font-size: 10.5px;
         letter-spacing: 0.09em; line-height: 1.9; }
.plate.portrait .ph .mono-mark { width: 78px; height: 78px; border-radius: 50%;
         background: var(--ox-deep); color: #fff; display: grid; place-items: center;
         font-family: var(--display); font-size: 30px; letter-spacing: 0.02em; }

/* ═══════ home hero: light ground, colour as punctuation not wallpaper ═══════ */
.hero { padding: clamp(44px, 6vw, 84px) 0 clamp(30px,4vw,48px); }
.hero-grid { display: grid; grid-template-columns: minmax(0, 1.22fr) minmax(0, 0.78fr);
             gap: clamp(26px, 4vw, 58px); align-items: start; }
.tagline { display: inline-flex; align-items: center; gap: 10px; font-family: var(--code);
             font-size: 12px; letter-spacing: 0.1em; color: var(--ox); font-weight: 700; }
.hero h1 { font-family: var(--display); text-transform: uppercase; color: var(--ink);
             font-size: clamp(42px, 6.6vw, 82px); line-height: 1.0; letter-spacing: 0.01em;
             margin: 16px 0 18px; max-width: 16ch; text-wrap: balance; }
.hero h1 em { font-style: normal; color: var(--ox); }
.hero .dek { max-width: 50ch; font-size: clamp(16.5px, 1.5vw, 19px); line-height: 1.65; }
.hero .cta-row { display: flex; align-items: center; gap: 20px; margin-top: 26px; flex-wrap: wrap; }
.hero .when { font-family: var(--code); font-size: 12px; letter-spacing: 0.06em; color: var(--meta); }

.receipt { background: var(--surface); border: 1px solid var(--hair); padding: 13px 15px 15px;
           font-family: var(--code); font-size: 11.5px; letter-spacing: 0.02em; color: var(--meta); }
.receipt .r-top { display: flex; justify-content: space-between; align-items: center; gap: 10px;
           padding-bottom: 10px; border-bottom: 1px solid var(--hair); }
.receipt .r-live { color: var(--ox); font-weight: 700; }
.receipt .r-rows { min-height: 116px; padding-top: 10px; }
.receipt .r-row { display: flex; justify-content: space-between; gap: 12px; padding: 5px 0;
           transform: translateY(6px); }
.receipt .r-row.in { animation: rowin 420ms var(--ease-expo) forwards; }
@keyframes rowin { to { transform: none; } }
.receipt .r-row b { color: var(--ink); font-weight: 700; }
.receipt .r-row .amt { color: var(--ox); }
.receipt .r-foot { display: flex; justify-content: space-between; gap: 10px; padding-top: 10px;
           margin-top: 6px; border-top: 1px solid var(--hair); }
.receipt .r-foot b { color: var(--ink); }

.hero-rail { padding-bottom: 4px; }
.hero-rail svg { display: block; width: 100%; height: auto; }
/* the rail is a 1440-unit drawing, so on a phone it renders at about a quarter
   scale and its annotations land at 2.4px: not small type, just grey smudges on
   the strokes. The rail itself still reads as four diverging rails, which is the
   whole point of it, so the annotations come off below the width where they can
   be read at all. The element is aria-hidden decoration, so nothing is lost. */
@media (max-width: 1000px) { .hero-rail .hr-anno { display: none; } }
.rail-cap { display: flex; justify-content: space-between; gap: 14px; flex-wrap: wrap;
            font-family: var(--code); font-size: 10.5px; letter-spacing: 0.08em;
            color: var(--meta); padding-bottom: 12px; }

/* ═══════ sections ═══════ */
.sec { padding: clamp(44px, 6vw, 82px) 0; }
.sec.tight { padding: clamp(30px, 4vw, 52px) 0; }
.sec.drench { background: var(--ox-deep); color: var(--ox-on-dark); }
.sec.sunk { background: var(--sunk); }
.rule-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 18px;
             flex-wrap: wrap; padding-bottom: 13px; position: relative; }
.rule-head::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
             background: var(--ink); transform: scaleX(0); transform-origin: left;
             transition: transform 900ms var(--ease-expo); }
.rule-head.seen::after { transform: scaleX(1); }
.sec.drench .rule-head::after { background: currentColor; }
.rule-head h2 { font-family: var(--display); text-transform: uppercase;
             font-size: clamp(28px, 3.6vw, 46px); line-height: 1.0; color: var(--ink); letter-spacing: 0.014em; }
.sec.drench .rule-head h2 { color: #fff; }
.rule-head .lede { font-size: 16px; color: var(--meta); max-width: 52ch; margin-top: 7px; }
.sec.drench .rule-head .lede { color: var(--ox-on-dark); }
.rule-head .all { font-family: var(--code); font-size: 12px; letter-spacing: 0.08em;
             font-weight: 700; color: var(--ox); border-bottom: 2px solid var(--ox); padding-bottom: 2px;
             display: inline-flex; align-items: center; gap: 7px; }
.rule-head .all:hover { color: var(--ink); border-color: var(--ink); }
.sec.drench .rule-head .all { color: #fff; border-color: #fff; }

/* ═══════ HOME list: condensed rows ═══════ */
.lots { position: relative; }
.lot { display: grid; grid-template-columns: minmax(0, 1fr) 190px 136px; gap: clamp(16px,2.4vw,32px);
       align-items: center; padding: 22px clamp(12px,2vw,20px); border-bottom: 1px solid var(--hair);
       position: relative; isolation: isolate; transition: color 280ms var(--ease-out); }
/* the latest row carries a permanent pale ground, which fades the moment the
   reader hovers any row so the oxblood flip reads cleanly, and returns on leave */
.lot--latest::after { content: ''; position: absolute; inset: 0; background: var(--ox-pale);
       z-index: -2; transition: opacity 300ms var(--ease-out); }
.lots:has(.lot:hover) .lot--latest::after { opacity: 0; }
.lot::before { content: ''; position: absolute; inset: 0; background: var(--ox); z-index: -1;
       transform: scaleY(0.1); opacity: 0; transform-origin: bottom;
       transition: transform 400ms var(--ease-expo), opacity 250ms var(--ease-out); }
.lot:hover::before, .lot:focus-visible::before { transform: scaleY(1); opacity: 1; }
.lot h3 { font-family: var(--label); text-transform: uppercase; font-weight: 700;
       font-size: clamp(20px, 2.1vw, 27px); line-height: 1.06; color: var(--ink);
       letter-spacing: 0.01em; transition: color 280ms var(--ease-out); }
.lot .sub { display: block; font-size: 15px; color: var(--meta); max-width: 62ch; margin-top: 6px;
       line-height: 1.5; transition: color 280ms var(--ease-out); }
.lot .spec { font-family: var(--code); font-size: 10.5px; letter-spacing: 0.06em; color: var(--meta);
       text-align: right; line-height: 1.9; white-space: nowrap; transition: color 280ms var(--ease-out); }
.lot:hover h3, .lot:focus-visible h3 { color: var(--on-ox); }
.lot:hover .sub, .lot:hover .spec,
.lot:focus-visible .sub, .lot:focus-visible .spec { color: var(--ox-pale); }
.lot .pillcell { display: flex; justify-content: flex-end; }
.lot:hover .pill, .lot:focus-visible .pill { border-color: rgba(255,255,255,0.55); background: transparent; }
.lot .latest { display: inline-block; font-family: var(--code); font-size: 9.5px; font-weight: 700;
       letter-spacing: 0.11em; color: var(--ox); margin-bottom: 7px; transition: color 280ms var(--ease-out); }
.lot:hover .latest, .lot:focus-visible .latest { color: var(--on-ox); }

/* ═══════ INSIGHTS list: cards, as the live site has them ═══════ */
.cards { display: grid; gap: clamp(18px,2.4vw,28px); }
.card { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
        background: var(--surface); border: 1px solid var(--hair); overflow: hidden;
        transition: border-color 260ms var(--ease-out), transform 260ms var(--ease-out); }
.card:hover, .card:focus-visible { border-color: var(--ox); transform: translateY(-2px); }
.card .c-body { padding: clamp(24px,3vw,40px); display: flex; flex-direction: column; }
.card .c-meta { display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
        font-family: var(--code); font-size: 11.5px; letter-spacing: 0.07em; color: var(--meta); }
.card h3 { font-family: var(--display); text-transform: uppercase; font-size: clamp(24px, 2.6vw, 38px);
        line-height: 1.04; letter-spacing: 0.012em; color: var(--ink); margin: 16px 0 12px; text-wrap: balance; }
.card p { font-size: 16px; color: var(--meta); max-width: 54ch; line-height: 1.55; }
.card .c-foot { margin-top: auto; padding-top: 22px; display: flex; align-items: center;
        justify-content: space-between; gap: 14px; }
.card .c-arrow { color: var(--ox); transition: transform 260ms var(--ease-out);
        display: inline-flex; font-size: 20px; }
.card:hover .c-arrow { transform: translateX(6px); }
.card .c-art { background: var(--sunk); display: flex; align-items: center; justify-content: center;
        padding: clamp(18px,2.4vw,32px); overflow: hidden; }
/* never a hard-coded height on an article image. Two "passing" reviews missed
   that every one of them was distorted by exactly that. */
.card .c-art img { display: block; width: 100%; height: auto; max-height: 300px;
        object-fit: contain; transition: transform 800ms var(--ease-expo); }
.card:hover .c-art img { transform: scale(1.035); }
/* the artless card: wero-payouts-moat has no hero plate yet, so the slot
   carries the topic mark on the sunk ground rather than collapsing */
.card .c-art.no-art { padding: clamp(28px,4vw,48px); }
.card .c-art.no-art .mark { width: clamp(56px,7vw,84px); height: auto; opacity: 0.5; }
.card--latest .c-art.no-art .mark { opacity: 0.85; filter: brightness(0) invert(1); }
/* the latest gets the drench, exactly one card on the page */
.card--latest { background: var(--ox-deep); border-color: var(--ox-deep); }
.card--latest .c-meta { color: var(--ox-on-dark); }
.card--latest .c-meta .latest-flag { color: #fff; font-weight: 700; }
.card--latest h3 { color: #fff; }
.card--latest p { color: var(--ox-on-dark); }
.card--latest .c-arrow { color: #fff; }
.card--latest .c-art { background: rgba(0,0,0,0.22); }
.card--latest .pill { background: transparent; border-color: rgba(255,255,255,0.5); color: #fff; }
.card--latest:hover { border-color: var(--ox-bright); }
.card[hidden] { display: none; }
.no-results { padding: 44px 0; font-family: var(--code); font-size: 13px; letter-spacing: 0.05em;
        color: var(--meta); border-top: 1px solid var(--hair); }
.no-results b { color: var(--ink); }
mark.hit { background: var(--ox-pale); color: var(--ink); padding: 0 2px; }
.card--latest mark.hit { background: rgba(255,255,255,0.22); color: #fff; }

/* ═══════ stat wall — the CV / experience page component ═══════ */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(188px, 1fr));
         border-top: 2px solid var(--ink); }
.stat { padding: 36px 22px 26px 0; border-bottom: 1px solid var(--hair);
         border-right: 1px solid var(--hair); }
.stat:last-child { border-right: none; }
.stat + .stat { padding-left: 24px; }
.stat .n { display: block; font-family: var(--display); font-size: clamp(42px, 5.4vw, 72px);
         line-height: 0.94; color: var(--ink); letter-spacing: 0.014em; font-variant-numeric: tabular-nums; }
.stat .l { display: block; font-family: var(--code); font-size: 10.5px; letter-spacing: 0.08em;
         color: var(--meta); margin-top: 12px; line-height: 1.7; }
.stat.tbc .n { color: var(--hair-2); }
.stat.tbc .l { color: var(--ox); }

/* ═══════ flagship ═══════ */
.flag-grid { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1.02fr);
         gap: clamp(28px, 5vw, 70px); align-items: center; }
.flag .tagline { color: var(--ox-on-dark); }
.flag h2 { font-family: var(--display); text-transform: uppercase; font-size: clamp(32px, 4.6vw, 58px);
         line-height: 1.0; color: #fff; margin: 14px 0 16px; max-width: 17ch; text-wrap: balance;
         letter-spacing: 0.012em; }
.flag p { color: var(--ox-on-dark); max-width: 50ch; }
.sec.drench .plate { background: rgba(0,0,0,0.2); border-color: rgba(255,255,255,0.34); }
.sec.drench .plate::before { border-color: rgba(255,255,255,0.18); }
.sec.drench .plate-cap { color: var(--ox-on-dark); }
.sec.drench .plate-cap b { color: #fff; }

/* ═══════ practice columns ═══════ */
.practice { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
         gap: clamp(26px, 4vw, 60px); }
.practice .col { border-top: 4px solid var(--ox); padding-top: 15px; }
.practice .col + .col { border-top-color: var(--slate-blue); }
.practice .lbl { font-family: var(--code); font-size: 10.5px; letter-spacing: 0.1em; color: var(--meta); }
.practice h3 { font-family: var(--label); text-transform: uppercase; font-weight: 700; font-size: 27px;
         line-height: 1.03; color: var(--ink); margin: 10px 0 10px; }
.practice p { color: var(--meta); font-size: 16px; max-width: 45ch; }
.practice a.more { display: inline-flex; align-items: center; gap: 8px; margin-top: 13px;
         font-family: var(--label); text-transform: uppercase; font-weight: 700; font-size: 18px;
         letter-spacing: 0.05em; color: var(--ink); border-bottom: 3px solid var(--ox); padding-bottom: 2px; }
.practice a.more:hover { color: var(--ox); }

/* ═══════ footer ═══════ */
footer { background: var(--ink-deep); color: rgba(255,255,255,0.62); }
.foot-inner { display: flex; justify-content: space-between; align-items: center; gap: 18px;
         flex-wrap: wrap; padding: 24px 0 40px; font-size: 14.5px; }
.foot-inner .who { display: inline-flex; align-items: center; gap: 10px; }
.foot-inner b { color: #fff; }
.foot-inner a.li-link { display: inline-flex; align-items: center; gap: 8px; font-family: var(--code);
         font-size: 11.5px; letter-spacing: 0.06em; color: var(--ox-on-dark); font-weight: 700; }
.foot-inner a.li-link:hover { color: #fff; }
.foot-inner .mono { font-size: 11.5px; letter-spacing: 0.06em; }

/* ═══════ page heads ═══════ */
.page-head { padding: clamp(42px, 5.6vw, 72px) 0 12px; border-bottom: 2px solid var(--ink); }
.page-head h1 { font-family: var(--display); text-transform: uppercase;
         font-size: clamp(46px, 7.4vw, 94px); line-height: 0.96; color: var(--ink); letter-spacing: 0.01em; }
.page-head h1 em { font-style: normal; color: var(--ox); }
.page-head p { max-width: 58ch; margin: 12px 0 20px; color: var(--meta); font-size: 18px; }
.page-head .tagline { margin-bottom: 10px; }
.controls { display: flex; gap: 20px; align-items: center; flex-wrap: wrap; padding: 16px 0 22px;
         border-bottom: 1px solid var(--hair); margin-bottom: clamp(22px,3vw,34px); }
.search { flex: 1 1 240px; position: relative; }
.search input { width: 100%; font-family: var(--code); font-size: 13px; padding: 11px 0;
         border: none; border-bottom: 2px solid var(--hair-2); background: transparent;
         color: var(--ink); letter-spacing: 0.03em; min-height: 44px; }
.search input:focus { outline: none; border-bottom-color: var(--ox); }
.search input:focus-visible { outline: 3px solid var(--ox); outline-offset: 3px; }
.search input::placeholder { color: var(--meta); }
.filters { display: flex; gap: 9px; flex-wrap: wrap; }
.filters .pill { cursor: pointer; }
.filters .pill:hover { border-color: var(--ox); }
.filters .pill[aria-pressed="true"] { background: var(--ink); color: var(--on-ink); border-color: var(--ink); }

/* ═══════ generic prose page (about, advisory, contact, 404) ═══════ */
.page-body { display: grid; grid-template-columns: minmax(0, 1fr) 300px;
         gap: clamp(28px, 4vw, 64px); padding: clamp(30px,4vw,52px) 0 clamp(44px,6vw,76px);
         align-items: start; }
.page-body.single { grid-template-columns: minmax(0, 1fr); max-width: 72ch; }
.copy { max-width: 68ch; }
.copy > * + * { margin-top: 1.4em; }
.copy h2 { font-family: var(--label); text-transform: uppercase; font-weight: 700;
         font-size: clamp(24px,2.6vw,31px); line-height: 1.05; color: var(--ink);
         margin-top: 2em; letter-spacing: 0.008em; }
.copy h3 { font-family: var(--label); text-transform: uppercase; font-weight: 600; font-size: 21px;
         color: var(--ink); margin-top: 1.6em; letter-spacing: 0.02em; }
.copy p { color: var(--body); }
.copy .lead { font-size: clamp(18px,1.7vw,21px); line-height: 1.55; color: var(--ink); }
.copy ul, .copy ol { padding-left: 0; list-style: none; }
.copy ul > li, .copy ol > li { position: relative; padding-left: 22px; margin-top: 0.6em; }
.copy ul > li::before { content: ''; position: absolute; left: 2px; top: 0.62em; width: 7px; height: 7px;
         border-radius: 50%; background: var(--ox); }
.copy ol { counter-reset: c; }
.copy ol > li { counter-increment: c; }
.copy ol > li::before { content: counter(c, decimal-leading-zero); position: absolute; left: 0;
         top: 0.15em; font-family: var(--code); font-size: 10.5px; font-weight: 700; color: var(--ox); }
.copy a:not(.btn) { color: var(--ox); font-weight: 600; border-bottom: 1.5px solid transparent; }
.copy a:not(.btn):hover, .copy a:not(.btn):focus-visible { border-bottom-color: currentColor; }

/* a definition row set: used on advisory and about */
.deflist { border-top: 2px solid var(--ink); }
.deflist .row { display: grid; grid-template-columns: minmax(150px, 210px) minmax(0, 1fr);
         gap: clamp(16px,2.4vw,32px); padding: 20px 0; border-bottom: 1px solid var(--hair); }
.deflist dt { font-family: var(--label); text-transform: uppercase; font-weight: 700; font-size: 20px;
         line-height: 1.1; color: var(--ink); letter-spacing: 0.012em; }
.deflist dd { color: var(--body); font-size: 16px; }
.deflist dd .k { display: block; font-family: var(--code); font-size: 10.5px; letter-spacing: 0.08em;
         color: var(--meta); margin-bottom: 5px; }

/* side rail on a standard page */
aside.side { display: grid; gap: 18px; font-size: 14.5px; }

/* ═══════ forms (contact) ═══════ */
.form { display: grid; gap: 18px; max-width: 620px; }
.field { display: grid; gap: 7px; }
.field label { font-family: var(--code); font-size: 10.5px; font-weight: 700; letter-spacing: 0.11em;
         text-transform: uppercase; color: var(--meta); }
.field input, .field textarea, .field select {
         font-family: var(--text); font-size: 16.5px; color: var(--ink); background: var(--surface);
         border: 1px solid var(--hair-2); padding: 12px 14px; min-height: 48px; width: 100%; }
.field textarea { min-height: 150px; resize: vertical; line-height: 1.6; }
.field input:focus, .field textarea:focus, .field select:focus {
         outline: none; border-color: var(--ox); box-shadow: inset 0 0 0 1px var(--ox); }
.field input:focus-visible, .field textarea:focus-visible { outline: 3px solid var(--ox); outline-offset: 2px; }
.field ::placeholder { color: var(--meta); }
.field .hint { font-size: 13.5px; color: var(--meta); }
.reasons { display: flex; gap: 9px; flex-wrap: wrap; }
.reasons .chip-btn { font-family: var(--code); font-size: 11px; font-weight: 700; letter-spacing: 0.07em;
         text-transform: uppercase; background: var(--surface); border: 1px solid var(--hair-2);
         color: var(--ink); padding: 9px 14px; min-height: 42px; cursor: pointer;
         transition: background 200ms var(--ease-out), border-color 200ms var(--ease-out), color 200ms var(--ease-out); }
.reasons .chip-btn:hover { border-color: var(--ox); color: var(--ox); }
.reasons .chip-btn[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--on-ink); }
.form-note { font-family: var(--code); font-size: 11.5px; letter-spacing: 0.05em; color: var(--meta); }
.form-status { font-family: var(--code); font-size: 12.5px; letter-spacing: 0.05em; padding: 12px 14px;
         border-left: 3px solid var(--ox); background: var(--ox-pale); color: var(--ink); }
.form-status[hidden] { display: none; }

/* ═══════ ESSAY ═══════ */
.essay-head { padding: clamp(38px, 5vw, 62px) 0 0; max-width: 64rem; }
.essay-head h1 { font-family: var(--display); text-transform: uppercase; color: var(--ink);
         font-size: clamp(36px, 5.2vw, 70px); line-height: 1.0; margin: 15px 0 16px;
         max-width: 20ch; text-wrap: balance; letter-spacing: 0.01em; }
.essay-head .dek { font-size: 20px; color: var(--meta); max-width: 52ch; line-height: 1.5; }
.essay-meta { display: flex; gap: 20px; flex-wrap: wrap; align-items: center; margin-top: 24px;
         padding: 13px 0; border-top: 2px solid var(--ink); border-bottom: 1px solid var(--hair);
         font-family: var(--code); font-size: 12px; letter-spacing: 0.05em; color: var(--meta); }
.essay-meta b { color: var(--ink); font-weight: 700; }
.byline { display: inline-flex; align-items: center; gap: 10px; }
/* circular profile photo, sized and placed as LinkedIn does it on an article
   page. The monogram fallback stays in the markup for a failed image load. */
.avatar { width: 38px; height: 38px; border-radius: 50%; overflow: hidden; flex-shrink: 0;
         background: var(--ox-deep); display: grid; place-items: center; position: relative; }
.avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.avatar .mono-fb { position: absolute; inset: 0; display: grid; place-items: center; color: #fff;
         font-family: var(--display); font-size: 15px; letter-spacing: 0.02em; z-index: -1; }
.li-btn { margin-left: auto; }

/* primary sources: a citation row, not a set of competing boxes */
.src-row { display: flex; flex-wrap: wrap; gap: 8px 26px; padding: 15px 0 0; }
.src-row .s { display: inline-flex; align-items: baseline; gap: 8px; font-size: 14.5px;
         color: var(--meta); transition: color 200ms var(--ease-out); }
.src-row .s i { font-family: var(--code); font-style: normal; font-size: 10.5px; font-weight: 700; color: var(--ox); }
.src-row .s b { color: var(--ink); font-weight: 600; }
.src-row .s:hover { color: var(--ink); }
.src-row .s:hover b { color: var(--ox); }

/* Highlights: the article in five lines, given real presence.
   The star is a 13px clip-path square, not a glyph, so it is optically
   identical in size to the 13px sources dot. No face ships U+2726 anyway. */
.hl-band { background: var(--ox-pale); padding: clamp(20px,2.6vw,30px) clamp(20px,3vw,34px);
         margin-top: clamp(24px,3vw,34px); }
.hl-head { display: flex; align-items: center; gap: 9px; font-family: var(--code); font-size: 10.5px;
         font-weight: 700; letter-spacing: 0.11em; text-transform: uppercase; color: var(--ox); }
.hl-star { width: 13px; height: 13px; flex-shrink: 0; font-size: 0; line-height: 0; background: var(--ox);
         clip-path: polygon(50% 0%, 65% 35%, 100% 50%, 65% 65%, 50% 100%, 35% 65%, 0% 50%, 35% 35%); }
.hl-band ol { list-style: none; counter-reset: hl; margin-top: 14px;
         grid-template-columns: repeat(auto-fit, minmax(268px, 1fr)); display: grid;
         gap: 2px clamp(24px,3vw,44px); padding-left: 0; }
.hl-band li { counter-increment: hl; display: flex; gap: 12px; padding: 10px 0;
         border-top: 1px solid var(--ox-hair); line-height: 1.5; font-size: 15.5px; color: var(--ink); }
.hl-band li::before { content: counter(hl, decimal-leading-zero); font-family: var(--code);
         font-size: 10.5px; font-weight: 700; color: var(--ox); flex-shrink: 0; padding-top: 4px; }

/* three columns: contents left, prose centre, article furniture right.
   Contents and sources never share a column, which is what caused the sticky
   contents list to scroll over the sources card in an earlier build. */
.essay-grid { display: grid; grid-template-columns: 196px minmax(0, 1fr) 272px;
         gap: clamp(24px, 2.8vw, 42px); padding: clamp(28px,4vw,44px) 0 clamp(44px,6vw,72px);
         align-items: start; }
/* the essay is the one page that outgrows the shared 1260 wrap: three columns
   plus a figure that has to stay inside its own column and still be legible */
.essay-wrap { max-width: 1372px; }

/* contents: unboxed, a plain list against a hairline with an oxblood edge
   indicator, as Harvest and Dub do it. Not a card. */
.toc { position: sticky; top: 86px; max-height: calc(100vh - 120px); overflow-y: auto; }
.toc .t-lbl { font-family: var(--code); font-size: 10.5px; font-weight: 700; letter-spacing: 0.11em;
         text-transform: uppercase; color: var(--meta); margin-bottom: 12px; display: block; }
.toc ol { list-style: none; counter-reset: ch; border-left: 1px solid var(--hair); padding-left: 0; }
.toc li { counter-increment: ch; }
.toc a { display: flex; gap: 10px; padding: 8px 0 8px 14px; margin-left: -1px;
         border-left: 2px solid transparent; color: var(--meta); font-size: 14px; line-height: 1.4;
         transition: color 200ms var(--ease-out), border-color 200ms var(--ease-out); }
.toc a::before { content: counter(ch, decimal-leading-zero); font-family: var(--code); font-size: 10px;
         color: var(--hair-2); padding-top: 3px; transition: color 200ms var(--ease-out); }
.toc a:hover { color: var(--ink); }
.toc a[aria-current="true"] { color: var(--ink); font-weight: 600; border-left-color: var(--ox); }
.toc a[aria-current="true"]::before { color: var(--ox); }
.toc-mobile { display: none; }

/* Running text keeps its 68ch measure. FIGURES DO NOT.
   The essay's centre column is about 820px wide at desktop, but the whole
   article used to be clamped to the 68ch measure, so every diagram was drawn
   into 532px. A 980-unit viewBox in a 532px box renders at 54%, which put the
   timeline's milestone labels on screen at 8.4px, and 5.4px on a phone. That is
   the same defect as the CV map's unreadable country labels, and it was hiding
   in plain sight because the figures are legible in the expanded view.
   So the measure moves from the container to the children: every direct child
   of .prose keeps the measure, and figures opt out and take the column. Nothing
   can silently lose its measure, because the default still applies to everything.

   The measure is in rem, not ch, on purpose. ch is relative to the element's own
   font, so 68ch on an h2 set in Barlow Condensed at 33px is over 1100px wide:
   moving the old container value to the children unchanged would have quietly
   un-measured every heading. 34rem is the same 544px the body face produced. */
.prose { --measure: 34rem; max-width: none; }
.prose > * { max-width: var(--measure); }
.prose > figure.plain, .prose > figure.fig, .prose > figure.hero-trace,
.prose > .fig, .prose > .hero-trace { max-width: min(100%, 720px); }
.prose p { margin: 0 0 1.5em; text-wrap: pretty; }
.prose h2 { font-family: var(--label); text-transform: uppercase; font-weight: 700; color: var(--ink);
         font-size: clamp(26px,2.8vw,33px); line-height: 1.04; margin: 1.7em 0 0.6em;
         letter-spacing: 0.008em; scroll-margin-top: 96px; }
/* INLINE TEXT LINKS, AND ONLY THOSE.
   Scoped to the elements that carry running text rather than excluding components
   one at a time. A component is never the child of a paragraph, so it can never
   be caught by this, and no future component has to be added to a list.

   The previous form was `.prose a:not(.btn):not(.fig-btn)`, which at three
   classes out-specified any component's own single-class hover rule. It broke two
   of them: the figure download button went black on black, and the article CTA
   went oxblood on oxblood, so its label vanished on hover and only the arrow was
   left. Both were the same bug wearing different clothes, and patching each with
   another :not() would only have queued up the third.

   The :where() wrapper contributes no specificity, so this whole rule sits at
   .prose's single class and any component style wins outright. That is the point:
   a generic prose rule should never be able to overpower a component's own
   states. */
.prose :where(p, li, blockquote, dd, dt, figcaption) :where(a:not(.btn, .fig-btn)) {
         color: var(--ox); font-weight: 600; border-bottom: 1.5px solid transparent; }
.prose :where(p, li, blockquote, dd, dt, figcaption) :where(a:not(.btn, .fig-btn)):hover,
.prose :where(p, li, blockquote, dd, dt, figcaption) :where(a:not(.btn, .fig-btn)):focus-visible {
         border-bottom-color: currentColor; }
/* figures escape the 34rem measure but keep the figure cap: July 2026, reined
   in from 1040px to 720px at Vishwanath's request. The expanded state's own
   max-width:none still wins, so expand takes the full screen as before. */
.prose > .fig, .prose > figure { max-width: min(100%, 720px); }
.prose ul, .prose ol { padding-left: 0; list-style: none; margin: 0 0 1.5em; }
.prose ul > li, .prose ol > li { position: relative; padding-left: 24px; margin-bottom: 0.55em; }
.prose ul > li::before { content: ''; position: absolute; left: 3px; top: 0.62em; width: 7px; height: 7px;
         border-radius: 50%; background: var(--ox); }
.prose ol { counter-reset: pc; }
.prose ol > li { counter-increment: pc; }
.prose ol > li::before { content: counter(pc, decimal-leading-zero); position: absolute; left: 0;
         top: 0.2em; font-family: var(--code); font-size: 10.5px; font-weight: 700; color: var(--ox); }
.prose strong { color: var(--ink); font-weight: 700; }
/* the pull quote: where the argument is raised out of the paragraph */
.prose blockquote { margin: 2.2em 0; padding: 4px 0 4px clamp(18px,2.4vw,28px);
         border-left: 4px solid var(--ox); font-size: clamp(19px,2vw,23px); line-height: 1.42;
         color: var(--ink); font-weight: 600; text-wrap: pretty; }
.prose blockquote strong em, .prose blockquote em strong { font-style: italic; color: var(--ox); }
.prose blockquote p { margin: 0; }
.prose blockquote p + p { margin-top: 0.6em; }
/* drop cap, aligned on the first line rather than floating above it */
.prose.dropcap > p:first-of-type::first-letter {
         float: left; font-family: var(--display); font-size: 3.44em; line-height: 0.78;
         padding: 0.06em 0.1em 0 0; color: var(--ox); text-transform: uppercase; }

/* the stat callout, vertically centred on its number */
.callout { display: grid; grid-template-columns: minmax(112px, auto) minmax(0, 1fr);
         gap: clamp(18px,3vw,30px); align-items: center; margin: 2.2em 0; padding: 22px 0;
         border-top: 2px solid var(--ink); border-bottom: 1px solid var(--hair); }
.callout .n { display: block; font-family: var(--display); font-size: clamp(44px, 5.8vw, 68px);
         line-height: 1; color: var(--ox); letter-spacing: 0.012em; font-variant-numeric: tabular-nums; }
.callout .t { font-size: 16.5px; color: var(--body); max-width: 44ch; line-height: 1.5; }
.callout .t b { color: var(--ink); }

/* the in-body invitation, as the live site frames it */
.discuss { border: var(--frame) solid var(--ox); background: var(--ox-pale); padding: clamp(18px,2.2vw,26px);
         margin: 2.4em 0 0; }
.discuss a { display: inline-flex; align-items: center; gap: 9px; font-family: var(--code);
         font-size: 12.5px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
         color: var(--ox); background: transparent; border: 1px solid var(--ox);
         padding: 12px 20px; min-height: 46px;
         transition: background 200ms var(--ease-out), color 200ms var(--ease-out); }
/* the label must travel with the fill. Ink on oxblood is 2.4:1, which is what
   made the hover state less legible than the resting one. */
.discuss a:hover, .discuss a:focus-visible { background: var(--ox); color: var(--on-ox); }
.discuss a:hover svg.i, .discuss a:focus-visible svg.i { color: var(--on-ox); }

/* right rail: sources, LinkedIn, read next */
aside.rail { font-size: 14.5px; display: grid; gap: 20px; }
.r-card { background: var(--surface); border: 1px solid var(--hair); padding: 16px 18px 19px; }
.r-card .r-lbl { display: flex; align-items: center; gap: 9px; font-family: var(--code);
         font-size: 10.5px; font-weight: 700; letter-spacing: 0.11em; text-transform: uppercase; color: var(--meta); }
/* 13px circle, optically identical to the 13px Highlights star */
.r-card .r-lbl .dot { width: 13px; height: 13px; border-radius: 50%; background: var(--ox); flex-shrink: 0; }
.source-list { list-style: none; margin-top: 12px; padding-left: 0; }
.source-list li { padding: 9px 0; border-bottom: 1px solid var(--hair); line-height: 1.5; }
.source-list li:last-child { border-bottom: none; }
.source-list a { display: block; color: var(--body); transition: color 200ms var(--ease-out), transform 200ms var(--ease-out); }
.source-list a b { color: var(--ink); }
.source-list a:hover { color: var(--ox); transform: translateX(3px); }
.source-list a:hover b { color: var(--ox); }
.li-card .r-lbl { color: var(--ink); font-family: var(--text); font-size: 15px; letter-spacing: 0;
         text-transform: none; font-weight: 700; }
.li-card p { color: var(--meta); font-size: 14.5px; margin: 9px 0 14px; line-height: 1.5; }
.li-card .btn { width: 100%; }
.li-card .btn + .btn { margin-top: 9px; }
.next-card { background: var(--ox-deep); border-color: var(--ox-deep); color: var(--ox-on-dark); }
.next-card .r-lbl { color: var(--ox-on-dark); }
.next-card h4 { font-family: var(--display); text-transform: uppercase; font-size: 21px; line-height: 1.08;
         letter-spacing: 0.014em; color: #fff; margin: 11px 0 12px; }
.next-card a.go { font-family: var(--code); font-size: 11.5px; font-weight: 700; letter-spacing: 0.07em;
         color: #fff; border-bottom: 2px solid var(--ox-on-dark); padding-bottom: 2px;
         display: inline-flex; align-items: center; gap: 7px; }
.next-card a.go:hover { border-color: #fff; }

/* ═══════ figures ═══════ */
.fig { margin: 2.6em 0; border: 2px solid var(--ink); background: var(--surface); }
.fig-head { padding: 15px 20px 13px; border-bottom: 1px solid var(--hair); }
.fig-no { font-family: var(--code); font-size: 10.5px; font-weight: 700; letter-spacing: 0.1em; color: var(--ox); }
.fig-title { font-family: var(--label); text-transform: uppercase; font-weight: 700;
         font-size: clamp(19px, 2.1vw, 26px); line-height: 1.04; color: var(--ink); margin-top: 5px;
         letter-spacing: 0.008em; }
.fig-stage { position: relative; }
.fig-stage svg { width: 100%; height: auto; display: block; }
.fig-stage > img { width: 100%; height: auto; display: block; }

/* ═══════ figures keep their paper, in both themes ═══════
   The inline SVG figures and the generated diagram masters are light-theme
   artwork: their labels are baked #1A1512 and #6B635C, and several of them
   paint an opaque panel over only PART of the viewBox. On a dark page the
   labels outside that panel — SETTLEMENT, INTERCHANGE: FUNDS MOVE HERE, the
   axis captions — were near-black on near-black and simply gone.

   So the figure sits on its own sheet of paper whatever the theme, and the
   sheet is the frame. This is the same decision already taken for the APM and
   milestone logo chips, which keep a white ground so a brand mark never fights
   the tint. --fig-paper is fixed, not themed, on purpose: the artwork inside it
   cannot follow a token, so neither can its ground. A dark master out of
   trace-diagrams would let this go; until then, this is what keeps them
   readable. */
.fig-stage,
.fig-stage > img { background: var(--fig-paper); }
[data-theme="dark"] .fig-stage,
[data-theme="dark"] .fig-stage > img { border-radius: 2px; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .fig-stage,
  :root:not([data-theme]) .fig-stage > img { border-radius: 2px; }
}
.fig-lane { transition: opacity 300ms var(--ease-out); }
.fig.focus-ctrl .fig-lane:not(.lane-ctrl), .fig.focus-coord .fig-lane:not(.lane-coord),
.fig.focus-settle .fig-lane:not(.lane-settle) { opacity: 0.2; }
.fig-bar { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; padding: 12px 20px;
         border-top: 1px solid var(--hair); border-bottom: 1px solid var(--hair); background: var(--bg); }
.fig-btn { display: inline-flex; align-items: center; gap: 7px; font-family: var(--code); font-size: 11px;
         letter-spacing: 0.07em; font-weight: 700; background: var(--surface); border: 1px solid var(--hair-2);
         color: var(--ink); padding: 8px 13px; min-height: 38px; cursor: pointer;
         transition: background 200ms var(--ease-out), color 200ms var(--ease-out), border-color 200ms var(--ease-out); }
.fig-btn:hover, .fig-btn:focus-visible { background: var(--ink); color: var(--on-ink);
         border-color: var(--ink); border-bottom-color: var(--ink); }
.fig-btn:hover svg.i, .fig-btn:focus-visible svg.i { color: var(--on-ink); }
.fig-btn.play { background: var(--ox); border-color: var(--ox); color: var(--on-ox); }
.fig-btn.play:hover { background: var(--ink); border-color: var(--ink); }
.fig-readout { margin-left: auto; font-family: var(--code); font-size: 11px; letter-spacing: 0.05em;
         color: var(--meta); display: flex; align-items: center; gap: 13px; }
.fig-readout .op { color: var(--ink); font-weight: 700; }
.fig-readout .clock { font-weight: 700; font-variant-numeric: tabular-nums; }
.fig-legend { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); }
.fig-legend.two { grid-template-columns: repeat(2, minmax(0,1fr)); }
.fig-plane { text-align: left; background: var(--surface); border: none; border-right: 1px solid var(--hair);
         padding: 15px 18px 17px; cursor: pointer; transition: background 240ms var(--ease-out); }
.fig-plane:last-child { border-right: none; }
.fig-plane:hover, .fig-plane[aria-pressed="true"] { background: var(--bg); }
.fig-plane .p-band { display: block; width: 34px; height: 5px; border-radius: 3px; margin-bottom: 9px; }
.fig-plane .p-name { display: block; font-family: var(--label); text-transform: uppercase;
         font-weight: 700; font-size: 20px; color: var(--ink); line-height: 1.05; }
.fig-plane .p-who { display: block; font-family: var(--code); font-size: 9.5px; letter-spacing: 0.08em;
         margin-top: 5px; font-weight: 700; }
.fig-plane .p-who.epi { color: var(--slate-blue); }
.fig-plane .p-who.not { color: var(--ox); }
.fig-plane .p-txt { display: block; font-size: 14.5px; color: var(--body); margin-top: 9px; line-height: 1.45; }
.fig-plane .p-det { display: block; font-family: var(--code); font-size: 10px; color: var(--meta);
         margin-top: 9px; line-height: 1.7; }
.fig-foot { display: flex; justify-content: space-between; gap: 14px; flex-wrap: wrap;
         padding: 11px 20px 13px; font-family: var(--code); font-size: 10px; letter-spacing: 0.04em; color: var(--meta); }
/* an interactive figure expands in place, so play and the coupled legend keep
   working. A static figure goes to the lightbox instead. */
/* The expanded figure is AS WIDE AS THE SCREEN ALLOWS AND ONLY AS TALL AS IT
   NEEDS. Stretching it to 94vh only invented space that nothing wanted: first as
   two dead bands above and below the stack, then, once the stack was pinned, as a
   void inside the detail panel. A drawing with a fixed aspect ratio cannot use
   spare height, so the honest answer is not to claim it. Top-aligned rather than
   centred, so the drawing sits in one place regardless of what is below it. */
.fig.expanded,
.hero-trace.expanded { position: fixed; top: 3vh; left: 3vw; right: 3vw; bottom: auto;
         height: auto; max-height: 94vh;
         z-index: var(--z-lb); margin: 0; overflow: auto;
         background: var(--surface); box-shadow: 0 0 0 100vmax rgba(23,18,15,0.88); }
/* the figure fills its own column and stops there. An earlier draft gave it a
   negative right margin to break out wider, which put the LinkedIn card on top
   of the diagram. If a figure needs more room, take it from the rail. */
.prose figure.plain { margin: 2.6em 0; }
.prose figure.plain img { display: block; width: 100%; height: auto; border: 1px solid var(--hair);
         background: var(--surface); }
.prose figure.plain figcaption, .fig figcaption { font-family: var(--code); font-size: 10.5px;
         letter-spacing: 0.06em; color: var(--meta); padding-top: 10px; line-height: 1.7; }
.prose figure.plain figcaption b, .fig figcaption b { color: var(--ox); }

/* ═══════ lightbox ═══════ */
.prose figure img, .card .c-art img, .lightboxable { cursor: zoom-in; }
dialog.lightbox { border: none; padding: 0; background: transparent; max-width: 96vw; max-height: 96vh;
         outline: none; overflow: auto; }
dialog.lightbox::backdrop { background: rgba(23,18,15,0.88); }
dialog.lightbox img { max-width: 94vw; max-height: 88vh; width: auto; height: auto; display: block;
         background: var(--surface); border: 1px solid var(--hair-2); cursor: zoom-in; }
dialog.lightbox img.lb-zoomed { max-width: none; max-height: none; cursor: zoom-out; }
.lb-bar { position: fixed; top: 14px; right: 16px; display: flex; gap: 8px; z-index: 2; }
.lb-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px;
         font-family: var(--code); font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase;
         color: #fff; background: rgba(23,18,15,0.62); border: 1px solid rgba(255,255,255,0.5);
         padding: 10px 14px; cursor: pointer; min-height: 44px; min-width: 44px;
         transition: border-color 150ms ease, background 150ms ease; }
.lb-btn:hover { border-color: #fff; background: rgba(23,18,15,0.92); }

/* ═══════ reveals: transform only, so nothing can ever ship invisible ═══════ */
.reveal-rise { transform: translateY(12px); transition: transform 640ms var(--ease-expo); }
.reveal-rise.seen { transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important;
         transition-duration: 0.01ms !important; }
  .rule-head::after { transform: scaleX(1); }
  .reveal-rise { transform: none; }
  .plate.dwell img.still { animation: none; }
  .nav-overlay a { transform: none; }
  .receipt .r-row { transform: none; }
}

/* contents collapses into an accordion before it can ever strand at the foot */
@media (max-width: 1180px) {
  .essay-grid { grid-template-columns: minmax(0,1fr) 272px; }
  .toc { display: none; }
  .toc-mobile { display: block; margin-top: clamp(20px,2.6vw,28px); border: 1px solid var(--hair);
         background: var(--surface); }
  .toc-mobile summary { list-style: none; cursor: pointer; padding: 14px 18px; font-family: var(--code);
         font-size: 11px; font-weight: 700; letter-spacing: 0.11em; text-transform: uppercase;
         color: var(--ink); display: flex; justify-content: space-between; align-items: center; min-height: 48px; }
  .toc-mobile summary::-webkit-details-marker { display: none; }
  .toc-mobile summary .tog { color: var(--ox); }
  .toc-mobile[open] summary .tog-open { display: none; }
  .toc-mobile:not([open]) summary .tog-close { display: none; }
  .toc-mobile .inner { padding: 0 18px 14px; }
  .toc-mobile ol { list-style: none; counter-reset: ch; padding-left: 0; }
  .toc-mobile li { counter-increment: ch; }
  .toc-mobile a { display: flex; gap: 10px; padding: 9px 0; color: var(--body); font-size: 14.5px;
         border-bottom: 1px solid var(--hair); }
  .toc-mobile li:last-child a { border-bottom: none; }
  .toc-mobile a::before { content: counter(ch, decimal-leading-zero); font-family: var(--code);
         font-size: 10px; color: var(--ox); padding-top: 3px; }
  .page-body { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 900px) {
  .hero-grid, .flag-grid { grid-template-columns: 1fr; }
  .receipt { max-width: 470px; }
  .essay-grid { grid-template-columns: 1fr; }
  .card { grid-template-columns: 1fr; }
  .card .c-art { order: -1; padding: 0; }
  .card .c-art img { max-height: 260px; width: 100%; object-fit: cover; }
  .card .c-art.no-art { padding: 36px; }
  .fig-legend, .fig-legend.two { grid-template-columns: 1fr; }
  .fig-plane { border-right: none; border-bottom: 1px solid var(--hair); }
  .fig-plane:last-child { border-bottom: none; }
  .deflist .row { grid-template-columns: 1fr; gap: 8px; }
}
@media (max-width: 860px) {
  nav.desk { display: none; }
  .nav-toggle { display: inline-flex; }
  /* metadata is mandatory on every row, so it reflows under the headline
     rather than being dropped on narrow screens */
  .lot { grid-template-columns: minmax(0,1fr) 168px; }
  .lot .spec { grid-column: 1; grid-row: 2; text-align: left; margin-top: 8px; }
  .lot .pillcell { grid-column: 2; grid-row: 1; align-items: flex-start; }
}
@media (max-width: 560px) {
  .lot { grid-template-columns: minmax(0,1fr); }
  .lot .pillcell { grid-column: 1; grid-row: 3; justify-content: flex-start; margin-top: 10px; }
  .lot .spec { grid-row: 2; }
  .callout { grid-template-columns: 1fr; gap: 12px; align-items: start; }
  .fig-readout { margin-left: 0; width: 100%; }
}

@media print {
  header, .nav-overlay, footer, .toc, .toc-mobile, .fig-bar, .discuss, aside.rail, .skip { display: none; }
  .essay-grid { grid-template-columns: 1fr; }
  body { background: #fff; font-size: 11pt; }
}

/* ═══════ the interactive timeline figure ═══════
   Milestones are drawn, dated, flagged and labelled on load. ms-ahead only ever
   applies while play is running, so with zero interaction nothing is dimmed and
   nothing is hidden: a complete static timeline that happens to also play. */
.fig-timeline .fig-stage { padding: 8px 4px 2px; }
.ms { cursor: pointer; }
.ms .ms-node, .ms .ms-stem, .ms .ms-text {
         transition: opacity 300ms var(--ease-out), transform 300ms var(--ease-out); }
.ms:hover .ms-node, .ms:focus-visible .ms-node, .ms.ms-on .ms-node {
         transform: scale(1.55); transform-box: fill-box; transform-origin: center; }
.ms:hover .ms-label, .ms:focus-visible .ms-label, .ms.ms-on .ms-label { fill: var(--ox); }
.ms:focus-visible { outline: none; }
.ms:focus-visible .ms-hit { fill: var(--ox-tint); stroke: var(--ox); stroke-width: 2; }
.ms.ms-ahead { opacity: 0.22; }
.ms.ms-on .ms-date { fill: var(--ox); }

/* ── motion B: milestones arrive rather than un-dim, and a playhead runs the
      axis between them. Applied by JS only while playing, and cleared on stop,
      so it can never leave anything hidden. ── */
.fig-timeline.anim-b.playing .ms { opacity: 0; }
.fig-timeline.anim-b.playing .ms.ms-in { opacity: 1; }
.fig-timeline.anim-b.playing .ms.ms-in .ms-node {
         animation: ms-pop 460ms var(--ease-expo) both; transform-box: fill-box; transform-origin: center; }
.fig-timeline.anim-b.playing .ms.ms-in .ms-text { animation: ms-rise 420ms var(--ease-expo) both; }
.fig-timeline.anim-b.playing .ms.ms-in .ms-stem { animation: ms-draw 380ms var(--ease-out) both; }
@keyframes ms-pop  { 0% { transform: scale(0); } 62% { transform: scale(1.28); } 100% { transform: scale(1); } }
@keyframes ms-rise { 0% { opacity: 0; transform: translateY(7px); } 100% { opacity: 1; transform: none; } }
@keyframes ms-draw { 0% { opacity: 0; } 100% { opacity: 1; } }
#msHead { transition: none; }

/* the motion switch */

/* ── the detail panel: flags and scheme marks where there is room to read them ── */
.ms-panel { border-top: 1px solid var(--hair); background: var(--ox-pale);
         padding: 15px clamp(16px,2.4vw,22px) 18px; }
.ms-p-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.ms-p-flags { display: inline-flex; align-items: center; gap: 5px; }
svg.ms-flag { width: 24px; height: 16px; display: block; border-radius: 2px; }
.ms-p-date { font-family: var(--code); font-size: 12.5px; font-weight: 700; letter-spacing: 0.09em;
         color: var(--ox); }
.ms-p-status { font-family: var(--code); font-size: 9.5px; font-weight: 700; letter-spacing: 0.11em;
         color: var(--ink); background: var(--chip-veil); padding: 3px 8px; }
.ms-p-status[hidden] { display: none; }
/* marks sit on their own white chip so a dark wordmark never fights the tint,
   and each is height-clamped so a square mark and a wordmark read as a family */
.ms-p-logos { display: inline-flex; align-items: center; gap: 7px; margin-left: auto; }
.ms-logo { display: inline-flex; align-items: center; justify-content: center; background: #fff;
         border: 1px solid var(--hair); border-radius: 4px; padding: 5px 9px; height: 32px; }
.ms-logo img { display: block; width: auto; height: 100%; max-height: 20px; max-width: 92px;
         object-fit: contain; }
.ms-p-label { font-family: var(--label); text-transform: uppercase; font-weight: 700;
         font-size: clamp(19px,2.1vw,24px); line-height: 1.06; color: var(--ink); margin: 10px 0 6px;
         letter-spacing: 0.008em; }
.ms-p-detail { font-size: 15.5px; line-height: 1.5; color: var(--body); max-width: 70ch; }

.fig-key { display: grid; gap: 9px; padding: 14px 18px 16px; }
.fig-key .k-row, .ms-p-key .k-row { display: flex; align-items: flex-start; gap: 10px; font-size: 13.5px;
         color: var(--body); line-height: 1.4; }
.fig-key .k-mark, .ms-p-key .k-mark { width: 13px; height: 13px; border-radius: 50%; flex-shrink: 0; margin-top: 3px; }
.fig-key .k-done, .ms-p-key .k-done { background: var(--ox); }
.fig-key .k-focal, .ms-p-key .k-focal { background: var(--ox); border-radius: 2px; transform: rotate(45deg); }
.fig-key .k-commit, .ms-p-key .k-commit { background: var(--surface); box-shadow: inset 0 0 0 2.4px var(--slate-blue); }
.fig-key .k-target, .ms-p-key .k-target { background: var(--surface); box-shadow: inset 0 0 0 2px var(--stone); opacity: 0.85; }

/* the signature, as on every generated Trace diagram */
.fig-sign { display: flex; align-items: center; justify-content: space-between; gap: 14px;
         flex-wrap: wrap; padding: 12px 20px 15px; border-top: 1px solid var(--hair); }
.fig-sign .fs-who { display: inline-flex; align-items: center; gap: 9px; font-size: 15px;
         color: var(--ink); }
.fig-sign .fs-who b { font-weight: 600; }
svg.fs-mark { width: 26px; height: 26px; flex-shrink: 0; }
.fig-sign .fs-src { font-family: var(--code); font-size: 10px; letter-spacing: 0.04em;
         color: var(--meta); text-align: right; }

/* A wide horizontal figure cannot be legible in a 350px column: the timeline's
   labels land at 5.4px there, which is not small text, it is no text. So below
   the point where the drawing would be squeezed under about 70%, the stage keeps
   a legible minimum and scrolls sideways instead, and the scroll container is
   focusable so a keyboard can reach it. Expand is still the better path on a
   phone and the button is right underneath. */
/* a grid item's automatic minimum is min-content, so without these the 760px
   minimum below would push the whole column, and the page, wider instead of
   scrolling inside the stage */
.essay-grid > *, .prose, .prose > .fig, .prose > figure, .fig-stage { min-width: 0; }
/* only shown at the widths where the stage actually scrolls, below */
.fig-scrollnote { display: none; margin: 0; padding: 9px 20px 11px;
         border-top: 1px solid var(--hair); font-family: var(--code); font-size: 10.5px;
         letter-spacing: 0.04em; text-transform: uppercase; color: var(--meta); }
@media (max-width: 1000px) {
  .fig .fig-stage, .hero-trace .ht-stage {
           overflow-x: auto; overscroll-behavior-x: contain;
           -webkit-overflow-scrolling: touch; }
  .fig .fig-stage > svg, .hero-trace .ht-stage > svg { min-width: 760px; }
  /* a cut-off drawing with no cue reads as a broken figure, so say what it is */
  .fig-scrollnote { display: block; }
}
/* expanded takes the viewport, so the minimum, the sideways scroll and the cue
   that explains it all come off */
.fig.expanded .fig-scrollnote { display: none; }
.fig.expanded .fig-stage { overflow-x: visible; }
.fig.expanded .fig-stage > svg { min-width: 0; }

/* ── EXPANDED: THE DRAWING TAKES THE SCREEN ──
   Three things were wrong here and they compounded.

   1. max-width. The inline rule capping a figure at 1040px still applied once the
      figure went position: fixed, so on an 1800px display the expanded view was
      1040px of drawing and 760px of dimmed page. On a larger monitor it was worse.
      The cap belongs to the in-column state only.

   2. justify-content: center on the column. The stack was centred as a block, so
      any spare height was split as equal dead bands above and below it instead of
      going to the drawing, which is the only thing anyone expanded the figure to
      see. The stage is now the flexible child: it takes every pixel the furniture
      does not need, and centres the drawing inside itself.

   3. The 62vh cap on the drawing. With the stage sized properly the cap only
      stopped the drawing growing into room that was already its own. */
.prose > .fig.expanded, .fig.expanded {
         display: flex; flex-direction: column; justify-content: flex-start;
         max-width: none; width: auto; }
/* The drawing is WIDTH limited: at this aspect ratio a full-width timeline is
   about 485px tall in 658px of available height, so there is always slack. Where
   that slack goes decides whether the figure is calm or twitchy.

   It used to go above and below the whole stack, as two dead bands, and the
   drawing was centred in the leftover, so every change of milestone re-measured
   the detail panel and the drawing slid up or down by up to 23px. Mousing along
   the timeline made the whole figure shiver.

   Now the stage is sized to its own content and pinned under the head, so the
   drawing cannot move at all, and the SLACK GOES TO THE DETAIL PANEL, which grows
   to fill it. The panel is a designed surface with its own ground, so space spent
   there reads as generous rather than as a void, and the legend and signature stay
   on the bottom edge where they belong. */
.fig.expanded .fig-stage { flex: 0 0 auto; min-height: 0;
         display: flex; align-items: flex-start; justify-content: center;
         padding: clamp(10px,1.6vh,22px) clamp(10px,1.4vw,26px); }
/* The drawing is capped by what the furniture below it needs at its FULLEST, not
   at its current size. The panel's detail text runs to three lines on the longest
   milestone, so if the drawing were allowed to take the room left by a one-line
   milestone, moving to a three-line one would push the signature off the bottom
   edge and the figure would start scrolling. Reserving the worst case costs the
   drawing some height on a short screen and buys a figure that never moves and
   never scrolls. --furniture is head, bar, three-line panel, legend, signature. */
.fig.expanded { --furniture: 512px; }
/* On a short screen the furniture and a drawing worth looking at do not both fit:
   at 720px tall, reserving all of it leaves the timeline 177px and expanding stops
   being worth doing. The legend comes off there, because the panel already states
   the selected milestone's status in words, and that 69px goes to the drawing. */
@media (max-height: 820px) {
  .fig.expanded { --furniture: 440px; }
  .fig.expanded .fig-legend { display: none; }
}
/* --furniture is now a FALLBACK only. site.js measures each figure's real
   furniture on expand (and on resize while expanded) and writes it inline, so a
   figure carrying 190px of head, bar and foot reserves 190px rather than the
   adoption timeline's 512px. Before that fix, expanding an essay figure made the
   sheet wider and the drawing SMALLER, which is the opposite of what the control
   promises. The timeline is unaffected: .ms-p-detail already reserves its
   worst-case three lines in CSS, so measuring it reads the same 512px it always
   reserved. */
.fig.expanded .fig-stage svg { max-height: max(220px, calc(94vh - var(--furniture)));
         max-width: 100%; height: auto; width: 100%; }

/* ── ZOOM IN THE EXPANDED FIGURE ──
   The photo lightbox has had zoom since it shipped. An interactive figure never
   goes to the lightbox, because play and the coupled legend have to keep working,
   so until now the drawings whose small labels most reward a close look were the
   only ones that could not be magnified. site.js injects these controls into
   every expanded figure's bar, so this is one implementation for all fifteen.
   Past 100% the stage becomes the scroll container and the drawing overflows it
   in both directions, which is what zoom means. */
.fz { display: none; align-items: center; gap: 6px; margin-left: auto; }
.fig.expanded .fz { display: inline-flex; }
.fz .fig-btn { min-width: 44px; justify-content: center; }
.fz-pct { font-family: var(--code); font-size: 11px; letter-spacing: 0.06em;
         color: var(--meta); min-width: 4.5ch; text-align: center; }
.fig.expanded.is-zoomed .fig-stage { overflow: auto; justify-content: flex-start;
         max-height: max(220px, calc(94vh - var(--furniture)));
         -webkit-overflow-scrolling: touch; }
.fig.expanded.is-zoomed .fig-stage > svg { min-width: 0; max-width: none; }
@media (max-width: 620px) { .fz-reset { display: none; } }

.fig.expanded .ms-panel { flex: 0 0 auto; padding-top: 14px; padding-bottom: 16px; }
/* An expanded sheet is around 1700px wide and a paragraph must not be, so the
   panel's text keeps its measure and the rest of the row would otherwise be a pale
   void. The legend moves up into it instead: two columns, the selected milestone's
   detail on the left and the key to the marks on the right, which is where a
   reader looking at an unfamiliar dot shape wants it anyway. The separate legend
   row below is suppressed, so nothing is said twice and the sheet gets shorter. */
@media (min-width: 1100px) {
  .fig.expanded .ms-panel { display: grid; grid-template-columns: minmax(0, 1fr) minmax(300px, 0.62fr);
           column-gap: clamp(28px, 4vw, 72px); align-items: start; }
  .fig.expanded .ms-panel > .ms-p-head,
  .fig.expanded .ms-panel > .ms-p-label,
  .fig.expanded .ms-panel > .ms-p-detail { grid-column: 1; }
  .fig.expanded .ms-panel > .ms-p-key { grid-column: 2; grid-row: 1 / span 3; align-self: center; }
  .fig.expanded > .fig-legend { display: none; }
  .fig.expanded .ms-p-key { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
           gap: 10px 22px; }
  .fig.expanded .ms-p-key .k-row { font-size: 13px; line-height: 1.35; }
  /* in one column the auto margin put the marks at the right edge of the sheet;
     in two it put them in the middle of it, attached to nothing */
  .fig.expanded .ms-p-logos { margin-left: 4px; }
}
/* the key lives inside the panel only in the expanded state */
.ms-p-key { display: none; }
/* the detail runs to three lines on the longest milestone and one on the shortest.
   Reserving three keeps the sheet's own height constant as the reader moves along
   the timeline, which costs two lines of the panel's ground and nothing else. */
.fig.expanded .ms-p-detail { min-height: calc(3 * 1.62em); }
.fig.expanded .fig-key { padding-top: 10px; padding-bottom: 12px; flex: 0 0 auto; }
.fig.expanded .fig-sign { padding-top: 9px; padding-bottom: 11px; flex: 0 0 auto; }
.fig.expanded .fig-legend { flex: 0 0 auto; }
.fig.expanded .fig-head { flex: 0 0 auto; }
.fig.expanded .fig-bar { flex: 0 0 auto; position: sticky; top: 0; z-index: 2; }

@media (prefers-reduced-motion: reduce) {
  .ms .ms-node, .ms .ms-stem, .ms .ms-text { transition: none; }
  .ms.ms-ahead { opacity: 1; }
  .fig-timeline.anim-b.playing .ms { opacity: 1; }
  .fig-timeline.anim-b.playing .ms.ms-in .ms-node,
  .fig-timeline.anim-b.playing .ms.ms-in .ms-text,
  .fig-timeline.anim-b.playing .ms.ms-in .ms-stem { animation: none; }
  #msHead { display: none; }
}
@media (max-width: 700px) {
  .ms-p-detail { font-size: 15px; }
  .fig-key { padding: 12px 16px 14px; }
  .ms-p-logos { margin-left: 0; }
  .fig-sign .fs-src { text-align: left; }
}

/* ═══════ the interactive article hero (CHALLENGER, one article) ═══════
   A payment that resolves into its own message trace as the reader moves across
   it. The interaction carries the article's argument: the operator field changes
   hands mid-scrub, and the rail changes colour under the cursor at the same
   point, which is the thing the piece is about.

   Zero interaction shows every stage drawn and labelled with the first stage
   resolved in the card, so nothing here is load-bearing for meaning. */
.hero-trace { background: var(--surface); border: var(--frame) solid var(--ink);
         margin: 0 0 clamp(24px,3vw,36px); position: relative; overflow: hidden; }
.hero-trace::before { content: ''; position: absolute; inset: 0; pointer-events: none;
         border: 1px solid var(--hair); margin: 5px; z-index: 3; }
.ht-cap { display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
         flex-wrap: wrap; padding: 15px clamp(18px,2.4vw,26px) 0; }
.ht-kick { font-family: var(--code); font-size: 10.5px; font-weight: 700; letter-spacing: 0.11em;
         color: var(--ox); }
.ht-amt { font-family: var(--code); font-size: 13px; font-weight: 700; letter-spacing: 0.06em;
         color: var(--ink); font-variant-numeric: tabular-nums; }
.ht-stage { padding: 4px clamp(8px,1.4vw,16px) 0; cursor: ew-resize; }
.ht-stage svg { display: block; width: 100%; height: auto; }

.ht-node .ht-dot, .ht-node .ht-msg, .ht-node .ht-who {
         transition: transform 220ms var(--ease-out), fill 220ms var(--ease-out),
                     stroke 220ms var(--ease-out), opacity 220ms var(--ease-out); }
.ht-node { cursor: pointer; }
.ht-node.is-past .ht-dot { fill: var(--ink); stroke: var(--ink); }
.ht-node .ht-dot { transform-box: fill-box; transform-origin: center; }
.ht-node.is-on .ht-dot { transform: scale(1.7); fill: var(--ox); stroke: var(--ox); }
/* the handover marker is a diamond, and a CSS transform replaces an inline one,
   so its rotation has to live here or it squares off the moment it scales */
.ht-node .ht-dot.ht-diamond { transform: rotate(45deg); }
.ht-node.is-on .ht-dot.ht-diamond { transform: rotate(45deg) scale(1.7); }
.ht-node.is-on .ht-msg { fill: var(--ox); }
.ht-node.is-on .ht-who { fill: var(--ink); }
.ht-node.is-ahead .ht-msg, .ht-node.is-ahead .ht-who { opacity: 0.45; }

/* the resolving data card: the Spade move, in his own material */
.ht-read { margin: 2px clamp(8px,1.4vw,16px) 0; background: var(--bg);
         border-top: 1px solid var(--hair); padding: 13px clamp(12px,1.8vw,18px) 15px; }
.ht-r-top { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; }
.ht-r-msg { font-family: var(--code); font-size: 15px; font-weight: 700; letter-spacing: 0.04em;
         color: var(--ink); }
.ht-r-plane { font-family: var(--code); font-size: 9.5px; font-weight: 700; letter-spacing: 0.11em;
         color: var(--ox); background: var(--ox-pale); padding: 3px 8px; }
.ht-r-clock { margin-left: auto; font-family: var(--code); font-size: 13px; font-weight: 700;
         color: var(--slate-blue); font-variant-numeric: tabular-nums; }
.ht-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
         gap: 4px clamp(20px,3vw,40px); margin-top: 11px; }
.ht-fields dt { font-family: var(--code); font-size: 9.5px; font-weight: 700; letter-spacing: 0.11em;
         color: var(--meta); }
.ht-fields dd { font-size: 15.5px; line-height: 1.45; color: var(--body); margin-top: 3px;
         min-height: 2.9em; }

.ht-steps { display: flex; flex-wrap: wrap; gap: 0; border-top: 1px solid var(--hair);
         margin: 0 clamp(8px,1.4vw,16px); }
.ht-step { flex: 1 1 130px; display: grid; gap: 3px; text-align: left; background: var(--surface);
         border: none; border-right: 1px solid var(--hair); padding: 10px 12px 12px;
         cursor: pointer; min-height: 52px;
         transition: background 200ms var(--ease-out); }
.ht-step:last-child { border-right: none; }
.ht-step:hover, .ht-step[aria-pressed="true"] { background: var(--bg); }
.ht-step[aria-pressed="true"] { box-shadow: inset 0 3px 0 0 var(--ox); }
.hs-msg { font-family: var(--code); font-size: 11px; font-weight: 700; letter-spacing: 0.05em;
         color: var(--ink); }
.ht-step[aria-pressed="true"] .hs-msg { color: var(--ox); }
.hs-node { font-family: var(--label); text-transform: uppercase; font-weight: 600; font-size: 13px;
         letter-spacing: 0.05em; color: var(--meta); }
.ht-hint { font-family: var(--code); font-size: 10px; letter-spacing: 0.08em; color: var(--meta);
         padding: 10px clamp(18px,2.4vw,26px) 13px; border-top: 1px solid var(--hair);
         margin: 0 clamp(8px,1.4vw,16px); }

@media (prefers-reduced-motion: reduce) {
  .ht-node .ht-dot, .ht-node .ht-msg, .ht-node .ht-who { transition: none; }
  .ht-node.is-ahead .ht-msg, .ht-node.is-ahead .ht-who { opacity: 1; }
}
@media (max-width: 620px) {
  .ht-stage { cursor: default; }
  .ht-step { flex: 1 1 44%; }
  .ht-fields dd { min-height: 0; }
  .ht-r-clock { margin-left: 0; }
}

/* ── the trace inside the reading column ─────────────────────────────────
   The six stage buttons are `flex: 1 1 130px`, which is right at the figure's
   original full-bleed 1260px: six across, one row. In the 720px reading column
   the same rule wraps them five-and-one, and the orphan stretches to the whole
   width — six stages of a payment reading as though the sixth were a summary of
   the other five. Measured: 135px each, then a 675px 'camt.054'.

   So in the column they become an explicit grid, three and three, which also
   pairs the stages the way the payment does: shopper to interchange, then
   settlement to merchant. The separators are drawn per cell rather than by the
   flex rule, because a wrapped flex row cannot know where its own edges are. */
.prose > .hero-trace .ht-steps { display: grid; grid-template-columns: repeat(3, 1fr); }
.prose > .hero-trace .ht-step { flex: none; border-bottom: 1px solid var(--hair); }
.prose > .hero-trace .ht-step:nth-child(3n) { border-right: none; }
.prose > .hero-trace .ht-step:nth-child(n+4) { border-bottom: none; }
/* ── the trace expanded ──────────────────────────────────────────────────
   Same sheet as .fig.expanded, but the furniture is the trace's own, not the
   interactive figures'. Theirs is head + bar + a three-line detail panel +
   legend + signature, which is why it reserves 512px. The trace carries a
   caption line, the resolving data card, six stage buttons in two rows and the
   bar, and no legend at all: measured at 390px, so the rail gets the rest.

   Reserving the WORST case matters here for the same reason it does there. The
   data card's "what happens" runs to two lines on the longest stage, so sizing
   the rail against a one-line stage would push the buttons off the bottom edge
   the moment someone moved along the trace, and the sheet would start to
   scroll under the pointer. */
/* `.prose > figure.hero-trace` is (0,2,1) and out-specifies a bare
   `.hero-trace.expanded` at (0,2,0), so the expanded sheet stayed clamped to the
   720px reading column and the whole gesture did nothing visible. Matching the
   column selector and adding the state takes it to (0,3,1). */
.prose > .hero-trace.expanded,
.hero-trace.expanded { --furniture: 390px; display: flex; flex-direction: column;
         max-width: none; width: auto; }
.hero-trace.expanded .ht-cap { flex: 0 0 auto; }
.hero-trace.expanded .ht-stage { flex: 0 0 auto; min-height: 0; overflow-x: visible;
         display: flex; align-items: flex-start; justify-content: center;
         padding: clamp(10px,1.6vh,22px) clamp(10px,1.4vw,26px); cursor: ew-resize; }
.hero-trace.expanded .ht-stage svg { min-width: 0; width: 100%; height: auto;
         max-width: 100%; max-height: max(180px, calc(94vh - var(--furniture))); }
.hero-trace.expanded .ht-read { flex: 1 1 auto; }
.hero-trace.expanded .ht-steps,
.hero-trace.expanded .fig-bar { flex: 0 0 auto; }
/* the sideways-scroll note is a phone instruction; expanded, nothing scrolls */
.hero-trace.expanded .fig-scrollnote { display: none; }
@media (max-height: 820px) {
  .hero-trace.expanded { --furniture: 340px; }
}
/* expanded it is a sheet, not a column, so the steps go back to one row */
.prose > .hero-trace.expanded .ht-steps { grid-template-columns: repeat(6, 1fr); }
.prose > .hero-trace.expanded .ht-step { border-bottom: none;
         border-right: 1px solid var(--hair); }
.prose > .hero-trace.expanded .ht-step:last-child { border-right: none; }

@media (max-width: 620px) {
  .prose > .hero-trace .ht-steps { grid-template-columns: repeat(2, 1fr); }
  .prose > .hero-trace .ht-step:nth-child(3n) { border-right: 1px solid var(--hair); }
  .prose > .hero-trace .ht-step:nth-child(2n) { border-right: none; }
  .prose > .hero-trace .ht-step:nth-child(n+4) { border-bottom: 1px solid var(--hair); }
  .prose > .hero-trace .ht-step:nth-child(n+5) { border-bottom: none; }
}
