/* Fonts, served from site/fonts/ (no requests leave the site). Variable Fraunces (opsz 9-144) and
   Source Serif 4 (opsz 8-60) with their italics, and IBM Plex Mono at 400, 500 and 600. Latin and
   latin-ext subsets, as Google Fonts cuts them; licences (SIL OFL 1.1) are next to the files. */
@font-face {
  font-family: "Fraunces";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("fonts/fraunces-normal-400-800-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-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Fraunces";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("fonts/fraunces-normal-400-800-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: "Fraunces";
  font-style: italic;
  font-weight: 400 600;
  font-display: swap;
  src: url("fonts/fraunces-italic-400-600-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-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Fraunces";
  font-style: italic;
  font-weight: 400 600;
  font-display: swap;
  src: url("fonts/fraunces-italic-400-600-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("fonts/source-serif-4-normal-400-600-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-20C4, 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("fonts/source-serif-4-normal-400-600-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: italic;
  font-weight: 400 600;
  font-display: swap;
  src: url("fonts/source-serif-4-italic-400-600-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-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Source Serif 4";
  font-style: italic;
  font-weight: 400 600;
  font-display: swap;
  src: url("fonts/source-serif-4-italic-400-600-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("fonts/ibm-plex-mono-normal-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-20C4, 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("fonts/ibm-plex-mono-normal-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: 500;
  font-display: swap;
  src: url("fonts/ibm-plex-mono-normal-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-20C4, 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("fonts/ibm-plex-mono-normal-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: 600;
  font-display: swap;
  src: url("fonts/ibm-plex-mono-normal-600-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-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("fonts/ibm-plex-mono-normal-600-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;
}

:root {
  --paper: #f3ede2;
  --paper-2: #ebe3d4;
  --card: #fbf8f2;
  --ink: #1f1b16;
  --ink-2: #4a4339;
  --ink-3: #6b6357;     /* 4.6:1 on --paper-2, the darkest paper it sits on */
  --rule: #d6cbb8;

  /* one colour per kind of text in the conversation */
  --sys: #617083;      /* instructions + tool manual (darkened from #6b7a8c for AA with white text) */
  --you: #c4841a;      /* your message */
  --model: #c8432b;    /* what the model writes */
  --result: #2c7a6f;   /* what the harness pastes back */

  /* The role colours as small text on paper. In light they're a step darker than the fills so
     they pass AA (4.5:1) on every paper; the fills themselves are unchanged. */
  --model-text: #b03c27;
  --result-text: #2a6c62;
  /* Text on a solid role fill (card heads, the who pill, the context bar). */
  --on-fill: var(--card);
  --on-you: var(--ink);

  /* What a call read and wrote, in the cache charts (chapter 3, and its picture on the contents
     page), all mixed from the tokens above. Cache reads: the result teal (the same text again, and
     cheap). New tokens: the "you" ochre, the loud one. What the model wrote: the model's own colour,
     as everywhere else on the site. "New" sits between the other two in a stack, so it takes the
     far lightness and touches both at 3:1 or more. WCAG contrast, light: cache 11.2 / new 3.45 /
     wrote 11.2 on --card, new 3.14 on --paper, cache-new 3.24, new-wrote 3.24. Dark: cache 3.29 /
     new 10.4 / wrote 3.12 on --card, cache-new 3.15, new-wrote 3.33. Cache and wrote never touch;
     their hues (teal, red) tell them apart. */
  --c-cached: color-mix(in srgb, var(--result) 35%, var(--ink));   /* #243c35 */
  --c-fresh: color-mix(in srgb, var(--you) 90%, var(--ink));       /* #b47a1a */
  --c-out: color-mix(in srgb, var(--model) 35%, var(--ink));       /* #5a291d */

  /* The one non-colour cue, for colour-blind readers: the teal family (--result, what the harness
     pastes back, and --c-cached, read from the cache) wears a fine 135° hatch of the surface
     colour, everywhere it fills a shape, legends included. With red-green colour blindness --result
     and --sys both turn grey, and --c-cached turns as dark as --c-out and --ink; the hatch keeps
     them apart. Layer it over the fill: `background: var(--tex-teal) var(--result)`, and
     `var(--tex-cached) var(--c-cached)`. The line is a quarter of the surface colour mixed in, which
     keeps every line at 3:1 or more against --card (light 3.0 on --result, 4.8 on --c-cached; dark 4.4, 5.0).
     In dark, the cached line mixes in --ink instead, as the surface would sink it into the dark fill. */
  --tex-teal: repeating-linear-gradient(135deg, transparent 0 3px, color-mix(in srgb, var(--card) 25%, transparent) 3px 4.5px);
  --tex-cached: var(--tex-teal);

  --focus: var(--ink);

  /* Motion: one set of curves and durations for every page (site/lib/motion.js reads these too).
     Entering and leaving: --ease-out. Moving or morphing on screen: --ease-in-out. Hover: ease. */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --dur-press: 160ms;   /* press feedback */
  --dur-fast: 150ms;    /* hover, a highlight that follows the pointer */
  --dur-base: 240ms;    /* a step's text, a highlight moving between parts */
  --dur-slow: 400ms;    /* a chart changing state, a number counting */
  --dur-reveal: 450ms;  /* a figure arriving on scroll, a bar growing in */

  --display: "Fraunces", Georgia, serif;
  --body: "Source Serif 4", Georgia, serif;
  --mono: "IBM Plex Mono", ui-monospace, Consolas, monospace;

  /* One grid for the page: a centred text column, and a wide track that outdents from it by the
     same amount on both sides. Text sits in the text column; the games and the player use the
     wide track. */
  --gutter: clamp(1rem, 4vw, 2.5rem);
  --measure: 40rem;
  --wide: 68rem;
  --outdent: calc((var(--wide) - var(--measure)) / 2);
  --cols:
    [full-start] minmax(var(--gutter), 1fr)
    [wide-start] minmax(0, var(--outdent))
    [text-start] min(var(--measure), 100% - 2 * var(--gutter)) [text-end]
    minmax(0, var(--outdent)) [wide-end]
    minmax(var(--gutter), 1fr) [full-end];
  --section: clamp(2.75rem, 6vw, 4.5rem);
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #17150f;
    --paper-2: #211e17;
    --card: #1d1a14;
    --ink: #eee6d6;
    --ink-2: #c6bba8;
    --ink-3: #998f7d;
    --rule: #3a342a;
    --sys: #93a3b6;
    --you: #e3a640;
    --model: #ef6a4f;
    --result: #4fb3a4;
    --model-text: var(--model);
    --result-text: var(--result);
    --on-you: var(--card);
    /* Light text on dark paper looks heavier than the same weight in light; ease it off a step. */
    --say: #e2d9c8;
    --c-cached: color-mix(in srgb, var(--result) 60%, var(--card)); /* #3b766a */
    --c-fresh: color-mix(in srgb, var(--you) 55%, var(--ink));      /* #e8c384 */
    --c-out: color-mix(in srgb, var(--model) 65%, var(--card));     /* #a64e3a */
    --tex-cached: repeating-linear-gradient(135deg, transparent 0 3px, color-mix(in srgb, var(--ink) 25%, transparent) 3px 4.5px);
    color-scheme: dark;
  }
}

* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scrollbar-color: color-mix(in srgb, var(--ink-3) 60%, transparent) transparent;
  accent-color: var(--model);
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 1.125rem/1.62 var(--body);
  font-optical-sizing: auto;
  background-image:
    radial-gradient(circle at 1px 1px, color-mix(in srgb, var(--ink) 7%, transparent) 1px, transparent 0);
  background-size: 22px 22px;
}

::selection { background: color-mix(in srgb, var(--model) 28%, transparent); color: inherit; }

code, pre { font-family: var(--mono); font-size: 0.86em; }
p code, li code, h3 code, q code {
  background: var(--paper-2);
  padding: 0.05em 0.35em;
  border-radius: 4px;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
a {
  color: var(--model-text);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
  transition: text-decoration-color var(--dur-fast) ease;
}
@media (hover: hover) and (pointer: fine) {
  a:hover { text-decoration-thickness: 2px; }
}
strong, b { font-weight: 600; }

/* Focus: one ring for every control on the page, in ink, clear of the control it marks. */
:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
[tabindex="-1"]:focus { outline: none; }

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

/* Skip links: hidden until a keyboard reaches them, then a solid tab at the top left. */
.skip-link {
  position: absolute;
  left: var(--gutter);
  top: 0.75rem;
  z-index: 100;
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0 1rem;
  font: 600 0.85rem/1 var(--mono);
  color: var(--card);
  background: var(--ink);
  border-radius: 8px;
  text-decoration: none;
  transform: translateY(-200%);
}
.skip-link:focus-visible { transform: none; outline-color: var(--ink); }

/* ---------- the page grid ---------- */
.band { display: grid; grid-template-columns: var(--cols); }
.band > * { grid-column: text; }
.band > .wide, main.band > .player { grid-column: wide; }
main.band > section:not(.player) {
  grid-column: full;
  display: grid;
  grid-template-columns: subgrid;
}
main.band > section > * { grid-column: text; }
main.band > section > .wide { grid-column: wide; }

/* ---------- masthead ---------- */
.masthead { padding-top: clamp(3rem, 9vw, 6.5rem); padding-bottom: clamp(1.75rem, 4vw, 2.75rem); }
.kicker {
  font: 600 0.78rem/1.3 var(--mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--model-text);
  margin: 0 0 1.1rem;
}
h1 {
  /* The title may run past the text column into the outdent, never past the wide track. */
  grid-column: text-start / wide-end;
  font: 700 clamp(2.75rem, 8.4vw, 6.4rem)/0.95 var(--display);
  font-variation-settings: "opsz" 144;
  letter-spacing: -0.03em;
  margin: 0 0 1.4rem;
  text-wrap: balance;
}
.masthead > h1 { grid-column: text-start / wide-end; }
.kicker a { color: inherit; text-decoration-color: color-mix(in srgb, currentColor 45%, transparent); }
.dek {
  font: 400 clamp(1.2rem, 2.3vw, 1.5rem)/1.42 var(--display);
  font-variation-settings: "opsz" 30;
  color: var(--ink-2);
  margin: 0;
  text-wrap: pretty;
}

/* ---------- prose ---------- */
.prose { padding-block: var(--section) 0; }
.prose h2 {
  font: 650 clamp(1.75rem, 4vw, 2.35rem)/1.1 var(--display);
  letter-spacing: -0.015em;
  margin: 0 0 1.1rem;
  text-wrap: balance;
}
.prose h3 {
  font: 600 1.3rem/1.25 var(--display);
  margin: 2.25rem 0 0.5rem;
  text-wrap: balance;
}
.prose p { margin: 0 0 1.15rem; text-wrap: pretty; }
.prose > :last-child { margin-bottom: 0; }

.prompt {
  margin: 1.5rem 0;
  padding: 1rem 1.25rem;
  border-left: 3px solid var(--you);
  background: color-mix(in srgb, var(--you) 10%, var(--card));
  border-radius: 0 10px 10px 0;
  font: 500 1.2rem/1.42 var(--display);
  font-variation-settings: "opsz" 36;
}

/* ---------- the games ---------- */
.game-open { padding-top: 0; }
.game {
  margin: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.9rem;
}
.game-close .game { margin-top: 1.75rem; }
/* Wide screens: the caption sits beside the game, like a label next to a picture. The game
   takes the width of its canvas (720 px plus the page's padding), the caption the rest. */
@media (min-width: 62rem) {
  .game {
    grid-template-columns: minmax(0, 47rem) minmax(13rem, 1fr);
    column-gap: clamp(1.5rem, 3vw, 2.5rem);
    align-items: start;
  }
}
.game figcaption {
  color: var(--ink-2);
  font-size: 1rem;
  line-height: 1.55;
  max-width: var(--measure);
  text-wrap: pretty;
}
@media (min-width: 62rem) {
  .game figcaption { padding-top: 0.85rem; border-top: 1px solid var(--rule); }
}
.game-label { display: block; font: 600 0.78rem/1.4 var(--mono); letter-spacing: 0.02em; margin-bottom: 0.3rem; }
.game[data-game="before"] .game-label { color: var(--model-text); }
.game[data-game="after"] .game-label { color: var(--result-text); }

/* The iframe is sized like the game page inside it: 16 px padding round a 3:2 canvas of at most
   720 px. The poster uses the same box, so pressing play doesn't move anything. */
.game-frame {
  position: relative;
  container-type: inline-size;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 14px;
  overflow: hidden;
  transition: border-color var(--dur-fast) ease;
}
/* While the game has the keys, or the poster has focus, the whole frame shows it. */
.game.is-playing .game-frame:focus-within,
.game-frame:has(.game-poster:focus-visible) { outline: 2px solid var(--focus); outline-offset: 3px; }
.game-frame iframe, .game-poster {
  display: block;
  width: 100%;
  height: calc(min(100cqi - 32px, 720px) * 2 / 3 + 36px);
  border: 0;
}
.game-poster {
  position: relative;
  margin: 0;
  padding: 16px;
  background: var(--paper);
  cursor: pointer;
  font: inherit;
  color: inherit;
}
.game-poster:focus-visible { outline: none; }
.game-still {
  display: block;
  width: min(100%, 720px);
  aspect-ratio: 3 / 2;
  height: auto;
  margin: 0 auto;
  transition: opacity var(--dur-fast) ease;
  opacity: 0.88;
}
/* The button sits between the ball and the paddle, where the first frame is empty. */
.game-play {
  position: absolute;
  left: 50%;
  top: calc(16px + (100% - 32px) * 0.71);
  transform: translate(-50%, -50%);
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 3rem;
  padding: 0 1.4rem 0 1.15rem;
  font: 600 0.95rem/1 var(--mono);
  white-space: nowrap;
  color: var(--card);
  background: var(--ink);
  border-radius: 999px;
  box-shadow: 0 10px 24px -12px color-mix(in srgb, var(--ink) 70%, transparent);
  transition: transform var(--dur-press) var(--ease-out), background-color var(--dur-fast) ease;
}
.game-play svg { width: 0.8rem; height: 0.8rem; fill: currentColor; flex: none; }
@media (hover: hover) and (pointer: fine) {
  .game-poster:hover .game-still { opacity: 1; }
  .game-poster:hover .game-play { background: var(--model-text); }
}
.game-poster:focus-visible .game-still { opacity: 1; }
.game-poster:active .game-play { transform: translate(-50%, -50%) scale(0.97); }
/* Pressing play: the still stays over the game while it loads, then fades into it (the game's
   first frame is the same picture). */
.game-fade {
  position: absolute;
  inset: 0 0 auto;
  height: calc(min(100cqi - 32px, 720px) * 2 / 3 + 36px);
  padding: 16px;
  background: var(--paper);
  pointer-events: none;
}
.game-fade img { display: block; width: min(100%, 720px); aspect-ratio: 3 / 2; height: auto; margin: 0 auto; }

.game-bar {
  display: flex;
  gap: 0.5rem;
  justify-content: flex-end;
  padding: 0.5rem;
  border-top: 1px solid var(--rule);
  background: var(--card);
}

.ghost {
  font: 600 0.82rem/1.2 var(--mono);
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 8px;
  min-height: 2.75rem;
  padding: 0.5rem 0.95rem;
  cursor: pointer;
  transition: transform var(--dur-press) var(--ease-out), border-color var(--dur-fast) ease, background-color var(--dur-fast) ease;
}
.ghost:active { transform: scale(0.97); }
@media (hover: hover) and (pointer: fine) {
  .ghost:hover { border-color: var(--ink-3); background: var(--card); }
}

/* ---------- player ---------- */
.player {
  max-width: var(--wide);
  margin: var(--section) auto 0;
  padding: clamp(1rem, 3vw, 2rem);
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 18px;
  box-shadow: 0 1px 0 var(--rule), 0 30px 60px -40px color-mix(in srgb, var(--ink) 45%, transparent);
}
main.band > .player { max-width: none; margin-inline: 0; }

.player-head { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem 1rem; flex-wrap: wrap; }
.step-meta { display: flex; gap: 0.75rem; align-items: center; flex-wrap: wrap; }
.step-num { font: 600 0.8rem/1 var(--mono); color: var(--ink-2); letter-spacing: 0.04em; font-variant-numeric: tabular-nums; }
.step-who {
  font: 600 0.72rem/1 var(--mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 0.4rem 0.6rem;
  border-radius: 999px;
  color: var(--on-fill);
  background: var(--ink-3);
}
.step-who[data-who="model"] { background: var(--model); }
.step-who[data-who="harness"] { background: var(--result); }
.step-who[data-who="you"] { background: var(--you); color: var(--on-you); }

.controls { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
.transport { display: flex; }
.controls button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font: 600 0.88rem/1 var(--mono);
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 10px;
  min-width: 2.75rem;
  height: 2.75rem;
  padding: 0 0.95rem;
  cursor: pointer;
  transition: transform var(--dur-press) var(--ease-out), background-color var(--dur-fast) ease, border-color var(--dur-fast) ease, color var(--dur-fast) ease;
}
.controls svg { width: 0.85rem; height: 0.85rem; flex: none; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.controls svg.solid { fill: currentColor; stroke: none; }
/* The three step buttons read as one group. */
.transport button { border-radius: 0; position: relative; }
.transport button + button { margin-left: -1px; }
.transport button:first-child { border-radius: 10px 0 0 10px; }
.transport button:last-child { border-radius: 0 10px 10px 0; }
.transport button:focus-visible { z-index: 1; }
#prev, #next { padding: 0; width: 2.75rem; }
#play { min-width: 6rem; }
@media (hover: hover) and (pointer: fine) {
  .controls button:not(:disabled):hover { background: var(--card); border-color: var(--ink-3); z-index: 1; }
}
.controls button:not(:disabled):active { transform: scale(0.97); }
.controls button:disabled { color: color-mix(in srgb, var(--ink) 35%, var(--paper)); cursor: default; }
#play[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--card); z-index: 1; }
.live-btn { min-width: 8.75rem; }
.live-btn .dot { width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--model); flex: none; }
.live-btn[aria-pressed="true"] { background: var(--model); border-color: var(--model); color: var(--on-fill); }
.live-btn[aria-pressed="true"] .dot { background: currentColor; }

/* the slider: a thin rule, the played part in the model colour */
.timebar { display: flex; align-items: center; gap: 1rem; margin: 0.75rem 0 0; }
#scrub {
  --p: 0;
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  height: 2.75rem;
  background: transparent;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
#scrub:focus-visible { outline-offset: 0; border-radius: 6px; }
#scrub::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 2px;
  background: linear-gradient(to right, var(--model) calc(9px + (100% - 18px) * var(--p)), var(--rule) 0);
}
#scrub::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 18px;
  height: 18px;
  margin-top: -7px;
  border-radius: 50%;
  background: var(--model);
  border: 3px solid var(--card);
  box-shadow: 0 0 0 1px var(--model);
  transition: transform var(--dur-press) var(--ease-out);
}
#scrub:active::-webkit-slider-thumb { transform: scale(1.15); }
#scrub::-moz-range-track { height: 4px; border-radius: 2px; background: var(--rule); }
#scrub::-moz-range-progress { height: 4px; border-radius: 2px; background: var(--model); }
#scrub::-moz-range-thumb {
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--model); border: 3px solid var(--card); box-shadow: 0 0 0 1px var(--model);
}
.clock { font: 600 0.82rem/1 var(--mono); color: var(--ink-2); white-space: nowrap; font-variant-numeric: tabular-nums; }

.live-status {
  margin: 0 0 1rem;
  min-height: 1.3em;
  font: 500 0.8rem/1.3 var(--mono);
  color: var(--ink-2);
  visibility: hidden;
}
.player.is-live .live-status { visibility: visible; }
.player.is-live .live-status::before {
  content: "";
  display: inline-block;
  width: 0.5rem; height: 0.5rem;
  margin-right: 0.5rem;
  border-radius: 50%;
  background: var(--model);
  animation: blink 1.2s ease-in-out infinite;
}
@keyframes blink { 50% { opacity: 0.25; } }

/* The stage: diagram and code on the left, the step on the right, split on the page's axis. */
.stage {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(1.25rem, 3vw, 2.5rem);
  align-items: start;
}

/* loop diagram */
.loop { width: 100%; max-width: 26rem; display: block; margin: 0 auto; color: var(--ink-3); }
.loop .edge {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-dasharray: 5 5;
  opacity: 0.35;
  transition: opacity var(--dur-base) ease, stroke var(--dur-base) ease;
}
.loop .edge.on {
  opacity: 1;
  stroke-width: 2.6;
  stroke-dasharray: 7 6;
  animation: flow 0.9s linear infinite;
}
@keyframes flow { to { stroke-dashoffset: -26; } }
#e-to-model.on { color: var(--sys); stroke: var(--ink-2); }
#e-to-harness.on { color: var(--model); stroke: var(--model); }
#e-to-pc.on, #e-from-pc.on { color: var(--result); stroke: var(--result); }
/* The token: what travels along an edge when a step starts (site/loop/app.js). At rest it's hidden. */
.loop .token { opacity: 0; pointer-events: none; stroke: var(--card); stroke-width: 2; }
.loop .token.t-to-model { fill: var(--ink-2); }
.loop .token.t-to-harness { fill: var(--model); }
.loop .token.t-to-pc, .loop .token.t-from-pc { fill: var(--result); }

.loop .node circle, .loop .node rect {
  fill: var(--paper);
  stroke: var(--rule);
  stroke-width: 1.5;
  transition: fill var(--dur-base) ease, stroke var(--dur-base) ease;
}
.loop .node text {
  font: 650 15px var(--display);
  fill: var(--ink);
  text-anchor: middle;
}
.loop .node text.sub { font: 400 10.5px var(--mono); fill: var(--ink-2); }
.loop .node.on circle { fill: color-mix(in srgb, var(--model) 16%, var(--card)); stroke: var(--model); }
#n-harness.on rect { fill: color-mix(in srgb, var(--result) 16%, var(--card)); stroke: var(--result); }
#n-pc.on rect { fill: color-mix(in srgb, var(--result) 10%, var(--card)); stroke: var(--result); }
.loop .edge-label { font: 500 10.5px var(--mono); fill: var(--ink-2); opacity: 0; transition: opacity var(--dur-base) ease; }
.loop .edge-label.on { opacity: 1; }

/* loop code */
.code-fig { margin: 1.5rem 0 0; }
.code-fig figcaption, .context-label, .hs .hs-title {
  font: 500 0.75rem/1.3 var(--mono);
  letter-spacing: 0.03em;
  color: var(--ink-2);
}
.code-fig figcaption { margin-bottom: 0.5rem; }
.loopcode {
  margin: 0;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 0.7rem 0;
  overflow-x: auto;
  white-space: normal;
  font-size: 0.78rem;
  line-height: 1.75;
}
.loopcode span {
  display: block;
  padding: 0 0.9rem;
  border-left: 3px solid transparent;
  transition: background-color var(--dur-base) ease, border-color var(--dur-base) ease;
  white-space: pre;
}
.loopcode i { font-style: normal; color: var(--ink-3); }
.loopcode span.on {
  background: color-mix(in srgb, var(--model) 13%, transparent);
  border-left-color: var(--model);
}
.loopcode span.on i { color: var(--ink-2); }

/* step narration */
.step-title {
  font: 650 clamp(1.4rem, 2.6vw, 1.8rem)/1.15 var(--display);
  letter-spacing: -0.01em;
  margin: 0 0 0.65rem;
  text-wrap: balance;
}
.step-text { margin: 0 0 1.25rem; color: var(--ink-2); text-wrap: pretty; }
.step-text strong { color: var(--ink); }
.step-actions:empty { display: none; }
.step-actions { margin: 0 0 1rem; }

/* ---------- cards ---------- */
.cards { display: grid; gap: 0.75rem; }
.cards .card-slot { display: grid; gap: 0.75rem; animation: rise var(--dur-base) var(--ease-out) both; }
.cards .card-slot:nth-child(2) { animation-delay: 50ms; }
.cards .card-slot:nth-child(3) { animation-delay: 100ms; }
.cards .card-slot:nth-child(n + 4) { animation-delay: 150ms; }
.cards .card-slot[hidden] { display: none; }
@keyframes rise { from { opacity: 0; transform: translateY(4px); } }

.card {
  border: 1px solid var(--rule);
  border-radius: 10px;
  background: var(--paper);
  overflow: hidden;
}
.card-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.6rem;
  font: 600 0.72rem/1.3 var(--mono);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0.55rem 0.85rem;
  color: var(--on-fill);
}
.card-head .meta { text-transform: none; letter-spacing: 0; font-weight: 500; text-align: right; }
.card.model .card-head { background: var(--model); }
.card.result .card-head { background: var(--result); }
.card.result.err .card-head { background: color-mix(in srgb, var(--model) 70%, var(--result)); }
.card.you .card-head { background: var(--you); color: var(--on-you); }
.card.sys .card-head { background: var(--sys); }
.card pre {
  margin: 0;
  padding: 0.8rem 0.85rem;
  font-size: 0.76rem;
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 22rem;
  overflow: auto;
}
.card pre mark {
  background: color-mix(in srgb, var(--model) 20%, transparent);
  color: inherit;
  border-radius: 3px;
  padding: 0 2px;
}
.card pre .del { color: var(--model-text); }
.card pre .add { color: var(--result-text); }
.card pre .more { color: var(--ink-3); font-style: italic; }
.card pre .ln { display: inline-block; width: 3ch; margin-right: 1ch; color: var(--ink-3); text-align: right; user-select: none; }
.card pre mark .ln { color: inherit; }

/* What the model writes, and what you typed: prose, set for reading. */
.card .say {
  margin: 0;
  padding: 0.85rem 1rem 0;
  font: 400 1rem/1.6 var(--body);
  color: var(--say, var(--ink));
  text-wrap: pretty;
}
.card .say:last-child { padding-bottom: 0.95rem; }
.card .say strong { font-weight: 600; }
.card .say code {
  font-size: 0.84em;
  background: var(--paper-2);
  padding: 0.05em 0.3em;
  border-radius: 4px;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
.card.you .say {
  font: 500 1.08rem/1.45 var(--display);
  font-variation-settings: "opsz" 24;
  padding-bottom: 0.95rem;
}
.card.text pre {
  margin: 0.85rem 1rem 0;
  padding: 0.65rem 0.8rem;
  background: var(--paper-2);
  border-radius: 6px;
}
.card.text pre:last-child { margin-bottom: 1rem; }
.card .note { margin: 0; padding: 0.85rem 1rem; font-size: 0.95rem; line-height: 1.55; color: var(--ink-2); }

.card-wait {
  margin: 0;
  padding: 1.1rem 0.9rem;
  border: 2px dashed color-mix(in srgb, var(--model) 45%, var(--rule));
  border-radius: 10px;
  font: 500 0.82rem/1.4 var(--mono);
  color: var(--ink-2);
}

/* ---------- context bar ---------- */
.context { margin-top: 2rem; padding-top: 1.25rem; border-top: 1px dashed var(--rule); }
.context-head { display: flex; justify-content: space-between; align-items: baseline; gap: 0.4rem 1rem; flex-wrap: wrap; margin-bottom: 0.65rem; }
.context-count { font: 500 1rem/1.35 var(--display); color: var(--ink-2); }
.context-count b { font-family: var(--mono); font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }

.stack {
  position: relative;
  display: flex;
  height: 3.25rem;
  border-radius: 8px;
  overflow: hidden;
  background: repeating-linear-gradient(135deg, var(--paper) 0 6px, var(--paper-2) 6px 12px);
  border: 1px solid var(--rule);
}
.blk {
  position: absolute;
  top: 0;
  bottom: 0;
  border-right: 2px solid var(--card);
  transform-origin: 0 50%;
}
.blk.sys { left: 0; background: var(--sys); }
/* A round sits at its place in the conversation and grows in from its left edge when it lands. */
.blk.round {
  background: var(--tex-teal) var(--result);
  box-shadow: inset 4px 0 0 var(--model);
  transform: scaleX(0);
  transition: transform var(--dur-slow) var(--ease-out);
}
.blk.round.in { transform: none; }
.blk span {
  position: absolute;
  left: 0.75rem; top: 50%;
  transform: translateY(-50%);
  font: 600 0.76rem/1 var(--mono);
  color: var(--on-fill);
  white-space: nowrap;
}
/* A round too thin to see at this width (loop/app.js decides, under about 4px): a tick under it
   and its size. Hidden until the round lands, like the round itself. */
.stack-marks { position: relative; height: 1.4rem; }
.stack-marks:not(.has) { display: none; }
.mark {
  position: absolute;
  top: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  transform: translateX(-50%);
  font: 600 0.7rem/1 var(--mono);
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-out);
}
.mark.in { opacity: 1; }
.mark[hidden] { display: none; }
.stack-marks.has + .legend { margin-top: 0.3rem; }
.mark::before { content: ""; width: 2px; height: 0.4rem; margin-bottom: 0.2rem; background: var(--ink-2); }
.mark[data-align="start"] { transform: translateX(-1px); align-items: flex-start; }
.mark[data-align="end"] { transform: translateX(calc(-100% + 1px)); align-items: flex-end; }
.mark.no-label b { visibility: hidden; }

/* While the model reads: a soft light passes over the whole conversation, left to right. */
.stack.reading::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 30%;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--card) 40%, transparent) 50%, transparent);
  animation: sweep 1.4s ease-in-out infinite;
  pointer-events: none;
}
@keyframes sweep { from { transform: translateX(-100%); } to { transform: translateX(334%); } }

.legend { display: flex; flex-wrap: wrap; gap: 0.4rem 1.25rem; margin-top: 0.7rem; font: 400 0.78rem/1.45 var(--mono); color: var(--ink-2); }
.legend i { display: inline-block; width: 0.75rem; height: 0.75rem; border-radius: 3px; margin-right: 0.45rem; vertical-align: -0.1rem; }
.legend .sw.sys { background: var(--sys); }
.legend .sw.round { background: var(--tex-teal) var(--result); box-shadow: inset 3px 0 0 var(--model); }

/* ---------- phone: one column, in reading order ---------- */
@media (max-width: 48rem) {
  .player { display: flex; flex-direction: column; }
  .stage, .col-left, .col-right { display: contents; }
  .player-head { order: 0; }
  .timebar { order: 1; }
  .live-status { order: 2; }
  .step-title { order: 3; margin-top: 0.5rem; }
  .step-text { order: 4; }
  .step-actions { order: 5; }
  .context { order: 6; margin: 0 0 1.25rem; padding-top: 0; border-top: 0; }
  .cards { order: 7; }
  .loop { order: 8; max-width: 19rem; margin-top: 2rem; padding-top: 1.5rem; border-top: 1px dashed var(--rule); }
  .code-fig { order: 9; }
  .loopcode { font-size: 0.7rem; }
  /* No sideways scrolling on a phone: a long line wraps, its tail indented under its start. */
  .loopcode span { white-space: pre-wrap; padding-left: 3.6rem; text-indent: -2.7rem; }
  .context-head { flex-direction: column-reverse; align-items: flex-start; }
}
@media (max-width: 30rem) {
  .player-head { gap: 0.75rem; }
  .controls { width: 100%; flex-wrap: nowrap; }
  .controls button { font-size: 0.84rem; gap: 0.4rem; padding: 0 0.65rem; white-space: nowrap; }
  .controls .live-btn { flex: 1 1 auto; min-width: 0; }
  #play { min-width: 0; width: 5.25rem; }
  .player { border-radius: 14px; }
}

/* ---------- stats + next + colophon ---------- */
.stats {
  margin-top: var(--section);
  padding-block: 1.5rem;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1.25rem;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}
main.band > section.stats { grid-column: text; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
main.band > section.stats > * { grid-column: auto; }
@media (max-width: 30rem) { main.band > section.stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.stats .n { display: block; font: 700 clamp(1.5rem, 4.5vw, 1.95rem)/1.05 var(--display); letter-spacing: -0.02em; white-space: nowrap; font-variant-numeric: lining-nums; }
.stats .l { display: block; font: 500 0.75rem/1.35 var(--mono); color: var(--ink-2); margin-top: 0.4rem; }

.next { padding-bottom: var(--section); }

.colophon {
  padding-block: 0 2.5rem;
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--ink-2);
}
.colophon > * { grid-column: text; }
.colophon h2 { font: 650 1.2rem/1.3 var(--display); color: var(--ink); margin: 0 0 0.8rem; padding-top: 2.25rem; border-top: 1px solid var(--rule); }
.colophon p { margin: 0 0 0.85rem; }
.runs { margin: 0 0 0.85rem; padding-left: 1.2rem; }
.runs li { margin-bottom: 0.3rem; }
.colophon a { padding-block: 0.15rem; }

/* ---------- chapter navigation (the foot of every chapter) ---------- */
.chapter-nav { padding-bottom: clamp(3rem, 8vw, 4.5rem); }
.chapter-nav > * { grid-column: text; }
.cn {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.75rem;
  padding-top: 2rem;
  border-top: 1px solid var(--rule);
}
.cn-link {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-height: 2.75rem;
  padding: 0.95rem 1.1rem 1rem;
  color: var(--ink);
  text-decoration: none;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 12px;
  transition: transform var(--dur-press) var(--ease-out), border-color var(--dur-fast) ease;
}
a.cn-link:active { transform: scale(0.98); }
@media (hover: hover) and (pointer: fine) {
  a.cn-link:hover { border-color: var(--ink-3); }
  a.cn-link:hover .cn-title { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 0.2em; }
}
.cn-next { grid-column: 2; text-align: right; }
.cn-dir { display: inline-flex; align-items: center; gap: 0.4rem; font: 600 0.72rem/1.3 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--model-text); }
.cn-next .cn-dir { justify-content: flex-end; }
.chev { width: 0.7rem; height: 0.7rem; flex: none; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; transition: transform var(--dur-base) var(--ease-out); }
@media (hover: hover) and (pointer: fine) {
  a.cn-prev:hover .chev { transform: translateX(-2px); }
  a.cn-next:hover .chev { transform: translateX(2px); }
}
.cn-title { font: 600 1.1rem/1.3 var(--display); text-wrap: balance; }
.cn-soon { background: transparent; border-style: dashed; color: var(--ink-2); }
.cn-soon .cn-dir { color: var(--ink-2); }
.cn-toc {
  grid-column: 1 / -1;
  justify-self: center;
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0 0.9rem;
  font: 600 0.82rem/1 var(--mono);
  color: var(--ink-2);
}
@media (max-width: 30rem) {
  .cn { grid-template-columns: minmax(0, 1fr); }
  .cn-next { grid-column: auto; text-align: left; }
  .cn-next .cn-dir { justify-content: flex-start; }
}

/* ---------- contents page (site/index.html) ---------- */
.toc-main { padding-bottom: clamp(3rem, 8vw, 5.5rem); }
.toc { grid-column: text-start / wide-end; list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule); }
.toc-item {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(15rem, 21rem);
  gap: 1rem clamp(1.5rem, 4vw, 3rem);
  align-items: center;
  padding-block: clamp(1.5rem, 3.5vw, 2.25rem);
  border-bottom: 1px solid var(--rule);
}
.toc-n { display: block; font: 600 0.78rem/1.3 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--model-text); margin-bottom: 0.5rem; }
.toc h3 { font: 650 clamp(1.6rem, 3.6vw, 2.2rem)/1.1 var(--display); letter-spacing: -0.015em; margin: 0 0 0.55rem; text-wrap: balance; }
.toc h3 a { color: var(--ink); text-decoration-color: var(--model); }
/* The whole row opens the chapter; the title is the link a screen reader hears. */
.toc h3 a::after { content: ""; position: absolute; inset: 0; border-radius: 14px; }
.toc h3 a:focus-visible { outline: none; }
.toc h3 a:focus-visible::after { outline: 2px solid var(--focus); outline-offset: 2px; }
.toc p { margin: 0; color: var(--ink-2); text-wrap: pretty; max-width: var(--measure); }
.toc-go { display: inline-flex; align-items: center; gap: 0.4rem; margin-top: 0.85rem; font: 600 0.8rem/1.3 var(--mono); color: var(--model-text); }

/* A small picture of each chapter, drawn from its own recording (site/contents.js). */
.toc-viz {
  margin: 0;
  padding: 1rem 1.1rem 0.85rem;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 12px;
  transition: transform var(--dur-base) var(--ease-out), border-color var(--dur-fast) ease;
}
.toc-viz:empty { display: none; }
.toc-art { height: 5.5rem; display: flex; }
.toc-cap { margin-top: 0.7rem; font: 500 0.72rem/1.4 var(--mono); color: var(--ink-2); font-variant-numeric: tabular-nums; }
@media (hover: hover) and (pointer: fine) {
  .toc-item:hover .toc-viz { transform: translateY(-3px); border-color: var(--ink-3); }
  .toc-item:hover h3 a { text-decoration-thickness: 2px; }
  .toc-item:hover .toc-go .chev { transform: translateX(3px); }
}
.toc-item:has(a:active) .toc-viz { transform: scale(0.98); transition-duration: var(--dur-press); }

/* chapter 1: one bar per call, everything that call read, to one scale */
.art-loop { flex-direction: column; justify-content: center; gap: 5px; }
.art-loop .row { position: relative; height: 12px; border-radius: 3px; background: repeating-linear-gradient(135deg, var(--paper) 0 4px, var(--paper-2) 4px 8px); overflow: hidden; }
.art-loop .row i { position: absolute; top: 0; bottom: 0; border-right: 2px solid var(--card); }
.art-loop .s { background: var(--sys); }
.art-loop .r { background: var(--tex-teal) var(--result); box-shadow: inset 3px 0 0 var(--model); }
/* chapter 2: the last call's conversation as squares, in reading order */
.art-waffle { display: grid; grid-template-columns: repeat(auto-fill, 9px); grid-auto-rows: 9px; gap: 2px; align-content: center; }
.art-waffle i { border-radius: 2px; }
.art-waffle .s { background: var(--sys); }
.art-waffle .m { background: var(--model); }
.art-waffle .b { background: var(--tex-teal) var(--result); }
/* chapter 3: each call's reading, the cached part and the new part */
.art-cache { align-items: flex-end; gap: 2px; }
.art-cache .col { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column-reverse; gap: 1px; height: 100%; }
.art-cache .col i { display: block; min-height: 1px; }
.art-cache .c { background: var(--tex-cached) var(--c-cached); }
.art-cache .f { background: var(--c-fresh); border-radius: 2px 2px 0 0; }
/* chapter 4: the conversation before compaction and what was left, areas to scale (chapter 4's colours) */
.art-compact { align-items: flex-end; gap: 0.75rem; }
.art-compact i { display: block; aspect-ratio: 1; border-radius: 3px; }
.art-compact .pre { height: 100%; background: color-mix(in srgb, var(--ink-3) 45%, var(--rule)); }
.art-compact .post { min-height: 2px; background: var(--ink); }
/* chapter 5: the first request to scale, tools, system and messages (chapter 5's three steps of --sys) */
.art-request {
  --a-system: color-mix(in srgb, var(--sys) 55%, var(--ink));
  --a-msg: color-mix(in srgb, var(--sys) 26%, var(--card));
  align-items: center; gap: 2px;
}
.art-request .part { display: flex; gap: 1px; min-width: 0; height: 2.75rem; }
.art-request .part:first-child i:first-child { border-radius: 6px 0 0 6px; }
.art-request .part:last-child i:last-child { border-radius: 0 6px 6px 0; }
.art-request i { display: block; min-width: 1px; height: 100%; }
.art-request .p-tools i { background: var(--sys); }
.art-request .p-system i { background: var(--a-system); }
.art-request .p-messages i { background: var(--a-msg); box-shadow: inset 0 0 0 1px var(--sys); }
@media (prefers-color-scheme: dark) {
  .art-request {
    --a-system: color-mix(in srgb, var(--sys) 52%, var(--card));
    --a-msg: color-mix(in srgb, var(--sys) 20%, var(--card));
  }
}

.toc-later-head { grid-column: text-start / wide-end; margin: clamp(2.5rem, 6vw, 3.5rem) 0 1rem; font: 600 0.78rem/1.3 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-2); }
.toc-later {
  grid-column: text-start / wide-end;
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr));
  gap: 0.75rem;
}
.toc-later li { padding: 1rem 1.1rem 1.1rem; border: 1px dashed var(--rule); border-radius: 12px; }
.toc-later .toc-n { color: var(--ink-2); margin-bottom: 0.35rem; }
.toc-later h3 { font: 600 1.2rem/1.25 var(--display); margin: 0 0 0.35rem; color: var(--ink); }
.toc-later p { margin: 0; font-size: 0.95rem; line-height: 1.5; color: var(--ink-2); text-wrap: pretty; }

.toc-later .toc-when { white-space: nowrap; }
/* A finished chapter waiting for its release keeps its picture: a full row, laid out like a published
   one but in the dashed "coming" card. */
.toc-later li.has-viz {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(15rem, 21rem);
  gap: 1rem clamp(1.5rem, 4vw, 3rem);
  align-items: center;
  padding: 1.1rem 1.1rem 1.1rem 1.25rem;
}
.toc-later li.has-viz h3 { font-size: 1.45rem; }
.toc-later li.has-viz .toc-viz { background: color-mix(in srgb, var(--card) 70%, transparent); }

@media (max-width: 48rem) {
  .toc-item, .toc-later li.has-viz { grid-template-columns: minmax(0, 1fr); }
  .toc-art { height: 4.5rem; }
}

/* ---------- release notes (site/lib/release.js) ---------- */
/* At the end of a published chapter: the next chapter to come, and how to hear when it's out. */
.prose .coming {
  margin: 2.25rem 0 0;
  padding: 1.1rem 1.25rem 0.5rem;
  border: 1px dashed var(--ink-3);
  border-radius: 12px;
  font-size: 1rem;
  line-height: 1.55;
}
.prose .coming p { margin: 0 0 0.6rem; color: var(--ink-2); }
.prose .coming .coming-when { margin-bottom: 0.35rem; font: 600 0.72rem/1.3 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-2); }
.prose .coming .coming-title { margin-bottom: 0.5rem; font: 600 1.3rem/1.25 var(--display); color: var(--ink); text-wrap: balance; }
.prose .coming .coming-star { margin: 0.15rem 0 0; }
.coming-star a {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  min-height: 2.75rem;
  font: 600 0.82rem/1 var(--mono);
  color: var(--model-text);
}
.coming-star a::before {
  content: "";
  width: 0.95rem;
  height: 0.95rem;
  flex: none;
  background: currentColor;
  clip-path: polygon(50% 4%, 61.8% 36.2%, 96% 37.6%, 69.1% 58.8%, 78.4% 91.8%, 50% 72.6%, 21.6% 91.8%, 30.9% 58.8%, 4% 37.6%, 38.2% 36.2%);
}

/* At the top of a chapter that isn't released yet. */
.preview { background: var(--paper-2); border-bottom: 1px dashed var(--rule); }
.preview > p { grid-column: text-start / wide-end; margin: 0; padding: 0.7rem 0; font: 500 0.8rem/1.4 var(--mono); color: var(--ink-2); }

/* The language switch, top right of the masthead; drawn only when a page has two languages or more. */
.lang-switch {
  grid-column: text-start / wide-end;
  justify-self: end;
  display: flex;
  gap: 0.25rem;
  margin: calc(-1 * clamp(1.5rem, 5vw, 3.5rem)) 0 clamp(1rem, 3vw, 2rem);
}
.lang-switch a {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0 0.75rem;
  border: 1px solid transparent;
  border-radius: 8px;
  font: 600 0.82rem/1 var(--mono);
  color: var(--ink-2);
  text-decoration: none;
}
.lang-switch a[aria-current="page"] { color: var(--ink); border-color: var(--rule); background: var(--card); }
@media (hover: hover) and (pointer: fine) {
  .lang-switch a:not([aria-current]):hover { color: var(--ink); text-decoration: underline; }
}

/* ---------- motion ---------- */
/* Reveal on scroll (site/lib/motion.js): only set on figures that start below the fold, and only
   while an observer is there to show them. */
.rv { opacity: 0; transform: translateY(12px); }
.rv.rv-in {
  opacity: 1;
  transform: none;
  transition: opacity var(--dur-reveal) var(--ease-out), transform var(--dur-reveal) var(--ease-out);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
  html { scroll-behavior: auto; }
}
.colophon .site-credit { margin-top: 1.4rem; padding-top: 1rem; border-top: 1px dashed var(--rule); }

/* ---------- Chinese pages (site/zh/) ---------- */
/* System CJK fonts only, nothing downloaded. Latin letters and digits still come from Fraunces,
   Source Serif 4 and IBM Plex Mono, which are first in each stack; Chinese characters fall through
   to a serif CJK face where one is installed (Noto Serif CJK / Source Han Serif, Songti on macOS for
   headings), otherwise to the system's sans (PingFang, Hiragino Sans GB, Microsoft YaHei, Noto Sans CJK). */
:root:lang(zh) {
  --cjk-sans: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "Noto Sans SC";
  --cjk-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Noto Serif SC";
  --display: "Fraunces", var(--cjk-serif), "Songti SC", var(--cjk-sans), Georgia, serif;
  --body: "Source Serif 4", var(--cjk-serif), var(--cjk-sans), Georgia, serif;
  --mono: "IBM Plex Mono", ui-monospace, Consolas, var(--cjk-sans), monospace;
}
/* Chinese needs more room between lines than Latin, and no tight tracking. Each rule tests the
   element's own language, so recorded English inside a Chinese page (lang="en") keeps the Latin
   settings. */
body:lang(zh) { line-height: 1.8; }
h1:lang(zh) { line-height: 1.15; letter-spacing: 0.01em; font-size: clamp(2.5rem, 7.6vw, 5.6rem); }
.dek:lang(zh) { line-height: 1.65; }
.prose h2:lang(zh), .toc h3:lang(zh) { line-height: 1.3; letter-spacing: 0.01em; }
.prose h3:lang(zh), .step-title:lang(zh), .cn-title:lang(zh), .prose .coming .coming-title:lang(zh) { line-height: 1.4; letter-spacing: 0; }
.game figcaption:lang(zh), .card .note:lang(zh), .toc-later p:lang(zh), .prose .coming:lang(zh) { line-height: 1.75; }
.colophon:lang(zh) { line-height: 1.75; }
.context-count:lang(zh), .stats .l:lang(zh), .legend:lang(zh) { line-height: 1.6; }
.toc-cap:lang(zh), .live-status:lang(zh), .hs .hs-note:lang(zh) { line-height: 1.6; }
/* Small labels: uppercase does nothing to Chinese, and wide tracking pulls the characters apart. */
.kicker:lang(zh), .toc-n:lang(zh), .toc-later-head:lang(zh), .cn-dir:lang(zh), .step-who:lang(zh),
.prose .coming .coming-when:lang(zh), .card-head:lang(zh) { letter-spacing: 0.04em; }
/* Chinese has no italics; a slanted CJK face is a synthetic one. */
em:lang(zh), i:lang(zh), .card pre .more:lang(zh) { font-style: normal; }
