/* ─────────────────────────────────────────────────────────────
   THE PRINT RUN — the shared visual system for the three talks.
   Editorial plate book: rag paper, ink, one loud second ink.
   Doctrine: runes/design/talks.md. Talk 3: burnt ochre, archetype field.
   ───────────────────────────────────────────────────────────── */

:root {
  color-scheme: light;

  --paper:    #efe8da;
  --paper-2:  #e5dcc9;
  --ink:      #15120c;
  --ink-2:    #4b4336;
  --ink-3:    #8d8373;

  /* talk 03: burnt ochre. --sig-text is the darkened cut used wherever
     the ink carries words (7.1:1 on paper, WCAG-checked against the
     01 precedent's own ratio); --sig itself stays for rules, hatching
     and lamps (4.6:1). */
  --sig:      #8a4b12;
  --sig-text: #733e0f;

  /* the one off-palette ink: a refused call, and nothing else */
  --refuse:   #1f6a5c;

  --serif:   "Newsreader", "Iowan Old Style", Georgia, "Times New Roman", serif;
  --display: "Fraunces", "Iowan Old Style", Georgia, "Times New Roman", serif;
  --mono:    ui-monospace, "SF Mono", Menlo, "Roboto Mono", monospace;

  /* Fraunces axes: SOFT rounds the terminals, WONK off keeps it sober */
  --soft-display: "SOFT" 70, "WONK" 0, "opsz" 144;
  --soft-large:   "SOFT" 60, "WONK" 0, "opsz" 96;
  --soft-text:    "SOFT" 50, "WONK" 0, "opsz" 40;

  --t-display: clamp(56px, 8vw, 132px);
  --t-plate:   clamp(38px, 5.2vw, 84px);
  --t-head:    clamp(26px, 2.7vw, 38px);
  --t-lead:    clamp(20px, 1.6vw, 25px);
  --t-body:    19px;
  --t-mono:    13px;

  --rail: 240px;

  /* one chapter timeline per chapter, named on that chapter's .steps */
  timeline-scope: --ch1, --ch2, --ch3, --ch4, --ch5;
}

/* ── the motion contract: one registered property per figure ────
   --fig1 … --fig5 are the five chapter figures' whole state, 0→1. The
   primary driver is a CSS scroll-driven animation on <body>; the
   fallback is rAF in talk.js writing the same custom properties on the
   same element. A chapter section republishes its own number as --fig,
   so every mark in every figure is the same one declaration over --fig
   and the two drivers cannot diverge.
   Two properties are read at the reader's hand rather than the scroll:
   --shape (which of chapter 3's two shapes the same content arrives in)
   and --probe (which of chapter 4's three retrieval probes is on), each
   set on that figure's own host. */

@property --fig1 { syntax: "<number>"; inherits: true;  initial-value: 0; }
@property --fig2 { syntax: "<number>"; inherits: true;  initial-value: 0; }
@property --fig3 { syntax: "<number>"; inherits: true;  initial-value: 0; }
@property --fig4 { syntax: "<number>"; inherits: true;  initial-value: 0; }
@property --fig5 { syntax: "<number>"; inherits: true;  initial-value: 0; }
@property --fig  { syntax: "<number>"; inherits: true;  initial-value: 0; }
@property --shape { syntax: "<number>"; inherits: true;  initial-value: 0; }
@property --probe { syntax: "<number>"; inherits: true;  initial-value: 0; }
@property --a    { syntax: "<number>"; inherits: false; initial-value: 0; }
@property --r    { syntax: "<number>"; inherits: false; initial-value: 1; }
@property --t    { syntax: "<number>"; inherits: false; initial-value: 0; }
@property --e    { syntax: "<number>"; inherits: false; initial-value: 0; }
/* --el is the same number, inherited, so a ::before can be lit too */
@property --el   { syntax: "<number>"; inherits: true;  initial-value: 0; }

@keyframes fig1-run { from { --fig1: 0; } to { --fig1: 1; } }
@keyframes fig2-run { from { --fig2: 0; } to { --fig2: 1; } }
@keyframes fig3-run { from { --fig3: 0; } to { --fig3: 1; } }
@keyframes fig4-run { from { --fig4: 0; } to { --fig4: 1; } }
@keyframes fig5-run { from { --fig5: 0; } to { --fig5: 1; } }

@supports (animation-timeline: view()) {
  html:not(.driver-js) #track-i   { view-timeline-name: --ch1; view-timeline-axis: block; }
  html:not(.driver-js) #track-ii  { view-timeline-name: --ch2; view-timeline-axis: block; }
  html:not(.driver-js) #track-iii { view-timeline-name: --ch3; view-timeline-axis: block; }
  html:not(.driver-js) #track-iv  { view-timeline-name: --ch4; view-timeline-axis: block; }
  html:not(.driver-js) #track-v   { view-timeline-name: --ch5; view-timeline-axis: block; }
  html:not(.driver-js) body {
    animation: fig1-run linear both, fig2-run linear both, fig3-run linear both,
               fig4-run linear both, fig5-run linear both;
    animation-timeline: --ch1, --ch2, --ch3, --ch4, --ch5;
    animation-range: contain 0% contain 100%, contain 0% contain 100%,
                     contain 0% contain 100%, contain 0% contain 100%,
                     contain 0% contain 100%;
  }
}
html.static body { animation: none !important;
  --fig1: 1; --fig2: 1; --fig3: 1; --fig4: 1; --fig5: 1; }

/* each chapter republishes its own number under the one name the marks read */
#ch-i   { --fig: var(--fig1); }
#ch-ii  { --fig: var(--fig2); }
#ch-iii { --fig: var(--fig3); }
#ch-iv  { --fig: var(--fig4); }
#ch-v   { --fig: var(--fig5); }

/* every ink mark: --a is where it starts, --r is 1/(end - start) */
.ink { --t: clamp(0, (var(--fig) - var(--a)) * var(--r), 1);
       --e: calc(var(--t) * var(--t) * (3 - 2 * var(--t)));
       --el: var(--e); }
.ink-draw  { stroke-dasharray: 1; stroke-dashoffset: calc(1 - var(--e)); }
.ink-fade  { opacity: var(--e); }
.ink-wipe  { transform-box: fill-box; transform-origin: 50% 100%;
             transform: scaleY(var(--e)); }
.ink-enter { opacity: var(--e);
             transform: translateX(calc((1 - var(--e)) * -14px)); }
/* a mark that leaves again: nest it inside the mark that brought it in */
.ink-out   { opacity: calc(1 - var(--e)); }
/* lines that close up over a line taken back out, by --dy at the finish */
.ink-close { transform: translateY(calc(var(--e) * var(--dy, 0px))); }
.ink-grow  { transform-box: fill-box; transform-origin: 0 50%; transform: scaleX(var(--e)); }
.ink-ride  { offset-distance: calc(var(--e) * 100%); offset-rotate: 0deg; }
/* a mark that stays on the plate but gives way to what draws over it */
.ink-dim   { opacity: calc(1 - .72 * var(--e)); }

/* the marks the reader's hand drives, on the same schedule shape as the
   scroll's. --shape is chapter 3's toggle, 0 or 1; --n is the position a
   mark belongs to, so one declaration hides one shape and shows the other. */
.at-shape { opacity: calc(clamp(0, var(--shape) - var(--n) + 1, 1)
                        * clamp(0, var(--n) + 1 - var(--shape), 1)); }
/* the needle travelling between the two recorded positions */
.shape-ride { offset-distance: calc(var(--shape) * 100%); offset-rotate: 0deg; }
/* a bar whose height is read off the position the toggle stands on */
.shape-bar { transform-box: fill-box; transform-origin: 50% 100%;
             transform: scaleY(var(--h, 1)); }

/* chapter 4: --probe is which retrieval probe the reader switched on,
   0 for none and 1, 2 or 3 for the three of them. A mark carries the
   probe it belongs to in --n, so the margin line and the probe's own
   row light from one declaration. */
.at-probe { opacity: calc(clamp(0, var(--probe) - var(--n) + 1, 1)
                        * clamp(0, var(--n) + 1 - var(--probe), 1)); }
/* the retrieval bar jiggles when a probe is switched on and settles back
   where the recording left it. talk.js takes the class off at the end of
   the run, so nothing on the plate is animating at rest. */
@keyframes probe-jiggle {
  0%   { transform: translateX(0); }
  22%  { transform: translateX(7px); }
  48%  { transform: translateX(-5px); }
  74%  { transform: translateX(2px); }
  100% { transform: translateX(0); }
}
.jiggle { animation: probe-jiggle .62s cubic-bezier(.3,.8,.4,1) both; }

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  font-size: var(--t-body);
  font-weight: 420;
  line-height: 1.66;
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
  /* laid-paper tooth: two hairline grids, cheap and static */
  background-image:
    repeating-linear-gradient(90deg, rgba(21,18,12,.028) 0 1px, transparent 1px 7px),
    repeating-linear-gradient(0deg, rgba(21,18,12,.018) 0 1px, transparent 1px 7px);
}

/* the wrap is measured out from the figure, not the other way round:
   240 rail + 440 prose + 56 gap + 900 stage + 80 right. 900 is the figure
   viewBox (720) × 1.25, the widest a plate may print. */
.wrap { max-width: 1716px; margin: 0 auto; padding-left: var(--rail); padding-right: 80px; }

/* ── chrome: progress hairline ───────────────────────────── */

.progress-rail { position: fixed; inset: 0 0 auto 0; height: 3px;
  background: rgba(21,18,12,.10); z-index: 40; }
.progress-ink { height: 100%; width: 100%; background: var(--sig);
  transform-origin: 0 50%; transform: scaleX(0); }
@supports (animation-timeline: scroll()) {
  html:not(.driver-js) .progress-ink { animation: grow linear both; animation-timeline: scroll(root block); }
}
@keyframes grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* ── chrome: numbered index + the archetype's four lamps ─── */

.index { position: fixed; left: 40px; top: 50%; transform: translateY(-50%);
  width: 172px; z-index: 30; font-family: var(--mono); font-size: var(--t-mono);
  letter-spacing: .02em; }
.index-head { display: block; color: var(--ink-2);
  border-bottom: 1px solid var(--ink-3); padding-bottom: 8px; margin-bottom: 14px; }
.index ol { list-style: none; margin: 0; padding: 0; }
.index li { margin: 0 0 10px; }
.index ol a { display: grid; grid-template-columns: 22px 1fr; gap: 6px;
  color: var(--ink-2); text-decoration: none; line-height: 1.45; }
.index ol a::before { content: attr(data-mark); color: var(--ink-2); }
.index ol a:hover { color: var(--ink); }
.index ol li.on a { color: var(--ink); text-decoration: underline;
  text-underline-offset: 4px; text-decoration-color: var(--sig); }
.index ol li.on a::before { color: var(--sig-text); }

.lamps { list-style: none; margin: 18px 0 0; padding: 14px 0 0;
  border-top: 1px solid var(--ink-3); opacity: .38; transition: opacity .4s; }
.index.in-ch .lamps { opacity: 1; }
.lamps li { display: grid; grid-template-columns: 9px 1fr; gap: 8px;
  align-items: center; margin: 0 0 9px;
  color: color-mix(in srgb, var(--ink-2), var(--ink) calc(var(--e) * 100%)); }
.lamps li::before { content: ""; width: 9px; height: 9px;
  background: color-mix(in srgb, transparent, var(--sig) calc(var(--el) * 100%));
  outline: 1px solid var(--ink-3); }

/* ── cover ───────────────────────────────────────────────── */

.cover { min-height: 100vh; display: flex; align-items: center;
  padding: 120px 0 80px; border-bottom: 2px solid var(--ink); }
.folio { font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .05em;
  color: var(--ink-2); margin: 0 0 40px; }
.display { font-family: var(--display); font-weight: 600;
  font-variation-settings: var(--soft-display); font-size: var(--t-display);
  line-height: .94; letter-spacing: -.018em; margin: 0; text-wrap: balance; }
.display em { font-style: italic; font-weight: 500; color: var(--sig-text); margin-right: .07em; }

/* the ornament is a closed measure: it draws once, then it is a printed
   rule. Nothing about it is still running when the cover is at rest. */
.cover-rule { display: block; width: 100%; height: 82px; margin: 34px 0 36px; }

.deck { font-family: var(--display); font-weight: 500; font-style: italic;
  font-variation-settings: var(--soft-large); font-size: clamp(23px, 2.3vw, 33px);
  line-height: 1.24; margin: 0 0 34px; max-width: 18ch; }
.course { font-family: var(--mono); font-size: 13px; letter-spacing: .04em;
  color: var(--ink-2); margin: 0; }
.dot { color: var(--sig-text); padding: 0 .3em; }
.scroll-cue { margin: 56px 0 0; font-family: var(--mono); font-size: var(--t-mono);
  letter-spacing: .06em; color: var(--ink-2); }
.cue-run::after { content: " ↓"; color: var(--sig-text); }
.cue-still { display: none; }
html.static .cue-run { display: none; }
html.static .cue-still { display: inline; }

/* ── full-bleed plate (chapter separator) ────────────────── */

.plate { position: relative; background: var(--ink); color: var(--paper);
  padding: 108px 0 116px; overflow: hidden; }
.plate-inner { position: relative; z-index: 2; }
.plate-folio { font-family: var(--mono); font-size: var(--t-mono);
  letter-spacing: .12em; text-transform: uppercase; color: var(--sig); margin: 0 0 22px; }
.plate-title { font-family: var(--display); font-weight: 600;
  font-variation-settings: var(--soft-large); font-size: var(--t-plate);
  line-height: .98; letter-spacing: -.014em; margin: 0 0 28px; color: var(--paper); }
.plate-claim { font-family: var(--serif); font-size: clamp(19px, 1.5vw, 23px);
  line-height: 1.55; max-width: 46ch; margin: 0;
  color: color-mix(in srgb, var(--paper) 78%, var(--ink)); }
.plate-hatch { position: absolute; left: 0; right: 0; bottom: 0; width: 100%;
  height: 160px; z-index: 1; opacity: 0; transform: translateY(46%); }
.plate-hatch line { stroke: var(--sig); stroke-width: 2; }
.plate.in .plate-hatch { transform: translateY(0); opacity: .85;
  transition: transform .9s cubic-bezier(.2,.7,.2,1), opacity .9s; }
@supports (animation-timeline: view()) {
  html:not(.driver-js) .plate-hatch { animation: hatch-rise linear both;
    animation-timeline: view(); animation-range: entry 10% cover 45%; }
}
@keyframes hatch-rise {
  from { transform: translateY(46%); opacity: 0; }
  to   { transform: translateY(0);   opacity: .85; }
}

/* ── chapter: prose column + sticky stage ────────────────── */

.chapter { padding: 96px 0 40px; }
/* the plate is the point of the page, so the stage track has a floor and
   the prose pays for the room before it does: 720px is the figure viewBox,
   so at the floor a plate prints at scale 1 and its smallest mono is a real
   12.5px on the wall. Prose gives way to 340px; the rail goes before either. */
.chapter-grid { display: grid; grid-template-columns: minmax(340px, 440px) minmax(720px, 1fr);
  gap: 0 56px; align-items: start; }
.chapter-head { font-family: var(--display); font-weight: 600;
  font-variation-settings: var(--soft-text); font-size: var(--t-head);
  line-height: 1.12; letter-spacing: -.012em; margin: 0 0 8px; padding-bottom: 18px;
  border-bottom: 2px solid var(--ink); }
.chapter-head .num { color: var(--sig-text); }

/* the track is tuned so the prose and the plate fill together: three
   steps of 62vh, so the figure is two-thirds inked by the second. */
.step { min-height: 62vh; display: flex; flex-direction: column; justify-content: center;
  border-bottom: 1px solid var(--paper-2); padding: 8px 0; opacity: .34;
  transition: opacity .35s ease; }
.step.on { opacity: 1; }
.step:last-child { border-bottom: 0; }
.step-mark { font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .06em;
  color: var(--sig-text); margin: 0 0 14px; }
.step .lead { font-family: var(--display); font-weight: 550;
  font-variation-settings: var(--soft-text); font-size: var(--t-lead);
  line-height: 1.32; letter-spacing: -.006em; margin: 0 0 18px; }
.step p { margin: 0 0 16px; max-width: 56ch; }
.step code { font-family: var(--mono); font-size: .88em; background: var(--paper-2);
  padding: 1px 5px; border-bottom: 1px solid var(--sig); }

/* 72px top and 72px bottom: the frame is centred in the window, so a plate
   taller than the box spills evenly instead of pushing its caption under
   the fold — which a full-width plate on a 720-tall projector will do. */
.stage { position: sticky; top: 72px; margin: 0; padding: 0;
  height: calc(100vh - 144px); display: flex; flex-direction: column; justify-content: center; }
.stage-inner { width: 100%; }
.stage svg { display: block; width: 100%; height: auto; max-height: calc(100vh - 208px); }
.stage figcaption { font-family: var(--mono); font-size: var(--t-mono);
  letter-spacing: .02em; line-height: 1.45; color: var(--ink-2); margin-top: 16px;
  padding-top: 10px; border-top: 1px solid var(--paper-2); }
.cap-mark { color: var(--sig-text); letter-spacing: .04em; margin-right: 10px; }

/* ── out ─────────────────────────────────────────────────── */

.out { padding: 120px 0 96px; }
.out-ask { max-width: 52ch; margin: 34px 0 0; }
.out-ask + .out-ask { margin-top: 14px; }
.out-line { font-family: var(--display); font-weight: 500; font-style: italic;
  font-variation-settings: var(--soft-large); font-size: clamp(21px, 2.1vw, 30px);
  line-height: 1.32; max-width: 24ch; margin: 0 0 44px; }
.out-link { display: inline-grid; grid-template-columns: auto auto; align-items: end;
  gap: 0 18px; text-decoration: none; color: var(--ink);
  border-bottom: 3px solid var(--sig); padding-bottom: 12px; }
.out-link-label { grid-column: 1; font-family: var(--mono); font-size: 13px;
  letter-spacing: .04em; color: var(--ink-2); padding-bottom: 10px; }
.out-link-slug { grid-column: 1; font-family: var(--display); font-weight: 600;
  font-variation-settings: var(--soft-large); font-size: clamp(34px, 4.6vw, 72px);
  line-height: 1; letter-spacing: -.018em; }
.out-arrow { grid-column: 2; grid-row: 1 / span 2; width: clamp(34px, 4vw, 58px);
  height: auto; color: var(--sig); transition: transform .3s cubic-bezier(.2,.7,.2,1); }
.out-link:hover .out-arrow { transform: translate(6px, -6px); }
.out-link:hover .out-link-slug { color: var(--sig-text); }
.colophon { font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .02em;
  color: var(--ink-2); margin: 60px 0 0; padding-top: 16px; border-top: 1px solid var(--paper-2); }

/* ── engraving: the marks every figure is made of ────────── */

.f-mono   { font-family: var(--mono); font-size: 15px; letter-spacing: .02em; fill: var(--ink-2); }
.f-strong { fill: var(--ink); letter-spacing: .05em; }
.f-muted  { fill: var(--ink-2); font-size: 13.5px; letter-spacing: .03em; }
.f-tiny   { font-family: var(--mono); font-size: 12.5px; letter-spacing: .09em;
            text-transform: uppercase; fill: var(--ink-2); }
.f-sig    { fill: var(--sig-text); }
.f-code   { font-family: var(--mono); font-size: 13px; letter-spacing: .01em; fill: var(--ink); }
/* the second ink on a code line: .f-code sets fill, so this has to follow it */
.f-dim    { fill: var(--ink-2); }
.f-refuse { fill: var(--refuse); }
.f-serif  { font-family: var(--display); font-style: italic; font-weight: 500;
            font-variation-settings: var(--soft-text); font-size: 23px; fill: var(--ink); }

.f-rule      { stroke: var(--ink-3); stroke-width: 1; fill: none; }
.f-rule-sig  { stroke: var(--sig); stroke-width: 1.6; fill: none; }
.f-draw      { fill: none; stroke: var(--ink); stroke-width: 1.6; stroke-linecap: square; }
.f-ring      { stroke: var(--ink); stroke-width: 1.4; }
.f-plate     { stroke-width: 2.6; }
.f-station   { stroke-width: 1.4; }
.f-fill      { fill: var(--paper); }
.f-cartouche { stroke-width: 2.2; }
.f-thin      { stroke-width: 1.2; }
.f-lead      { stroke: var(--ink-2); stroke-width: 1.4; }
.f-lead-sig  { stroke: var(--sig); stroke-width: 2; }
.f-spine     { stroke: var(--sig); stroke-width: 2; }
.f-node      { fill: var(--sig); }
.f-refuse-s  { stroke: var(--refuse); stroke-width: 1.6; fill: none; }

/* the key plate: the whole vocabulary as legible underdrawing from the
   first frame, so no frame is ever an empty box. Ink draws over it. */
.f-key { opacity: .14; }

/* marks the five chapter figures add to the vocabulary */
.f-dot      { fill: var(--sig); stroke: var(--ink); stroke-width: 1.4; }
.f-paper    { fill: var(--paper-2); }
.f-arrow    { stroke: var(--ink-2); stroke-width: 1.4; fill: none; }
.f-arrow-sig{ stroke: var(--sig); stroke-width: 1.8; fill: none; }
/* chapter 1: one file of the model, before anything has typed it */
.f-file     { fill: var(--paper); stroke: var(--ink-2); stroke-width: 1.2; }
/* a typed bucket, and the tally marks that land in it */
.f-bucket   { fill: none; stroke: var(--ink); stroke-width: 1.8; }
.f-tally    { stroke: var(--ink-2); stroke-width: 1.2; }
.f-tally-sig{ stroke: var(--sig); stroke-width: 1.4; }
/* chapter 2's pipeline strip: one stage of it, dark once it has run */
.f-stage    { fill: var(--paper); stroke: var(--ink); stroke-width: 1.6; }
.f-stage-lit{ fill: var(--paper-2); stroke: var(--sig); stroke-width: 2.2; }
/* chapter 3's needle, and the rail it travels */
.f-needle   { fill: var(--sig); stroke: var(--ink); stroke-width: 1.4; }
.f-rail     { stroke: var(--ink); stroke-width: 1.6; fill: none; }
/* chapter 5's marker, walking the graph one call at a time */
.f-mark     { fill: var(--sig); stroke: var(--ink); stroke-width: 1.6; }
/* the graph in chapters 2 and 5: typed elements and the links between them */
.f-edge     { stroke: var(--ink-2); stroke-width: 1.4; fill: none; }
.f-edge-lit { stroke: var(--sig); stroke-width: 2.2; fill: none; }
.f-vertex   { fill: var(--paper); stroke: var(--ink); stroke-width: 1.8; }
.f-vertex-lit { fill: var(--paper); stroke: var(--sig); stroke-width: 2.4; }
/* relighting a box already on the plate: stroke only, so the word under it
   is never covered by a second fill */
.f-vertex-hot { fill: none; stroke: var(--sig); stroke-width: 2.4; }
/* the bars in chapters 3 and 4: correctness in ink, cost in hatch */
/* the plate supplies the hatch as its own pattern, inline, per plate */
.f-bar      { stroke: var(--ink); stroke-width: 1.2; }
.f-bar-sig  { fill: var(--sig); stroke: var(--sig); stroke-width: 1.2; }
.f-bar-cost { fill: none; stroke: var(--ink-2); stroke-width: 1.4;
              stroke-dasharray: 3 3; }
/* a frozen card: a result recorded elsewhere, quoted here whole */
.f-card     { fill: var(--paper-2); stroke: var(--ink); stroke-width: 2; }
.f-rung     { fill: var(--paper); stroke: var(--ink-2); stroke-width: 1.4; }
.f-rung-lit { fill: var(--paper-2); stroke: var(--sig); stroke-width: 2.2; }
.f-thr      { stroke: var(--sig); stroke-width: 2.4; stroke-dasharray: 6 5; fill: none; }
/* the instrument readouts in chapters 3, 4 and 5: one number, read big */
.f-read     { font-family: var(--mono); font-size: 30px; letter-spacing: .01em;
              fill: var(--ink); }

/* a margin note beside the prose: the one sourced number lives here */
.margin { font-family: var(--mono); font-size: 12.5px; line-height: 1.5;
  letter-spacing: .02em; color: var(--ink-2); max-width: 44ch;
  border-left: 2px solid var(--sig); padding: 2px 0 2px 14px; margin: 0 0 16px; }

/* the controls a figure hands the reader, ruled under its plate */
.fig-controls { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--paper-2); }
.fig-row { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.fig-btn { font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .06em;
  text-transform: uppercase; background: var(--paper); color: var(--ink);
  border: 1.5px solid var(--ink); padding: 9px 14px; cursor: pointer; }
.fig-btn:hover { background: var(--ink); color: var(--paper); }
.fig-btn[aria-pressed="true"] { background: var(--sig); border-color: var(--sig);
  color: var(--paper); }
.fig-btn[disabled] { color: var(--ink-3); border-color: var(--ink-3); cursor: default; }
.fig-btn[disabled]:hover { background: var(--paper); color: var(--ink-3); }
.fig-note { font-family: var(--mono); font-size: var(--t-mono); color: var(--ink-2);
  letter-spacing: .02em; margin: 10px 0 0; }

/* the index reverses out while an ink plate passes behind it */
.index.on-dark .index-head,
.index.on-dark .lamps { color: color-mix(in srgb, var(--paper) 55%, transparent);
  border-color: color-mix(in srgb, var(--paper) 35%, transparent); }
.index.on-dark ol a { color: color-mix(in srgb, var(--paper) 55%, transparent); }
.index.on-dark ol a:hover,
.index.on-dark ol li.on a { color: var(--paper); }
.index.on-dark .lamps li { color: color-mix(in srgb, var(--paper) 55%, var(--paper) calc(var(--e) * 45%)); }

/* ── responsive: the two floors decide the breakpoints ─────
   Rail + prose floor + gap + stage floor + right padding is
   240 + 340 + 56 + 720 + 80 = 1436, so below that the rail cannot stay.
   Without it, 40 + 340 + 56 + 720 + 40 = 1196, below which the stage
   floor cannot hold either. Single column and static at 860, unchanged. */

@media (max-width: 1435px) {
  :root { --rail: 40px; }
  .index { display: none; }
  .wrap { padding-right: 40px; }
}
/* the stage floor has to release here or the grid would overflow; tighten
   the gap so the plate keeps as much of the width as there is left */
@media (max-width: 1195px) {
  .chapter-grid { grid-template-columns: minmax(0, 340px) minmax(0, 1fr); gap: 0 32px; }

}
@media (max-width: 860px) {
  .chapter-grid { grid-template-columns: minmax(0,1fr); gap: 40px; }
  .stage { position: static; height: auto; margin-top: 32px; }
  .stage svg { max-height: none; }
  .step { min-height: 0; opacity: 1; padding: 22px 0; }
  .cover { min-height: auto; padding: 80px 0 60px; }
}

/* ── the static contract ─────────────────────────────────────
   One treatment, reached three ways: narrow, reduced motion, or the
   ?motion=reduce test flag. talk.js sets html.static for all three.
   The media block below repeats it verbatim so the contract holds
   with JavaScript off; keep the two in step.                      */

html.static { scroll-behavior: auto; }
/* the reader's-hand figures are pinned at their finished reading too */
html.static body { --shape: 1; --probe: 0; }
html.static .stage { position: static; height: auto; }
html.static .stage svg { max-height: none; }
html.static .step { min-height: 0; opacity: 1; }
html.static .index { display: none; }
html.static .progress-rail { display: none; }
html.static .progress-ink,
html.static .plate-hatch { animation: none !important; transition: none !important; }
html.static .plate-hatch { transform: none; opacity: .85; }
html.static .cover { min-height: auto; }
html.static .cover-rule line { stroke-dashoffset: 0 !important; }
html.static .out-arrow { transition: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  body { animation: none !important;
    --fig1: 1; --fig2: 1; --fig3: 1; --fig4: 1; --fig5: 1;
    --shape: 1; --probe: 0; }
  .steps { view-timeline-name: none; }
  .stage { position: static; height: auto; }
  .stage svg { max-height: none; }
  .step { min-height: 0; opacity: 1; }
  .index { display: none; }
  .progress-rail { display: none; }
  .progress-ink, .plate-hatch { animation: none !important; transition: none !important; }
  .plate-hatch { transform: none; opacity: .85; }
  .cover { min-height: auto; }
  .cover-rule line { stroke-dashoffset: 0 !important; }
  .cue-run { display: none; }
  .cue-still { display: inline; }
  .out-arrow { transition: none; }
}

/* chapter 1: a file card flying from the pile into the stack. --dx/--dy
   are where it started and --rot how far over it lay; at the finish every
   one of them is 0, which is the stack. */
.file-fly { transform-box: fill-box; transform-origin: 50% 50%;
  transform: translate(calc((1 - var(--e)) * var(--dx, 0px)),
                       calc((1 - var(--e)) * var(--dy, 0px)))
             rotate(calc((1 - var(--e)) * var(--rot, 0deg))); }

/* chapter 3's needle travels rather than jumps: the transition is on the
   registered property itself, so the one declaration in .shape-ride still
   describes where the needle is. It ends; nothing runs at rest. */
#mount-3 { transition: --shape .45s cubic-bezier(.3, .8, .4, 1); }
