/* =====================================================================
   GroundTruth — "specimen sheet"

   Structure: a margin rail carries the apparatus (name, nav, colophon);
   everything else is one continuous stream of specimens on a 6-column
   grid. No header bar, no hero split, no footer bar.

   Type: Space Grotesk does all the talking. Space Mono is restricted to
   DATA — plate numbers, years, the training readout — which is the only
   place monospacing earns anything. Fraunces sets the name and nothing
   else; Instrument Serif italic is the emphasis voice.
   ===================================================================== */

:root {
  /* ink + paper -------------------------------------------------- */
  --paper:      #FFFFFF;
  --ink:        #111111;
  --ink-soft:   #565656;
  --ink-faint:  #8C8C8C;
  --rule:       #111111;
  --rule-soft:  #DADADA;
  --grid:       rgba(17, 17, 17, 0.045);
  --rule-faint: #C4C4C4;

  /* surfaces that sit just off the page ground */
  --wash:       #F2F2F2;   /* inline code */
  --wash-2:     #F1F1EF;   /* bar tracks, spent chips */
  --wash-3:     #FAFAF8;   /* the baseline row of a results table */
  --wash-hover: #F6F6F3;
  --bar-track:  #EDEDED;   /* the groove a score bar sits in */
  --bar-idle:   #C8C8C8;   /* that bar while another one is selected */

  /* the blocks that are deliberately inverted — colophon, cta, pre, the
     readout, a live tab. These swap direction with the theme rather than
     following ink and paper, or they would come out white-on-white. */
  --invert-bg:   #111111;
  --invert-ink:  #FFFFFF;
  --invert-soft: #C9C9C9;
  --invert-rule: #555555;
  --invert-code: #F2F2F2;  /* code inside a <pre>: off-white in both themes */

  /* Neuron-sensitivity diagram: the wash behind a highlighted row or column.
     These carry cell numbers, which are --ink, so unlike the rest of the
     palette they have to invert — a pale wash keeps its hue but goes deep
     instead of light, or the white numerals sit on near-white. */
  --nrn-gate:      #DDE4FF;
  --nrn-up:        #FFDCEC;
  --nrn-down:      #FFE3D2;
  --nrn-prod:      #D6FF3D;
  --nrn-prod-edge: #111111;

  /* text sitting on a bright fill — the lime, the tints. Those fills are the
     same in both themes, so this never flips either. */
  --on-bright:  #111111;

  color-scheme: light;

  /* the loud bits — full-chroma, for fills that have to carry a series
     on their own. Neighbours on the wheel fight when they sit side by
     side in one chart; pick across the list, not along it. --------- */
  --blue:    #0B34FF;
  --cyan:    #00A9E0;
  --teal:    #009B8E;
  --green:   #3CC575;
  --lime:    #D6FF3D;   /* highlighter only — black text on top */
  --yellow:  #FFD400;
  --amber:   #FFB200;
  --orange:  #FF5A00;
  --red:     #D02525;
  --pink:    #FF2D8F;
  --magenta: #C4009B;
  --violet:  #7A2BFF;
  --indigo:  #4B2ED6;

  /* the quiet bits — same hues taken down, for the second and third
     bar in a group, for rules and for anything that should sit behind
     rather than compete. ------------------------------------------ */
  --navy:    #12246B;
  --slate:   #3D4A57;
  --gray:    #5D6F7B;
  --sage:    #7E9B76;
  --forest:  #0A7A45;
  --olive:   #8C8A3D;
  --rust:    #B33A00;
  --maroon:  #8A1538;
  --plum:    #EA9FCC;
  --mauve:   #9B7BA8;

  /* tints — pale enough to hold black text on top, for washes, cell
     backgrounds and the low end of a ramp. ------------------------ */
  --sky:     #A8C6FF;
  --mint:    #B9F0D6;
  --sand:    #E8DCC8;
  --peach:   #FFC9A3;
  --blush:   #FFD3E4;
  --lilac:   #C9B6FF;

  /* per-component accent; overridden per block and per card */
  --c: var(--blue);

  --font-ui:       "Space Grotesk", "Helvetica Neue", Arial, sans-serif;
  --font-data:     "Space Mono", ui-monospace, Menlo, monospace;
  --font-wordmark: "Fraunces", Georgia, serif;          /* the name, and only the name */
  --font-emphasis: "Instrument Serif", Georgia, serif;  /* italic emphasis */

  --rail: 240px;
  --gutter: 50px;
  --bw: 1.5px;          /* the one border width used everywhere */
}

/* Dark: ink and paper trade places and the neutrals move with them. Nothing
   else does — every palette colour, and anything sitting on one, is the same
   in both themes. */
:root[data-theme="dark"] {
  --paper:      #121212;
  --ink:        #F2F2F2;
  --ink-soft:   #A8A8A8;
  --ink-faint:  #7A7A7A;
  --rule:       #F2F2F2;
  --rule-soft:  #3A3A3A;
  --rule-faint: #4A4A4A;
  --grid:       rgba(255, 255, 255, 0.05);

  --wash:       #1E1E1E;
  --wash-2:     #1E1E1E;
  --wash-3:     #191919;
  --wash-hover: #232323;
  --bar-track:  #2A2A2A;
  --bar-idle:   #5A5A5A;

  /* the inverted blocks lift off the page here instead of dropping below it */
  --invert-bg:   #262626;
  --invert-ink:  #F2F2F2;
  --invert-soft: #A8A8A8;
  --invert-rule: #4A4A4A;

  /* the same four hues taken down rather than up, so the row/column each
     step lights up still reads as blue / pink / orange / lime */
  --nrn-gate:      #1B2450;
  --nrn-up:        #4A1B30;
  --nrn-down:      #4A2616;
  --nrn-prod:      #38430E;
  /* the lime outline the dark olive needs, where the light theme uses ink */
  --nrn-prod-edge: #D6FF3D;

  color-scheme: dark;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

body {
  margin: 0;
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  background-color: var(--paper);
  background-image:
    linear-gradient(to right,  var(--grid) 1px, transparent 1px),
    linear-gradient(to bottom, var(--grid) 1px, transparent 1px);
  background-size: 28px 28px;
}

a { color: inherit; text-decoration: none; }
::selection { background: var(--lime); color: var(--on-bright); }
:focus-visible { outline: 2.5px solid var(--blue); outline-offset: 3px; }

h1, h2, h3 {
  margin: 0;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.05;
}

/* Space Grotesk has no true italic, so emphasis borrows the display serif
   rather than letting the browser fake a slant. */
em {
  font-family: var(--font-emphasis);
  font-style: italic;
  font-weight: 400;
  font-size: 1.06em;
}

/* the only monospace on the site: things that are numbers */
.num {
  font-family: var(--font-data);
  font-weight: 700;
  font-feature-settings: "tnum" 1;
  letter-spacing: -0.02em;
}

/* Highlighter pen. The 3px bleed is padding rather than a spread shadow: a
   shadow paints behind the background, and the seam where the two meet let a
   hairline of the page through — invisible on white, a grey box hugging the
   text on a dark ground. The negative margin cancels the horizontal padding,
   so the words either side do not move; vertical padding never affects an
   inline line box, so nothing reflows. */
.hl {
  background: var(--lime);
  color: var(--on-bright);
  padding: 3px;
  margin: 0 -3px;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

/* the wordmark ------------------------------------------------------ */
.wordmark {
  font-family: var(--font-wordmark);
  /* low opsz keeps the widest, softest cut of Fraunces; WONK on for the
     canted letterforms that make the name read as a mark. */
  font-variation-settings: "opsz" 24, "SOFT" 40, "WONK" 1;
  font-weight: 600;
  letter-spacing: 0;
  font-synthesis: none;
}
p .wordmark { font-size: 1.08em; line-height: 1; }

/* =====================================================================
   Shell: margin rail + specimen stream
   ===================================================================== */
.shell {
  max-width: 1280px;
  margin: 0 auto;
  padding: 40px 34px 96px;
  display: grid;
  grid-template-columns: var(--rail) 1fr;
  gap: var(--gutter);
  align-items: stretch;
}

/* ---------------------------------------------------------------------
   The rail
--------------------------------------------------------------------- */
.rail {
  border-right: var(--bw) solid var(--rule-soft);
  padding-right: 26px;
}

/* The whole margin travels as one block rather than the name sliding down
   over the note beneath it. Nothing follows it in the rail, so nothing can
   be overlapped. When the viewport is too short to hold the rail, it scrolls
   inside itself rather than giving up and scrolling away with the page —
   which is what a plain max-height guard did, and it tripped on any window
   with a couple of browser toolbars in it. */
.rail-inner {
  position: sticky;
  top: 40px;
  max-height: calc(100vh - 80px);
  overflow-y: auto;
  overscroll-behavior: contain;
  /* overflow-y makes this a scroll container, and a scroll container clips
     both axes — which would shear the left edge off the nav and TOC hovers,
     since those hang 6px outside the rail so their text still lines up with
     the wordmark. The margin/padding pair widens the padding box to cover the
     overhang and puts the contents straight back where they were.

     The right side needs the same treatment for a different reason: the
     highlighter pen bleeds 3px past its text, and with nothing to catch it
     that lands outside the content box and puts a two-pixel scrollbar in the
     margin. Both pairs cancel out, so the column is the width it always was. */
  margin-left: -6px;
  padding-left: 6px;
  margin-right: -6px;
  padding-right: 6px;
}

.mark { display: flex; align-items: center; gap: 11px; }
.mark-glyph { display: block; flex: none; }
.mark-text {
  font-family: var(--font-wordmark);
  font-variation-settings: "opsz" 24, "SOFT" 40, "WONK" 1;
  font-size: 27px;
  font-weight: 600;
  line-height: 1;
}

.rail-nav {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  margin-top: 22px;
}
.rail-nav a {
  font-size: 15px;
  font-weight: 500;
  padding: 2px 6px;
  margin-left: -6px;
  transition: background 120ms ease;
}
.rail-nav a:hover { background: var(--lime); color: var(--on-bright); }

.rail-note {
  margin: 40px 0 0;
  font-size: 14.5px;
  line-height: 1.62;
  color: var(--ink-soft);
}
.rail-note .wordmark { color: var(--ink); }

.rail-meta {
  margin: 30px 0 0;
  padding-top: 18px;
  border-top: var(--bw) solid var(--rule-soft);
  display: grid;
  gap: 7px;
  font-size: 13.5px;
}
.rail-meta > div { display: flex; justify-content: space-between; gap: 12px; }
.rail-meta dt { color: var(--ink-faint); }
.rail-meta dd { margin: 0; }

.rail-links {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  margin-top: 26px;
  font-size: 13.5px;
}
.rail-links a { border-bottom: var(--bw) solid var(--rule-soft); padding-bottom: 1px; }
.rail-links a:nth-child(1):hover { color: var(--blue);   border-color: var(--blue); }
.rail-links a:nth-child(2):hover { color: var(--pink);   border-color: var(--pink); }
.rail-links a:nth-child(3):hover { color: var(--violet); border-color: var(--violet); }

/* Light/dark, in the margin with the rest of the apparatus. The key is printed
   on it because the shortcut is the way it is actually meant to be used. */
/* Light/dark. This is the site's own switch, so the one control out in the
   margin is the same object as the switches inside the plates. Sun and moon
   sit either side the way .switch-row labels always do, and the key is
   printed beside it because the shortcut is how it is meant to be used. */
.theme-toggle-row {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-top: 26px;
  padding-top: 14px;
  border-top: var(--bw) solid var(--rule-soft);
}
/* the sides here are glyphs rather than words, and need the size back */
.theme-toggle-row .switch-side { font-size: 14px; line-height: 1; }
.theme-toggle-key {
  font-family: var(--font-data);
  font-size: 11px;
  font-weight: 700;
  padding: 1px 5px;
  border: var(--bw) solid var(--rule-soft);
  color: var(--ink-faint);
}

/* ---------------------------------------------------------------------
   The stream
--------------------------------------------------------------------- */
.stream {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  column-gap: 26px;
  row-gap: 56px;
  align-content: start;
}

.statement {
  grid-column: span 6;
  font-size: clamp(38px, 5vw, 62px);
  padding-top: 4px;
}

/* =====================================================================
   Atoms: the framed box and its tag
   ===================================================================== */
.frame {
  position: relative;
  background: var(--paper);
  border: var(--bw) solid var(--rule);
  padding: 32px 26px 26px;
}
.frame-tag {
  position: absolute;
  top: 0;
  left: 22px;
  /* the tag straddles the top rule, so it can never be wider than the box it
     is labelling: a long caption wraps onto a second line instead of running
     off the side of the frame and, on a phone, off the page. It is centred on
     the rule whatever its height, so a two- or three-line tag still hangs less
     far into the box than the 32px of top padding waiting for it. */
  max-width: calc(100% - 44px);
  transform: translateY(-50%);
  background: var(--c);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.35;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 4px 9px 3px;
}
.frame-tag .num { font-size: 11px; letter-spacing: 0; }

/* tag chips: lowercase, no tracking — labels, not stamps */
.tag {
  display: inline-block;
  font-size: 12.5px;
  font-weight: 500;
  line-height: 1.35;
  padding: 2px 8px;
  border: var(--bw) solid var(--rule);
  background: var(--paper);
  white-space: nowrap;
}
.tag-lime { background: var(--lime); color: var(--on-bright); font-weight: 600; }

/* button */
.cta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  padding: 13px 20px;
  border: var(--bw) solid var(--invert-bg);
  background: var(--invert-bg);
  color: var(--invert-ink);
  transition: transform 120ms ease, box-shadow 120ms ease, background 120ms ease;
}
.cta:hover {
  background: var(--blue);
  border-color: var(--blue);
  transform: translate(-3px, -3px);
  box-shadow: 5px 5px 0 var(--ink);
}
.cta:active { transform: none; box-shadow: none; }

/* ---------------------------------------------------------------------
   Plate 00 — the live demo, first specimen in the sequence
--------------------------------------------------------------------- */
.plate-demo {
  --c: var(--pink);
  grid-column: span 4;
  padding: 34px 22px 20px;
}
#demo-svg,
.plate-body > img,
.plate-body > svg,
.plate-body > canvas {
  display: block;
  width: 100%;
  height: auto;
  border: var(--bw) solid var(--rule-soft);
  background-color: var(--paper);
  background-image:
    linear-gradient(to right,  var(--grid) 1px, transparent 1px),
    linear-gradient(to bottom, var(--grid) 1px, transparent 1px);
  background-size: 20px 20px;
}
/* [plain] figures keep the plate frame but lose the graph paper and the
   faint inner rule around the drawing itself */
.plate-plain .plate-body > img,
.plate-plain .plate-body > svg,
.plate-plain .plate-body > canvas {
  background-image: none;
  border: 0;
}
#demo-dots circle { transition: fill 200ms ease; }
#demo-links line { stroke: var(--rule-faint); stroke-width: 1; }

.demo-controls {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 14px;
  margin-top: 18px;
}
.demo-controls label { font-size: 13px; font-weight: 500; color: var(--ink-soft); }
.demo-readout {
  font-size: 12.5px;
  background: var(--invert-bg);
  color: var(--invert-ink);
  padding: 3px 8px;
  min-width: 54px;
  text-align: center;
}

/* the annotation beside the plate */
.plate-note {
  grid-column: span 2;
  align-self: end;
  padding-bottom: 4px;
}
.plate-note p {
  margin: 0 0 18px;
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--ink-soft);
}
.jump {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--pink);
  border-bottom: var(--bw) solid var(--pink);
  padding-bottom: 2px;
}
.jump:hover { background: var(--lime); color: var(--on-bright); border-color: var(--on-bright); }

/* slider, restyled to match the hard-edged system */
input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 22px;
  background: transparent;
  cursor: pointer;
}
input[type="range"]::-webkit-slider-runnable-track { height: 3px; background: var(--ink); }
input[type="range"]::-moz-range-track { height: 3px; background: var(--ink); }
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  margin-top: -6.5px;
  background: var(--pink);
  border: var(--bw) solid var(--ink);
  border-radius: 0;
}
input[type="range"]::-moz-range-thumb {
  width: 16px;
  height: 16px;
  background: var(--pink);
  border: var(--bw) solid var(--ink);
  border-radius: 0;
}

/* ---------------------------------------------------------------------
   Index header + card rhythm
--------------------------------------------------------------------- */
.stream-head {
  grid-column: span 6;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  column-gap: 26px;
  align-items: end;
  border-top: var(--bw) solid var(--rule);
  padding-top: 20px;
}
.stream-head h2 { grid-column: span 2; font-size: clamp(26px, 3vw, 34px); }
.stream-sub {
  grid-column: 3 / span 4;
  margin: 0;
  color: var(--ink-soft);
  font-size: 15px;
}

.papers-grid {
  grid-column: span 6;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 26px;
}

/* Wide/narrow alternation: [wide][narrow] then [narrow][wide]. The index
   gets texture instead of reading like a spreadsheet. */
.paper-card {
  grid-column: span 4;
  display: flex;
  flex-direction: column;
  min-height: 250px;
  padding: 36px 24px 22px;
  transition: transform 130ms ease, box-shadow 130ms ease;
}
.paper-card:nth-child(4n+2),
.paper-card:nth-child(4n+3) { grid-column: span 2; }

.paper-card:hover {
  transform: translate(-4px, -4px);
  box-shadow: 6px 6px 0 var(--c);
}

.plate-no {
  position: absolute;
  top: 13px;
  right: 16px;
  font-size: 12px;
  color: var(--ink-faint);
}

.paper-title { font-size: 24px; line-height: 1.18; margin: 6px 0 5px; }
.paper-authors { font-size: 16px; line-height: 1.18; margin: 1px 0 16px; color: var(--ink-soft); max-width: 46ch; }
/* wide cards get the full list; the narrow-card override is below */
.paper-authors .authors-short { display: none; }
.paper-summary {
  color: var(--ink-soft);
  font-size: 15px;
  margin: 0 0 22px;
  max-width: 46ch;
}

/* the narrow cards are index entries: tighter title, clipped summary */
.paper-card:nth-child(4n+2) .paper-title,
.paper-card:nth-child(4n+3) .paper-title { font-size: 19px; }
.paper-card:nth-child(4n+2) .paper-authors .authors-full,
.paper-card:nth-child(4n+3) .paper-authors .authors-full { display: none; }
.paper-card:nth-child(4n+2) .paper-authors .authors-short,
.paper-card:nth-child(4n+3) .paper-authors .authors-short { display: inline; }

.paper-card:nth-child(4n+2) .paper-summary,
.paper-card:nth-child(4n+3) .paper-summary {
  font-size: 14px;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.paper-footer {
  margin-top: auto;
  padding-top: 14px;
  border-top: var(--bw) solid var(--rule-soft);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.paper-tags { display: flex; gap: 6px; flex-wrap: wrap; }

.paper-arrow {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--c);
  white-space: nowrap;
}
.paper-arrow svg { transition: transform 130ms ease; }
.paper-card:hover .paper-arrow svg { transform: translateX(3px); }

/* ---------------------------------------------------------------------
   Colophon — the one inverted block, closing the stream
--------------------------------------------------------------------- */
.colophon {
  grid-column: span 6;
  background: var(--invert-bg);
  color: var(--invert-ink);
  padding: 26px 28px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
}
.colophon p { margin: 0; font-size: 15px; color: var(--invert-soft); max-width: 52ch; }
.colophon a {
  font-size: 13.5px;
  font-weight: 600;
  border-bottom: var(--bw) solid var(--invert-rule);
  padding-bottom: 2px;
  white-space: nowrap;
}
.colophon a:hover { color: var(--lime); border-color: var(--lime); }

/* ---------------------------------------------------------------------
   Document pages (paper detail, 404)
--------------------------------------------------------------------- */
.doc-head {
  grid-column: span 6;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  border-bottom: var(--bw) solid var(--rule-soft);
  padding-bottom: 14px;
}
.doc-venue { font-size: 13.5px; color: var(--ink-soft); }

.back-link {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-soft);
  padding-bottom: 2px;
  border-bottom: var(--bw) solid transparent;
}
.back-link:hover { color: var(--blue); border-color: var(--blue); }

.doc-title { grid-column: span 6; max-width: 22ch; }

.doc-authors {
  grid-column: span 5;
  margin: 0;
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink-soft);
  max-width: 62ch;
}
.doc-title + .doc-authors { margin-top: -34px; }
.doc-authors + .doc-lede { margin-top: -30px; }

/* The paper itself, when the content file gives a `link:` — a paperclip at
   the end of the title rather than a line of its own. It is a reference to
   follow later, not the thing the page is asking you to do, so it takes an
   icon's worth of room and no more. Sized in em, so it tracks the title
   through its clamp instead of needing a breakpoint of its own. */
.title-link {
  position: relative;
  display: inline-block;
  margin-left: 0.2em;
  line-height: 0;
  color: var(--blue);
}
/* the icon is deliberately small, which is no reason for the target to be:
   this grows the tap area to something a thumb can hit without changing the
   layout or the size of the mark itself */
.title-link::after {
  content: "";
  position: absolute;
  inset: -0.3em;
}
.title-link svg {
  width: 0.4em;
  height: 0.4em;
  vertical-align: baseline;
}
.title-link:hover { color: var(--ink); }

.doc-lede { grid-column: span 4; }
.doc-lede p { margin: 0; font-size: 18px; color: var(--ink-soft); max-width: 50ch; }
.tag-row { display: flex; gap: 7px; flex-wrap: wrap; margin-top: 24px; }

.placeholder-block { --c: var(--orange); grid-column: span 5; }
.placeholder-block p { margin: 0; color: var(--ink-soft); max-width: 60ch; }

/* the stream's rows are generous; documents want less air near the top */
.doc-head + .doc-title { margin-top: -22px; }
.doc-title + .doc-lede { margin-top: -26px; }

/* ---------------------------------------------------------------------
   Responsive
--------------------------------------------------------------------- */
@media (max-width: 1040px) {
  .shell {
    grid-template-columns: 1fr;
    --stack-gap: 44px;
    gap: var(--stack-gap);
    padding-top: 30px;
  }

  /* Stacked, the document head is a band between the rail's rule and its
     own. The space above it is the shell's gap and the space below is its
     own padding — 44px against 14px, which reads as the tags hanging off
     the rule above rather than sitting between the two. Pulled back up
     against that rule, it carries the same inset on both sides instead.
     The gap is taken from the shell rather than written out again, so the
     two cannot drift apart. */
  .doc-head {
    margin-top: calc(-1 * var(--stack-gap));
    padding-top: 16px;
    padding-bottom: 16px;
  }
  .rail {
    border-right: 0;
    border-bottom: var(--bw) solid var(--rule-soft);
    padding-right: 0;
    --rail-foot: 30px;
    padding-bottom: var(--rail-foot);
  }
  /* a grid row is sized by its widest child's minimum, so anything that
     refuses to get narrower — the two-column contents list, a wide figure —
     would otherwise stretch the whole page past the viewport rather than
     shrink. Below the stacked breakpoint the shell is one column and nothing
     in it is allowed that veto. */
  .rail,
  .stream { min-width: 0; }
  /* stacked: the rail is a band above the stream, so it neither sticks nor
     needs a height of its own */
  .rail-inner {
    position: static;
    max-height: none;
    overflow-y: visible;
    /* Two columns only so the theme toggle can share the wordmark's line.
       Everything else spans both, so every rule in the rail keeps the full
       width it had — narrowing them is what makes a rule stop lining up with
       the text under it. */
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: 16px;
    align-content: start;
  }
  .rail-inner > * { grid-column: 1 / -1; }
  .rail-inner > .rail-top { grid-column: 1; }
  .rail-note { max-width: 62ch; }

  /* Stacked, the meta rows sit directly above the tag band and share its
     edges, so the 340px cap that keeps the pair legible beside a full-width
     stream only left their rule short of every other rule on the page and
     the values they right-align inside the column they are read against. */
  .rail-meta { max-width: none; }

  /* When nothing follows them in the rail they are a band between two rules
     — their own above, the rail's below — and belong in the middle of it.
     The foot is read off the rail rather than written out a second time, so
     the two cannot drift apart. The index has a links row after them, so
     they are not the last child there and this leaves them alone. */
  .rail-meta:last-child { padding-top: var(--rail-foot); }

  /* Stacked, the rail is a band across the top of the page and the room
     beside the wordmark is where a theme control gets looked for. It is
     placed into that row rather than taken out of flow, so it still occupies
     space and cannot come to sit on top of anything. */
  .rail-inner > .theme-toggle-row {
    grid-column: 2;
    grid-row: 1;
    align-self: start;
    margin: 0;
    padding: 0;
    border-top: 0;
  }

  .plate-demo { grid-column: span 6; }
  .plate-note { grid-column: span 6; align-self: start; }
  .paper-card,
  .paper-card:nth-child(4n+2),
  .paper-card:nth-child(4n+3) { grid-column: span 3; }
  .paper-card .authors-full { display: none; }
  .paper-card .authors-short { display: inline; }
  .doc-lede, .placeholder-block { grid-column: span 6; }
}

/* A device that cannot hover has no key to press either, and the cap is only
   ever a hint. Dropping it also buys back the width the wordmark needs when
   the two share a line. */
@media (hover: none) {
  .theme-toggle-key { display: none; }
}

@media (max-width: 680px) {
  .shell { padding: 24px 20px 70px; }
  .stream { row-gap: 42px; }
  .mark-text { font-size: 23px; }
  .paper-card,
  .paper-card:nth-child(4n+2),
  .paper-card:nth-child(4n+3) { grid-column: span 6; }
  .paper-card .authors-full { display: inline !important; }
  .paper-card .authors-short { display: none !important; }
  .paper-card:nth-child(4n+2) .paper-title,
  .paper-card:nth-child(4n+3) .paper-title { font-size: 21px; }
  .stream-head h2 { grid-column: span 6; }
  .stream-sub { grid-column: span 6; margin-top: 10px; }
}

/* =====================================================================
   Paper pages: the margin table of contents
   ===================================================================== */
.toc { margin-top: 44px; }

.toc-back {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-soft);
  padding-bottom: 2px;
  border-bottom: var(--bw) solid transparent;
}
.toc-back:hover { color: var(--blue); border-color: var(--blue); }

.toc-head {
  margin: 26px 0 12px;
  padding-bottom: 10px;
  border-bottom: var(--bw) solid var(--rule);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.toc-list { list-style: none; margin: 0; padding: 0; }
.toc-list li + li { margin-top: 2px; }
.toc-list a {
  display: grid;
  grid-template-columns: 26px 1fr;
  gap: 8px;
  align-items: baseline;
  padding: 5px 8px 5px 6px;
  margin-left: -6px;
  font-size: 14.5px;
  line-height: 1.35;
  color: var(--ink-soft);
  border-left: 2.5px solid transparent;
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
}
.toc-list a .num { font-size: 11px; color: var(--ink-faint); }
.toc-list a:hover { color: var(--on-bright); background: var(--lime); }

/* set by the scroll-spy in main.js */
.toc-list a.is-active {
  color: var(--ink);
  font-weight: 600;
  border-left-color: var(--c, var(--blue));
}
.toc-list a.is-active .num { color: var(--c, var(--blue)); }

.toc .rail-meta { margin-top: 26px; }

/* =====================================================================
   Paper pages: sections and prose
   ===================================================================== */
.paper-section { grid-column: span 6; }

.section-rule {
  display: grid;
  grid-template-columns: 34px 1fr;
  align-items: baseline;
  gap: 10px;
  border-top: var(--bw) solid var(--rule);
  padding-top: 16px;
  margin-bottom: 22px;
}
.section-rule .num { font-size: 12px; color: var(--c); }
.section-rule h2 { font-size: clamp(24px, 2.6vw, 31px); }

/* The measure lives on the text, not on the container, so a figure can be
   wider than the paragraphs it sits between. */
.prose { font-size: 16.5px; line-height: 1.65; }
.prose > p,
.prose > ul,
.prose > ol,
.prose > h3,
.prose > h4,
.prose > blockquote,
.prose > .table-wrap,
.prose > .plate { max-width: 68ch; }
.prose > *:first-child { margin-top: 0; }
.prose > *:last-child { margin-bottom: 0; }
.prose p { margin: 0 0 1.1em; }
.prose h3 { font-size: 20px; margin: 1.8em 0 .6em; }
.prose h4 { font-size: 16.5px; margin: 1.6em 0 .5em; }

/* Numbered off the section beside it, in the same accent and the same
   monospace as the section's own 02 — so 4.1 under 04 reads as one scheme at
   two depths. Hung on its own inline-block so the titles below a heading line
   up with each other rather than with the digits. */
.prose .head-num {
  display: inline-block;
  margin-right: .55em;
  color: var(--c, var(--blue));
  font-weight: 700;
}
.prose h3 .head-num { font-size: 14px; }
.prose h4 .head-num { font-size: 12.5px; }

/* Every subheading is linkable, so every subheading offers its own link. The
   anchor only shows on hover, and keeps its width either way so nothing on the
   line moves when it appears. */
.prose h3, .prose h4 { scroll-margin-top: 28px; }
.prose .head-link {
  margin-left: .35em;
  font-weight: 400;
  color: var(--c, var(--blue));
  border-bottom: 0;
  opacity: 0;
  transition: opacity 140ms ease;
}
.prose h3:hover .head-link,
.prose h4:hover .head-link,
.prose .head-link:focus-visible { opacity: 1; }
.prose a {
  color: var(--blue);
  border-bottom: var(--bw) solid var(--blue);
  padding-bottom: 1px;
}
.prose a:hover { background: var(--lime); color: var(--on-bright); border-color: var(--on-bright); }
.prose ul, .prose ol { margin: 0 0 1.1em; padding-left: 1.3em; }
.prose li { margin-bottom: .35em; }
.prose li::marker { color: var(--ink-faint); }

.prose blockquote {
  margin: 0 0 1.1em;
  padding: 2px 0 2px 18px;
  border-left: 2.5px solid var(--c, var(--blue));
  color: var(--ink-soft);
}

.prose code {
  font-family: var(--font-data);
  font-size: 0.86em;
  background: var(--wash);
  border: var(--bw) solid var(--rule-soft);
  padding: 0 4px;
}
.prose pre {
  margin: 0 0 1.2em;
  padding: 16px 18px;
  overflow-x: auto;
  background: var(--invert-bg);
  border: var(--bw) solid var(--invert-bg);
}
.prose pre code {
  background: none;
  border: 0;
  padding: 0;
  color: var(--invert-code);
  font-size: 13px;
  line-height: 1.6;
}

.prose hr { border: 0; border-top: var(--bw) solid var(--rule-soft); margin: 2em 0; }

.prose figure { margin: 0 0 1.4em; }
.prose figure img, .prose img { max-width: 100%; height: auto; display: block; border: var(--bw) solid var(--rule); }
.prose figcaption { margin-top: 9px; font-size: 13.5px; color: var(--ink-faint); }

/* tables scroll inside their own box rather than stretching the page */
.table-wrap { overflow-x: auto; margin: 0 0 1.3em; border: var(--bw) solid var(--rule); }
.prose table { border-collapse: collapse; width: 100%; font-size: 14.5px; }
.prose th, .prose td { text-align: left; padding: 9px 14px; border-bottom: var(--bw) solid var(--rule-soft); }
.prose th { font-weight: 700; border-bottom-color: var(--rule); white-space: nowrap; }
.prose tbody tr:last-child td { border-bottom: 0; }

/* ---------------------------------------------------------------------
   Plates — every figure on the site, static or interactive, is one of
   these: the same framed box as the demo on the landing page, with its
   caption riding the top rule.
--------------------------------------------------------------------- */
.prose .plate {
  margin: 0 0 1.5em;
  padding: 34px 22px 22px;
}
.prose .plate-body > *:last-child { margin-bottom: 0; }
.prose .plate-body > p:first-child { margin-top: 0; }

/* [compact]: a handful of numbers, so the table shrinks to fit them instead
   of stretching to the measure. Numeric columns go monospace and flush right,
   which is the only kind of table this flag is meant for. */
.table-wrap.table-compact { width: fit-content; max-width: 100%; }
.prose .table-compact table { width: auto; font-size: 13px; }
.prose .table-compact th,
.prose .table-compact td {
  padding: 5px 14px;
  line-height: 1.35;
  white-space: nowrap;
}
.prose .table-compact th { font-size: 11px; }
.prose .table-compact th:not(:first-child),
.prose .table-compact td:not(:first-child) { text-align: right; }
.prose .table-compact td:not(:first-child) {
  font-family: var(--font-data);
  font-size: 12px;
}
/* the row the paper is arguing for, lit the same way the results table lights
   its own — one highlighter pen across the whole site */
.prose tbody tr.is-ours td { background: var(--lime); color: var(--on-bright); font-weight: 700; }

/* a plate — or a table written under a [wide] line — may break the prose
   measure to use the full column */
.prose > .plate-wide,
.prose > .table-wrap.table-wide { max-width: none; }

/* the interactivity badge, sat inside the box opposite the caption */
.plate-live {
  position: absolute;
  top: 12px;
  right: 14px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  color: var(--ink-soft);
  background: var(--paper);
  border: var(--bw) solid var(--rule-soft);
  padding: 4px 8px 3px;
}
.plate-live-dot {
  width: 6px;
  height: 6px;
  background: var(--c, var(--blue));
  animation: plate-pulse 1.8s ease-in-out infinite;
}
@keyframes plate-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.25; }
}

@media (max-width: 1040px) {
  .toc { margin-top: 30px; }
  .toc-list { columns: 2; column-gap: 26px; }
  .prose { max-width: none; }
}

/* =====================================================================
   Components a content file can use inside a plate
   ===================================================================== */

/* comparison bars, with a dashed target line running through every row.
   Set --target on .bars to place the line. */
.bars { display: grid; gap: 7px; }
.bar-row {
  display: grid;
  grid-template-columns: 108px 1fr 42px;
  align-items: center;
  gap: 12px;
  font-size: 13.5px;
}
.bar-label { color: var(--ink-soft); }
.bar-track {
  position: relative;
  height: 20px;
  background: var(--wash-2);
  border: var(--bw) solid var(--rule-soft);
}
.bar-track::after {
  content: "";
  position: absolute;
  left: var(--target, 100%);
  top: -4px;
  bottom: -4px;
  border-left: var(--bw) dashed var(--ink);
}
.bar-fill { display: block; height: 100%; background: var(--ink-faint); }
.bar-fill.is-target { background: var(--lime); }
.bar-value { text-align: right; font-size: 12px; }

.bar-row.is-ours .bar-label { color: var(--ink); font-weight: 700; }
.bar-row.is-ours .bar-fill { background: var(--c, var(--blue)); }

.bars-note {
  margin: 14px 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-faint);
}

/* headline numbers */
.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 0;
  margin: 0 0 1.5em;
  border: var(--bw) solid var(--rule);
}
.stat-row > div { padding: 18px 18px 16px; border-right: var(--bw) solid var(--rule-soft); }
.stat-row > div:last-child { border-right: 0; }
.stat-row b {
  display: block;
  font-size: 30px;
  line-height: 1;
  margin-bottom: 8px;
  color: var(--c, var(--blue));
}
.stat-row span { font-size: 13px; line-height: 1.45; color: var(--ink-soft); }

/* inline SVG diagrams written into a content file */
.diagram { display: block; width: 100%; height: auto; overflow: visible; }
.diagram text { font-family: var(--font-ui); fill: var(--ink); }
.diagram .d-label { font-size: 11px; font-weight: 600; }
.diagram .d-note { font-size: 11px; fill: var(--ink-faint); }
/* upside / downside annotations. These beat `.diagram text` on specificity,
   which a fill set on the parent <g> would not. */
.diagram .d-pro { fill: var(--forest); }
.diagram .d-con { fill: var(--red); }
/* math, set by KaTeX ------------------------------------------------ */
.math-display {
  display: block;
  margin: 1.4em 0;
  text-align: center;
  /* a long equation scrolls inside itself rather than widening the page */
  overflow-x: auto;
  overflow-y: hidden;
  padding: 2px 0;
}
.prose .katex { font-size: 1.03em; }
.frame-tag .katex { font-size: 1em; }

/* the knapsack demo: objects slide between the bag and the row above it */
.knap-item { transition: transform 340ms cubic-bezier(.2, .7, .3, 1); }
.knap-item rect { transition: fill 240ms ease, stroke 240ms ease; }
.diagram .knap-w {
  font-family: var(--font-data);
  font-size: 11px;
  font-weight: 700;
  fill: var(--ink);
}
.diagram .knap-imp {
  font-family: var(--font-data);
  font-size: 10px;
  font-weight: 700;
  fill: var(--ink-soft);
}
.diagram .is-out .knap-w,
.diagram .is-out .knap-imp { fill: var(--ink-faint); }
.diagram .knap-panel-title { font-size: 11px; font-weight: 600; fill: var(--ink); }
.diagram .knap-chip-text { font-family: var(--font-data); font-size: 10px; font-weight: 700; }
.knap-chip { transition: opacity 200ms ease; }
.knap-chip.is-live { animation: knap-flag 1.4s ease-in-out infinite; }
@keyframes knap-flag {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

/* axis + series labels on a plotted diagram */
.diagram .d-tick { font-size: 9px; fill: var(--ink-soft); }
.diagram .d-tick.is-ours { fill: var(--ink); font-weight: 700; }

@media (max-width: 560px) {
  .bar-row { grid-template-columns: 88px 1fr 38px; gap: 8px; font-size: 12.5px; }
  .stat-row > div { border-right: 0; border-bottom: var(--bw) solid var(--rule-soft); }
  .stat-row > div:last-child { border-bottom: 0; }
}

/* importance-estimation demo: the same components scored two ways ---- */
.imp { display: grid; gap: 18px; }
.imp-panel {
  border: var(--bw) solid var(--rule-soft);
  padding: 14px 14px 12px;
  background: var(--paper);
}
.imp-title {
  margin: 0 0 12px;
  font-size: 12px;
  font-weight: 700;
  line-height: 1.35;
}
.imp-title span {
  display: block;
  font-weight: 400;
  color: var(--ink-faint);
  font-size: 11.5px;
}
.imp-row { display: flex; flex-wrap: wrap; gap: 7px; }
.imp-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 5px 9px;
  border: var(--bw) solid var(--ink);
  background: var(--paper);
  font-size: 12.5px;
  transition: background 220ms ease, border-color 220ms ease, opacity 220ms ease;
}
.imp-chip b { font-weight: 700; }
.imp-chip .imp-score { font-style: normal; font-size: 11.5px; color: var(--ink-soft); }
/* a component the iterative pass has just re-priced upward */
.imp-chip.is-rescored {
  background: var(--lime);
  color: var(--on-bright);
  border-color: var(--on-bright);
}
.imp-chip.is-rescored .imp-score { color: var(--on-bright); font-weight: 700; }
.imp-chip.is-cut {
  border-color: var(--rule-soft);
  color: var(--ink-faint);
  background: var(--wash-2);
  text-decoration: line-through;
}
.imp-chip.is-cut .imp-score { color: var(--ink-faint); }

.imp-caps-label {
  margin: 14px 0 6px;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.imp-caps { display: flex; flex-wrap: wrap; gap: 6px; }
.imp-cap {
  padding: 3px 9px;
  font-size: 12px;
  border: var(--bw) solid var(--ink);
  background: var(--paper);
  transition: background 220ms ease, color 220ms ease, border-color 220ms ease;
}
.imp-cap.is-dead {
  border-color: var(--rule-soft);
  color: var(--ink-faint);
  background: var(--wash-2);
  text-decoration: line-through;
}
/* when the two schemes part company, ring the panel that came out ahead */
.imp.is-diverged .imp-panel:last-child { border-color: var(--ink); box-shadow: 4px 4px 0 var(--lime); }

/* which model this panel's scores are being measured against */
.imp-ref {
  margin: 0 0 10px;
  font-size: 11.5px;
  color: var(--ink-soft);
}
.imp-ref b { font-family: var(--font-data); font-weight: 700; color: var(--ink); }

/* the component being priced at this step */
.imp-chip.is-scoring {
  border-color: var(--ink);
  box-shadow: 3px 3px 0 var(--ink);
}
/* not reached yet: it has no score on the tape */
.imp-chip.is-unscored { border-color: var(--rule-soft); color: var(--ink-faint); }
.imp-chip.is-unscored .imp-score { color: var(--rule-soft); }

/* a capability's remaining providers, and the moment it is down to one */
.imp-cap .imp-count {
  font-style: normal;
  margin-left: 6px;
  font-size: 11px;
  color: var(--ink-faint);
}
.imp-cap.is-critical { background: var(--lime); color: var(--on-bright); }
.imp-cap.is-critical .imp-count { color: var(--on-bright); font-weight: 700; }
.imp-cap.is-dead .imp-count { color: var(--ink-faint); }

/* what this panel concluded: one score, or the whole final model */
.imp-verdict {
  margin: 12px 0 0;
  font-size: 12.5px;
  color: var(--ink-soft);
  border-top: var(--bw) solid var(--rule-soft);
  padding-top: 9px;
}
.imp-verdict b { font-family: var(--font-data); color: var(--ink); }
.imp-verdict.is-loud b { background: var(--lime); color: var(--on-bright); padding: 1px 5px; }

/* Neuron sensitivity: three matrices, one neuron's slices at a time ---- */
.diagram .nrn-num {
  font-family: var(--font-data);
  font-size: 11px;
  fill: var(--ink);
}
.diagram .nrn-idx { font-size: 9.5px; fill: var(--ink-faint); }
.diagram .nrn-op { font-size: 16px; fill: var(--ink-soft); }
.diagram .nrn-badge { font-size: 10px; font-weight: 700; fill: var(--invert-ink); }
.diagram .nrn-work {
  font-family: var(--font-data);
  font-size: 12px;
  fill: var(--ink-soft);
}
.diagram .nrn-result {
  font-family: var(--font-data);
  font-size: 13px;
  font-weight: 700;
  fill: var(--ink);
}
.diagram .nrn-flag {
  font-size: 10px;
  font-weight: 700;
  fill: var(--red);
}
.nrn-svg rect, .nrn-svg text { transition: fill 180ms ease, stroke 180ms ease; }

/* a row of buttons in the site's hard-edged idiom, shared by any figure
   that cycles through a handful of alternatives */
.nrn-tabs,
.tab-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-top: 18px;
}
.tab-row { margin: 0 0 16px; }
.nrn-tab,
.tab {
  font-family: var(--font-data);
  font-size: 12px;
  font-weight: 700;
  color: var(--ink);
  background: var(--paper);
  border: var(--bw) solid var(--ink);
  padding: 5px 12px;
  cursor: pointer;
}
.nrn-tab:hover,
.tab:hover { background: var(--lime); color: var(--on-bright); }
.nrn-tab.is-on,
.tab.is-on { background: var(--invert-bg); color: var(--invert-ink); }
.nrn-tab:focus-visible,
.tab:focus-visible { outline: var(--bw) solid var(--blue); outline-offset: 2px; }

/* main results: the paper's tables, one model at a time ---------------- */
.res-block + .res-block { margin-top: 20px; }
.res-cr {
  margin: 0 0 8px;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.res-cr b { color: var(--ink); }
/* tight enough that eight columns hold the prose measure */
.prose .res-table { font-size: 11.5px; }
.prose .res-table th,
.prose .res-table td {
  padding: 5px 7px;
  white-space: nowrap;
}
.prose .res-table th { font-size: 10.5px; }
.prose .res-table td { font-family: var(--font-data); font-size: 10.5px; }

/* A switch rather than another button: the tabs pick *which* results you
   are looking at, this picks *which run of them*, and the two controls
   should not look interchangeable. Square, because nothing else here is
   round, but with a knob that slides so it still reads as a toggle. */
.switch-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 9px;
  margin: 0 0 18px;
}
.switch-side {
  font-size: 12px;
  color: var(--ink-faint);
  transition: color 160ms ease;
}
.switch-side.is-active { color: var(--ink); font-weight: 600; }
.switch {
  position: relative;
  width: 46px;
  height: 24px;
  flex: none;
  padding: 0;
  background: var(--paper);
  border: var(--bw) solid var(--ink);
  cursor: pointer;
}
.switch-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  background: var(--ink);
  transition: transform 180ms cubic-bezier(.2, .7, .3, 1);
}
.switch.is-on { background: var(--lime); }
.switch.is-on .switch-knob { transform: translateX(20px); background: var(--on-bright); }
/* A tap on a touch screen leaves :hover latched on the control until
   something else is touched, so the switch would sit lime after being turned
   off. Only devices that can actually hover get the affordance. */
@media (hover: hover) {
  .switch:hover:not(:disabled) { background: var(--lime); }
}
.switch:focus-visible { outline: var(--bw) solid var(--blue); outline-offset: 2px; }
/* locked on, because this model has no other run to show */
.switch:disabled { cursor: not-allowed; opacity: 0.5; }
.switch-row.is-locked .switch-side.is-active { color: var(--ink-soft); }
.switch-lock {
  font-size: 11.5px;
  font-style: italic;
  color: var(--ink-faint);
}
.prose .res-table td.res-method {
  font-family: var(--font-ui);
  font-size: 11.5px;
  font-weight: 500;
}
/* the unpruned model, for reference rather than comparison */
.prose .res-table tr.is-base td { color: var(--ink-soft); background: var(--wash-3); }
.prose .res-table tr.is-base td.res-method { font-style: italic; }
/* the paper's own method */
.prose .res-table tr.is-ours td { background: var(--lime); color: var(--on-bright); font-weight: 700; }
.res-source {
  margin: 10px 0 0;
  font-size: 11.5px;
  color: var(--ink-faint);
}

/* ?[term](explanation): a word that explains itself on hover ----------- */
.tip {
  /* dashed rather than dotted, in ink rather than grey: a link's underline is
     solid, so this still reads as a different kind of affordance without
     disappearing into the text the way a faint dotted rule did */
  border-bottom: 2px dotted var(--ink);
  cursor: help;
  transition: background 140ms ease, border-color 140ms ease;
}
.tip:hover,
.tip:focus-visible {
  background: var(--lime);
  color: var(--on-bright);
  border-bottom-style: solid;
  outline: none;
}

/* One bubble for the whole page, parked on <body> and positioned with
   fixed coordinates — so it is never clipped by a figure's frame or by the
   horizontal scroll on a wide table. */
.tip-bubble {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 60;
  max-width: 260px;
  padding: 9px 11px;
  background: var(--invert-bg);
  color: var(--invert-ink);
  font-family: var(--font-ui);
  font-size: 12.5px;
  line-height: 1.45;
  opacity: 0;
  transform: translateY(3px);
  transition: opacity 130ms ease, transform 130ms ease;
  pointer-events: none;
}
.tip-bubble.is-on { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .tip-bubble { transition: none; transform: none; }
}

/* ablation: two headline numbers and the switches that move them ------ */
.abl-figures {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
.abl-stat {
  border: var(--bw) solid var(--ink);
  padding: 14px 16px 12px;
  background: var(--paper);
}
.abl-value {
  display: block;
  font-size: clamp(30px, 4.4vw, 42px);
  line-height: 1;
  letter-spacing: -0.02em;
  transition: color 200ms ease;
}
.abl-delta {
  display: block;
  margin-top: 6px;
  font-size: 13px;
  font-weight: 700;
  min-height: 17px;
}
.abl-name {
  display: block;
  margin-top: 8px;
  font-size: 11.5px;
  color: var(--ink-soft);
  line-height: 1.35;
}
/* moved the wrong way: the ablation cost something */
.abl-stat.is-worse { border-color: var(--red); }
.abl-stat.is-worse .abl-value,
.abl-stat.is-worse .abl-delta { color: var(--red); }
/* moved the right way, which does happen and should not be hidden */
.abl-stat.is-better .abl-value,
.abl-stat.is-better .abl-delta { color: var(--forest); }
.abl-stat.is-unknown { border-color: var(--rule-soft); }
.abl-stat.is-unknown .abl-value { color: var(--ink-faint); }

.abl-legend {
  margin: 10px 0 16px;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.abl-list { display: grid; gap: 2px; }
.abl-row {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 10px;
  align-items: start;
  padding: 8px 10px;
  border: var(--bw) solid transparent;
  cursor: pointer;
}
.abl-row:hover { background: var(--wash-hover); }
.abl-row.is-child { margin-left: 26px; }
.abl-row b { display: block; font-size: 13px; font-weight: 600; }
.abl-row i {
  display: block;
  font-style: normal;
  font-size: 11.5px;
  color: var(--ink-faint);
  margin-top: 2px;
}
/* switched off: struck through, so the list itself reads as the variant */
.abl-row.is-off b { text-decoration: line-through; color: var(--ink-faint); }
/* its stage is gone, so this control has nothing left to describe */
.abl-row.is-dead { opacity: 0.4; cursor: not-allowed; }

/* a square checkbox, to match everything else here */
.abl-box {
  appearance: none;
  -webkit-appearance: none;
  width: 17px;
  height: 17px;
  margin: 1px 0 0;
  flex: none;
  border: var(--bw) solid var(--ink);
  background: var(--paper);
  cursor: pointer;
  position: relative;
}
.abl-box:checked { background: var(--lime); }
.abl-box:checked::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 0px;
  width: 5px;
  height: 10px;
  border: solid var(--on-bright);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.abl-box:disabled { cursor: not-allowed; }
.abl-box:focus-visible { outline: var(--bw) solid var(--blue); outline-offset: 2px; }

.abl-source {
  margin: 14px 0 0;
  font-size: 11.5px;
  color: var(--ink-faint);
}
.abl-source.is-warn { color: var(--ink-soft); font-style: italic; }

@media (max-width: 620px) {
  .abl-figures { grid-template-columns: 1fr; }
}

/* the two kinds of ablation, side by side: take a part out (square boxes,
   left) or leave everything in and swap one part for another (round
   radios, right). Different shape because they are different operations. */
.abl-controls {
  display: grid;
  grid-template-columns: 1.35fr 1fr;
  gap: 22px;
  align-items: start;
}
.abl-col-swap {
  border-left: var(--bw) solid var(--rule-soft);
  padding-left: 20px;
}
.abl-col-head {
  margin: 0 0 8px;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.abl-swaps { display: grid; gap: 2px; }
/* the chosen replacement is in the model, so it is marked, not struck out */
.abl-swap.is-on { border-color: var(--on-bright); background: var(--lime); color: var(--on-bright); }
.abl-swap.is-on b { text-decoration: none; }
.abl-radio {
  appearance: none;
  -webkit-appearance: none;
  width: 17px;
  height: 17px;
  margin: 1px 0 0;
  flex: none;
  border: var(--bw) solid var(--ink);
  border-radius: 50%;
  background: var(--paper);
  cursor: pointer;
  position: relative;
}
/* filled solid rather than carrying an inner dot: a dot has to be centred
   against a padding box whose size moves with the rendered border width,
   and there is nothing to centre if the whole control is the mark */
.abl-radio:checked { background: var(--ink); }
.abl-radio:disabled { cursor: not-allowed; }
.abl-radio:focus-visible { outline: var(--bw) solid var(--blue); outline-offset: 2px; }
.abl-swap-lock {
  margin: 10px 0 0;
  font-size: 11.5px;
  font-style: italic;
  color: var(--ink-faint);
}

@media (max-width: 620px) {
  .abl-controls { grid-template-columns: 1fr; }
  .abl-col-swap {
    border-left: 0;
    padding-left: 0;
    border-top: var(--bw) solid var(--rule-soft);
    padding-top: 16px;
  }
}

/* Post-pruning throughput (Figure 4b). Columns rather than the horizontal
   bars used for compression, because this is a different quantity measured a
   different way and should not be mistaken for the same chart twice. Each
   family keeps the colour the taxonomy diagram at the top of the page gave it
   — depth blue, width pink, dual-axis violet — so the reader is not asked to
   learn a second code.

   The track holds nothing but the bars, so a percentage inside it is a
   percentage of the value axis. The names and the family rules hang below it
   out of flow, which is what the bottom margin is reserving room for. */
.speed-track {
  position: relative;
  display: flex;
  gap: 26px;
  height: 226px;
  margin: 26px 0 92px;
}
.speed-fam { position: relative; display: flex; flex: 1; gap: 10px; }

/* the family's name on a rule in the family's own colour */
.speed-fam::after {
  content: attr(data-fam);
  position: absolute;
  left: 0;
  right: 0;
  top: calc(100% + 44px);
  padding-top: 7px;
  border-top: var(--bw) solid var(--c);
  text-align: center;
  font-size: 11px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.speed-col {
  position: relative;
  flex: 1;
  display: flex;
  align-items: flex-end;
}
.speed-bar {
  width: 100%;
  height: var(--h);
  background: var(--c);
  border: var(--bw) solid var(--ink);
}
/* shrink-wrapped rather than stretched across the column, so the paper-white
   background knocks a hole in the base-model line only where a value actually
   crosses it */
.speed-val {
  position: absolute;
  left: 50%;
  bottom: calc(var(--h) + 2px);
  transform: translateX(-50%);
  z-index: 3;
  padding: 0 3px;
  background: var(--paper);
  white-space: nowrap;
  font-size: 11.5px;
  color: var(--ink-soft);
}
.speed-name {
  position: absolute;
  left: -5px;
  right: -5px;
  top: calc(100% + 9px);
  text-align: center;
  font-size: 10.5px;
  line-height: 1.25;
  color: var(--ink-soft);
}
/* the paper's own method: named in full ink, everything else in soft */
.speed-col.is-ours .speed-bar { border-width: 2.5px; }
.speed-col.is-ours .speed-val,
.speed-col.is-ours .speed-name { color: var(--ink); font-weight: 700; }

/* the unpruned model, drawn across every family so the two width pruners are
   visibly below the thing they were meant to speed up */
.speed-rule {
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--base);
  z-index: 2;
  border-top: var(--bw) dashed var(--ink);
  pointer-events: none;
}
/* on the left, where the two width pruners leave the most room above them */
.speed-rule-tag {
  position: absolute;
  left: 0;
  bottom: 4px;
  padding: 0 4px;
  background: var(--paper);
  font-size: 10.5px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

@media (max-width: 560px) {
  .speed-track { gap: 14px; height: 190px; }
  .speed-fam { gap: 6px; }
  .speed-val { font-size: 10.5px; }

  /* At this width a column is under 40px and most names are wider than the
     bar they belong to, so they wrap onto a second line instead of running
     into the neighbouring name. They are single words with nowhere to break
     on their own; the break opportunities are put in at the case boundary
     by the block of the same name in main.js. The label box is allowed a
     few pixels past its bar on each side — just enough that the gap between
     two labels is never smaller than the gap between two bars. */
  .speed-name {
    font-size: 9.5px;
    left: -3px;
    right: -3px;
    line-height: 1.2;
  }

  /* The base-model tag ran the width of two columns, which put it across
     SLEB's bar and hard against SliceGPT's value. Broken onto two lines it
     fits in the space the two width pruners leave above them, which is the
     space it was put on the left for in the first place. */
  .speed-rule-tag {
    max-width: 72px;
    bottom: 7px;
    font-size: 9.5px;
    line-height: 1.25;
    letter-spacing: 0.02em;
  }
}

/* the failing end of the alpha sweep, in the same red the taxonomy diagram
   uses for a downside */
.diagram .over { fill: var(--red); }

/* End-to-end pruning time. Horizontal, because elapsed time reads left to
   right, and because the point of the figure is a proportion inside one bar:
   almost all of SNIPER's runtime is importance estimation, and the pruning
   step is the hatched sliver on the end. */
.rt { display: grid; gap: 18px; }
.rt-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 6px;
  font-size: 13.5px;
}
.rt-fam {
  font-size: 10.5px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--c);
}
/* kept beside the name rather than flushed right: on a short bar the
   number would otherwise float half a plate away from the thing it
   measures */
.rt-total { font-size: 12.5px; color: var(--ink-soft); }
.rt-min { margin-left: 4px; font-size: 11px; color: var(--ink-faint); }
.rt-bar {
  display: flex;
  height: 30px;
  border: var(--bw) solid var(--ink);
}
.rt-row.is-ours .rt-bar { border-width: 2.5px; }
.rt-seg { flex: none; width: 100%; background: var(--c); }
.rt-seg.is-prune {
  border-left: var(--bw) solid var(--ink);
  background: repeating-linear-gradient(
    45deg, var(--c) 0 2.5px, var(--paper) 2.5px 5px
  );
}

/* a sentence with two swatches set into it, not a row of boxes: as a flex
   container each phrase became its own column and broke into a stack of two
   words apiece as soon as the plate got narrow */
.rt-key {
  --c: var(--violet);
  margin: 2px 0 0;
  font-size: 11.5px;
  line-height: 1.7;
  color: var(--ink-faint);
}
.rt-key-swatch {
  display: inline-block;
  vertical-align: -1px;
  margin-right: 5px;
  width: 15px;
  height: 11px;
  border: var(--bw) solid var(--ink);
  background: var(--indigo);
}
.rt-key-swatch.is-prune {
  margin-left: 8px;
  background: repeating-linear-gradient(
    45deg, var(--indigo) 0 2.5px, var(--paper) 2.5px 5px
  );
}

/* Score transfer. Deliberately short: two rows of choices and two numbers,
   so the whole figure stays inside a screenful next to the prose. */
.tf-pick {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 14px;
  margin-bottom: 16px;
}
.tf-lab {
  margin: 0 0 6px;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.tf-pick .tab-row { margin: 0; }
.tf-arrow {
  padding-bottom: 5px;
  font-size: 17px;
  color: var(--ink-faint);
}

.tf-out { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.tf-stat {
  border: var(--bw) solid var(--ink);
  padding: 11px 14px 10px;
  background: var(--paper);
}
.tf-value {
  display: block;
  font-size: 28px;
  line-height: 1;
  letter-spacing: -0.02em;
  transition: color 200ms ease;
}
.tf-delta {
  display: block;
  margin-top: 5px;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ink-faint);
}
.tf-name {
  display: block;
  margin-top: 7px;
  font-size: 11px;
  color: var(--ink-faint);
}
/* the delta is tiny in every cell of this table, which is the finding, so the
   colour is carried by the number rather than by a border shouting about it */
.tf-stat.is-worse .tf-value,
.tf-stat.is-worse .tf-delta { color: var(--red); }
.tf-stat.is-better .tf-value,
.tf-stat.is-better .tf-delta { color: var(--forest); }
.tf-stat.is-same .tf-delta { color: var(--ink-faint); }

.tf-note {
  margin: 12px 0 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink-faint);
}
.tf-note.is-base { font-style: italic; }

@media (max-width: 560px) {
  .tf-out { grid-template-columns: 1fr; }
  .tf-arrow { display: none; }
}

/* Calibration robustness: three settings across, three methods within each.
   Colour separates the *methods* here rather than the pruning families used
   elsewhere on the page — 2SSP and SNIPER are both dual-axis, so the family
   code would paint the two bars this figure exists to compare in one colour. */
.cal-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin: 0 0 16px;
  font-size: 12px;
}
.cal-key {
  display: flex;
  align-items: center;
  gap: 7px;
}
.cal-swatch {
  width: 15px;
  height: 11px;
  flex: none;
  background: var(--c);
  border: var(--bw) solid var(--ink);
}
.cal-key.is-ours .cal-swatch { border-width: 2.5px; }

/* the plot: a fixed-height band of bars with the y scale hung off its left */
.cal-plot {
  position: relative;
  padding-left: 30px;
  margin: 0 0 6px;
}
.cal-band {
  position: relative;
  height: 186px;
}
.cal-line {
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--y);
  border-top: 1px solid var(--rule-soft);
}
.cal-line.is-base { border-top: var(--bw) solid var(--ink); }
.cal-ytick {
  position: absolute;
  right: calc(100% + 7px);
  bottom: -0.55em;
  font-size: 10.5px;
  color: var(--ink-faint);
  white-space: nowrap;
}
/* above the gridlines: the scale is a backdrop for the bars, not a
   hatching drawn across them */
.cal-groups {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  gap: 30px;
}
.cal-group {
  display: flex;
  flex: 1;
  align-items: flex-end;
  gap: 7px;
}
.cal-bar {
  position: relative;
  flex: 1;
  height: var(--h);
  background: var(--c);
  border: var(--bw) solid var(--ink);
  transition: height 280ms cubic-bezier(.2, .7, .3, 1);
}
.cal-bar.is-ours { border-width: 2.5px; }
/* shrink-wrapped and centred so a tall neighbour never crowds the digits */
.cal-val {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 5px);
  transform: translateX(-50%);
  font-size: 10.5px;
  white-space: nowrap;
}
.cal-names {
  display: flex;
  gap: 30px;
  margin-left: 30px;
}
.cal-name {
  flex: 1;
  padding-top: 9px;
  text-align: center;
  font-size: 12px;
  font-weight: 500;
}
.cal-note {
  margin: 14px 0 0;
  font-size: 12px;
  line-height: 1.55;
  color: var(--ink-soft);
}
@media (max-width: 560px) {
  .cal-groups, .cal-names { gap: 14px; }
  .cal-group { gap: 4px; }
  .cal-val { font-size: 9px; }
}

/* Annotated equation terms. A term is wrapped by \htmlClass in the content
   file; the note beneath it carries the matching data-term. Colour keys the
   two together, the underline says "there is something to read here", and the
   lime wash is the same one .tip uses when a word is being explained. */
.eq-t {
  /* lifted out of the flat stack KaTeX builds: a cases environment lays a
     .vlist over its own rows, and that span swallows the pointer for every
     term on the second line. Position alone changes no layout. */
  position: relative;
  z-index: 1;
  border-bottom: 2px solid var(--c, var(--ink));
  cursor: help;
  transition: background 140ms ease;
}
.eq-t.is-lit { background: var(--lime); color: var(--on-bright); }

.eq-notes {
  margin: -0.6em 0 0;
  padding: 0;
  list-style: none;
  font-size: 13px;
  line-height: 1.55;
  color: var(--ink-soft);
}
.eq-notes li {
  position: relative;
  padding: 3px 6px 3px 20px;
  cursor: help;
  transition: background 140ms ease;
}
.eq-notes li.is-lit { background: var(--lime); color: var(--on-bright); }
/* the chip repeats the term's colour, so the pairing survives for a reader
   who cannot separate the hues by eye */
.eq-notes li::before {
  content: "";
  position: absolute;
  top: 0.62em;
  left: 3px;
  width: 9px;
  height: 9px;
  background: var(--c, var(--ink));
}
.eq-notes b { color: var(--ink); font-weight: 600; }
.eq-notes .katex { font-size: 1em; }

/* one hue per term, picked to stay apart at two pixels of underline */
.eq-t-now,  .eq-notes li[data-term="now"]  { --c: var(--slate); }
.eq-t-fit,  .eq-notes li[data-term="fit"]  { --c: var(--amber); }
.eq-t-skip, .eq-notes li[data-term="skip"] { --c: var(--teal); }
.eq-t-pick, .eq-notes li[data-term="pick"] { --c: var(--violet); }
.eq-t-take, .eq-notes li[data-term="take"] { --c: var(--rust); }

@media (prefers-reduced-motion: reduce) {
  .eq-t, .eq-notes li { transition: none; }
}

/* =====================================================================
   Small screens

   The stream itself already reflows at 1040px and 680px. What follows is
   the handful of pieces that only misbehave once the column is narrower
   than the figure inside it wants to be. Layout only — none of this
   changes a word of what any page says.
   ===================================================================== */
@media (max-width: 680px) {
  /* every millimetre of side padding is one the figure does not get */
  .prose .plate { padding: 32px 15px 18px; }
  .plate-demo { padding: 32px 15px 18px; }
  .frame-tag {
    left: 13px;
    max-width: calc(100% - 26px);
    font-size: 10.5px;
    letter-spacing: 0.04em;
  }
  /* the badge cannot stay pinned opposite the caption once the caption is
     long enough to wrap underneath it, so it steps into the flow and sits
     above the figure instead — still top-right, still the first thing in
     the box, no longer in a fight for the same corner */
  .plate-live {
    position: static;
    display: flex;
    width: fit-content;
    margin: 0 0 14px auto;
  }

  /* a table narrow enough to fit beats a table that scrolls, and the widest
     column in most of them is a header held on one line by a rule that only
     ever earned its keep on a wide screen. Letting the headers wrap and
     tightening the cells brings the smaller tables back inside the column;
     anything still too wide goes on scrolling inside .table-wrap. */
  .prose th { white-space: normal; }
  .prose th,
  .prose td { padding: 8px 10px; }
  .prose table { font-size: 13.5px; }
  /* [compact] sets its own roomier side padding, which is what keeps a small
     numeric table from looking cramped on a wide screen and the only thing
     pushing it out of a narrow one */
  .prose .table-compact th,
  .prose .table-compact td { padding: 5px 10px; }
}

@media (max-width: 620px) {
  /* A diagram drawn for a 600px column is 6px type at 360px, which is not
     a layout that fits so much as one that cannot be read. Below this width
     it holds a legible size and scrolls sideways inside its own box — the
     same bargain .table-wrap has always struck for a wide table. :has()
     keeps the scroll box off plates whose figures reflow on their own,
     where it would do nothing but clip a bar's overshooting target line. */
  .plate-body:has(> svg.diagram) {
    overflow-x: auto;
    overscroll-behavior-x: contain;
  }
  .plate-body > svg.diagram { min-width: 430px; }
  /* a diagram that re-lays itself out for the width it is given asks for no
     minimum: it has already compacted rather than shrunk */
  .plate-body > svg.diagram.diagram-fluid { min-width: 0; }
}

@media (max-width: 560px) {
  /* display math is set at a fixed size by KaTeX and cannot be wrapped; one
     step down puts most of it back inside the column, and anything still
     too wide goes on scrolling inside .math-display as before */
  .math-display { font-size: 0.86em; }
}

/* ---------------------------------------------------------------------
   Scroll cues — the mark that says a box has more of itself off to one
   side. The box that scrolls is also the box that clips, so a cue placed
   inside it can only sit on top of the content it is pointing at. Each
   scroller therefore gets a plain wrapper that does not clip, and the
   chevrons hang in the padding just beyond its edge. Built and toggled by
   the block of the same name at the end of main.js.
--------------------------------------------------------------------- */
.scroll-box { position: relative; }

/* The wrapper stands exactly where the box it holds used to stand, so the
   rules keyed to .prose's own children go on meaning what they meant: the
   measure, the full-width break-out, the shrink-wrap of a small table and
   the flush bottom of a last child. */
.prose > .scroll-box { max-width: 68ch; }
.prose > .scroll-box:has(> .table-wide) { max-width: none; }
.prose > .scroll-box:has(> .table-compact) { width: fit-content; max-width: 100%; }
.prose > .scroll-box:last-child > .table-wrap { margin-bottom: 0; }

.scroll-cue {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 11px;
  text-align: center;
  font-size: 17px;
  line-height: 1;
  color: var(--ink);
  /* it points at the gesture; it is not one */
  pointer-events: none;
  opacity: 0;
  transition: opacity 160ms ease;
}
.scroll-cue.cue-l { left: -13px; }
.scroll-cue.cue-r { right: -13px; }
.scroll-box.can-l > .cue-l,
.scroll-box.can-r > .cue-r { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .scroll-cue { transition: none; }
}
