/* ══ THE LECTURE SURFACE — A DECK ════════════════════════════════════════════
   js/11i-lecture.js (#screen-lecture, #screen-notebook) and
   js/11j-lecture-teacher.js (#screen-lecture-teacher).

   MARK'S VERDICT, 2026-08-30, which this file is the answer to:
     "there is no formatting that makes it feel cohesive and clean"
     "i need more of a slide deck aesthetic for all of this and it would be
      nice if it could go full screen"
   It used to render a card with prose in it — a handout. It is now a DECK.

   THE DIRECTION: THE BROADSHEET, PROJECTED. Not a slide library dropped into
   this app. Everything below is the same printing system 08-bs-masthead.css
   and 28-stations.css already speak — Everglade ink on newsprint, Fraunces
   display, Archivo body, agate mono furniture, hard edges, RULES INSTEAD OF
   BOXES — set at wall size. A slide here is a folio: a heavy rule at the top,
   a running head across it, one headline big enough to carry to the back of a
   room, the story unfolding beneath it, and a folio number hanging in the
   right margin. No cards, no shadows, no rounded corners, no gradient.

   THE ASYMMETRY IS THE PRODUCT'S OWN RULING. Mark: "we should not really have
   a large margin on the left, just the right hand side" (01-tokens.css,
   --doc-gutter). The slide honours it — `--deck-pad-l` is roughly half
   `--deck-pad-r`, so the type hangs on a firm left edge and the generous space
   is on the right, where the folio number and the marginalia live.

   TYPE SCALES WITH THE STAGE, NOT WITH THE VIEWPORT, and that is the whole
   reason fullscreen is worth having. `cqi` resolves against the stage's own
   inline size, so the same slide is ~53px of headline inside the 1080px page
   column and ~75px filling a 1440px screen — it genuinely gets bigger when he
   presses F, which a `vw` clamp would not do. Every `cqi` line is preceded by
   a `vw` fallback: a browser without container queries drops the second
   declaration and keeps a sane approximation rather than a broken page.

   ONE PALETTE, RETARGETED — the same trick 01-tokens.css documents for the
   broadsheet conversion, one level down. The deck is written ONCE against the
   `--bs-*` names; the dark theme redefines those NAMES on `body` while a
   lecture screen is active. So `T` repaints the running head, the page head,
   the slide, the boxes and the console together and nothing can be left
   behind, because there is no second set of declarations to forget.

   NOTHING ANIMATES ON A BLOCK CHANGE — but a REVEAL does, and that is a
   reversal of this file's old rule, made deliberately. A re-render when the
   teacher moves the class is still a hard cut, because a transition on that
   boundary reads as a jolt (28-stations.css makes the same argument for the
   rotation). A step arriving inside a slide is the opposite: it is the room
   being shown something, one gesture, the rule drawing down before the words
   land. Both come off entirely under prefers-reduced-motion.

   TWO BOXES THAT MUST NEVER LOOK ALIKE. "Your answer" goes to the teacher;
   "your notes" do not and outlive the lecture. Three independent signals carry
   it — a stamped SHARED / PRIVATE chip, the ink/pencil treatment, and a dashed
   rule that puts the note below the line the answer sits above. A student on a
   Chromebook must never wonder which box the question wants.

   THE SAME SLIDE RENDERS ON BOTH SCREENS. There is deliberately no
   `.deck-stage.is-teacher` rule below — the class is emitted so the mirror can
   be asserted and inspected (tests/test_lecture_surface.py), not so it can be
   restyled. The first declaration written against it is the moment the two
   surfaces start diverging again.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── the page these screens sit on ────────────────────────────────────────── */

.bs .lec, .bs .ltt {
  /* ONE reading measure, shared with the daily (01-tokens.css) and used for
     everything on this surface that is READ rather than PROJECTED: the
     notebook lede, a failure panel, a student's own answer box. */
  --lec-read: var(--read-measure);
  width: 100%;
}

/* THE PAGE TITLE IS DEMOTED ON A DECK SCREEN, and that is a hierarchy fix
   rather than a space saving. 01-tokens.css sets the ladder — "nothing in the
   content column is allowed to outrank the page's own name" — and on every
   other screen the loudest thing IS a page title at 46px. Here the loudest
   thing is the slide's own headline at ~53px in the column and ~75px
   fullscreen, so leaving "Lecture" at 46px above it puts two display serifs
   eight pixels apart doing the same job: exactly the "not an organic
   header-title pairing" defect 02-chrome.css already logged.

   So the word "Lecture" becomes furniture — agate, tracked, the same register
   as the back link beside it — and the slide's running head carries the
   session identity instead. The ladder is restored, not broken: wordmark 34 ·
   page label 12 · SLIDE TITLE 53. */
.bs .lec .page-head,
.bs .ltt .page-head {
  padding-top: 18px;
  margin-bottom: 14px;
  max-width: none;
  display: flex;
  align-items: baseline;
  gap: 18px;
}

.bs .lec .page-head .page-kicker,
.bs .ltt .page-head .page-kicker { margin: 0; }

.bs .lec .page-head .page-title,
.bs .ltt .page-head .page-title {
  font-family: var(--bs-mono);
  font-variation-settings: normal;
  font-weight: 600;
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
  line-height: 1.6;
}

/* ── the deck ─────────────────────────────────────────────────────────────── */

.bs .deck-shell {
  --deck-paper: var(--bs-sheet);
  --deck-ground: var(--bs-stock);
  --deck-ink: var(--bs-ink);
  --deck-ink-2: var(--bs-ink-2);
  --deck-ink-3: var(--bs-ink-3);
  --deck-rule: var(--bs-rule);
  --deck-accent: var(--bs-blue);      /* readable — bronze */
  --deck-mark: var(--bs-camel);       /* decorative only — 2.13:1 */

  /* The console is the one object on this surface that is NOT the slide, so it
     is the one object with its own ground. These two are the only colours the
     retarget cannot derive: inverting `--bs-ink` in dark would turn the bar
     into a floodlight in a room with the lights off. */
  --deck-console-bg: var(--bs-ink);
  --deck-console-fg: var(--bs-sheet);

  /* ── SHRINK TO FIT ──────────────────────────────────────────────────────
     A SLIDE THAT SCROLLS IS A BUG, not a feature. Measured 2026-08-30 at
     1440x900 on the real 130-block session, at each dense slide's LAST reveal
     step: block 12 (the nine-word lūx glossary — the payoff of the Latin hour)
     rendered 949px into a 746px stage and hid its last three glosses. Mark
     drives this from a projector with arrow keys; he will not scroll, and every
     mirrored student screen was cut in the same place.

     So the deck does what presentation software does: it sets the type as large
     as it can while the whole slide still FITS. `_deckFit` in 11i-lecture.js
     measures scrollHeight against clientHeight after every render and every
     reveal, and walks this one multiplier down in 4% steps until the overflow
     is gone. Everything that contributes to slide HEIGHT goes through it — the
     type ladder and the vertical rhythm both — because shrinking type alone
     recovers about half of what is needed.

     THREE THINGS ARE DELIBERATELY NOT SCALED. The agate furniture (strand,
     folio, key hints) stays put: it is 10-13px already and it is the
     orientation a student at the back reads, not the content. The horizontal
     padding stays put: narrowing the column would re-wrap every line and
     change the height it is trying to measure, which is a feedback loop rather
     than a fit. And `container-type: inline-size` means `cqi` resolves against
     WIDTH only, so scaling the type cannot change the container and the walk
     is monotonic — it terminates.

     The floor is 0.66. Below that the room cannot read it and the honest
     answer is that the slide is too long and should be split, so the stage
     keeps `overflow-y: auto` underneath as a safety net rather than clipping
     silently. See ALLOWED_SCROLLERS in tests/test_page_wrapper_scrolling.py
     for what that measures today. */
  --deck-fit: 1;

  /* THE LADDER. Fallback first (vw), real value second (cqi). See the header. */
  --deck-t-agate: 11px;
  --deck-t-agate: clamp(10px, .85cqi, 13px);
  --deck-t-folio: clamp(15px, 1.2vw, 20px);
  --deck-t-folio: clamp(15px, 1.5cqi, 22px);
  --deck-t-title: calc(clamp(30px, 4vw, 56px) * var(--deck-fit));
  --deck-t-title: calc(clamp(30px, 5.2cqi, 76px) * var(--deck-fit));
  --deck-t-lead: calc(clamp(20px, 2.2vw, 32px) * var(--deck-fit));
  --deck-t-lead: calc(clamp(20px, 2.8cqi, 40px) * var(--deck-fit));
  --deck-t-body: calc(clamp(16px, 1.4vw, 21px) * var(--deck-fit));
  --deck-t-body: calc(clamp(16px, 1.8cqi, 26px) * var(--deck-fit));
  --deck-t-dev: calc(clamp(20px, 2.4vw, 34px) * var(--deck-fit));
  --deck-t-dev: calc(clamp(20px, 3cqi, 46px) * var(--deck-fit));
  /* The board's own two sizes, tokens rather than literals precisely so the
     fit walk reaches the glossary — which is the slide that needed it. */
  --deck-t-word: calc(clamp(19px, 2.1cqi, 32px) * var(--deck-fit));
  --deck-t-gloss: calc(clamp(14px, 1.3cqi, 19px) * var(--deck-fit));
  --deck-t-wall: calc(clamp(20px, 2.6cqi, 40px) * var(--deck-fit));
  /* The react phase. Deliberately the SMALLEST thing on the board: the word is
     the teaching content and the tag is the instrument. It still rides the fit
     walk, because a tag row a projector cannot read is not a control. */
  --deck-t-tag: calc(clamp(11px, 1.05cqi, 15px) * var(--deck-fit));
  --deck-t-tagn: calc(clamp(13px, 1.35cqi, 20px) * var(--deck-fit));

  --deck-pad-y: calc(clamp(18px, 2vw, 34px) * var(--deck-fit));
  --deck-pad-y: calc(clamp(18px, 2.6cqi, 44px) * var(--deck-fit));
  --deck-pad-l: clamp(18px, 2.4vw, 40px);
  --deck-pad-l: clamp(18px, 3.2cqi, 54px);
  --deck-pad-r: clamp(18px, 4.4vw, 76px);
  --deck-pad-r: clamp(18px, 6.5cqi, 110px);
  --deck-gap: calc(clamp(14px, 1.5vw, 24px) * var(--deck-fit));
  --deck-gap: calc(clamp(14px, 1.9cqi, 30px) * var(--deck-fit));

  display: block;
}

/* THE SLIDE. A sheet on the stock, under a heavy rule — the same folio device
   the masthead and the front page use, which is what makes a deck built here
   read as the same printing system rather than a second visual language. */
.bs .deck-stage {
  container-type: inline-size;
  display: flex;
  flex-direction: column;
  gap: var(--deck-gap);
  position: relative;
  background: var(--deck-paper);
  border-top: 3px solid var(--deck-ink);
  border-bottom: 1px solid var(--deck-rule);
  /* NO VERTICAL PADDING HERE. The sticky head and foot own it instead (their
     own padding), so their paper ground covers the scrollport's top and bottom
     edge completely. With the padding on the stage there was a band of it
     BELOW the pinned foot, and the answer box scrolled through it in full
     view — seen in a browser at 1440, which is where it is visible. */
  padding: 0 var(--deck-pad-r) 0 var(--deck-pad-l);
  /* No blur. A printed sheet has an EDGE, not a glow — one hard offset rule
     reads as paper lying on paper, a soft shadow reads as a web card. */
  box-shadow: 2px 2px 0 var(--deck-rule);
  min-height: min(74vh, 680px);
  max-height: calc(100vh - 150px);
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-gutter: stable;
}

/* NO FOCUS RING ON THE SLIDE ITSELF. The stage is `tabindex="-1"` — it is a
   PROGRAMMATIC focus target, taken on entering fullscreen so the arrow keys
   reach the document instead of whatever button was last clicked. It can never
   be reached by Tab, so a ring on it tells a keyboard user nothing, and Chrome
   still matched :focus-visible after a keypress and drew a 2px gold frame
   around the whole projected slide. Seen in fullscreen at 1440. Every control
   INSIDE the slide keeps its own ring. */
.bs .deck-stage:focus, .bs .deck-stage:focus-visible { outline: none; }

/* ── the running head ─────────────────────────────────────────────────────── */

/* Sticky against the stage's own scroller, so a slide long enough to scroll
   never loses which strand and which slide the room is on. */
/* THE COMPOSITION IS CENTRED IN THE FRAME, and this one declaration is what
   does it. `.deck-foot` already carried `margin-top: auto`, which pinned the
   foot to the bottom and left every slide's content stranded against the top
   with the whole difference as a dead band underneath — measured 2026-08-31 at
   1440x900 fullscreen: 82.1% of the readable band empty on a one-beat slide,
   44.8% on a six-beat one. A second auto margin under the HEAD makes the free
   space split above and below the content instead of all falling to the foot.
   When the slide is full both margins resolve to zero and nothing moves. */
.bs .deck-head {
  position: sticky;
  top: 0;
  z-index: 3;
  display: flex;
  align-items: baseline;
  gap: clamp(10px, 2cqi, 26px);
  margin: 0 0 auto;
  padding: var(--deck-pad-y) 0 8px;
  background: var(--deck-paper);
  border-bottom: 1px solid var(--deck-rule);
  font-family: var(--bs-mono);
  font-size: var(--deck-t-agate);
  letter-spacing: .16em;
  text-transform: uppercase;
}

/* The three cores are colour-coded because they repeat every session for three
   years; a student should know which core they are in before reading a word.
   Measured against the stock: blue 4.98, ok 5.36, pen 4.76. Camel is NEVER on
   a `color:` — it is 2.13:1 and decorative only (01-tokens.css). */
.bs .deck-strand { color: var(--deck-ink-3); font-weight: 600; }
.bs .deck-stage[data-strand="latin"]   .deck-strand { color: var(--bs-blue); }
.bs .deck-stage[data-strand="logic"]   .deck-strand { color: var(--bs-ok); }
.bs .deck-stage[data-strand="writing"] .deck-strand { color: var(--bs-pen); }

.bs .deck-sess {
  flex: 1;
  min-width: 0;
  color: var(--deck-ink-3);
  letter-spacing: .1em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The folio number, set like one: display serif, tabular, hanging into the
   right margin the asymmetric padding opened for it. */
.bs .deck-pos {
  flex: 0 0 auto;
  font-family: var(--bs-ed);
  font-size: var(--deck-t-folio);
  font-weight: 700;
  letter-spacing: .02em;
  font-variant-numeric: tabular-nums;
  color: var(--deck-ink-2);
  margin-right: calc(var(--deck-pad-r) * -0.5);
}

/* ── the headline ─────────────────────────────────────────────────────────── */

/* THE ONE THING THAT CARRIES ACROSS A ROOM. Everything else on the slide is
   quiet so that this can be loud; hierarchy is scale contrast, not four
   competing weights. */
.bs .deck-title {
  font-family: var(--bs-ed);
  font-variation-settings: 'SOFT' 0, 'WONK' 1;
  font-weight: 900;
  font-size: var(--deck-t-title);
  line-height: 1.02;
  letter-spacing: -.022em;
  text-wrap: balance;
  color: var(--deck-ink);
  margin: 0;
  max-width: 20ch;
}

/* ── the staged reveal ────────────────────────────────────────────────────── */

.bs .deck-beats {
  display: flex;
  flex-direction: column;
  gap: var(--deck-gap);
}

/* An unrevealed step is GONE, not faint. Mark teaches socratically: step 0 is
   a bare question, and a step that is merely dimmed is still on the projector
   and still readable from the second row. `display:none` also keeps it out of
   the accessibility tree, so a screen reader cannot read the answer out either. */
.bs .deck-beat { display: none; }

.bs .deck-beat.is-on {
  display: block;
  position: relative;
  padding-left: clamp(14px, 2.2cqi, 32px);
}

/* The beat's own rule, which is what makes a reveal read as deliberate: the
   bar draws DOWN first, then the words arrive under it. One gesture.

   ONLY THE FOCAL BEAT CARRIES IT. Every revealed beat used to, and nine
   identical gold bars down the left of a slide is a bulleted list wearing a
   different mark — Mark, 2026-08-31: "nothing is more important than anything
   else." The bar now says WHERE THE ROOM IS: it moves down the slide as he
   reveals, and what has already been worked through keeps a hairline. */
.bs .deck-beat.is-on::before {
  content: '';
  position: absolute;
  left: 0;
  top: .18em;
  bottom: .1em;
  width: 1px;
  background: var(--deck-rule);
  transform-origin: top center;
}

.bs .deck-beat.is-focal::before { width: 3px; background: var(--deck-mark); }

/* Receding is a COLOUR step, not an opacity one. A worked-through beat still
   has to be legible from the back row — this deck is read at four metres — so
   it goes to the agate ink the folio already uses, measured 6.24:1 on the sheet
   and 5.34:1 on the stock, rather than being faded toward the paper. */
.bs .deck-beat.is-on .lec-prose { color: var(--deck-ink-3); }
.bs .deck-beat.is-focal .lec-prose { color: var(--deck-ink-2); }
.bs .deck-beat.is-on .lec-prose strong,
.bs .deck-beat.is-on .lec-prose em { color: var(--deck-ink-2); }
.bs .deck-beat.is-focal .lec-prose strong,
.bs .deck-beat.is-focal .lec-prose em { color: var(--deck-ink); }
.bs .deck-beat.is-on .lec-lead-t { color: var(--deck-ink-2); }
.bs .deck-beat.is-focal .lec-lead-t { color: var(--deck-ink); }

/* ── the four shapes a slide takes ────────────────────────────────────────── */

/* WHY THERE ARE FOUR. The Day 1 deck runs three-word definitions, five-item
   prefix lists, a ten-item glossary and a page of prose, and until 2026-08-31
   all four rendered as the same top-left stack of 17px body type. `data-layout`
   is written by `_deckLayout` in 11i-lecture.js off the beat COUNT and the
   longest beat, over the whole slide rather than the current reveal, so the
   frame does not re-compose itself under the room on every →.

   The SIZES here are ratios, not absolutes: `--deck-fit` walks the whole ladder
   up until the slide fills its frame and down until it fits, so what these
   declare is how loud each shape is relative to the rest of the slide. */

/* CARD — one or two short beats. The words ARE the slide, so they are set at
   the headline's own register and the frame belongs to them. */
.bs .deck-stage[data-layout="card"] .deck-beats { gap: calc(var(--deck-gap) * .6); }

.bs .deck-stage[data-layout="card"] .deck-beat .lec-prose,
.bs .deck-stage[data-layout="card"] .deck-beat .lec-p {
  font-size: var(--deck-t-dev);
  line-height: 1.18;
  max-width: 22ch;
}

/* FEW — three or four beats. A spaced group at lead size: still one idea per
   line, but the lines are heard rather than read. */
.bs .deck-stage[data-layout="few"] .deck-beats { gap: calc(var(--deck-gap) * 1.15); }

.bs .deck-stage[data-layout="few"] .deck-beat .lec-prose,
.bs .deck-stage[data-layout="few"] .deck-beat .lec-p {
  font-size: var(--deck-t-lead);
  line-height: 1.24;
  max-width: 26ch;
}

/* GRID — five or more short beats. Nine glosses down one edge is a list; the
   same nine in two columns is a board. The container query is on the STAGE
   (`container-type: inline-size`), so a phone keeps the single column without
   a viewport breakpoint having to guess where the slide is. */
.bs .deck-stage[data-layout="grid"] .deck-beats {
  column-gap: calc(var(--deck-gap) * 1.6);
  row-gap: calc(var(--deck-gap) * .8);
}

@container (min-width: 620px) {
  .bs .deck-stage[data-layout="grid"] .deck-beats {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-content: start;
  }
  .bs .deck-stage[data-layout="grid"] .deck-beat.is-lede { grid-column: 1 / -1; }
}

.bs .deck-stage[data-layout="grid"] .deck-beat .lec-p { max-width: none; }

/* THE LEADING QUESTION. Every socratic slide opens with one and then answers
   it, so it stands over the answers instead of sharing a cell with the first
   of them. Quieter than the answers on purpose: once the room has answered it,
   it is scaffolding. */
.bs .deck-beat.is-lede .lec-prose,
.bs .deck-beat.is-lede .lec-p {
  font-size: var(--deck-t-lead);
  line-height: 1.24;
  max-width: 30ch;
}

.bs .deck-stage[data-layout="grid"] .deck-beat.is-lede { margin-bottom: calc(var(--deck-gap) * .4); }

.bs .deck-beat.is-fresh::before { animation: deck-rule-in 240ms var(--ease-out) both; }
.bs .deck-beat.is-fresh > * { animation: deck-beat-in 420ms var(--ease-out) 130ms both; }

@keyframes deck-rule-in {
  from { transform: scaleY(0); }
  to   { transform: scaleY(1); }
}

@keyframes deck-beat-in {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

/* ── prose inside a slide ─────────────────────────────────────────────────── */

/* THE MEASURE IS ON THE PARAGRAPH, NOT ON THE COLUMN. Capping `.lec-prose`
   itself also capped the stem TABLE inside it — a five-row table of Latin sat
   at 626px in the middle of a 1024px slide with a third of the sheet empty
   beside it, measured at 1440. A sentence wants 34em; a table wants the slide. */
.bs .lec-prose {
  font-size: var(--deck-t-body);
  line-height: 1.5;
  color: var(--deck-ink-2);
}

.bs .lec-p { margin: 0 0 .62em; max-width: 34em; }
.bs .lec-prose > .lec-p:last-child { margin-bottom: 0; }
.bs .lec-prose strong { color: var(--deck-ink); font-weight: 700; }
.bs .lec-prose em { color: var(--deck-ink); font-style: italic; }

/* A line that is nothing but a bolded phrase — `**PORTĀRE** — to carry.` — is
   a lead-in, not a sentence. Set as prose at body weight it read as shouting;
   set as a second display line it becomes the sub-head it always was, and the
   Latin finally gets a register of its own. */
/* THE TERM AND ITS GLOSS ON ONE LINE, in two registers. The term takes the
   display face at half the headline — big enough that a root is the second
   loudest thing on a Latin slide, which is the point — and the gloss stays in
   the body face beside it so the pair reads as one object rather than as a
   heading with an orphaned sentence under it. */
.bs .lec-lead {
  max-width: 32em;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 .1em;
  margin: 0 0 .4em;
}

.bs .lec-lead-t {
  font-family: var(--bs-ed);
  font-variation-settings: 'SOFT' 0, 'WONK' 1;
  font-weight: 800;
  font-size: var(--deck-t-lead);
  line-height: 1.1;
  letter-spacing: -.012em;
  color: var(--deck-ink);
}

.bs .lec-lead-w {
  font-size: var(--deck-t-body);
  line-height: 1.4;
  color: var(--deck-ink-2);
}

.bs .lec-lead-w::before {
  content: '\2014';
  color: var(--deck-ink-3);
  margin: 0 .42em 0 .34em;
}

/* A wide table scrolls inside its own box rather than panning the whole slide
   sideways — the containment argument 01-tokens.css makes for display maths. */
.bs .lec-table-wrap {
  overflow-x: auto;
  overflow-y: hidden;
  max-width: 100%;
  padding-bottom: 4px;
}

.bs .lec-table {
  border-collapse: collapse;
  margin: 0;
  width: 100%;
  font-size: max(14px, calc(var(--deck-t-body) * .82));
}

.bs .lec-table th {
  font-family: var(--bs-mono);
  font-size: var(--deck-t-agate);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--deck-ink-3);
  text-align: left;
  border-bottom: 2px solid var(--deck-ink);
  padding: 0 clamp(12px, 2cqi, 30px) 6px 0;
  white-space: nowrap;
}

.bs .lec-table td {
  border-bottom: 1px solid var(--deck-rule);
  padding: .55em clamp(12px, 2cqi, 30px) .55em 0;
  color: var(--deck-ink-2);
  vertical-align: baseline;
}

/* The first column of a stem table is the Latin. It is the subject of the
   slide, so it is set in the display face at the display colour rather than
   being one more cell of grey. */
.bs .lec-table td:first-child {
  font-family: var(--bs-ed);
  font-weight: 700;
  color: var(--deck-ink);
  white-space: nowrap;
}

.bs .lec-table td:first-child strong { font-weight: 700; }
.bs .lec-table tr:last-child td { border-bottom: none; }

/* ── the devotional slide ─────────────────────────────────────────────────── */

/* The one block with nothing to fill in: five questions and a room, read aloud.
   Its constraint is legibility at distance and nothing else, so the questions
   take the whole measure at near-headline size and the numerals hang out to
   the left as markers the eye skips off. */
.bs .lec-devotional-list {
  list-style: none;
  counter-reset: devotional;
  margin: 0;
  padding: 0;
  max-width: none;
}

.bs .lec-devotional-q {
  counter-increment: devotional;
  display: flex;
  align-items: baseline;
  gap: clamp(14px, 2.4cqi, 34px);
  border-top: 1px solid var(--deck-rule);
  padding: clamp(12px, 1.8cqi, 26px) 0;
}

.bs .lec-devotional-q:first-child { border-top: none; padding-top: 0; }
.bs .lec-devotional-q:last-child { padding-bottom: 0; }

/* Bronze, not ink-3: the numeral is a marker, so it wants to be a DIFFERENT
   colour from the question rather than a weaker one. Never camel — that is the
   2.13:1 trap in 01-tokens.css. */
.bs .lec-devotional-n {
  flex: 0 0 auto;
  min-width: 1.7em;
  font-family: var(--bs-mono);
  font-size: max(13px, calc(var(--deck-t-dev) * .5));
  letter-spacing: .04em;
  font-variant-numeric: tabular-nums;
  color: var(--deck-accent);
}

.bs .lec-devotional-n::before { content: counter(devotional) '.'; }

.bs .lec-devotional-text {
  font-family: var(--bs-ed);
  font-weight: 600;
  font-size: var(--deck-t-dev);
  line-height: 1.22;
  text-wrap: balance;
  color: var(--deck-ink);
}

/* ── the ask ──────────────────────────────────────────────────────────────── */

.bs .deck-ask {
  border-left: 3px solid var(--deck-ink);
  padding: 2px 0 2px clamp(12px, 1.6cqi, 22px);
  max-width: 40em;
}

.bs .deck-ask-k, .bs .cs-ask-k {
  font-family: var(--bs-mono);
  font-size: var(--deck-t-agate);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--deck-ink-3);
  margin-bottom: 6px;
}

.bs .deck-ask .lec-prose {
  font-family: var(--bs-ed);
  font-weight: 500;
  font-size: max(17px, calc(var(--deck-t-body) * 1.06));
  line-height: 1.34;
  color: var(--deck-ink);
}

/* ── the crowd-sourced board ──────────────────────────────────────────────── */

.bs .cs { display: flex; flex-direction: column; gap: var(--deck-gap); }

.bs .cs-ask { max-width: 40em; }

.bs .cs-ask .lec-prose {
  font-family: var(--bs-ed);
  font-weight: 500;
  font-size: max(17px, calc(var(--deck-t-body) * 1.06));
  line-height: 1.32;
  color: var(--deck-ink);
}

/* THE INPUT IS A RULED LINE, not a form field. A student is calling a word out
   loud and writing it down at the same time; a boxed control with a rounded
   border would be the most web-app object on the whole deck. */
.bs .cs-form {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  max-width: 34em;
}

.bs .cs-input {
  flex: 1 1 12em;
  min-width: 0;
  font-family: var(--bs-ed);
  font-weight: 600;
  /* 16px floor prevents iOS zooming the page on focus. */
  font-size: max(18px, calc(var(--deck-t-body) * .92));
  line-height: 1.3;
  color: var(--deck-ink);
  background: none;
  border: none;
  border-bottom: 2px solid var(--deck-ink);
  border-radius: 0;
  padding: 4px 2px;
}

.bs .cs-input::placeholder { color: var(--deck-ink-3); font-weight: 400; }
.bs .cs-input:focus { outline: none; border-bottom-color: var(--deck-accent); }
.bs .cs-input:focus-visible {
  outline: 2px solid var(--deck-accent);
  outline-offset: 3px;
}

.bs .cs-send {
  flex: 0 0 auto;
  font-family: var(--bs-mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--deck-console-fg);
  background: var(--deck-console-bg);
  border: 1px solid var(--deck-console-bg);
  border-radius: 0;
  padding: 8px 18px;
  cursor: pointer;
  transition: background-color var(--dur-fast) ease, color var(--dur-fast) ease;
}

.bs .cs-send:hover { background: var(--deck-accent); border-color: var(--deck-accent); }
.bs .cs-send:focus-visible { outline: 2px solid var(--deck-accent); outline-offset: 3px; }
.bs .cs-send:disabled { opacity: .5; cursor: default; }

.bs .cs-status {
  font-family: var(--bs-mono);
  font-size: 11px;
  letter-spacing: .06em;
  color: var(--bs-pen);
}

/* A student's own tries. Not a status table — a margin note about what they
   personally threw at the board, in the hand-adjacent register that marks
   everything private on this surface. */
.bs .cs-mine {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  max-width: 40em;
}

.bs .cs-mine-chip {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 15px;
  color: var(--deck-ink-2);
  padding-bottom: 2px;
  border-bottom: 1px dashed var(--bs-graphite);
}

.bs .cs-mine-s {
  font-family: var(--bs-mono);
  font-size: 9px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--deck-ink-3);
}

.bs .cs-mine-chip.is-approved { border-bottom-style: solid; border-bottom-color: var(--deck-ink); }
.bs .cs-mine-chip.is-approved .cs-mine-t { color: var(--deck-ink); font-weight: 600; }
.bs .cs-mine-chip.is-approved .cs-mine-s { color: var(--deck-accent); }
/* Struck, muted, and worded gently. A rejected guess is not an error state —
   it is the norm Mark sets on day one, and it must not read as a red flag on a
   twelve-year-old's screen. */
.bs .cs-mine-chip.is-rejected .cs-mine-t { text-decoration: line-through; color: var(--deck-ink-3); }

.bs .cs-board.is-display-only.is-empty { display: none; }

.bs .cs-board-h {
  display: flex;
  align-items: baseline;
  gap: 12px;
  border-top: 2px solid var(--deck-ink);
  padding-top: 8px;
}

.bs .cs-board-k {
  font-family: var(--bs-mono);
  font-size: var(--deck-t-agate);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--deck-ink-3);
}

/* SHAPE ONE — THE WALL. While the room is throwing words at it. Each entry is
   a word ON A LINE, the way you would write it up, not a pill in a box. */
.bs .cs-board-items.is-wall {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(10px, 1.4cqi, 20px) clamp(16px, 2.4cqi, 34px);
  align-items: baseline;
  padding-top: 4px;
}

/* THE BOARD IS THE LOUDEST THING ON THIS SLIDE AFTER THE HEADLINE, and that
   is the design brief: a twelve-year-old watching their own word arrive on the
   shared board should feel something, which a 17px chip cannot deliver from
   the back of a room. */
.bs .cs-board-items.is-wall .cs-tile {
  display: inline-flex;
  align-items: baseline;
  gap: .42em;
  font-family: var(--bs-ed);
  font-weight: 600;
  font-size: var(--deck-t-wall);
  line-height: 1.16;
  color: var(--deck-ink);
  background: none;
  border: none;
  border-bottom: 1px solid var(--deck-rule);
  border-radius: 0;
  padding: 0 0 4px;
  text-align: left;
}

/* SHAPE TWO — THE GLOSSARY. Once Mark starts glossing, the wall becomes a
   column grid of term-and-breakdown pairs. Nine of these on screen at once is
   the densest slide in the deck and is the case this is measured against:
   3 columns at 1400, 2 at 1024, 1 at 375. */
/* Rows tighter than columns, and deliberately so: a glossary is read DOWN a
   column, and the horizontal gutter is what separates one entry from the next
   rather than vertical air. Nine pairs is the densest thing in the deck and
   this is where the height is won back before the fit walk has to touch the
   type. */
.bs .cs-board-items.is-glossary {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 14em), 1fr));
  gap: clamp(10px, 1.2cqi, 18px) clamp(18px, 3cqi, 44px);
  align-items: start;
  padding-top: 2px;
}

.bs .cs-board-items.is-glossary .cs-tile,
.bs .cs-col-items .cs-tile {
  display: flex;
  flex-direction: column;
  gap: 1px;
  background: none;
  border: none;
  border-top: 1px solid var(--deck-rule);
  border-radius: 0;
  padding: 6px 0 0;
  text-align: left;
}

.bs .cs-word {
  font-family: var(--bs-ed);
  font-weight: 700;
  font-size: var(--deck-t-word);
  line-height: 1.14;
  color: var(--deck-ink);
}

.bs .cs-gloss {
  font-size: var(--deck-t-gloss);
  line-height: 1.34;
  color: var(--deck-ink-2);
}

/* "That is MY word up there." One camel rule and one agate mark, and it is the
   only place on the board where camel appears — which is what makes it read. */
.bs .cs-tile.is-mine { border-bottom-color: var(--deck-mark); border-bottom-width: 2px; }
.bs .cs-board-items.is-glossary .cs-tile.is-mine,
.bs .cs-col-items .cs-tile.is-mine { border-top-color: var(--deck-mark); border-top-width: 2px; }

/* The mark that says a word is yours. Agate, camel-deep, and set OFF the word
   rather than against it — jammed to the last letter it read as part of the
   spelling ("lucidYOURS"), seen at 1440. */
.bs .cs-yours {
  flex: 0 0 auto;
  font-family: var(--bs-mono);
  font-size: 9px;
  letter-spacing: .16em;
  text-transform: uppercase;
  /* On the light stock, not on ink: camel-deep measures 3.61:1 here and
     camel 2.13:1, both under AA, and this is 9px agate. The gold accent on a
     student's own word is carried by the underline, which is decoration and
     may be camel; the WORD "yours" has to be readable. */
  color: var(--bs-ink-3);
  white-space: nowrap;
}

.bs .cs-board-items.is-glossary .cs-tile .cs-yours,
.bs .cs-col-items .cs-tile .cs-yours { margin-top: 3px; }

/* SHAPE THREE — THE COLUMNS. Several rounds reviewed together (PREVENT /
   PROTECT / RECOVER). Items stack ONE PER ROW and the columns are top-aligned
   so their HEIGHTS are directly comparable — that comparison is the teaching
   point of the slide, and any wrapping inside a column would destroy it. */
.bs .cs-board-items.is-columns {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13em), 1fr));
  gap: clamp(16px, 2.4cqi, 36px);
  align-items: start;
  padding-top: 6px;
}

.bs .cs-col { display: flex; flex-direction: column; gap: 2px; }

.bs .cs-col-k {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  border-bottom: 2px solid var(--deck-ink);
  padding-bottom: 5px;
  margin: 0 0 4px;
}

.bs .cs-col-t {
  font-family: var(--bs-mono);
  font-size: var(--deck-t-agate);
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--deck-ink);
}

.bs .cs-col-n {
  font-family: var(--bs-ed);
  font-weight: 700;
  font-size: clamp(16px, 1.6cqi, 24px);
  font-variant-numeric: tabular-nums;
  color: var(--deck-ink-3);
}

.bs .cs-col.is-live .cs-col-k { border-bottom-color: var(--deck-mark); }
.bs .cs-col.is-live .cs-col-t { color: var(--deck-accent); }

.bs .cs-col-items { display: flex; flex-direction: column; }

/* The column's own heavy rule already closes the head; the first entry's
   hairline stacked under it read as a double rule at the top of every column. */
.bs .cs-col-items .cs-tile:first-child { border-top: none; padding-top: 2px; }

/* Every board tile is a button on the teacher's screen (clicking attaches a
   gloss) and a div on everyone else's. The states are designed either way, so
   the hover is scoped to the interactive form only. */
.bs button.cs-tile { cursor: pointer; font: inherit; transition: color var(--dur-fast) ease, border-color var(--dur-fast) ease; }
.bs button.cs-tile:hover { color: var(--deck-accent); border-color: var(--deck-accent); }
.bs button.cs-tile:hover .cs-word { color: var(--deck-accent); }
.bs button.cs-tile:focus-visible { outline: 2px solid var(--deck-accent); outline-offset: 3px; }

/* Arrival. Short, once, and never on anything already on the board. */
.bs .cs-tile.is-fresh { animation: deck-stamp-in 320ms var(--ease-out) both; }

@keyframes deck-stamp-in {
  from { opacity: 0; transform: translateY(7px) scale(.96); }
  to   { opacity: 1; transform: none; }
}


/* ── the react phase ──────────────────────────────────────────────────────── */

/* PHASE C OF ONE SLIDE. The room submits, Mark approves, and the SAME slide
   becomes the pinned board they tag. So this is not a new component sitting
   below the board — it is a rank under each pinned word, and it has to read as
   part of the word rather than as furniture attached to it.

   NOT PILLS. The board's whole idea is "a word ON A LINE, the way you would
   write it up"; a row of rounded chips under each one turns a blackboard into a
   comment thread. A tag is a mono label on a hairline, and the only thing that
   ever goes bold or gold is the one this child chose. */
.bs .cs-tags {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 0 clamp(10px, 1.2cqi, 18px);
  margin-top: 5px;
}

/* The wall lays its tiles out on a shared baseline, which is right for bare
   words and wrong the moment a word carries a rank under it. */
.bs .cs-board-items.is-wall .cs-tile.has-tags {
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
}

.bs .cs-tag {
  display: inline-flex;
  align-items: baseline;
  gap: .5em;
  font-family: var(--bs-mono);
  font-size: var(--deck-t-tag);
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--deck-ink-3);
  background: none;
  border: none;
  border-top: 1px solid var(--deck-rule);
  border-radius: 0;
  padding: 4px 0 0;
  text-align: left;
}

.bs button.cs-tag {
  cursor: pointer;
  transition: color var(--dur-fast) ease, border-color var(--dur-fast) ease;
}

.bs button.cs-tag:hover { color: var(--deck-accent); border-top-color: var(--deck-accent); }
.bs button.cs-tag:focus-visible { outline: 2px solid var(--deck-accent); outline-offset: 3px; }
.bs button.cs-tag[disabled] { cursor: default; opacity: .55; }

/* THE ONE THIS CHILD CHOSE. A 2px camel rule and ink type — the same mark the
   board already uses for "that is my word up there", so the two readings of
   ownership on this slide look like one language. */
.bs .cs-tag.is-picked {
  border-top-width: 2px;
  border-top-color: var(--deck-mark);
  color: var(--deck-ink);
  font-weight: 600;
}

/* Having voted, the tag not chosen steps back rather than disappearing: the
   count beside it is the thing worth reading afterwards. */
.bs .cs-tag.is-other { color: var(--deck-ink-3); }

.bs .cs-tag-g { font-size: 1.1em; line-height: 1; letter-spacing: 0; }

.bs .cs-tag-l { white-space: nowrap; }

/* The count is EDITORIAL, not mono, and tabular so a tally does not jitter as
   it climbs during a live vote. */
.bs .cs-tag-n {
  font-family: var(--bs-ed);
  font-weight: 700;
  font-size: var(--deck-t-tagn);
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
  color: var(--deck-ink);
}

.bs .cs-tag.is-other .cs-tag-n { color: var(--deck-ink-3); }

/* Mark's copy of the row is a READOUT — he is not one of the twelve and his
   thumb would sit in their tally. No hover, no target, just the number. */
.bs .cs-tag.is-readout { color: var(--deck-ink-3); }
.bs .cs-tag.is-readout .cs-tag-n { color: var(--deck-accent); }

.bs .cs-tag-err {
  font-family: var(--bs-mono);
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--bs-pen);
  align-self: center;
}

/* On a tagged slide the whole tile can no longer be the gloss button (a button
   inside a button is not buildable), so the WORD is. It has to keep looking
   exactly like the word it replaced. */
.bs button.cs-word-act {
  background: none;
  border: none;
  border-radius: 0;
  padding: 0;
  margin: 0;
  text-align: left;
  cursor: pointer;
  transition: color var(--dur-fast) ease;
}

.bs button.cs-word-act:hover { color: var(--deck-accent); }
.bs button.cs-word-act:focus-visible { outline: 2px solid var(--deck-accent); outline-offset: 3px; }

/* The question the tags are asking, said out loud in the board's own head —
   two buttons with no question above them is a guess. */
.bs .cs-board-ask {
  font-family: var(--bs-ed);
  font-style: italic;
  font-size: var(--deck-t-gloss);
  line-height: 1.3;
  color: var(--deck-ink-2);
}

/* The gloss editor — opened pre-filled from `config.glosses` so Mark clicks
   rather than composing a definition in front of twelve children. */
.bs .cs-gloss-edit {
  grid-column: 1 / -1;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 10px;
  width: 100%;
  border-left: 3px solid var(--deck-mark);
  padding: 8px 0 8px 12px;
  margin: 6px 0;
}

.bs .cs-gloss-k {
  font-family: var(--bs-mono);
  font-size: 10px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--deck-ink-3);
  flex: 0 0 auto;
}

.bs .cs-gloss-input {
  flex: 1 1 16em;
  min-width: 0;
  font: inherit;
  font-size: 16px;
  color: var(--deck-ink);
  background: var(--deck-ground);
  border: 1px solid var(--deck-rule);
  border-radius: 0;
  padding: 6px 9px;
}

.bs .cs-gloss-input:focus-visible { outline: 2px solid var(--deck-accent); outline-offset: 2px; }

.bs .cs-gloss-save, .bs .cs-gloss-cancel {
  font-family: var(--bs-mono);
  font-size: 11px;
  letter-spacing: .12em;
  text-transform: uppercase;
  border-radius: 0;
  padding: 6px 12px;
  cursor: pointer;
}

.bs .cs-gloss-save {
  color: var(--deck-console-fg);
  background: var(--deck-console-bg);
  border: 1px solid var(--deck-console-bg);
}

.bs .cs-gloss-cancel {
  color: var(--deck-ink-2);
  background: none;
  border: 1px solid var(--deck-rule);
}

.bs .cs-gloss-err { font-size: 11px; color: var(--bs-pen); }

/* ── the two student boxes ────────────────────────────────────────────────── */

/* Spacing comes from the stage's `gap`; a margin here would stack on top of it
   and give the boxes a different rhythm from everything above them. */
.bs .lec-box { margin: 0; max-width: var(--lec-read); }

.bs .lec-box-head {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--bs-mono);
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  margin-bottom: 4px;
}

.bs .lec-box-label { color: var(--deck-ink-2); }
/* Pushed to the far edge by the auto margin rather than by space-between,
   which the stamp sitting between it and the label would otherwise break. */
.bs .lec-box-status {
  margin-left: auto;
  color: var(--deck-ink-3);
  text-transform: none;
  letter-spacing: .04em;
}

.bs .lec-box-hint {
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--deck-ink-3);
  margin: 0 0 8px;
}

/* THE STAMP. The label says what the box is; the stamp says who reads it, and
   it is the signal a student catches without reading anything. Inked and solid
   when the work leaves them, dashed and empty when it does not. */
.bs .lec-box-mark {
  flex: 0 0 auto;
  font-size: 9.5px;
  letter-spacing: .16em;
  padding: 2px 7px 1px;
  border: 1px solid transparent;
}

.bs .lec-mark-shared {
  color: var(--bs-camel-on-ink);
  background: var(--bs-ink);
  border-color: var(--bs-ink);
}

.bs .lec-mark-private {
  color: var(--deck-ink-3);
  background: none;
  border-style: dashed;
  border-color: var(--bs-graphite);
}

/* The teacher's slot, standing exactly where the student's answer box stands. */
.bs .lec-mark-live {
  color: var(--bs-sheet);
  background: var(--bs-pen);
  border-color: var(--bs-pen);
}

.bs .lec-textarea {
  width: 100%;
  box-sizing: border-box;
  font: inherit;
  font-size: 16px;          /* 16px prevents iOS zooming the page on focus */
  line-height: 1.5;
  color: var(--deck-ink);
  background: var(--deck-ground);
  border: 1px solid var(--deck-rule);
  border-radius: 0;
  padding: 10px 12px;
  resize: vertical;
}

/* A Chromebook is a keyboard, so the focus ring is not optional and is not the
   border going one shade darker. */
.bs .lec-textarea:focus { border-color: var(--deck-ink); }

.bs .lec-textarea:focus-visible {
  outline: 2px solid var(--deck-accent);
  outline-offset: 2px;
  border-color: var(--deck-ink);
}

/* The answer is inked: it leaves the student and reaches the teacher. */
.bs .lec-answer .lec-textarea { border-left: 3px solid var(--deck-ink); }

/* The note is pencil: private, softer ground, no heavy edge — and it sits
   BELOW A LINE, so the answer is above the fold of the slide and the note is
   below it. Placement is the third signal, after the stamp and the treatment. */
.bs .lec-note {
  border-top: 1px dashed var(--deck-rule);
  padding-top: var(--deck-gap);
}

.bs .lec-note .lec-textarea {
  background: var(--bs-stock-2);
  border-style: dashed;
  color: var(--deck-ink-2);
}

.bs .lec-wordcount {
  font-family: var(--bs-mono);
  font-size: 11px;
  letter-spacing: .1em;
  color: var(--deck-ink-3);
  text-align: right;
  margin-top: 4px;
  font-variant-numeric: tabular-nums;
}

/* ── the foot ─────────────────────────────────────────────────────────────── */

.bs .deck-foot {
  position: sticky;
  bottom: 0;
  z-index: 3;
  margin: auto 0 0;
  padding: 10px 0 var(--deck-pad-y);
  background: var(--deck-paper);
  border-top: 1px solid var(--deck-rule);
  display: flex;
  align-items: center;
  gap: clamp(10px, 2cqi, 24px);
  flex-wrap: wrap;
}

/* The reveal ledger: one bar per step, filled as the room earns it. Bars and
   not dots, because a bar shows how much of the slide is still to come. */
.bs .deck-ticks {
  list-style: none;
  display: flex;
  gap: 4px;
  margin: 0;
  padding: 0;
  flex: 0 1 clamp(60px, 22cqi, 260px);
}

.bs .deck-tick {
  flex: 1 1 0;
  min-width: 10px;
  height: 3px;
  background: var(--deck-rule);
  transition: background-color var(--dur) var(--ease-out);
}

.bs .deck-tick.is-on { background: var(--deck-ink); }
/* Only one step means there is no ledger to read. */
.bs .deck-ticks:has(.deck-tick:only-child) { display: none; }

.bs .deck-count {
  font-family: var(--bs-mono);
  font-size: var(--deck-t-agate);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--deck-ink-3);
  font-variant-numeric: tabular-nums;
}

.bs .deck-hints {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

.bs .deck-hint { display: inline-flex; align-items: center; gap: 5px; }

/* THE TEACHER ALREADY HAS THE CONSOLE, so the slide's own key hints are a
   second copy of the same five affordances eight pixels above them. They stay
   on the STUDENT's slide, which has no console and where `F` and `T` are the
   only two things they can do. The ledger and the count stay on both — those
   are state, not controls. */
.bs .deck-stage.is-teacher .deck-hints { display: none; }

.bs .deck-kbd {
  font-family: var(--bs-mono);
  font-size: 10px;
  line-height: 1;
  color: var(--deck-ink-2);
  border: 1px solid var(--deck-rule);
  padding: 3px 5px 2px;
  min-width: 1.6em;
  text-align: center;
}

.bs .deck-hint-w {
  font-family: var(--bs-mono);
  font-size: 9.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--deck-ink-3);
}

/* ── the teacher's console ────────────────────────────────────────────────── */

.bs .deck-shell.is-teacher { display: flex; flex-direction: column; gap: 12px; }

/* ── present ──────────────────────────────────────────────────────────────── */

/* THE ONE UNMISTAKABLE ACT ON ARRIVAL. It is the width of the deck and it is
   inked, so it is the only thing on the screen that looks like a button worth
   pressing before the class starts; the console below it is a row of equals.
   It disappears in fullscreen — see the fullscreen block. */
.bs .deck-present {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

.bs .deck-present-go {
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex: 1 1 auto;
  min-width: 0;
  cursor: pointer;
  text-align: left;
  font: inherit;
  border: 1px solid var(--bs-ink);
  border-radius: 0;
  padding: 13px 18px;
  color: var(--bs-sheet);
  background: var(--bs-ink);
  box-shadow: 3px 3px 0 var(--bs-rule);
  transition: box-shadow var(--dur-fast) ease, transform var(--dur-fast) ease;
}

.bs .deck-present-go:hover { box-shadow: 5px 5px 0 var(--bs-rule); }
.bs .deck-present-go:active { box-shadow: 1px 1px 0 var(--bs-rule); }
.bs .deck-present-go:focus-visible { outline: 3px solid var(--bs-blue); outline-offset: 3px; }

.bs .deck-present-k {
  font-family: var(--bs-ed);
  font-variation-settings: 'SOFT' 0, 'WONK' 1;
  font-weight: 900;
  font-size: clamp(20px, 2.4vw, 30px);
  line-height: 1;
  letter-spacing: -.015em;
  flex: 0 0 auto;
}

/* Gold on the inked bar goes through the on-ink token — `--bs-camel` itself is
   2.13:1 and banned as a text colour (tests/test_palette_contrast.py). */
.bs .deck-present-w {
  font-family: var(--bs-mono);
  font-size: 11.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--bs-camel-on-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bs .deck-present-hint {
  font-family: var(--bs-mono);
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
  flex: 0 0 auto;
}

@media (prefers-reduced-motion: reduce) {
  .bs .deck-present-go { transition: none; }
}

/* The strip: every slide of the morning, named. Named and not dotted — a rail
   of anonymous pips is not something anyone can navigate while talking. */
.bs .deck-strip {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--bs-rule);
  scrollbar-width: thin;
}

.bs .ltt-chip {
  display: flex;
  align-items: baseline;
  gap: 7px;
  flex: 0 0 auto;
  background: none;
  border: 1px solid var(--bs-rule);
  border-radius: 0;
  padding: 5px 10px;
  cursor: pointer;
  font: inherit;
  font-size: 13px;
  color: var(--bs-ink-2);
  max-width: 210px;
  transition: border-color var(--dur-fast) ease, background-color var(--dur-fast) ease,
              color var(--dur-fast) ease;
}

.bs .ltt-chip:hover {
  border-color: var(--bs-ink);
  background: var(--bs-stock-2);
  color: var(--bs-ink);
}

.bs .ltt-chip:focus-visible { outline: 2px solid var(--bs-blue); outline-offset: 2px; }
.bs .ltt-chip:active { background: var(--bs-stock-2); }

.bs .ltt-chip.is-current {
  border-color: var(--bs-ink);
  background: var(--bs-ink);
  color: var(--bs-sheet);
}

.bs .ltt-chip.is-current:hover {
  background: var(--bs-ink-2);
  border-color: var(--bs-ink-2);
  color: var(--bs-sheet);
}

.bs .ltt-chip-n {
  font-family: var(--bs-mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  opacity: .7;
}

.bs .ltt-chip-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.bs .ltt-strand-latin:not(.is-current)   { border-left: 3px solid var(--bs-camel); }
.bs .ltt-strand-logic:not(.is-current)   { border-left: 3px solid var(--bs-blue); }
.bs .ltt-strand-writing:not(.is-current) { border-left: 3px solid var(--bs-pen); }

.bs .deck-main {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 14px;
  align-items: start;
}

/* The rail only earns a column when there is room for one; below that it goes
   under the slide rather than squeezing the thing the room is looking at. */
@media (min-width: 1100px) {
  .bs .deck-main { grid-template-columns: minmax(0, 1fr) minmax(0, 21rem); }
}

.bs .deck-rail {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
  container-type: inline-size;
}

/* THE CONSOLE. The one object on this surface that is not the slide, so it is
   the one object with its own ground: an inked bar under the deck, reversed
   out, so his eye never has to hunt for the control he is about to press while
   talking. */
.bs .deck-console {
  position: sticky;
  bottom: 0;
  z-index: 6;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  background: var(--deck-console-bg);
  padding: 10px clamp(12px, 2vw, 20px);
}

.bs .deck-ctl {
  font-family: var(--bs-mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--deck-console-fg);
  background: none;
  border: 1px solid color-mix(in srgb, var(--deck-console-fg) 45%, transparent);
  border-radius: 0;
  padding: 9px 14px;
  cursor: pointer;
  transition: background-color var(--dur-fast) ease, border-color var(--dur-fast) ease,
              color var(--dur-fast) ease;
}

.bs .deck-ctl:hover {
  /* The console ground is --bs-ink, where camel measures 4.76:1. Going through
     --bs-camel-on-ink says so in the declaration; a bare --bs-camel does not,
     which is why the palette test cannot tell it from the 2.13:1 mistake. */
  border-color: var(--bs-camel);
  color: var(--bs-camel-on-ink);
}

.bs .deck-ctl:focus-visible {
  outline: 2px solid var(--bs-camel);
  outline-offset: 2px;
}

/* Reveal is the key he presses most and it is the only filled control. */
.bs .deck-ctl.is-primary {
  background: var(--bs-camel);
  border-color: var(--bs-camel);
  color: var(--bs-ink);
  min-width: 8.5em;
}

.bs .deck-ctl.is-primary:hover {
  background: var(--bs-camel-on-ink);
  border-color: var(--bs-camel-on-ink);
  color: var(--bs-ink);
}

.bs .deck-ctl.is-end { margin-left: 4px; opacity: .72; }
.bs .deck-ctl.is-end:hover { opacity: 1; border-color: var(--bs-pen); color: var(--bs-pen); }

.bs .deck-console-pos {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin-left: auto;
  text-align: right;
  font-family: var(--bs-mono);
  letter-spacing: .12em;
  text-transform: uppercase;
}

.bs .deck-console-n { font-size: 11px; color: var(--deck-console-fg); }
.bs .deck-console-s {
  font-size: 10px;
  color: color-mix(in srgb, var(--deck-console-fg) 65%, transparent);
  font-variant-numeric: tabular-nums;
}

/* Never a silent divergence: if /runs/step is not answering, the slide still
   moves on HIS screen and the bar says the room cannot see it. */
.bs .deck-detached {
  flex: 1 0 100%;
  font-family: var(--bs-mono);
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  /* Also on the console's ink ground. This one is a WARNING that the room is
     not seeing what Mark is seeing, so it is the last place to accept type
     that is hard to read. */
  color: var(--bs-camel-on-ink);
  border-top: 1px dashed color-mix(in srgb, var(--bs-camel) 60%, transparent);
  padding-top: 6px;
}

/* ── speaker notes ────────────────────────────────────────────────────────── */

/* TEACHER ONLY, and off the slide by construction — this panel is never built
   on the student path and `config.notes` is stripped server-side as well. */
/* IT SITS UNDER THE RUNNING HEAD RATHER THAN FIGHTING IT. `.screen` is
   `position:relative; z-index:1` (01-tokens.css), which makes it a stacking
   context, so no z-index inside it can beat #global-bar's 30 — this panel
   opened with its own header hidden behind the masthead, seen at 1440. Raising
   the screen instead would put a full-height transparent layer over the
   wordmark and eat the click. The bar's painted bottom is y=66 (measured in
   02-chrome.css), and it steps down with .gb-brand on a phone. In fullscreen
   there is no bar at all, so the panel takes the whole edge. */
.bs .deck-notes {
  display: none;
  position: fixed;
  z-index: 40;
  right: 0;
  top: 66px;
  bottom: 0;
  width: min(30rem, 92vw);
  background: var(--bs-sheet);
  border-left: 3px solid var(--bs-ink);
  box-shadow: -8px 0 0 rgba(0, 0, 0, .06);
  padding: 20px 24px 28px;
  overflow-y: auto;
}

.bs .deck-notes.is-on { display: block; animation: deck-notes-in 220ms var(--ease-out) both; }

@keyframes deck-notes-in {
  from { transform: translateX(14px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

.bs .deck-notes-h {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  border-bottom: 2px solid var(--bs-ink);
  padding-bottom: 8px;
  margin-bottom: 14px;
}

.bs .deck-notes-k {
  font-family: var(--bs-mono);
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
}

.bs .deck-notes-x {
  font-family: var(--bs-mono);
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--bs-ink-2);
  background: none;
  border: 1px solid var(--bs-rule);
  border-radius: 0;
  padding: 4px 9px;
  cursor: pointer;
}

.bs .deck-notes-x:hover { border-color: var(--bs-ink); color: var(--bs-ink); }
.bs .deck-notes-x:focus-visible { outline: 2px solid var(--bs-blue); outline-offset: 2px; }

/* Notes are READ, at reading size — they are the one thing on this surface
   that is not projected, so they do not take the deck's scale. */
.bs .deck-notes .lec-prose {
  font-size: 15px;
  line-height: 1.55;
  max-width: none;
  color: var(--bs-ink-2);
}

.bs .deck-notes .lec-lead-t { font-size: 19px; }
.bs .deck-notes .lec-lead-w { font-size: 15px; }
.bs .deck-notes-none { font-size: 14px; color: var(--bs-ink-3); }

/* ── the moderation queue ─────────────────────────────────────────────────── */

.bs .mq-k, .bs .ltt-resp-k {
  font-family: var(--bs-mono);
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
  margin-bottom: 8px;
}

.bs .mq-list { list-style: none; margin: 0; padding: 0; }

.bs .mq-row {
  display: grid;
  /* THE WORD GETS ITS OWN ROW WHEN THE THREE BUTTONS LEAVE IT NO ROOM.
     Measured 2026-08-31 in fullscreen at 1440x900 with the rail up: the word
     column resolved to 80px and `translucent` broke across two lines as
     "translu / cent". A submission read mid-word is a submission he has to
     decode before he can approve it, and every crowd-source word in the Latin
     hour is longer than eight characters. `overflow-wrap: anywhere` stays as
     the last-resort guard against one very long paste; this stops it firing on
     ordinary English. */
  grid-template-columns: minmax(9ch, 1fr) auto;
  grid-template-areas: 'who who' 'word acts';
  align-items: center;
  gap: 2px 10px;
  border-top: 1px solid var(--bs-rule);
  padding: 9px 0 10px;
}

.bs .mq-row:last-child { border-bottom: 1px solid var(--bs-rule); }

/* The row he is about to act on is marked, so Enter-through has somewhere
   visible to be. */
.bs .mq-row.is-next {
  box-shadow: inset 3px 0 0 var(--bs-camel);
  padding-left: 10px;
}

.bs .mq-who {
  grid-area: who;
  font-family: var(--bs-mono);
  font-size: 10px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
}

.bs .mq-word {
  grid-area: word;
  font-family: var(--bs-ed);
  font-weight: 600;
  font-size: 19px;
  line-height: 1.15;
  color: var(--bs-ink);
  overflow-wrap: anywhere;
}

/* Below the width where a word and three buttons can share a line, the buttons
   drop under the word rather than squeezing it. */
@container (max-width: 21rem) {
  .bs .mq-row {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: 'who' 'word' 'acts';
  }
  .bs .mq-acts { justify-content: flex-start; }
}

.bs .mq-input {
  grid-area: word;
  font: inherit;
  font-size: 16px;
  color: var(--bs-ink);
  background: var(--bs-sheet);
  border: 1px solid var(--bs-ink);
  border-radius: 0;
  padding: 5px 8px;
  min-width: 0;
}

.bs .mq-input:focus-visible { outline: 2px solid var(--bs-blue); outline-offset: 2px; }

.bs .mq-acts { grid-area: acts; display: flex; align-items: center; gap: 6px; }

.bs .mq-yes, .bs .mq-edit, .bs .mq-no {
  font-family: var(--bs-mono);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  border-radius: 0;
  cursor: pointer;
  transition: background-color var(--dur-fast) ease, border-color var(--dur-fast) ease,
              color var(--dur-fast) ease;
}

/* APPROVE IS THE BIG TARGET AND REJECT IS THE SMALL ONE, on purpose. He is
   driving this in front of twelve children and the expensive mistake is
   rejecting the wrong child's word, so the two are not peers: approve is a
   filled block, reject is a hairline ghost, and reject ARMS before it fires
   (one click says "Sure?", a second within four seconds does it). */
.bs .mq-yes {
  color: var(--bs-sheet);
  background: var(--bs-ink);
  border: 1px solid var(--bs-ink);
  padding: 8px 14px;
}

.bs .mq-yes:hover { background: var(--bs-ok); border-color: var(--bs-ok); }
.bs .mq-yes:focus-visible { outline: 2px solid var(--bs-blue); outline-offset: 2px; }

.bs .mq-edit {
  color: var(--bs-ink-2);
  background: none;
  border: 1px solid var(--bs-rule);
  padding: 7px 9px;
}

.bs .mq-edit:hover { border-color: var(--bs-ink); color: var(--bs-ink); }
.bs .mq-edit:focus-visible { outline: 2px solid var(--bs-blue); outline-offset: 2px; }

.bs .mq-no {
  color: var(--bs-ink-3);
  background: none;
  border: 1px solid transparent;
  padding: 7px 9px;
}

.bs .mq-no:hover { color: var(--bs-pen); border-color: var(--bs-pen); }
.bs .mq-no:focus-visible { outline: 2px solid var(--bs-pen); outline-offset: 2px; }

.bs .mq-no.is-armed {
  color: var(--bs-sheet);
  background: var(--bs-pen);
  border-color: var(--bs-pen);
}

.bs .mq-err { font-size: 10px; color: var(--bs-pen); grid-area: who; text-align: right; }

.bs .mq-done { margin-top: 12px; }

.bs .mq-done-k {
  font-family: var(--bs-mono);
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
  cursor: pointer;
}

.bs .mq-done-list { list-style: none; margin: 8px 0 0; padding: 0; }

.bs .mq-done-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-size: 13px;
  color: var(--bs-ink-2);
  border-bottom: 1px solid var(--bs-rule);
  padding: 5px 0;
}

.bs .mq-done-row .mq-word { font-size: 14px; flex: 1; }
.bs .mq-done-row.is-rejected .mq-word { text-decoration: line-through; color: var(--bs-ink-3); }

.bs .mq-stat {
  font-family: var(--bs-mono);
  font-size: 9.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
}

/* ── who is writing ───────────────────────────────────────────────────────── */

/* The live panel's own head. In the console rail it sits under the moderation
   queue, so without a rule it read as a loose sentence rather than a panel. */
.bs .ltt-responses { border-top: 1px solid var(--bs-rule); padding-top: 10px; }
.bs .lec-live .ltt-responses { border-top: none; padding-top: 0; }
.bs .ltt-resp-none { letter-spacing: .04em; text-transform: none; font-size: 12.5px; }

/* Name, words, wait, read — in that order, because the teacher's live question
   is "who has not started", not "what did they write". */
.bs .ltt-resp-row {
  display: flex;
  align-items: baseline;
  gap: 12px;
  border-bottom: 1px solid var(--deck-rule);
  padding: 6px 0;
  font-size: 14px;
}

.bs .ltt-resp-name { flex: 1; color: var(--deck-ink); }

.bs .ltt-resp-words {
  font-family: var(--bs-mono);
  font-size: 12px;
  color: var(--deck-ink-2);
  font-variant-numeric: tabular-nums;
  min-width: 44px;
  text-align: right;
}

.bs .ltt-resp-wait {
  font-family: var(--bs-mono);
  font-size: 11px;
  color: var(--deck-ink-3);
  min-width: 104px;
}

.bs .ltt-resp-see {
  background: none;
  border: 1px solid var(--deck-rule);
  border-radius: 0;
  padding: 2px 8px;
  font: inherit;
  font-size: 12px;
  color: var(--deck-ink-2);
  cursor: pointer;
  transition: border-color var(--dur-fast) ease, background-color var(--dur-fast) ease;
}

.bs .ltt-resp-see:hover {
  border-color: var(--deck-ink);
  background: var(--bs-stock-2);
  color: var(--deck-ink);
}

.bs .ltt-resp-see:focus-visible { outline: 2px solid var(--deck-accent); outline-offset: 2px; }

.bs .ltt-resp-body {
  max-width: var(--lec-read);
  font-size: 15px;
  line-height: 1.5;
  color: var(--deck-ink-2);
  background: var(--bs-stock-2);
  padding: 10px 12px;
  margin: 0 0 10px;
  white-space: pre-wrap;
}

/* ── fullscreen ───────────────────────────────────────────────────────────── */

/* The FULLSCREEN TARGET IS THE SHELL, not the stage, so the teacher keeps his
   console and his notes when he projects. A student's shell contains only the
   slide, so for them the two are the same thing. */
.bs .deck-shell:fullscreen,
.bs .deck-shell:-webkit-full-screen {
  background: var(--deck-ground);
  padding: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: 0;
}

.bs .deck-shell:fullscreen .deck-strip { display: none; }
.bs .deck-shell:fullscreen .deck-main {
  flex: 1;
  min-height: 0;
  gap: 0;
  /* The rail is `display: none` up there unless the slide collects, but the
     grid still RESERVED its 21rem column: measured 2026-08-31 at 1440x900 in
     real fullscreen, the stage was 1104px wide and 336px of the projector was
     empty. One column unless `is-moderating` asks for two. */
  grid-template-columns: minmax(0, 1fr);
}
.bs .deck-shell:fullscreen .deck-rail { display: none; }

/* PRESENT IS THE WAY IN, so it is not also in the way once he is there. */
.bs .deck-shell:fullscreen .deck-present { display: none; }

@media (min-width: 1100px) {
  /* The rail comes back when the screen is genuinely wide enough to carry the
     queue beside a full-size slide — AND when there is a queue. Mark projects
     his own screen, so everything in the shell is on the wall: on the 113 of
     130 slides that collect nothing, the rail was 336px of the projector
     holding the sentence "Nothing is collected on this one", measured at
     1440x900. `is-moderating` is set by _lttPaint off `_deckCollects`. */
  .bs .deck-shell.is-moderating:fullscreen .deck-main {
    grid-template-columns: minmax(0, 1fr) minmax(0, 22rem);
  }
  .bs .deck-shell.is-moderating:fullscreen .deck-rail {
    display: flex;
    overflow-y: auto;
    height: 100%;
    padding: 20px 22px;
    background: var(--deck-ground);
    border-left: 1px solid var(--deck-rule);
  }
}

/* ── THE PICTURE SLIDE ───────────────────────────────────────────────────────
   On these slides the image IS the content, so it takes the frame's remaining
   height rather than a fixed one — `min-height: 0` on a flex/grid child is what
   lets `max-height` actually bind, and without it the picture pushes the foot
   rule off the bottom of the projector. `object-fit: contain` because a
   photograph of a house with the roof cropped off is the one thing this slide
   cannot afford. */
.bs .deck-stage.is-figure .deck-title { display: none; }

.bs .deck-figure {
  margin: 0;
  min-height: 0;
  /* Without this, a stage whose total content (head + figure + note + foot)
     exceeds max-height lets the flex algorithm shrink this item's OWN box
     below the img's rendered size (the img keeps its max-height:62vh
     regardless), and the note box — which starts right after this item in
     the column — opens on top of the still-full-size photograph instead of
     below it. Measured on slide 85 ("One house") at 1440x900: the figure's
     box collapsed to 74px while the img inside it still rendered at 496px,
     and "Your notes" landed 148px inside the photo. flex-shrink: 0 keeps this
     item's box matching what it actually draws, so later slide furniture
     never opens inside a photograph; a stage that still doesn't fit just
     scrolls, exactly as it already does for the note box on tall slides. */
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: flex-start;
}

.bs .deck-figure img {
  display: block;
  max-width: 100%;
  max-height: 62vh;
  height: auto;
  object-fit: contain;
  border: 1px solid var(--deck-rule);
}

.bs .deck-shell:fullscreen .deck-figure img { max-height: 72vh; }

.bs .deck-figure.is-missing {
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}

.bs .deck-figure-fail {
  font-family: var(--bs-mono);
  font-size: 13px;
  color: var(--bs-pen);
}

.bs .deck-shell:fullscreen .deck-notes { top: 0; }

.bs .deck-shell:fullscreen .deck-stage {
  height: 100%;
  max-height: none;
  min-height: 0;
  border-top-width: 4px;
  border-bottom: none;
  box-shadow: none;
}

/* ── reduced motion ───────────────────────────────────────────────────────── */

/* The reveal, the board arrival and the notes panel all come off for anyone who
   asked for that. The STATES stay — a student who cannot see where the keyboard
   is has a worse problem than one who sees it arrive instantly — and so does
   the reveal itself, which is content and not decoration. */
@media (prefers-reduced-motion: reduce) {
  .bs .deck-beat.is-fresh::before,
  .bs .deck-beat.is-fresh > *,
  .bs .cs-tile.is-fresh,
  .bs .deck-notes.is-on {
    animation: none;
  }
  .bs .ltt-chip,
  .bs .ltt-resp-see,
  .bs .lec-nbk-row,
  .bs .deck-tick,
  .bs .deck-ctl,
  .bs .cs-send,
  .bs button.cs-tile,
  .bs button.cs-tag,
  .bs button.cs-word-act,
  .bs .mq-yes, .bs .mq-edit, .bs .mq-no {
    transition: none;
  }
}

/* ── the notebook ─────────────────────────────────────────────────────────── */

/* Coming back to a session should look like the session, so the notebook is
   the same slides with every step already revealed — a printed deck rather
   than a transcript of one. What it drops is the furniture that only means
   something live: no foot, no sticky head, no viewport-height floor. */
.bs .deck-shell.is-notebook { display: flex; flex-direction: column; gap: 26px; }

.bs .deck-stage.is-static {
  min-height: 0;
  max-height: none;
  overflow: visible;
}

.bs .deck-stage.is-static { padding-block: 0 var(--deck-pad-y); }
.bs .deck-stage.is-static .deck-head { position: static; }

.bs .lec-session-head {
  border-bottom: 2px solid var(--bs-ink);
  padding-bottom: 8px;
  margin-bottom: 22px;
}

.bs .lec-session-n {
  font-family: var(--bs-mono);
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
}

.bs .lec-session-t {
  font-family: var(--bs-ed);
  font-size: 22px;
  line-height: 1.15;
  color: var(--bs-ink);
  margin-top: 2px;
}

.bs .lec-nbk-lede {
  max-width: var(--lec-read);
  font-size: 15px;
  color: var(--bs-ink-2);
  margin-bottom: 18px;
}

.bs .lec-nbk-row {
  display: flex;
  align-items: baseline;
  gap: 16px;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-bottom: 1px solid var(--bs-rule);
  padding: 12px 0;
  cursor: pointer;
  font: inherit;
  transition: background-color var(--dur-fast) ease, box-shadow var(--dur-fast) ease;
}

.bs .lec-nbk-row:hover {
  background: var(--bs-stock-2);
  box-shadow: inset 3px 0 0 var(--bs-ink);
}

.bs .lec-nbk-row:focus-visible {
  outline: 2px solid var(--bs-blue);
  outline-offset: -2px;
  background: var(--bs-stock-2);
}

.bs .lec-nbk-n {
  font-family: var(--bs-mono);
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
  min-width: 92px;
}

.bs .lec-nbk-t { flex: 1; color: var(--bs-ink); font-size: 16px; }

.bs .lec-nbk-c {
  font-family: var(--bs-mono);
  font-size: 11px;
  color: var(--bs-ink-3);
  font-variant-numeric: tabular-nums;
}

.bs .lec-nbk-back { margin-bottom: 18px; }

/* ── idle / failure / picker ──────────────────────────────────────────────── */

.bs .ltt-lede { font-size: 15px; color: var(--bs-ink-2); margin-bottom: 16px; }

.bs .lec-idle, .bs .lec-fail {
  border-top: 2px solid var(--bs-ink);
  padding-top: 18px;
  max-width: var(--lec-read);
}

.bs .lec-idle-k, .bs .lec-fail-what {
  font-family: var(--bs-ed);
  font-size: 22px;
  color: var(--bs-ink);
  margin-bottom: 6px;
}

.bs .lec-idle-w, .bs .lec-fail-why {
  font-size: 15px;
  color: var(--bs-ink-2);
  margin-bottom: 14px;
}

.bs .lec-loading { color: var(--bs-ink-3); font-size: 14px; }

/* ── phone ────────────────────────────────────────────────────────────────── */

/* Type is on clamp() and needs no override here; what is left is the handful of
   layouts that genuinely change shape on a narrow screen. */
@media (max-width: 640px) {
  .bs .lec .page-head,
  .bs .ltt .page-head { flex-wrap: wrap; gap: 4px 14px; }
  .bs .lec-session-t { font-size: 19px; }
  /* The session line is the first thing to go: strand and folio number are
     what a student needs from the running head on a 375px screen. */
  .bs .deck-sess { display: none; }
  .bs .deck-pos { margin-right: 0; }
  .bs .deck-stage { min-height: min(70vh, 520px); max-height: none; overflow: visible; }
  .bs .deck-head, .bs .deck-foot { position: static; margin-top: 0; }
  .bs .deck-hints { margin-left: 0; gap: 10px; }
  .bs .deck-hint-w { display: none; }
  .bs .deck-console { gap: 8px; padding: 10px 12px; }
  .bs .deck-console-pos { margin-left: 0; text-align: left; }
  .bs .deck-notes { top: 52px; width: 100vw; border-left: none; border-top: 3px solid var(--bs-ink); }
  .bs .ltt-resp-row { flex-wrap: wrap; gap: 8px; }
  .bs .ltt-resp-name { flex: 1 0 100%; }
  .bs .lec-nbk-row { flex-wrap: wrap; gap: 4px 14px; }
  .bs .lec-nbk-n { min-width: 0; }
  .bs .lec-nbk-t { flex: 1 0 100%; }
}

/* ── the dark theme ───────────────────────────────────────────────────────── */

/* ONE MAPPING, NOT A SECOND STYLESHEET. The `--bs-*` NAMES are redefined and
   every rule in this file — and in 02-chrome.css, and in the page head — picks
   the new values up by inheritance. It is the same move 01-tokens.css makes to
   turn the dark app into a broadsheet, run in reverse and scoped tight.

   SCOPED BY `:has()` TO A LIVE LECTURE SCREEN. The preference is persisted on
   <html> because Mark toggles it with `T` and expects it to stick, but the
   REPAINT only happens while a deck is on screen: the rest of the app is a
   printed newspaper and has no dark theme, and leaving him on a half-dark home
   page would be worse than not offering one.

   MEASURED, not picked. Against the #12211b ground: ink 14.8:1, ink-2 9.8:1 on
   the sheet, ink-3 6.3:1 (5.5:1 on stock-2, the darkest ground it is ever set
   on), accent 8.1:1, pen 5.8:1, ok 7.3:1. All clear AA for body text.
   `--bs-camel-on-ink` INVERTS: reversed out on a near-white ink it would be
   the 2.13:1 trap again, so the SHARED stamp becomes the ground colour on a
   light chip instead. */
html[data-deck-theme="dark"]:has(#screen-lecture.active,
                                 #screen-lecture-teacher.active,
                                 #screen-notebook.active) body {
  --bs-stock: #12211b;
  --bs-stock-2: #1a2f25;
  --bs-sheet: #17281f;
  --bs-ink: #eef2e6;
  --bs-ink-2: #c6d2c2;
  --bs-ink-3: #93a597;
  --bs-rule: #2f4a3b;
  --bs-blue: #d9ad63;
  --bs-camel: #c19a5b;
  --bs-camel-deep: #d9ad63;
  --bs-camel-on-ink: #12211b;
  --bs-graphite: #6f8076;
  --bs-pen: #e8736a;
  --bs-ok: #6bbd8a;
  --bs-miss: #e8736a;
  --bg: #12211b;
  --surface: #17281f;
  --card: #17281f;
  --card-hi: #1d3327;
  --border: #2f4a3b;
  --border-hi: #3d5c4a;
  --text: #eef2e6;
  --ink: #eef2e6;
  --muted: #93a597;
  --gold: #d9ad63;
  --volt: #d9ad63;
  background: #12211b;
}

/* The console keeps its role — a distinct object under the deck — but not by
   going bright. In a dark room the bar sinks BELOW the ground rather than
   floating above it. */
html[data-deck-theme="dark"]:has(#screen-lecture.active,
                                 #screen-lecture-teacher.active,
                                 #screen-notebook.active) .deck-shell {
  --deck-console-bg: #091310;
  --deck-console-fg: #eef2e6;
}

/* The printed edge is a light-on-paper effect. On the dark stock the same rule
   reads as a seam, so the sheet lifts by a hairline instead. */
html[data-deck-theme="dark"]:has(#screen-lecture.active,
                                 #screen-lecture-teacher.active,
                                 #screen-notebook.active) .deck-stage {
  box-shadow: none;
  border-left: 1px solid var(--deck-rule);
  border-right: 1px solid var(--deck-rule);
}

html[data-deck-theme="dark"]:has(#screen-lecture.active,
                                 #screen-lecture-teacher.active,
                                 #screen-notebook.active) .deck-notes {
  box-shadow: -8px 0 0 rgba(0, 0, 0, .35);
}

/* ── the gate bands ───────────────────────────────────────────────────────── */

/* `.gate-station` carries no container rules of its own — it borrows the inner
   .gate-resume-* classes from the markup, so the WRAPPER had no border, padding
   or bottom margin. With one band that reads as merely tight; with two it is a
   collision, and the second band's button lands on top of the first track card.

   Styled together rather than only fixing the new one: they are siblings that
   stack, and giving them different spacing is how they drift apart. */
.bs .gate-station,
.bs .gate-lecture {
  border-top: 2px solid var(--bs-ink);
  border-bottom: 1px solid var(--bs-rule);
  padding: 12px 0 16px;
  margin-bottom: clamp(24px, 4vh, 40px);
}

/* When both are live the lecture is the earlier claim, so it keeps the heavy
   rule and the station band below it takes a quieter one — two 2px rules
   stacked read as a box. */
.bs .gate-lecture + .gate-station {
  border-top-width: 1px;
  border-top-color: var(--bs-rule);
}

/* Blue, not camel — same 2.13:1 reason as the strand labels. */
.bs .gate-lecture .gate-resume-k { color: var(--bs-blue); }

.bs .cr-consoles {
  padding: 6px 0 14px;
  border-bottom: 1px solid var(--bs-rule);
  margin-bottom: 14px;
}
