/* ==========================================================================
   tc-ventures.ca  —  Thomas Cheesman
   One stylesheet, no build step, no framework.

   Palette: paper white with a faint cool cast, near-black ink, and one accent
   — a deep teal that reads as considered rather than corporate-blue. Deliberately
   NOT the dark instrument look of thomascheesman.ca: this is the professional
   front, and it should feel like a well-set document rather than a control panel.

   Type: Familjen Grotesk for headings (slightly quirky grotesque, holds up at
   large sizes), Source Serif 4 for reading, IBM Plex Mono for labels and data.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Self-hosted fonts. No third-party request before text renders, and nothing
   outside this repo to rot. Familjen Grotesk and Source Serif 4 are variable
   fonts, so one file covers every weight we use.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: 'Familjen Grotesk';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(/assets/fonts/familjen-grotesk-latin.woff2) format('woff2');
  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: 'Familjen Grotesk';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(/assets/fonts/familjen-grotesk-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, 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: 'Source Serif 4';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url(/assets/fonts/source-serif-4-latin.woff2) format('woff2');
  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: 'Source Serif 4';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url(/assets/fonts/source-serif-4-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, 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: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/assets/fonts/ibm-plex-mono-400-latin.woff2) format('woff2');
  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: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/assets/fonts/ibm-plex-mono-400-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, 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: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/assets/fonts/ibm-plex-mono-500-latin.woff2) format('woff2');
  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: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/assets/fonts/ibm-plex-mono-500-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, 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;
}

:root {
  --paper:      #FCFCFD;
  --paper-tint: #F2F5F6;
  --ink:        #14181F;
  --ink-soft:   #3D4653;
  --muted:      #5F6A79;
  --rule:       #E2E6EB;
  --accent:     #0F5F6B;
  --accent-ink: #0A464F;

  /* Contrast More (copy-review-007): the colours short of 7:1 for text or 3:1
     for rules, each moved toward the ink until it passes on both grounds. */
  --muted-more:  #4A5360;
  --accent-more: #0F5B67;
  --rule-more:   #878B91;

  --focus-w: 2px;                               /* focus rings: 3px under Contrast More */
  --move: 1;                                    /* motion: 0 under Reduced and Off */
  --fade: 1;                                    /* fades: 0 under Off */

  --display: "Familjen Grotesk", "Segoe UI", system-ui, sans-serif;
  --body:    "Source Serif 4", Georgia, "Times New Roman", serif;
  --mono:    "IBM Plex Mono", ui-monospace, Consolas, monospace;

  --measure: 66ch;

  /* ---- scales (plan-001 §4d) ------------------------------------------
     Written down once so every new component uses the same steps. The
     values match what the existing pages already use, so nothing shifts;
     new work picks from these instead of typing pixels.
     --------------------------------------------------------------------- */

  /* space: a 4px base, roughly ×1.5 per step */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;
  --s-8: 64px;
  --s-9: 96px;

  /* type: fixed steps for labels and data, fluid steps for headings. In rem, so
     the reader's browser text size scales them, and so will the Display panel's
     text size (copy-review-007 P3-0). SVG diagram labels stay in px: they are in
     the picture's own units and scale with the picture. */
  --t-label: 0.6875rem;                              /* mono eyebrows in tables and meta */
  --t-tag:   0.75rem;                              /* mono labels, captions */
  --t-mono:  0.8125rem;                              /* mono body: nav, buttons, receipts */
  --t-small: 0.97rem;                           /* serif, secondary prose */
  --t-body:  1.125rem;                              /* serif reading size */
  --t-lede:  clamp(1.1rem, 2vw, 1.25rem);       /* page-head lede */
  --t-h3:    1.2rem;                            /* display, component titles */
  --t-h2:    clamp(1.3rem, 2.6vw, 1.75rem);     /* display, section heads */
  --t-title: clamp(1.9rem, 4.4vw, 2.7rem);      /* display, article titles */
  --t-h1:    clamp(2.4rem, 6.5vw, 4.1rem);      /* display, page h1 */

  /* lanes */
  --lane-read: var(--measure);                  /* prose */
  --lane-wide: 1040px;                          /* figures, tables, headers */
  --lane-page: 1280px;                          /* wide lane + the side rail */
  --rail:      200px;
  --gutter:    clamp(20px, 5vw, 48px);

  /* rhythm inside a case study */
  --block: clamp(30px, 4vw, 44px);              /* figure / table / aside gap */
  --section: clamp(56px, 8vw, 96px);            /* between numbered sections */
}

/* Dark. The OS decides, unless the reader has chosen in the Display panel:
   /assets/prefs.js sets data-theme="light" or "dark" on <html> for a
   choice, and nothing for System. The dark colours are written
   once, here; the two rules after only switch to them, and site_check.py
   checks that both routes give the same colours. */
:root {
  --dark-paper:      #0C0F14;
  --dark-paper-tint: #12171E;
  --dark-ink:        #E9ECF2;
  --dark-ink-soft:   #C2C9D4;
  --dark-muted:      #939DAC;
  --dark-rule:       #222A34;
  --dark-accent:     #5CC7D1;
  --dark-accent-ink: #8CDDE4;
  --dark-muted-more: #99A3B1;
  --dark-rule-more:  #60666F;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper:      var(--dark-paper);
    --paper-tint: var(--dark-paper-tint);
    --ink:        var(--dark-ink);
    --ink-soft:   var(--dark-ink-soft);
    --muted:      var(--dark-muted);
    --rule:       var(--dark-rule);
    --accent:     var(--dark-accent);
    --accent-ink: var(--dark-accent-ink);
    --muted-more: var(--dark-muted-more);
    --accent-more: var(--dark-accent);
    --rule-more:  var(--dark-rule-more);
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --paper:      var(--dark-paper);
  --paper-tint: var(--dark-paper-tint);
  --ink:        var(--dark-ink);
  --ink-soft:   var(--dark-ink-soft);
  --muted:      var(--dark-muted);
  --rule:       var(--dark-rule);
  --accent:     var(--dark-accent);
  --accent-ink: var(--dark-accent-ink);
  --muted-more: var(--dark-muted-more);
  --accent-more: var(--dark-accent);
  --rule-more:  var(--dark-rule-more);
  color-scheme: dark;
}
:root[data-theme="light"] { color-scheme: light; }

/* Contrast. The OS's "more contrast" decides, unless the reader has chosen
   data-contrast="standard" or "more". More swaps in the -more colours of the
   theme in effect, and thickens focus rings. */
@media (prefers-contrast: more) {
  :root:not([data-contrast="standard"]) {
    --muted:   var(--muted-more);
    --accent:  var(--accent-more);
    --rule:    var(--rule-more);
    --focus-w: 3px;
  }
}
:root[data-contrast="more"] {
  --muted:   var(--muted-more);
  --accent:  var(--accent-more);
  --rule:    var(--rule-more);
  --focus-w: 3px;
}

/* Text size (Q-P3-3 A): on top of the reader's own browser setting, since
   every text size is in rem. */
:root[data-text="large"]  { font-size: 112.5%; }
:root[data-text="larger"] { font-size: 125%; }

/* Motion. Full: everything. Reduced: fades and colour changes only, so
   anything that moves multiplies its time by --move. Off: nothing animates.
   System (no data-motion): the OS's "reduce motion" means Reduced (Q-P3-2 A). */
@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion]) { --move: 0; }
}
:root[data-motion="reduced"] { --move: 0; }
:root[data-motion="off"] { --move: 0; --fade: 0; }
:root[data-motion="off"] *, :root[data-motion="off"] *::before, :root[data-motion="off"] *::after {
  transition: none !important;
  animation: none !important;
}

* { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-size: 1.125rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

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

.skip {
  position: absolute; left: -9999px;
  background: var(--ink); color: var(--paper);
  padding: 10px 16px; z-index: 10;
}
.skip:focus { left: 12px; top: 12px; }

.wrap {
  max-width: 1040px;
  margin: 0 auto;
  padding-inline: clamp(20px, 5vw, 48px);
}

/* .narrow limits the reading measure WITHOUT re-centring the box — otherwise
   those sections sit indented against every other section's left edge. */
.wrap.narrow > * { max-width: 760px; }
.wrap.narrow.prose { max-width: 1040px; } /* .prose must cap the children, not the wrap */

/* ---- labels ---- */

.label {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.label--accent { color: var(--accent); }

/* ---- top bar ---- */

.topbar {
  border-bottom: 1px solid var(--rule);
  background: var(--paper);
}
.topbar__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px 24px;
  padding-block: 18px;
}
.wordmark {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-decoration: none;
}
.topbar nav {
  display: flex;
  gap: 22px;
  font-family: var(--mono);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
}
/* Four items since /method (2026-09-25): on a phone, tighten the gap so the nav
   keeps its side margin at 375px, and wrap rather than scroll sideways below that. */
@media (max-width: 420px) {
  .topbar nav { gap: 6px 14px; flex-wrap: wrap; }
}
.topbar nav a { color: var(--muted); text-decoration: none; }
.topbar nav a:hover { color: var(--ink); }
.topbar nav a[aria-current="page"] { color: var(--ink); text-decoration: underline; text-decoration-color: var(--accent); }
/* a case study marks Projects as the section it sits under */
.topbar nav a[aria-current="true"] { color: var(--ink); text-decoration: underline; text-decoration-color: var(--accent); }

/* ---- Projects sub-menu: the case studies ---------------------------------
   Without JavaScript the list is visually hidden but stays in the tab order,
   and shows on hover or while focus is inside it; /projects also links to
   every case study. With JavaScript (assets/nav.js) it is a disclosure: the
   button opens and closes it, Esc closes it, and it closes when focus or a
   click goes elsewhere. The list overlays the page; nothing below it moves.
   -------------------------------------------------------------------------- */
.navsub { position: relative; display: inline-flex; align-items: center; gap: 2px; }

.navsub__toggle {
  display: none;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  min-height: 24px;
  margin-block: -4px;
  padding: 0;
  border: 0;
  border-radius: 2px;
  background: none;
  color: var(--muted);
  cursor: pointer;
}
@media (scripting: enabled) { .navsub__toggle { display: inline-flex; } }
.navsub[data-enhanced] .navsub__toggle { display: inline-flex; }
.navsub__toggle:hover { color: var(--ink); }
.navsub__toggle:focus-visible { outline: var(--focus-w) solid var(--accent); outline-offset: 1px; }
.navsub__toggle svg { transition: transform calc(0.15s * var(--move)) ease; }
.navsub__toggle[aria-expanded="true"] svg { transform: rotate(180deg); }

.navsub__list {
  position: absolute;
  top: calc(100% + 19px);               /* hangs from the top bar's bottom rule */
  left: calc(-1 * var(--s-4));
  z-index: 20;
  min-width: 15rem;
  margin: 0;
  padding: var(--s-2) 0;
  list-style: none;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 3px;
  box-shadow: 0 10px 28px rgb(0 0 0 / 0.08);
  white-space: nowrap;
}
/* closed, without JS: visually hidden, still reachable by Tab and by screen readers */
.navsub:not(:hover):not(:focus-within):not([data-open]) .navsub__list {
  width: 1px;
  height: 1px;
  min-width: 0;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
}
/* closed, with JS: out of the tab order until the button opens it */
.navsub[data-enhanced]:not([data-open]) .navsub__list { display: none; }
/* a bridge over the gap, so the pointer can travel from Projects to the list */
.navsub__list::before { content: ""; position: absolute; left: 0; right: 0; bottom: 100%; height: 20px; }
.navsub__list a { display: block; padding: var(--s-2) var(--s-4); }

/* ---- Display: the button and its panel (copy-review-007, Phase 3) --------
   The button comes after the nav. On a narrow screen, where the bar already
   takes two lines, it moves up to the name's line, so the bar doesn't grow a
   third. It shows only when scripts run, like the sub-menu's caret; the panel
   is built by display.js and overlays the page, moving nothing.
   -------------------------------------------------------------------------- */
.topbar__inner { position: relative; }
.topbar nav { margin-left: auto; }
.display__toggle {
  display: none;
  align-items: center;
  gap: 6px;
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--mono);
  font-size: var(--t-mono);
  letter-spacing: 0.04em;
  color: var(--muted);
  cursor: pointer;
}
@media (scripting: enabled) { .display__toggle { display: inline-flex; } }
.display__toggle[data-enhanced] { display: inline-flex; }
.display__toggle:hover, .display__toggle[aria-expanded="true"] { color: var(--ink); }
.display__toggle:focus-visible { outline: var(--focus-w) solid var(--accent); outline-offset: 3px; border-radius: 2px; }
.display__toggle svg { transition: transform calc(0.15s * var(--move)) ease; }
.display__toggle[aria-expanded="true"] svg { transform: rotate(180deg); }
@media (max-width: 720px) and (scripting: enabled) {
  .display__toggle { order: 1; margin-left: auto; }
  .topbar nav { order: 2; flex-basis: 100%; margin-left: 0; }
}

.display__panel {
  position: absolute;
  top: calc(100% + 1px);                        /* just under the bar's bottom rule */
  right: var(--gutter);
  z-index: 20;
  width: min(21rem, calc(100vw - 2 * var(--s-4)));
  padding: var(--s-4) var(--s-4) var(--s-3);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 3px;
  box-shadow: 0 10px 28px rgb(0 0 0 / 0.08);
}
.display__panel[hidden] { display: none; }
.display__panel fieldset { margin: 0 0 var(--s-4); padding: 0; border: 0; min-width: 0; }
.display__panel legend {
  margin: 0 0 var(--s-2);
  padding: 0;
  font-family: var(--mono);
  font-size: var(--t-label);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.display__options { position: relative; display: flex; border: 1px solid var(--rule); border-radius: 3px; }
.display__options input { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; }
.display__options label {
  flex: 1 1 0;
  padding: 7px 4px;
  text-align: center;
  font-family: var(--mono);
  font-size: var(--t-tag);
  letter-spacing: 0.02em;
  color: var(--ink-soft);
  border-left: 1px solid var(--rule);
  cursor: pointer;
  transition: background calc(.15s * var(--fade)) ease, color calc(.15s * var(--fade)) ease;
}
.display__options label:first-of-type { border-left: 0; border-radius: 2px 0 0 2px; }
.display__options label:last-of-type { border-radius: 0 2px 2px 0; }
.display__options label:hover { color: var(--ink); background: var(--paper-tint); }
.display__options input:checked + label { color: var(--paper); background: var(--accent); }
.display__options input:focus-visible + label {
  position: relative;
  z-index: 1;
  outline: var(--focus-w) solid var(--accent);
  outline-offset: 2px;
}
.display__note { margin: 0; font-family: var(--mono); font-size: var(--t-tag); color: var(--muted); }
/* forced colours paint over the fill, so the chosen option gets a ring instead */
@media (forced-colors: active) {
  .display__options input:checked + label { outline: 3px solid Highlight; outline-offset: -3px; }
}

/* ---- hero / page heads ---- */

.hero, .pagehead { padding-block: clamp(56px, 9vw, 104px) clamp(32px, 5vw, 56px); }
.pagehead { padding-bottom: clamp(24px, 4vw, 40px); }

h1 {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(2.4rem, 6.5vw, 4.1rem);
  line-height: 1.02;
  letter-spacing: -0.03em;
  text-wrap: balance;
  margin: 0.28em 0 0;
}

/* the home h1 is a sentence, not a name (copy-review-002 R2): cap its line length */
.hero h1 { max-width: 20ch; }

.hero__lede {
  font-size: clamp(1.2rem, 2.4vw, 1.45rem);
  line-height: 1.5;
  color: var(--ink-soft);
  max-width: 34ch;
  margin: 0.8em 0 0;
}
.pagehead .hero__lede { max-width: 52ch; font-size: clamp(1.1rem, 2vw, 1.25rem); }

.hero__body {
  max-width: var(--measure);
  color: var(--muted);
  margin: 1.4em 0 0;
}

/* ---- buttons ---- */

.actions { display: flex; flex-wrap: wrap; gap: 12px; margin: 2em 0 0; }

.btn {
  display: inline-flex;
  align-items: center;
  font-family: var(--mono);
  font-size: 0.8125rem;
  letter-spacing: 0.06em;
  padding: 12px 22px;
  border: 1px solid var(--rule);
  border-radius: 2px;
  color: var(--ink);
  text-decoration: none;
  transition: border-color calc(.2s * var(--fade)) ease, background calc(.2s * var(--fade)) ease, color calc(.2s * var(--fade)) ease;
}
.btn:hover { border-color: var(--accent); color: var(--accent); }
.btn--solid {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--paper);
}
.btn--solid:hover { background: var(--accent-ink); border-color: var(--accent-ink); color: var(--paper); }

/* ---- bands ---- */

.band { padding-block: clamp(48px, 7vw, 84px); border-top: 1px solid var(--rule); }
.band--tint { background: var(--paper-tint); }
.hero + .band { border-top: 1px solid var(--rule); }

.section-head {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.3rem, 2.6vw, 1.75rem);
  letter-spacing: -0.015em;
  margin: 0 0 1.2em;
}
/* A section head that leads to its own page (home "How I work" -> /method). */
.section-head a { color: var(--ink); text-decoration-color: var(--accent); text-decoration-thickness: 2px; }
.section-head a:hover { color: var(--accent); }

/* ---- prose ---- */

.prose { max-width: var(--measure); }
.prose p { margin: 0 0 1.15em; }
.prose p:last-child { margin-bottom: 0; }
.prose h3 {
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 2.2em 0 0.9em;
}
.narrow > p { max-width: var(--measure); }

/* ---- the three proofs: hairline rows, not cards ---- */

.proofs { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule); counter-reset: proof; }

.proof {
  display: grid;
  grid-template-columns: 1fr;
  gap: 18px 48px;
  padding-block: clamp(26px, 4vw, 38px);
  border-bottom: 1px solid var(--rule);
}
@media (min-width: 860px) {
  .proof { grid-template-columns: 1.65fr 1fr; }
}

.proof h3 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.35rem;
  letter-spacing: -0.015em;
  margin: 0 0 0.5em;
}
.proof h3 a { color: var(--ink); text-decoration-color: var(--accent); }
.proof h3 a:hover { color: var(--accent); }
.proof p { margin: 0; color: var(--ink-soft); }

.proof__meta { margin: 0; align-self: start; }
.proof__meta > div { margin-bottom: 12px; }
.proof__meta dt {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}
.proof__meta dd {
  margin: 2px 0 0;
  font-family: var(--mono);
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

/* ---- method ---- */

.method { display: grid; grid-template-columns: 1fr; gap: 36px; }
@media (min-width: 800px) { .method { grid-template-columns: repeat(3, 1fr); gap: 40px; } }

.method h3 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.2rem;
  letter-spacing: -0.01em;
  margin: 0.55em 0 0.5em;
}
.method p { margin: 0; color: var(--ink-soft); font-size: 0.97rem; }

/* ---- project pages ---- */

.project h2 {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.9rem, 4.4vw, 2.7rem);
  line-height: 1.06;
  letter-spacing: -0.025em;
  text-wrap: balance;
  margin: 0.3em 0 0.8em;
}

.shot { margin: clamp(30px, 4vw, 44px) 0 0; display: flex; flex-direction: column; gap: 12px; }
.shot img {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
  object-fit: contain;
  border: 1px solid var(--rule);
  border-radius: 3px;
  background: #060913;
}
.shot figcaption {
  font-family: var(--mono);
  font-size: 0.75rem;
  line-height: 1.6;
  color: var(--muted);
  max-width: 70ch;
}
/* prose straight after a figure needs the block gap, or it reads as part of the caption */
.shot + .prose { margin-top: var(--block); }

/* ---- spec tables ---- */

.spec { margin: clamp(30px, 4vw, 44px) 0 0; border-top: 1px solid var(--rule); }
.spec > div {
  display: grid;
  grid-template-columns: minmax(130px, 190px) 1fr;
  gap: 6px 24px;
  padding-block: 12px;
  border-bottom: 1px solid var(--rule);
}
.spec--wide > div { grid-template-columns: minmax(150px, 220px) 1fr; }
.spec dt {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  padding-top: 0.35em;
}
.spec dd {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.84375rem;
  line-height: 1.6;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.spec--wide dd { font-family: var(--body); font-size: 1rem; }
@media (max-width: 560px) {
  .spec > div, .spec--wide > div { grid-template-columns: 1fr; gap: 2px; }
}

.repo { font-family: var(--mono); font-size: 0.8125rem; color: var(--muted); margin: 22px 0 0; }

/* ---- cv ---- */

.cv { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule); }
.cv li { padding-block: clamp(22px, 3vw, 30px); border-bottom: 1px solid var(--rule); max-width: var(--measure); }
.cv h3 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.15rem;
  letter-spacing: -0.01em;
  margin: 0.4em 0 0.4em;
}
.cv p:last-child { margin: 0; color: var(--ink-soft); }

/* ---- footer ---- */

.footer { border-top: 1px solid var(--rule); background: var(--paper-tint); }
.footer__inner {
  display: grid;
  grid-template-columns: 1fr;
  gap: 32px 48px;
  padding-block: clamp(40px, 6vw, 64px);
}
@media (min-width: 700px) { .footer__inner { grid-template-columns: 1fr 1fr; } }

.footer__email {
  font-family: var(--display);
  font-size: clamp(1.15rem, 3vw, 1.6rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  margin: 0.4em 0 0;
  word-break: break-word;
}
.footer__email a { color: var(--ink); text-decoration-color: var(--accent); }
.footer__email a:hover { color: var(--accent); }
.footer__note { font-family: var(--mono); font-size: 0.75rem; color: var(--muted); margin: 0.8em 0 0; }

.footer__list { list-style: none; margin: 0.9em 0 0; padding: 0; font-family: var(--mono); font-size: 0.8125rem; line-height: 2; color: var(--muted); }
.footer__list a { color: var(--ink); text-decoration-color: var(--rule); }
.footer__list a:hover { text-decoration-color: var(--accent); }

/* ---- contact ---- */

.contact__email {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.6rem, 5vw, 3rem);
  letter-spacing: -0.03em;
  line-height: 1.1;
  margin: 0.35em 0 0;
  word-break: break-word;
}
.contact__email a { color: var(--ink); text-decoration-color: var(--accent); text-decoration-thickness: 2px; }
.contact__email a:hover { color: var(--accent); }

.actions--tight { margin-top: 1.4em; }

.contact__note {
  max-width: var(--measure);
  color: var(--muted);
  margin: 1.6em 0 0;
  font-size: 0.97rem;
}

.moves dt { padding-top: 0.5em; }
.moves dd { color: var(--ink-soft); }

.tz { display: grid; grid-template-columns: 1fr; gap: 28px 56px; align-items: start; }
@media (min-width: 760px) { .tz { grid-template-columns: minmax(220px, 1fr) 2fr; } }

.tz__now { margin: 0; }
.tz__clock {
  display: block;
  font-family: var(--mono);
  font-size: clamp(1.5rem, 4vw, 2.2rem);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  margin-top: 0.35em;
  line-height: 1.2;
}
.tz__table { margin-top: 0; }
.tz__table dd { font-family: var(--body); font-size: 1rem; }

button.btn { background: transparent; cursor: pointer; font: inherit; font-family: var(--mono); font-size: 0.8125rem; }
button.btn:focus-visible { outline: var(--focus-w) solid var(--accent); outline-offset: 3px; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}


/* ---- the live graph demo (projects page) --------------------------------
   The canvas keeps its own dark ground in both themes, the same near-black
   the still screenshots sit on. A 3D scene lit for paper is unreadable, and
   flipping it per theme would mean two sets of node colours to keep honest.
   -------------------------------------------------------------------------- */

.gd { margin: clamp(30px, 4vw, 44px) 0 0; }

/* .btn, .gd__stage and .gd__still all set `display`, which outranks the user
   agent's [hidden] rule. Without this the button shows with JavaScript off and
   an empty canvas box shows before the click — the two things this whole block
   is built to avoid. */
.gd [hidden] { display: none !important; }

.gd__frame {
  position: relative;
  border: 1px solid var(--rule);
  border-radius: 3px;
  background: #060913;
  overflow: hidden;
}

.gd__still { display: block; margin: 0; }
.gd__still img {
  display: block;
  width: 100%;
  height: auto;
  object-fit: contain;
}

.gd__loadwrap {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  justify-content: center;
  padding: 0 16px 20px;
}
.gd__load {
  background: rgba(6, 9, 19, 0.82);
  border-color: rgba(233, 236, 242, 0.35);
  color: #E9ECF2;
  backdrop-filter: blur(3px);
}
.gd__load:hover:not(:disabled) { border-color: #5CC7D1; color: #5CC7D1; }
.gd__load:disabled { cursor: progress; opacity: 0.7; }
.gd__load:focus-visible { outline: var(--focus-w) solid #5CC7D1; outline-offset: 3px; }

.gd__stage { display: block; line-height: 0; }
/* The canvas paints over any outline on the stage itself,
   so the focus ring goes on the frame. */
.gd__stage:focus-visible { outline: none; }
.gd__frame:has(.gd__stage:focus-visible) { outline: var(--focus-w) solid var(--accent); outline-offset: 3px; }
.gd__stage canvas { display: block; touch-action: pan-y; }

/* Panel: the reading half. Stays on paper, because it is text. */
.gd__panel {
  border: 1px solid var(--rule);
  border-top: 0;
  border-radius: 0 0 3px 3px;
  padding: 18px 20px;
  min-height: 104px;
  background: var(--paper-tint);
}
.gd__panel p { margin: 0; }
.gd__hint { font-family: var(--mono); font-size: 0.78125rem; line-height: 1.7; color: var(--muted); max-width: 70ch; }
.gd__kicker {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}
.gd__title {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.05rem;
  line-height: 1.35;
  letter-spacing: -0.01em;
  margin-top: 6px !important;
  color: var(--ink);
}
.gd__rel { font-family: var(--mono); font-size: 0.75rem; text-transform: lowercase; color: var(--accent); letter-spacing: 0.04em; }
.gd__meta { font-family: var(--mono); font-size: 0.75rem; color: var(--muted); margin-top: 6px !important; }
.gd__basis {
  margin: 10px 0 0;
  padding-left: 14px;
  border-left: 2px solid var(--accent);
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--ink-soft);
  max-width: 78ch;
}
.gd__src { font-family: var(--mono); font-size: 0.75rem; margin-top: 10px !important; }

.gd__grade {
  display: inline-block;
  min-width: 1.5em;
  text-align: center;
  padding: 1px 5px;
  border-radius: 2px;
  font-family: var(--mono);
  color: #060913;
}
.gd__grade--A { background: #7FC9B8; }
.gd__grade--B { background: #C9BE7F; }
.gd__grade--C { background: #C99A7F; }

.gd__tip {
  font-family: var(--mono);
  font-size: 0.75rem;
  line-height: 1.5;
  background: rgba(6, 9, 19, 0.9);
  color: #E9ECF2;
  border: 1px solid rgba(233, 236, 242, 0.25);
  border-radius: 2px;
  padding: 6px 9px;
  max-width: 34ch;
}

/* Legend — explains the still as much as the live scene, so it is always here. */
.gd__legend {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
  margin: 14px 0 0;
  padding: 0;
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--muted);
}
.gd__legend li { display: flex; align-items: center; gap: 8px; }
.gd__swatch {
  width: 11px; height: 11px;
  border-radius: 50%;
  flex: none;
  /* The root node is white, which is invisible on paper without this. */
  box-shadow: inset 0 0 0 1px rgba(20, 24, 31, 0.35);
}

/* The written chain. This is the part that survives with JavaScript off. */
.gd__chain {
  list-style: none;
  margin: clamp(26px, 3vw, 34px) 0 0;
  padding: 0;
  border-top: 1px solid var(--rule);
  max-width: 78ch;
  counter-reset: step;
}
.gd__chain li {
  padding: 14px 0 14px 34px;
  border-bottom: 1px solid var(--rule);
  position: relative;
}
.gd__chain li::before {
  counter-increment: step;
  content: counter(step);
  position: absolute;
  left: 0;
  top: 15px;
  font-family: var(--mono);
  font-size: 0.6875rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.gd__chain b { font-weight: 600; }
.gd__chain .gd__step {
  display: block;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 3px;
}
.gd__chain .gd__step em { font-style: normal; color: var(--accent); }

/* ---- the lander embed (lander.html) -------------------------------------
   Reuses the graph demo's frame, button and panel wholesale — same contract,
   same look, one stylesheet. Only the sizing differs: a game wants a 16:9
   box that is present before anything loads, where the graph was free to
   take its height from a still.
   -------------------------------------------------------------------------- */

.gd__frame--tall { min-height: 320px; }
.gd__frame--tall .gd__stage,
.gd__frame--tall .gd__stage iframe {
  display: block;
  width: 100%;
  border: 0;
}
.gd__frame--tall .gd__stage iframe {
  aspect-ratio: 16 / 9;
  min-height: 320px;
  max-height: 78vh;
}
/* The button sits over dark ground rather than over a still, so centre it in
   the empty frame instead of resting it on the bottom edge. */
.gd__frame--tall .gd__loadwrap {
  position: absolute;
  inset: 0;
  align-items: center;
  padding: 16px;
}

kbd {
  font-family: var(--mono);
  font-size: 0.85em;
  line-height: 1;
  padding: 3px 6px;
  border: 1px solid var(--rule);
  border-bottom-width: 2px;
  border-radius: 3px;
  background: var(--paper);
  color: var(--ink);
  white-space: nowrap;
}


/* ==========================================================================
   CASE STUDIES  (plan-001 §4a, §4d)  —  added 2026-09-21, session 2
   One template, used seven times. Everything below is additive: nothing
   above this line changed. Palette, fonts and dark mode are the ones
   already declared; these are layout and components only.
   ========================================================================== */

/* ---- page grid: wide lane + a side rail on big screens -----------------
   Below 1180px the rail folds into a horizontal index under the header,
   and the page is the same single column every other page uses.
   -------------------------------------------------------------------------- */

.wrap--page { max-width: var(--lane-page); }

.cs-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0 var(--s-8); }
.cs-layout__main { min-width: 0; max-width: var(--lane-wide); }

/* Two copies of the index are in the HTML: one inline under the header, one
   in the rail. Exactly one is displayed at any width, so assistive tech and
   keyboards see one list, never two. */
.cs-layout__rail { display: none; }
@media (min-width: 1180px) {
  .cs-layout { grid-template-columns: minmax(0, var(--lane-wide)) var(--rail); }
  .cs-layout__rail { display: block; position: sticky; top: var(--s-6); align-self: start; padding-top: clamp(48px, 8vw, 88px); }
  .cs-index--inline { display: none; }
}

/* ---- case-study header ---------------------------------------------------
   Eyebrow, title, one-line claim, then a meta strip: stack · status · live ·
   repo. The strip is a <dl>, so it reads as pairs with CSS off.
   -------------------------------------------------------------------------- */

.cs-head { padding-block: clamp(48px, 8vw, 88px) 0; }
.cs-head h1 { font-size: var(--t-h1); }

.cs-head__claim {
  font-size: clamp(1.2rem, 2.4vw, 1.45rem);
  line-height: 1.45;
  color: var(--ink-soft);
  max-width: 40ch;
  margin: 0.8em 0 0;
  text-wrap: pretty;
}

.cs-head__meta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--s-4) var(--s-6);
  margin: clamp(28px, 4vw, 40px) 0 0;
  padding-block: var(--s-4);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}
.cs-head__meta > div { min-width: 0; }
.cs-head__meta dt {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}
.cs-head__meta dd {
  margin: 3px 0 0;
  font-family: var(--mono);
  font-size: var(--t-mono);
  line-height: 1.55;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.cs-head__meta dd a { color: var(--ink); text-decoration-color: var(--rule); }
.cs-head__meta dd a:hover { color: var(--accent); text-decoration-color: var(--accent); }

/* status tag — a word, not a colour. */
.tag {
  display: inline-block;
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  line-height: 1;
  padding: 5px 7px 4px;
  border: 1px solid var(--rule);
  border-radius: 2px;
  color: var(--ink);
  vertical-align: 1px;
}
.tag--live { border-color: var(--accent); color: var(--accent); }

/* ---- on-this-page index --------------------------------------------------
   The six fixed sections, numbered. Same list on every case study, so the
   method is visible by repetition before a word is read.
   -------------------------------------------------------------------------- */

.cs-index { margin: var(--s-6) 0 0; }
.cs-index .label { margin-bottom: var(--s-2); }
.cs-index ol {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-5);
  font-family: var(--mono);
  font-size: var(--t-mono);
  line-height: 1.5;
  counter-reset: cs;
}
.cs-index li { counter-increment: cs; }
.cs-index a { color: var(--muted); text-decoration: none; }
.cs-index ol a::before {
  content: counter(cs, decimal-leading-zero) " ";
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.cs-index a:hover { color: var(--ink); text-decoration: underline; text-decoration-color: var(--accent); }

@media (min-width: 1180px) {
  .cs-index { margin-top: 0; padding-top: var(--s-2); }
  .cs-index ol { flex-direction: column; gap: var(--s-2); }
  .cs-index li { padding-left: var(--s-3); border-left: 1px solid var(--rule); }
  .cs-index li:hover { border-left-color: var(--accent); }
  .cs-layout__rail .cs-index__back { margin-top: var(--s-6); }
}
.cs-index__back {
  display: block;
  margin: var(--s-4) 0 0;
  font-family: var(--mono);
  font-size: var(--t-tag);
  color: var(--muted);
  text-decoration: none;
}
.cs-index__back:hover { color: var(--accent); }

/* ---- numbered sections ---------------------------------------------------
   Each section opens with its number and a fixed name. Prose sits in the
   reading lane; anything with the .wide class takes the whole main column.
   -------------------------------------------------------------------------- */

.cs-body { counter-reset: cs-sec fig; padding-bottom: var(--section); }

.cs-section { padding-top: var(--section); }
.cs-section:first-of-type { padding-top: clamp(40px, 6vw, 64px); }

.cs-section__num {
  display: block;
  font-family: var(--mono);
  font-size: var(--t-tag);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 var(--s-3);
  font-variant-numeric: tabular-nums;
}
.cs-section__num::before {
  counter-increment: cs-sec;
  content: counter(cs-sec, decimal-leading-zero) "\2002\2014\2002";
  color: var(--muted);
}

.cs-section h2 {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--t-title);
  line-height: 1.06;
  letter-spacing: -0.025em;
  text-wrap: balance;
  margin: 0 0 0.7em;
  max-width: 24ch;
}

/* the reading lane is the default; .wide opts out. Kept at one class of
   specificity so the h2 rule above (24ch) still wins for headings. */
.cs-section > * { max-width: var(--lane-read); }
.cs-section > .wide { max-width: none; }

.cs-section h3 {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--t-h3);
  letter-spacing: -0.01em;
  margin: 1.8em 0 0.5em;
}
.cs-section p { margin: 0 0 1.15em; }
.cs-section p:last-child { margin-bottom: 0; }
/* a paragraph that follows a block (quote, excerpt, figure, list, table)
   needs its own top gap; one that follows a heading or paragraph does not */
.cs-section > :not(p):not(h2):not(h3) + p { margin-top: var(--block); }
.cs-section .prose h3 { margin-top: 2.2em; } /* .prose h3 keeps its mono eyebrow style from above */

/* ---- receipts ------------------------------------------------------------
   The inline mark: small, mono, the same everywhere. It links to the actual
   file — a handoff section, a commit, a validator, a review block.
   -------------------------------------------------------------------------- */

.receipt {
  font-family: var(--mono);
  font-size: max(0.75rem, 0.72em);
  letter-spacing: 0.02em;
  color: var(--muted);
  text-decoration: none;
  border-bottom: 1px dotted var(--muted);
  padding-bottom: 1px;
  white-space: nowrap;
  vertical-align: 0.08em;
  margin-left: 0.35em;
}
.receipt::before { content: "\2192\2009"; color: var(--accent); } /* → */
.receipt:hover { color: var(--accent); border-bottom-color: var(--accent); }

/* the closing list: one row per receipt — what it proves, then the file. */
.receipts { list-style: none; margin: var(--block) 0 0; padding: 0; border-top: 1px solid var(--rule); }
.receipts li {
  display: grid;
  grid-template-columns: 1fr;
  gap: 4px var(--s-6);
  padding-block: var(--s-4);
  border-bottom: 1px solid var(--rule);
}
@media (min-width: 720px) { .receipts li { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); align-items: baseline; } }
.receipts__what { margin: 0; color: var(--ink-soft); font-size: var(--t-small); }
.receipts__file { margin: 0; font-family: var(--mono); font-size: var(--t-mono); line-height: 1.55; overflow-wrap: anywhere; }
.receipts__file a { color: var(--ink); text-decoration-color: var(--rule); }
.receipts__file a:hover { color: var(--accent); text-decoration-color: var(--accent); }
.receipts__file time { color: var(--muted); white-space: nowrap; }

/* ---- figures -------------------------------------------------------------
   .shot already exists above. These add a numbered caption, a paper-ground
   variant for screenshots that are not dark, and a two-up. Still `contain`.
   -------------------------------------------------------------------------- */

.shot--paper img { background: var(--paper-tint); }
.shot--fixed img { aspect-ratio: 16 / 9; }          /* same slot, same shape, per plan §4d */
.shot--fixed.shot--tall img { aspect-ratio: 4 / 3; }

/* numbered captions — inside a case study only, so projects.html is untouched */
.cs-body .shot figcaption { counter-increment: fig; }
.cs-body .shot figcaption::before {
  content: "Fig. " counter(fig) "\2002";
  color: var(--accent);
  font-weight: 500;
}

.shot--pair { display: grid; grid-template-columns: 1fr; gap: var(--s-5) var(--s-5); margin: var(--block) 0 0; }
@media (min-width: 760px) { .shot--pair { grid-template-columns: 1fr 1fr; } }
.shot--pair .shot { margin: 0; }

/* ---- rule table ----------------------------------------------------------
   the correction → the standing rule → where it is enforced. A real <table>;
   under 640px each row stacks and the header text comes from data-th.
   -------------------------------------------------------------------------- */

.rules {
  width: 100%;
  border-collapse: collapse;
  margin: var(--block) 0 0;
  font-size: var(--t-small);
  line-height: 1.55;
}
.rules caption {
  text-align: left;
  font-family: var(--mono);
  font-size: var(--t-tag);
  color: var(--muted);
  padding-bottom: var(--s-3);
}
.rules th, .rules td {
  text-align: left;
  vertical-align: top;
  padding: var(--s-3) var(--s-5) var(--s-3) 0;
  border-top: 1px solid var(--rule);
}
.rules th:last-child, .rules td:last-child { padding-right: 0; }
.rules thead th {
  font-family: var(--mono);
  font-size: var(--t-label);
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  border-top: 0;
  padding-top: 0;
}
.rules tbody th { font-weight: 400; color: var(--ink-soft); font-style: italic; }
.rules td { color: var(--ink); }
.rules td:last-child { font-family: var(--mono); font-size: var(--t-mono); color: var(--muted); }
.rules tbody tr:last-child th, .rules tbody tr:last-child td { border-bottom: 1px solid var(--rule); }

@media (max-width: 640px) {
  .rules thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .rules tr { display: block; padding-block: var(--s-4); border-top: 1px solid var(--rule); }
  .rules tbody tr:last-child { border-bottom: 1px solid var(--rule); }
  .rules th, .rules td { display: block; border: 0; padding: 2px 0; }
  .rules tbody th, .rules td { padding-left: 0; }
  .rules tbody th::before, .rules td::before {
    content: attr(data-th);
    display: block;
    font-family: var(--mono);
    font-size: var(--t-label);
    font-style: normal;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--muted);
    margin: var(--s-2) 0 1px;
  }
  .rules tbody th::before { margin-top: 0; }
}

/* ---- pull quote: his own words -----------------------------------------
   Serif, larger, a teal rule. The cite says where it was said.
   -------------------------------------------------------------------------- */

.pull {
  margin: var(--block) 0 0;
  padding: var(--s-2) 0 var(--s-2) var(--s-5);
  border-left: 3px solid var(--accent);
  max-width: var(--lane-read);
}
.pull p {
  margin: 0;
  font-size: clamp(1.2rem, 2.2vw, 1.4rem);
  line-height: 1.45;
  color: var(--ink);
  text-wrap: pretty;
}
.pull footer {
  margin-top: var(--s-3);
  font-family: var(--mono);
  font-size: var(--t-tag);
  color: var(--muted);
}
.pull cite { font-style: normal; }

/* ---- excerpt: a curated slice of a working file --------------------------
   Handoff traps, review rulings, a validator's failure line. Mono, tinted,
   with the file it came from underneath. Curated, never raw (plan §6 Q3).
   -------------------------------------------------------------------------- */

.excerpt {
  margin: var(--block) 0 0;
  border: 1px solid var(--rule);
  border-radius: 3px;
  background: var(--paper-tint);
  max-width: var(--lane-read);
}
.excerpt pre {
  margin: 0;
  padding: var(--s-4) var(--s-5);
  font-family: var(--mono);
  font-size: var(--t-mono);
  line-height: 1.65;
  color: var(--ink);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.excerpt pre mark { background: transparent; color: var(--accent); font-weight: 500; }
.excerpt figcaption {
  padding: var(--s-2) var(--s-5) var(--s-3);
  border-top: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: var(--t-tag);
  color: var(--muted);
}

/* ---- miss / catch --------------------------------------------------------
   Section 3 lists the misses, M1 M2 M3; section 4 lists the catches with the
   same numbers, so the reader can pair them. One component, two flavours.
   -------------------------------------------------------------------------- */

.mc { list-style: none; margin: var(--s-5) 0 0; padding: 0; border-top: 1px solid var(--rule); counter-reset: mc; }
.mc li {
  position: relative;
  padding: var(--s-5) 0 var(--s-5) 52px;
  border-bottom: 1px solid var(--rule);
}
.mc li::before {
  counter-increment: mc;
  content: "M" counter(mc);
  position: absolute;
  left: 0;
  top: calc(var(--s-5) + 0.2em);
  font-family: var(--mono);
  font-size: var(--t-tag);
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.mc--catch li::before { content: "C" counter(mc); }
.mc--weak li::before { content: "W" counter(mc); }       /* /method: where AI is weak */
.mc h3 { margin: 0 0 0.4em; font-size: 1.1rem; }
.mc p { margin: 0; color: var(--ink-soft); }
.mc p + p { margin-top: 0.6em; }
.mc__how {
  display: block;
  margin-top: var(--s-2);
  font-family: var(--mono);
  font-size: var(--t-tag);
  color: var(--muted);
}
.mc__how b { font-weight: 500; color: var(--ink); }

/* ---- the loop diagram ----------------------------------------------------
   brief → spec → build → review → rule → next session. Inline SVG, currentColor
   and the palette variables, so it follows the theme. Each node is a link.
   -------------------------------------------------------------------------- */

.loop { margin: var(--block) 0 0; }
/* On a phone the six nodes cannot shrink to 390px and stay readable, so the
   drawing keeps a minimum width and the box scrolls sideways instead. */
.loop__scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.loop__scroll:focus-visible { outline: var(--focus-w) solid var(--accent); outline-offset: 3px; }
.loop svg { display: block; width: 100%; min-width: 640px; height: auto; max-width: 880px; }
.loop .loop__edge { fill: none; stroke: var(--rule); stroke-width: 1.5; }
.loop .loop__edge--back { stroke: var(--accent); stroke-dasharray: 4 5; }
.loop .loop__node { fill: var(--paper); stroke: var(--ink); stroke-width: 1.5; }
.loop .loop__node--accent { fill: var(--accent); stroke: var(--accent); }
.loop .loop__label { font-family: var(--display); font-weight: 600; font-size: 15px; fill: var(--ink); }
.loop .loop__label--on { fill: var(--paper); }
.loop .loop__sub { font-family: var(--mono); font-size: 10.5px; fill: var(--muted); }
.loop .loop__arrow { fill: var(--rule); }
.loop .loop__arrow--back { fill: var(--accent); }
.loop a:hover .loop__node { stroke: var(--accent); }
.loop a:hover .loop__label { fill: var(--accent); }
.loop a:hover .loop__node--accent { fill: var(--accent-ink); }
.loop a:focus-visible { outline: none; }
.loop a:focus-visible .loop__node { stroke: var(--accent); stroke-width: 3; }
/* /method variant (copy-review-003 Q-M2): each step also points down to the
   file it writes. Taller drawing, so a slightly wider minimum keeps the file
   names readable in the sideways scroll. */
.loop--files svg { min-width: 720px; }
.loop .loop__sub--on { fill: var(--paper); }
.loop .loop__flow { fill: none; stroke: var(--rule); stroke-width: 1.5; }
.loop .loop__file { fill: var(--paper-tint); stroke: var(--rule); stroke-width: 1.5; }
.loop .loop__file--git { stroke-dasharray: 3 4; }       /* commits, not a .md */
.loop .loop__file-label { font-family: var(--mono); font-size: 11px; fill: var(--ink-soft); }
.loop a:hover .loop__file { stroke: var(--accent); }
.loop a:hover .loop__file-label { fill: var(--accent); }
.loop a:focus-visible .loop__file { stroke: var(--accent); stroke-width: 3; }
.loop figcaption {
  font-family: var(--mono);
  font-size: var(--t-tag);
  line-height: 1.6;
  color: var(--muted);
  max-width: 70ch;
  margin-top: var(--s-3);
}

/* ---- draft banner (template only) --------------------------------------
   Shown on the template page and on any case study still under review.
   Delete the element when the copy is ruled on; nothing else depends on it.
   -------------------------------------------------------------------------- */

.draft {
  font-family: var(--mono);
  font-size: var(--t-tag);
  letter-spacing: 0.04em;
  color: var(--paper);
  background: var(--ink);
  padding: var(--s-2) 0;
}
.draft p { margin: 0; }
.draft b { font-weight: 500; color: var(--accent); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .draft { background: var(--paper-tint); color: var(--ink); border-bottom: 1px solid var(--rule); } }
:root[data-theme="dark"] .draft { background: var(--paper-tint); color: var(--ink); border-bottom: 1px solid var(--rule); }

/* ---- work index (/work/) — the seven case studies as rows -------------- */

.work { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule); counter-reset: work; }
.work li {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr);
  gap: var(--s-2) var(--s-4);
  padding-block: clamp(22px, 3vw, 30px);
  border-bottom: 1px solid var(--rule);
}
@media (min-width: 860px) { .work li { grid-template-columns: 44px minmax(0, 1.65fr) minmax(0, 1fr); } }
.work li::before {
  counter-increment: work;
  content: counter(work, decimal-leading-zero);
  font-family: var(--mono);
  font-size: var(--t-tag);
  color: var(--muted);
  padding-top: 0.45em;
  font-variant-numeric: tabular-nums;
}
.work h3 { font-family: var(--display); font-weight: 600; font-size: 1.35rem; letter-spacing: -0.015em; margin: 0 0 0.4em; }
.work h3 a { color: var(--ink); text-decoration-color: var(--accent); }
.work h3 a:hover { color: var(--accent); }
.work p { margin: 0; color: var(--ink-soft); }
.work__meta { margin: 0; font-family: var(--mono); font-size: var(--t-mono); line-height: 1.7; color: var(--muted); grid-column: 2; }
@media (min-width: 860px) { .work__meta { grid-column: 3; padding-top: 0.3em; } }

/* ---- build ledger (home hero) --------------------------------------------
   Drawn by scripts/export-ledger.py from the handoffs (copy-review-006). Two
   pictures of the same data: landscape, and portrait at 600px and under. The
   list after it is the text equivalent and carries the links; <details> opens
   without JavaScript. Closed items are --muted, open ones the accent, parked
   ones accent dots.
   -------------------------------------------------------------------------- */

.ledger { margin: var(--block) 0 0; }
.ledger__pic { display: block; width: 100%; height: auto; }
.ledger__pic--port { display: none; max-width: 440px; }
@media (max-width: 600px) {
  .ledger__pic--land { display: none; }
  .ledger__pic--port { display: block; }
}
.ledger .lg__col, .ledger .lg__sep { stroke: var(--rule); stroke-width: 1; }
.ledger .lg__num, .ledger .lg__date, .ledger .lg__band { font-family: var(--mono); font-size: 10px; fill: var(--muted); }
.ledger .lg__band { letter-spacing: 0.08em; }
.ledger .lg__t { stroke: var(--muted); stroke-width: 1.2; stroke-linecap: round; }
.ledger .lg__t--open { stroke: var(--accent); stroke-width: 1.8; }
.ledger .lg__t--parked { stroke: var(--accent); stroke-width: 2; stroke-dasharray: 0 4; }
.ledger .lg__dot { fill: var(--muted); }
.ledger .lg__dot--open { fill: var(--accent); }
.ledger .lg__end { stroke: var(--muted); stroke-width: 1.2; }
.ledger figcaption {
  font-family: var(--mono);
  font-size: var(--t-tag);
  line-height: 1.6;
  color: var(--muted);
  max-width: 70ch;
  margin-top: var(--s-3);
}

.ledger__list { margin: var(--s-4) 0 0; font-family: var(--mono); font-size: var(--t-tag); color: var(--muted); }
.ledger__list summary { cursor: pointer; color: var(--accent); width: fit-content; }
.ledger__list summary:focus-visible { outline: var(--focus-w) solid var(--accent); outline-offset: 3px; }
.ledger__list ol { list-style: none; margin: var(--s-3) 0 0; padding: 0; max-width: 92ch; }
.ledger__list li {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr);
  gap: 2px var(--s-3);
  align-items: baseline;
  padding-block: 6px;
  border-top: 1px solid var(--rule);
}
.ledger__list li span { font-family: var(--body); font-size: var(--t-small); line-height: 1.5; color: var(--ink-soft); }
@media (max-width: 600px) {
  .ledger__list li { grid-template-columns: auto minmax(0, 1fr); }
  .ledger__list li span { grid-column: 1 / -1; }
}
