/* vidtheque demo — the projection room, one click deeper.
   DESIGN.md is normative here: every colour is a token, the radius is zero
   everywhere, mono means the machine said it, lime means the machine read it
   off the screen, and nothing moves unless the machine is working.
   `docs/design/demo-site.md` still wins on function, data, clamps and copy.

   The register is the landing's, quieter: same ground, same gold, same faces,
   same receipt slab — but the page is here to be *used*, so imagery serves a
   result rather than a mood and there is no ambient anything. */

/* Both faces are vendored beside this file, latin subset, SIL OFL 1.1.
   The `src` is relative on purpose (DESIGN.md, Fonts rule 3): this surface is
   served through tunnels on ports nobody predicted, and an absolute URL built
   from PUBLIC_URL would break the moment one of them is wrong. */
@font-face {
  font-family: "Archivo VF";
  src: url("fonts/archivo-latin-wght-normal.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: block;
}
@font-face {
  font-family: "JetBrains Mono VF";
  src: url("fonts/jetbrains-mono-latin-wght-normal.woff2") format("woff2");
  font-weight: 100 800;
  font-style: normal;
  font-display: block;
}

:root {
  color-scheme: dark;

  /* ── the ground, deepest first (DESIGN.md § The ground) */
  --pitch: #040405;
  --void: #08080a;
  --console: #0a0a0e;
  --plate: #0e0e12;
  --plate2: #141419;
  --plate3: #1a1a20;
  --seam: #242429;
  --seam2: #33333b;
  --black: #000000;
  --edge-in: rgba(255, 255, 255, .06);
  --scrim: rgb(0 0 0 / 0.66);

  /* ── the ink */
  --fg: #f3f0ea;
  --fg2: #9d968c;
  --fg3: #6b665e;
  --fg-max: #ffffff;

  /* ── the one accent: gold means "this is the moment you are pointing at" */
  --gold: #e7b455;
  --gold-hi: #f6cd78;
  --gold-ink: #120c02;
  --gold-6: rgba(231, 180, 85, .06);
  --gold-12: rgba(231, 180, 85, .12);
  --gold-24: rgba(231, 180, 85, .24);
  --gold-44: rgba(231, 180, 85, .44);

  /* ── the seen channel. The Lime Rule: this marks evidence the machine read
     OFF THE SCREEN, and nothing else. Not a highlight, not a hover, not a
     second accent. If it appears on a row with no on-screen text, it is a bug. */
  --seen: #c9f938;
  --seen-10: rgba(201, 249, 56, .10);
  --seen-24: rgba(201, 249, 56, .24);

  /* ── the state tones this surface actually shows. Ink is the hex; ground and
     border are mixed from it, which is why a tone needs one value and not
     three. Two of the five: a refusal and a wait. The other three belong to the
     dashboard, and a tone declared here with nothing in it is a tone somebody
     spends on decoration. */
  --tone-warn: #ffa057;
  --tone-bad: #ff9592;
  --tone-bad-bg: color-mix(in srgb, var(--tone-bad) 10%, var(--plate));
  --tone-bad-line: color-mix(in srgb, var(--tone-bad) 26%, var(--plate));

  /* ── the six pinned role aliases (`--fg` is declared with the ink above and
     is its own role). Aliases only: they carry no value of their own, which is
     what keeps this surface and the dashboard one world — `test_dashboard.py
     ::test_the_dashboard_palette_matches_the_demos` compares the two as
     mappings, so what has to agree is what each role resolves to. */
  --bg: var(--pitch);
  --muted: var(--fg2);
  --line: var(--seam);
  --accent: var(--gold);
  --raised: var(--plate);

  /* ── the two faces, one meaning each */
  --sans: "Archivo VF", system-ui, -apple-system, sans-serif;
  --mono: "JetBrains Mono VF", ui-monospace, SFMono-Regular, monospace;

  /* ── layout */
  --gut: clamp(20px, 4.2vw, 72px);
  --beat: clamp(44px, 5.2vw, 78px);
  --gap-head: clamp(22px, 2.4vw, 34px);
  --gap-copy: clamp(16px, 1.9vw, 26px);
  --prose: 70ch;
  /* The working column's own measure: the query bar and the controls that
     belong to it. Not a system primitive — a container width for one box. */
  --query-w: 44rem;
  --lede: 60ch;
  --note: 74ch;
  --maxw: 1460px;
  --bp-stack: 1120px;
  /* The demo's one own breakpoint (DESIGN.md allows a surface exactly one, for
     a real structural change): above it the ask pane puts its sources beside
     the answer instead of under it. Not a taste threshold — it is the width at
     which `--prose` plus a source row that can still print a whole receipt
     both fit inside the plate. */
  --bp-wide: 1380px;

  /* ── spacing, on the 4px grid */
  --s1: 0.25rem;
  --s2: 0.5rem;
  --s3: 0.75rem;
  --s4: 1rem;
  --s5: 1.25rem;
  --s6: 1.5rem;
  --s8: 2rem;
  --s10: 2.5rem;
  --s12: 3rem;

  /* ── depth. A shadow means lifted off the wall; four things get one. */
  --drop: 0 40px 90px -24px rgba(0, 0, 0, .94);
  --drop-sm: 0 22px 50px -18px rgba(0, 0, 0, .9);
  --edge: inset 0 0 0 1px var(--edge-in);

  /* ── motion. Two durations and one animation: there is nothing on this page
     that moves except a control changing colour and the caret that says the
     machine is inside the corpus. */
  --t-fast: 0.16s;
  --t-state: 0.25s;
  --blink: 1.05s steps(1, end) infinite;
}

/* Zero radius everywhere: a frame has square corners, and every box here is a
   frame, a plate or a rule. */
* { margin: 0; padding: 0; box-sizing: border-box; border-radius: 0; }

/* `display:flex` on a chip row would otherwise beat the [hidden] attribute. */
[hidden] { display: none !important; }

/* The gutter is reserved whether or not there is a scrollbar in it. An ask
   empties the results, which can shorten the document past one screen, and the
   answer landing lengthens it again: without this the scrollbar leaves and
   comes back, and every column on the page moves ~15px sideways and back while
   the model works (Tom, 2026-08-11). */
html { -webkit-text-size-adjust: 100%; scrollbar-gutter: stable; }

body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--sans);
  font-size: 16px;
  font-weight: 340;
  line-height: 1.55;
  letter-spacing: -.004em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  padding-bottom: env(safe-area-inset-bottom);
}

img { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
::selection { background: var(--gold); color: var(--gold-ink); }
:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* A corpus string can be an 80-character token. `anywhere` and not
   `break-word`: only `anywhere` also reports a zero min-content width, which is
   what stops a flex or grid parent being *widened* by one id. */
.wrap {
  width: 100%;
  /* The system's chassis (DESIGN.md, Layout): `--maxw` at `--gut` gutters.
     It used to be `--bp-stack` — 1120px — on the argument that this surface is
     read rather than toured. What that actually bought was a page where the
     evidence had a third of a 1440 screen and the browser chrome had the rest,
     and an ask pane the width of a sidebar (Tom, 2026-08-11). The corpus is
     the content here, so it gets the column the system already defines; the
     measures inside it (`--prose`, `--lede`, `--query-w`) are what keep text
     readable, and they are unchanged. */
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--gut);
}

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

/* ─────────────────────────────────────────────── the two channels, in type */

/* Mono is the machine's: every id, timecode, count, query string and label. */
.id,
.mono { font-family: var(--mono); font-variant-ligatures: none; }

.ml {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: .19em;
  text-transform: uppercase;
  font-variant-ligatures: none;
  /* --fg3 is atmosphere, not a fact-carrier (DESIGN.md, the measured
     exception). On this surface a micro-label is usually the only thing that
     names a fact, so label ink is --fg2. */
  color: var(--fg2);
}

.ml.gold { color: var(--gold); }
.ml.dim { color: var(--fg3); }

.kick { display: flex; align-items: center; gap: 12px; }
.kick s { display: block; flex: none; width: 34px; height: 1px; background: var(--gold); }

h2, h3 { font-weight: 210; letter-spacing: -.034em; line-height: 1; text-wrap: balance; }

/* One rung down from the landing: the demo's big line is `headline`, never
   `display` — the input is the point, not the sentence above it. */
h2.big {
  font-size: clamp(1.9rem, 3.5vw, 3.35rem);
  letter-spacing: -.034em;
  line-height: 1.0;
  max-width: 22ch;
  color: var(--fg-max);
}

h2.big em { font-style: normal; color: var(--gold); }

.lede {
  font-size: clamp(.98rem, 1.12vw, 1.1rem);
  font-weight: 340;
  line-height: 1.62;
  letter-spacing: -.004em;
  color: var(--fg2);
  max-width: var(--lede);
}

.lede b { color: var(--fg); font-weight: 500; }

/* ───────────────────────────────────────────────────────────────── the rail */

.rail { border-bottom: 1px solid var(--seam); background: var(--pitch); }

.railin {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  /* Vertical only: the horizontal padding is `.wrap`'s gutter, and a shorthand
     here would quietly zero it. */
  padding-top: 15px;
  padding-bottom: 14px;
  flex-wrap: wrap;
}

/* The Font-Logo Rule: the logo is the word and nothing else, with the full
   stop in gold — the receipt's own punctuation. v5's numbers are the contract. */
.mark b {
  font-family: var(--sans);
  font-size: 16.5px;
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -.035em;
  color: var(--fg);
}

.mark b i { font-style: normal; color: var(--gold); }

.railmeta { display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap; }

/* One link out, and a quiet one: the search box is this page's one primary
   action, so the way to the browsable corpus is an outline and not a button. */
.browse {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 2.125rem;
  padding: 0 12px;
  border: 1px solid var(--seam2);
  color: var(--fg2);
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .19em;
  text-transform: uppercase;
  font-variant-ligatures: none;
  transition: color var(--t-fast) linear, border-color var(--t-fast) linear;
}

.browse:hover { color: var(--gold); border-color: var(--gold-44); }

/* ───────────────────────────────────────────────────── beat 1 · the query */

/* One screen, and it contains the working input: no beat exists on this page
   that a visitor cannot act on. */
.hero { padding: clamp(34px, 4.6vw, 64px) 0 0; }
.deck { padding: var(--s6) 0 var(--beat); }

.herocopy { display: flex; flex-direction: column; gap: var(--gap-copy); }
.herocopy .kick { margin-bottom: 2px; }

/* The input, its filters and its count are one object: the gap between them
   is tighter than the gap between the copy above them. */
.querybox { display: flex; flex-direction: column; }

/* The query bar — the machine's one input, and the landing's own component:
   a gold cue on the left, the query in mono, the machine's state on the right,
   and the action. It is lifted off the page because it is the thing you use. */
.slug {
  display: flex;
  align-items: stretch;
  width: 100%;
  max-width: var(--query-w);
  height: 52px;
  background: var(--console);
  border: 1px solid var(--seam2);
  box-shadow: var(--drop-sm);
}

.slug .ic {
  display: grid;
  place-items: center;
  width: 44px;
  flex: none;
  color: var(--gold);
  border-right: 1px solid var(--seam);
}

.slug .ic svg { width: 13px; height: 13px; }

#q {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0 13px;
  background: transparent;
  border: 0;
  color: var(--fg);
  font-family: var(--mono);
  font-size: 13.5px;
  font-variant-ligatures: none;
}

#q::placeholder { color: var(--fg3); opacity: 1; }

/* The ring goes on the bar, not on the field inside it: a second rectangle
   drawn inside a bordered box reads as a box in a box. */
#q:focus-visible { outline: none; }
.slug:has(#q:focus-visible) { outline: 2px solid var(--gold); outline-offset: 2px; }

/* The machine's own word for what it is doing. Gold while it is working, and
   it prints the word — colour is reinforcement, never the message. */
.st {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  /* Wide enough for the longest word it prints (`scanning`, `no reply`), so
     changing state does not resize the cell and shove the input's right edge
     — a 13px jiggle on the one control the visitor is looking at. */
  min-width: 88px;
  padding: 0 14px;
  border-left: 1px solid var(--seam);
  background: var(--console);
  color: var(--fg3);
  font-family: var(--mono);
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: background var(--t-state) linear, color var(--t-state) linear;
}

.st[data-s="working"] { background: var(--gold); color: var(--gold-ink); }
.st[data-s="found"] { color: var(--gold); }
.st[data-s="refused"] { color: var(--tone-bad); }

button {
  font-family: var(--sans);
  cursor: pointer;
  border-radius: 0;
}

button[disabled] { opacity: .5; cursor: default; }

/* The one filled control on the page. */
.cta {
  display: inline-flex;
  align-items: center;
  flex: none;
  padding: 0 20px;
  background: var(--gold);
  color: var(--gold-ink);
  border: 1px solid var(--gold);
  border-left: 0;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -.012em;
  transition: background var(--t-fast) linear;
}

.cta:hover { background: var(--gold-hi); }

/* Disabled while a question is in flight: the accent steps back to its wash
   rather than going muddy at half opacity, and the state cell beside it is
   the thing carrying the word. */
.cta[disabled] {
  opacity: 1;
  background: var(--gold-12);
  border-color: var(--gold-24);
  color: var(--gold);
}

/* ──────────────────────────────────────────────────────────────── the chips */

/* The filters and the mode switch belong to the input, so they share its
   measure: a mode switch flung to the far edge of a 1120px column reads as
   chrome that belongs to the page rather than to the box above it. */
.controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  margin-top: var(--s4);
  max-width: var(--query-w);
}

/* One bordered group, the way the landing draws a switch: the chips butt
   against each other and the row reads as one control, not four loose ones. */
.chips { display: flex; flex-wrap: wrap; border: 1px solid var(--seam2); }

.chips button {
  height: 2.75rem;
  padding: 0 12px;
  background: transparent;
  border: 0;
  border-right: 1px solid var(--seam);
  color: var(--fg2);
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: .17em;
  text-transform: uppercase;
  font-variant-ligatures: none;
  white-space: nowrap;
  transition: color var(--t-fast) linear, background var(--t-fast) linear;
}

.chips button:last-child { border-right: 0; }
.chips button:hover { color: var(--fg); }
.chips button.is-on { background: var(--gold-12); color: var(--gold); }

.modes { margin-left: auto; }

/* The count line, and the notes under it. `all` means all: a leg that could
   not run says so here rather than narrowing the answer in silence. */
.status {
  display: block;
  min-height: 1.2em;
  margin-top: var(--s4);
  color: var(--fg2);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .005em;
  font-variant-numeric: tabular-nums;
  font-variant-ligatures: none;
}

.status .note { display: block; max-width: var(--note); color: var(--gold); }

/* ────────────────────────────────────────── the cold page — teach it first */

/* The cold page belongs to the input, not to the results: it sits under the
   query bar at the query bar's measure, so the four chips and the corpus list
   read as the box's own suggestions rather than as a very wide empty table. */
.empty { max-width: var(--query-w); }

.empty h2 { font-size: 14px; font-weight: 520; letter-spacing: -.02em; margin-top: var(--s4); }

/* One line under the heading, and it exists because the page now opens in ask.
   A search box teaches itself — you type words, you get rows with those words
   in them. A question box does not: nothing on screen says whether an answer
   is read out of these talks or invented over them, and that distinction is
   the whole product. So the cold page says it once, in the human channel,
   before it hands anyone a question to click. */
.exnote { margin-top: 8px; max-width: var(--note); color: var(--fg2); font-size: 13px; line-height: 1.5; }

.examples {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  margin-top: var(--s3);
  list-style: none;
}

/* An example's own text *is* the query, so it is set as one: mono, and never
   uppercased — `owl:FunctionalProperty` and `FlashAttention-4` are
   case-sensitive strings and a mangled one is a broken promise. */
.example {
  padding: 9px 12px;
  background: transparent;
  border: 1px solid var(--seam);
  color: var(--fg2);
  font-family: var(--mono);
  font-size: 13.5px;
  font-variant-ligatures: none;
  text-align: left;
  transition: color var(--t-fast) linear, border-color var(--t-fast) linear;
}

.example:hover { color: var(--gold); border-color: var(--gold-44); }

.corpus { margin-top: clamp(26px, 3vw, 40px); border: 1px solid var(--seam2); background: var(--plate); }

/* The one ornament in the system: a plate on a light table. One corner, never
   four. */
.corpus,
.vcard,
.answer,
.connect-box { position: relative; }

.corpus::before,
.vcard::before,
.answer::before,
.connect-box::before {
  content: "";
  position: absolute;
  left: -1px;
  top: -1px;
  width: 12px;
  height: 1px;
  background: var(--gold);
}

.corpus::after,
.vcard::after,
.answer::after,
.connect-box::after {
  content: "";
  position: absolute;
  left: -1px;
  top: -1px;
  width: 1px;
  height: 12px;
  background: var(--gold);
}

.phead {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: 32px;
  padding: 9px 14px;
  background: var(--plate2);
  border-bottom: 1px solid var(--seam);
  flex-wrap: wrap;
}

.phead .r { margin-left: auto; }

.corpus ul { list-style: none; }

.corpus li {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
  padding: 10px 14px;
  border-bottom: 1px solid var(--seam);
  min-width: 0;
}

.corpus li:last-child { border-bottom: 0; }
.corpus li a { font-size: 14px; overflow-wrap: anywhere; }
.corpus li a:hover { color: var(--gold); }

.corpus li .who {
  margin-left: auto;
  flex: none;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg3);
  letter-spacing: .005em;
}

/* ───────────────────────────────────────────── the results — a card per talk */

.results { margin-top: var(--s6); display: flex; flex-direction: column; gap: var(--s5); }

.vcard { background: var(--plate); border: 1px solid var(--seam2); }

.vcard-head {
  display: flex;
  gap: var(--s5);
  align-items: flex-start;
  padding: 14px 16px;
}

.vcard-id { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 6px; }

.vcard-title {
  font-size: 16px;
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: -.02em;
  color: var(--fg);
  overflow-wrap: anywhere;
}

.vcard-title a:hover { color: var(--gold); }

.vcard-meta {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  color: var(--fg2);
  font-size: 13px;
}

.vcard-count {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg3);
  letter-spacing: .005em;
  font-variant-numeric: tabular-nums;
}

/* The moments inside this talk: a hairline list, one line each. The row holds
   two controls — the link over the text and the receipt — because a link
   inside a link is neither valid nor operable. */
.moments { list-style: none; border-top: 1px solid var(--seam); }

.moment {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
  padding: 9px 16px;
  min-width: 0;
  border-bottom: 1px solid var(--seam);
  transition: background var(--t-fast) linear;
}

.moment:last-child { border-bottom: 0; }
.moment:hover { background: var(--plate2); }

.moment-link {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
  flex: 1 1 auto;
  min-width: 0;
}

/* The second it happened, in the machine's face and tabular so a column of
   them lines up. */
.at {
  flex: none;
  font-family: var(--mono);
  font-size: 13px;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
  font-variant-ligatures: none;
}

.moment .badges { flex: none; }

/* One line, and the line is the point: this is a list to skim, and the whole
   sentence is one click away, in the talk, at the second it was said. */
.moment .snip {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.moment .rcpt { margin-left: auto; align-self: center; }

/* A frame moment carries its own picture: the image is what matched, and the
   card's cover is a different second of the same talk. */
.moment.is-frame { align-items: center; padding: 12px 16px; }
.moment.is-frame .moment-link { align-items: center; flex-wrap: wrap; }

/* ─────────────────────────────────────────────────────────── the receipts */

/* The product's signature artifact, and the landing's own slab. The card head
   prints the talk (`youtu.be/<id>`); a moment prints the second (`?t=`), which
   is the pair the whole argument rests on. */
.rcpt {
  display: inline-flex;
  align-items: center;
  flex: none;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -.01em;
  font-variant-ligatures: none;
  color: var(--gold);
  border: 1px solid var(--gold-24);
  padding: 5px 8px;
  white-space: nowrap;
  transition: background var(--t-fast) linear, border-color var(--t-fast) linear;
}

.rcpt .host { color: var(--fg3); }
.rcpt .vid { color: var(--fg); }
.rcpt:hover,
a:hover > .rcpt,
.moment-link:hover .rcpt { background: var(--gold-6); border-color: var(--gold); }

/* The full slab, with the action block filled: three of these on a page of
   sources, and one in the lightbox. Not ten. */
.rcpt.lg { font-size: 13px; border-color: var(--gold); background: var(--gold-6); padding: 0; }
.rcpt.lg .host,
.rcpt.lg .vid { padding: 9px 0 9px 0; }
.rcpt.lg .host { padding-left: 11px; }
.rcpt.lg .t {
  background: var(--gold);
  color: var(--gold-ink);
  font-weight: 700;
  padding: 9px 11px;
  margin-left: 11px;
}
.rcpt.lg:hover { background: var(--gold-12); }

/* ────────────────────────────────────────────────────────────── provenance */

/* Three sources, three kinds of evidence, said in a word. The word carries the
   meaning: face and border differ alongside the colour, so a badge still reads
   on a monochrome screen and to a colour-blind visitor. */
.badges { display: inline-flex; gap: 6px; flex-wrap: wrap; align-items: baseline; }

.badge {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: .19em;
  text-transform: uppercase;
  font-variant-ligatures: none;
  padding: 1px 5px;
  border: 1px solid var(--seam2);
  color: var(--fg2);
  white-space: nowrap;
}

/* Someone said it. */
.badge-spoken { border-style: solid; }

/* The machine read it off the screen — the one thing lime is for. */
.badge-screen { color: var(--seen); border-color: var(--seen-24); background: var(--seen-10); }

/* A visual match: no text was read, so there is no lime on it. */
.badge-frame { color: var(--fg2); background: var(--plate3); border-color: var(--seam2); }

/* ── the snippets, presented as what they are evidence of */

.snip { font-size: 14px; line-height: 1.45; overflow-wrap: anywhere; color: var(--fg); }

/* Spoken: a human said this, so it is set in the human face and quoted. */
.snip-spoken::before { content: "\201C"; color: var(--fg3); }
.snip-spoken::after { content: "\201D"; color: var(--fg3); }

/* On screen: the machine read this, so it is mono and lime — the same green
   as the detection box it was read from. Never a quotation of speech. */
.snip-screen {
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--seen);
  font-variant-ligatures: none;
}

/* A frame matched on its picture. Whatever text rode along is what happened to
   be visible, not the thing that matched: muted, mono, never quoted, never lime. */
.snip-frame {
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--fg2);
  font-variant-ligatures: none;
}

/* Both channels agreed and the longer text won, so it is presented as neither. */
.snip-mixed { font-style: normal; }

.snip mark { background: transparent; color: var(--gold); font-weight: 500; }
.snip-screen mark,
.snip-frame mark { color: var(--fg-max); font-weight: 600; }

/* ───────────────────────────────────────────────────────────────── frames */

/* A still sits on black behind a 1px inset hairline — the glass in front of a
   frame, not elevation. Fixed box, mirrored in the img attributes, so the row
   keeps its height whether the frame loaded, failed, or never existed. */
/* **One box, every kind of hit** (demo-site.md §5, amended 2026-08-11).
   160×90 is the facade's `w=320` at 2x, and it is now the only thumbnail
   geometry on the surface: a card cover, a frame moment, a `frame` citation
   and a `transcript+ocr` citation beside it are the same rectangle. There were
   three sizes — 96×54 for a text hit, 128×72 for a card cover, 160×90 for a
   frame — each defensible on its own and, in one Sources list, a column of
   pictures that changes width per row for a reason the reader cannot see
   (Tom, 2026-08-11). The number is the contract's, not the browser's. */
.hit-thumb {
  flex: none;
  width: 160px;
  height: 90px;
  object-fit: cover;
  background: var(--black);
  box-shadow: var(--edge);
}

.hit-shot {
  flex: none;
  padding: 0;
  background: none;
  border: 0;
  line-height: 0;
  cursor: zoom-in;
}

.hit-shot .hit-thumb { transition: box-shadow var(--t-state) linear; }
.hit-shot:hover .hit-thumb { box-shadow: inset 0 0 0 2px var(--gold); }

.hit-thumb.placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--plate3);
  color: var(--fg3);
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: .17em;
  text-transform: uppercase;
}


/* ─────────────────────────────────────────────── loading, empty, refused */

/* The shape the results will take, reserved before they land. No pulse: a
   breathing block is decoration, and the honest signal that something is
   running is the word in the query bar's state cell. */
.skel { pointer-events: none; }
.skel:nth-child(n + 2) { opacity: .62; }
.skel:nth-child(n + 3) { opacity: .36; }
.skel:nth-child(n + 4) { opacity: .2; }

.skel-block { background: var(--plate2); }
.skel-line { height: .75rem; }
.skel-line + .skel-line { margin-top: var(--s2); }
.moment-skel { height: .9rem; margin: 14px 16px; }
.w-40 { width: 40%; }
.w-70 { width: 70%; }
.w-90 { width: 90%; }

/* A dead end, an error or a wait is a sentence, so it is a sentence's width —
   not a 1300px plate holding six words. */
.notice {
  margin-top: var(--s4);
  padding: var(--s5) var(--s5) var(--s5);
  max-width: var(--note);
  background: var(--plate);
  border: 1px solid var(--seam2);
}

.notice-bad { background: var(--tone-bad-bg); border-color: var(--tone-bad-line); }
.notice-title { font-size: 14px; font-weight: 520; letter-spacing: -.02em; color: var(--fg); }
.notice-detail { margin-top: 6px; color: var(--fg2); font-size: 13px; }

.notice-tips { list-style: none; margin-top: var(--s3); color: var(--fg2); font-size: 13px; }
.notice-tips li + li { margin-top: 6px; }

.ghost {
  display: inline-flex;
  align-items: center;
  height: 2.125rem;
  margin-top: var(--s3);
  padding: 0 12px;
  background: transparent;
  border: 1px solid var(--seam2);
  color: var(--fg2);
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .19em;
  text-transform: uppercase;
  transition: color var(--t-fast) linear, border-color var(--t-fast) linear;
}

.ghost:hover { color: var(--gold); border-color: var(--gold-44); }

/* A button that reads as a link, for the one inline action inside a sentence. */
.linky {
  padding: 0;
  background: none;
  border: 0;
  font: inherit;
  color: var(--gold);
  border-bottom: 1px solid var(--gold-44);
}

#results-foot { margin-top: var(--s4); }
.more { margin-top: 0; }

/* ────────────────────────────────────────────────────────────── the answer */

/* The ask pane. Two rules hold it together, and they are not the same rule:
   **prose is capped at `--prose`**, and **the pane is the page's main event.**

   They used to be one. `--prose` sat on the paragraph inside a plate at the
   full chassis — 620px of text in a 947px box, every line breaking two thirds
   of the way across a bordered plate, a wrap bug rather than a measure — so
   the cap moved to the container and the container shrank to the measure
   (Tom, 2026-08-11). That fixed the paragraph and made the answer, its sources
   and its work log a 670px strip down the left of a 1440 screen: the thing the
   page is for, laid out like a sidebar (Tom, same review, one round later).

   So the measure stays on a *column* and the pane gets the width: above
   `--bp-wide` the sources move beside the answer, the log spans both, and the
   prose still breaks at 70ch because that is what the column is. Below it,
   nothing to put beside anything — the pane is the measure, as before. */
.answer {
  --answer-pad: clamp(18px, 2.2vw, 26px);
  --answer-gap: clamp(24px, 2.6vw, 40px);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-content: start;
  margin-top: var(--s6);
  padding: var(--answer-pad);
  font-size: 15.5px;
  max-width: calc(var(--prose) + 2 * var(--answer-pad));
  background: var(--plate);
  border: 1px solid var(--seam2);
}

/* Every child spans, and the ones that do not are named below. A row that is
   placed by name cannot be moved by a row that is missing. */
.answer > * { grid-column: 1 / -1; min-width: 0; }

/* …except a control, which is its own size. A grid item stretches by default,
   and an `inline-flex` button with an auto width stretches with it: the
   degraded pane's "Search instead" would be a 670px slab of border. */
.answer > .ghost { justify-self: start; }

.answer-prose { max-width: var(--prose); }

@media (min-width: 1380px) {
  /* The pane takes the width the moment it has something to spend it on: a
     live work log, or an answer with sources. A degraded pane — one sentence
     and a button — stays at the measure, because widening a refusal is the
     one place the extra room says nothing.
     The log is the *working* state's whole content, so it is the state that
     decides: `> .worklist` matches only the live log, never the one folded
     into the disclosure under a finished answer. Both states are full width,
     which is why the answer landing changes the pane's *rows* and never its
     width — one reflow at the one moment that has earned it. */
  .answer:has(> .worklist),
  .answer:has(.answer-sources) { max-width: none; }

  /* Three rows, named, because the left column is not just the prose: the
     answer, then how it was found, then what found it. The evidence spans all
     three — a source list is nearly always taller than the paragraphs it
     supports, and letting it set the row heights is what puts the disclosure
     and the model line at the *bottom* of a 900px column instead of tucked
     under two paragraphs with 600px of plate under them. */
  .answer:has(.answer-sources) {
    grid-template-columns: minmax(0, var(--prose)) minmax(0, 1fr);
    grid-template-rows: auto auto minmax(0, 1fr);
    column-gap: var(--answer-gap);
  }

  .answer:has(.answer-sources) > .answer-prose { grid-column: 1; grid-row: 1; }
  .answer:has(.answer-sources) > .worklog { grid-column: 1; grid-row: 2; }
  /* Row 3 takes the slack the Sources column creates, and the model line sits
     at the *top* of it — under the disclosure it belongs to. Pushed to the
     bottom instead, the gap lands between two things that belong together and
     reads as a rendering fault; left here, the same gap is the end of a
     column, which is what it is. */
  .answer:has(.answer-sources) > .answer-foot {
    grid-column: 1;
    grid-row: 3;
    align-self: start;
  }

  /* Beside the answer, separated by the hairline that would otherwise have
     been above it. It stretches to the rows it spans, so the rule runs the
     full height of the pair and reads as a column rule, not a box edge. */
  .answer:has(.answer-sources) > .answer-sources {
    grid-column: 2;
    grid-row: 1 / 4;
    margin-top: 0;
    padding-left: var(--answer-gap);
    border-top: 0;
    border-left: 1px solid var(--seam);
  }

  .answer > .answer-sources .sources-title { margin-top: 0; }
}

/* The answer is prose a human reads, so it is the human face at the measure its
   container carries. The markers are the only gold in it. */
.answer p {
  font-size: 15.5px;
  line-height: 1.68;
  color: var(--fg);
}

.answer p + p { margin-top: var(--s3); }

.answer .cite {
  font-family: var(--mono);
  font-size: .72em;
  font-weight: 700;
  color: var(--gold);
  vertical-align: super;
  letter-spacing: 0;
}

.answer .cite:hover { border-bottom: 1px solid var(--gold-44); }

.degraded { color: var(--fg); }
.degraded-wait { color: var(--tone-warn); }

.thinking { color: var(--fg2); font-family: var(--mono); font-size: 12.5px; }

/* The idle line while a tool call owns the caret: gone from the screen and from
   the accessibility tree, still holding its line. `visibility` and not
   `display`, because a line that leaves the flow six times an ask is a document
   that changes height six times an ask — which a reader parked at the bottom of
   the page reads as the page jumping under them. Nothing here moves; one line
   of console ground goes quiet. */
.thinking.is-parked { visibility: hidden; }

/* The one moving thing on this page, and it reports real work: a gold block
   that blinks while the machine is actually inside the corpus. It is the
   landing's caret, doing the landing's job — "the agent is asking". */
.thinking::after,
.work.is-running::after {
  content: "";
  display: inline-block;
  width: 8px;
  height: 13px;
  margin-left: 6px;
  background: var(--gold);
  vertical-align: -2px;
  animation: blink var(--blink);
}

@keyframes blink { 0%, 52% { opacity: 1; } 53%, 100% { opacity: 0; } }

.answer-sources { margin-top: var(--s6); border-top: 1px solid var(--seam); }

.sources-title {
  margin-top: var(--s4);
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .19em;
  text-transform: uppercase;
  color: var(--fg2);
}

/* A cited moment is a search row: same evidence, same badges, same receipt.

   A grid, not a flex row: as a wrapping flex line the row's shape was a
   function of how long the talk's title happened to be — a short title sat
   beside its frame, a long one pushed the whole text column onto a second flex
   line *under* the frame, and the receipt onto a third (Tom, 2026-08-11). The
   corpus's titles run from 20 to 120 characters, so that is a row that changes
   layout per talk. Three columns instead: the marker, the frame at the fixed
   box `.hit-thumb` already reserves, and everything else — with every child
   placed by name, so a row rendered without its `[n]` (a search row, one day)
   collapses the first column rather than shifting the other two. */
.hit {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr);
  column-gap: var(--s4);
  row-gap: var(--s3);
  align-items: start;
  padding: var(--s4) 0;
  border-bottom: 1px solid var(--seam);
}

.hit:last-child { border-bottom: 0; }
.hit > .cite-n { grid-column: 1; grid-row: 1; }
.hit > .hit-shot,
.hit > .hit-thumb { grid-column: 2; grid-row: 1; }
.hit > .hit-link { grid-column: 3; grid-row: 1; }
/* Under the text it belongs to, never under the frame: the receipt ends the
   row the way it ends a moment, and it is the widest fixed thing here — on the
   same line it would eat the title's column at the answer's measure. */
.hit > .rcpt { grid-column: 3; grid-row: 2; justify-self: end; }
.hit .snip { display: block; }

.cite-n {
  width: 1.6rem;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
}

.hit-link { min-width: 0; }
.hit-body { display: flex; flex-direction: column; gap: 6px; min-width: 0; }

/* Two lines, then an ellipsis. The title is the row's label, not its content:
   the evidence is the snippet under it and the receipt beside it, and the whole
   thing is one click from the second it happened. */
.hit-title {
  font-size: 14px;
  font-weight: 520;
  letter-spacing: -.02em;
  overflow-wrap: anywhere;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

.hit-link:hover .hit-title { color: var(--gold); }

.hit-meta {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  color: var(--fg2);
  font-size: 13px;
}

.answer-foot {
  margin-top: var(--s5);
  color: var(--fg3);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .005em;
}

/* ──────────────────────────────────────────────────────────── the work log */

/* What the model did, while it does it: one line per tool call, the result
   appended when it lands. The console's own ground, because this is a surface
   the machine writes into. */
.worklist {
  list-style: none;
  padding: 14px 16px;
  background: var(--console);
  border: 1px solid var(--seam2);
  font-family: var(--mono);
  font-size: 12.5px;
  line-height: 1.85;
  color: var(--fg2);
  font-variant-ligatures: none;
  overflow-wrap: anywhere;
}

/* **The live log is a fixed box.** It used to be a list that grew: one line
   per tool call, and every line pushed the rest of the document — the connect
   band and the footer — down 46px, four to eight times an ask. That is the
   stutter Tom kept seeing after the idle line was parked (2026-08-11); the
   idle line was one symptom of it and the log itself was the rest. Measured on
   the stub at 1440: 4 growth shifts totalling CLS 0.078 before the answer even
   arrived.

   So the space the log will need is reserved before it needs it, exactly as
   the results skeleton reserves the shape of a page of cards (§6.1) — and for
   the same reason: the page must not move under a reader while the machine
   works. Six lines is the reservation; a longer run scrolls inside the box and
   the newest line is scrolled to, which is the one you want to be reading.
   Nothing outside the box changes height at all until the answer lands.
   `scrollbar-gutter` so the moment a seventh line appears the text inside does
   not reflow either, and `overscroll-behavior` so reaching the bottom of the
   log does not then start scrolling the page. */
.answer > .worklist {
  --log-lines: 6;
  height: calc(var(--log-lines) * 1.85em + 30px);
  overflow-y: auto;
  scrollbar-gutter: stable;
  overscroll-behavior: contain;
}

.work.is-running { color: var(--fg); }
.work-result { color: var(--fg); }
.work.is-running .work-result { display: none; }

.thinking { margin-top: var(--s3); }

/* Folded away once the answer owns the pane. */
.worklog { margin-top: var(--s5); border-top: 1px solid var(--seam); padding-top: var(--s3); }

.worklog > summary {
  cursor: pointer;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .19em;
  text-transform: uppercase;
  color: var(--fg2);
}

.worklog > summary:hover { color: var(--gold); }
.worklog[open] > summary { margin-bottom: var(--s3); }
.worklog .worklist { margin-top: var(--s2); }

/* ──────────────────────────────────────────────────────────── the lightbox */

/* A thumbnail is 160 pixels of somebody's slide: enough to recognise, never
   enough to read. The dialog element is the *backdrop* — it carries no padding,
   so `.shot-inner` covers every pixel of the box and a click that lands on the
   dialog itself landed outside the picture. */
.shot {
  width: min(96vw, 64rem);
  max-width: min(96vw, 64rem);
  padding: 0;
  background: var(--plate);
  border: 1px solid var(--seam2);
  box-shadow: var(--drop);
  color: var(--fg);
}

.shot::backdrop { background: var(--scrim); }
.shot-inner { position: relative; }

.shot-img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: contain;
  background: var(--black);
  box-shadow: var(--edge);
}

.shot-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: 12px 14px;
  background: var(--plate2);
  border-top: 1px solid var(--seam);
}

.shot-caption { font-size: 14px; overflow-wrap: anywhere; min-width: 0; }

.shot-close {
  position: absolute;
  top: 12px;
  right: 12px;
  margin: 0;
  background: var(--console);
}

/* ────────────────────────────────────────────── add it to your own agent */

.connect { background: var(--void); border-top: 1px solid var(--seam); padding: var(--beat) 0; }
.connect h2 { font-size: clamp(1.5rem, 2.4vw, 2rem); font-weight: 210; color: var(--fg); }
.connect .lede { margin-top: var(--s3); }

.connect-box {
  margin-top: var(--gap-head);
  background: var(--plate);
  border: 1px solid var(--seam2);
}

.copyrow { display: flex; align-items: stretch; min-width: 0; border-top: 1px solid var(--seam); }
.copyrow + .phead { border-top: 1px solid var(--seam); }

.copyrow code {
  flex: 1 1 0;
  min-width: 0;
  padding: 14px 16px;
  overflow-x: auto;
  white-space: nowrap;
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: -.01em;
  font-variant-ligatures: none;
  color: var(--fg);
}

.copyrow code i { font-style: normal; color: var(--fg3); }

.copyrow button {
  flex: none;
  padding: 0 15px;
  background: transparent;
  border: 0;
  border-left: 1px solid var(--seam2);
  color: var(--gold);
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .19em;
  text-transform: uppercase;
  transition: background var(--t-fast) linear, color var(--t-fast) linear;
}

.copyrow button:hover { background: var(--gold); color: var(--gold-ink); }
.copyrow button[disabled]:hover { background: transparent; color: var(--gold); }

/* ────────────────────────────────────────────────────────────── the footer */

footer { background: var(--void); border-top: 1px solid var(--seam); padding: var(--beat) 0 clamp(34px, 4vw, 56px); }

.fgrid {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: clamp(24px, 3vw, 60px);
  align-items: start;
}

.fgrid p { font-size: 14.5px; line-height: 1.62; color: var(--fg2); max-width: 56ch; }
.fgrid p + p { margin-top: var(--s3); }
.fgrid .ml { display: block; margin-bottom: var(--s3); }
.fgrid a.lnk { color: var(--gold); border-bottom: 1px solid var(--gold-44); }

/* The signature, at the end, after the argument — the one place the word is
   allowed to be large. */
.fmark { display: flex; align-items: flex-end; justify-content: flex-end; }

.fmark b {
  font-size: clamp(2.2rem, 4.4vw, 4rem);
  font-weight: 250;
  line-height: .9;
  letter-spacing: -.045em;
  color: var(--fg);
}

.fmark b i { font-style: normal; color: var(--gold); }

.fbase {
  display: flex;
  gap: var(--s5);
  flex-wrap: wrap;
  align-items: baseline;
  margin-top: clamp(24px, 2.6vw, 38px);
  padding-top: var(--s4);
  border-top: 1px solid var(--seam);
}

.fbase .r { margin-left: auto; }

/* ─────────────────────────────────────────────────────────────── stacking */

/* --bp-hand: body drops to 15px, chrome tightens, on-image marks are dropped
   rather than shrunk, and multi-column grids go to one column. */
@media (max-width: 780px) {
  body { font-size: 15px; }
  .mark b { font-size: 15px; }
  .mark { flex: none; }
  .railin { padding-top: 12px; padding-bottom: 12px; gap: 10px; }
  .railmeta { gap: 10px; }
  .browse { padding: 0 9px; letter-spacing: .12em; }
  /* Words that a 390px rail has no room for. The label still says what the
     link does; it just stops saying it twice. */
  .hide-s { display: none; }

  /* The action goes full width under the input: a 390px screen has no room
     for a pair that still fits a query. The state cell stays on the input's
     own line, because it is the input's state. */
  .slug { flex-wrap: wrap; height: auto; }
  .slug .ic,
  #q,
  .st { height: 46px; }
  #q { width: 0; }
  .st { padding: 0 10px; }
  .cta {
    order: 4;
    flex: 1 0 100%;
    height: 46px;
    justify-content: center;
    border: 1px solid var(--gold);
    border-top: 0;
  }

  .controls { justify-content: flex-start; }
  .modes { margin-left: 0; }
  .chips button { flex: 1 1 auto; }

  .vcard-head { gap: var(--s3); padding: 12px; }
  /* Still one box for every kind, one size down: 160×90 beside a 390px row
     leaves no room for the text. The full-width variant a frame moment used to
     take here is gone with the second geometry — the moment row already goes
     to three lines at this width (§6.5), so the picture has the row above its
     text either way. */
  .hit-thumb { width: 112px; height: 63px; }

  /* The moment goes to three lines rather than losing its receipt: the receipt
     is the argument, and a truncated one proves nothing. The snippet is still
     a skim — two lines, clamped — because the whole sentence is one tap away
     in the talk itself. */
  .moment { flex-wrap: wrap; padding: 10px 12px; gap: var(--s2); }
  .moment-link { flex: 1 0 100%; flex-wrap: wrap; gap: var(--s2); }
  .moment .snip {
    flex: 1 0 100%;
    white-space: normal;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .moment .rcpt { margin-left: 0; }

  .fgrid { grid-template-columns: 1fr; gap: var(--s6); }
  .fmark { justify-content: flex-start; }
  .hit { gap: var(--s3); }
  /* The receipt is wider than the text column a 390px screen leaves, and a
     truncated receipt proves nothing — so it takes the whole row rather than
     hanging out of one third of it. The frame and the text keep their columns. */
  .hit > .rcpt { grid-column: 1 / -1; }
  .shot { width: 96vw; max-width: 96vw; }
}

/* Everything freezes, and freezing means painting the end state — never
   dropping the content. */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
  .thinking::after,
  .work.is-running::after { animation: none; opacity: 1; }
}
