/* ============================================================================
   DECK SLIDE TEMPLATES - A MIRROR. Do not edit this file to change a slide.

   The AUTHORITATIVE copy is `class-app/public/css/slide-templates.css`, and the
   two are byte-identical below this header, which is the first 51 lines of this
   file. Change that one, then re-copy:

     head -n 51 public/css/deck-slide-templates.css > /tmp/hdr.css
     cat /tmp/hdr.css class-app/public/css/slide-templates.css \
       > public/css/deck-slide-templates.css
     diff <(tail -n +52 public/css/deck-slide-templates.css) \
          class-app/public/css/slide-templates.css      # must print nothing

   ── WHY A COPY EXISTS AT ALL

   A slide body in `session_slides.body_html` is staff-authored markup composed
   out of the `.tpl-*` classes below, and TWO applications render it: the lecture
   room (class-app, class.10xcourse.ai) and the student's own quiet copy of the
   deck in the student area (`StudentSessionDeck.vue`, students.10xcourse.ai).
   The student area had none of these rules, so every template collapsed into a
   plain stack: the three-up OS logo row on meeting 1 became one uncropped
   penguin the height of the reading column, comparison tables became two lists,
   and timelines became sentences. Nothing errored, and nothing said so.

   ── THE STUDENT AREA NOW DRAWS THE `.slide--display` BRANCH, AT EVERY WIDTH

   This reverses the note that stood here until 2026-09-06, and the sibling
   mirror `deck-slides.css` is the other half of the change. `StudentSessionDeck`
   used to put `slide slide--phone` on a bare reading column; it now renders the
   room's own article - `slide slide--display` inside a fixed 1920x1080 canvas
   that is transform-scaled to fit a 16:9 box - exactly as `SlideStage` and
   `SlideRenderer` do in the room. So the `cqw` coordinate system these rules are
   written in resolves against the same 1920px container on both sites, and the
   student's copy of a slide is the picture the projector drew.

   The `.slide--phone` branch below is therefore no longer used by this site. It
   is kept because this file is a straight copy, not a rewrite: the moment it
   stops being one, a re-sync stops being a `cat` and starts being a merge.

   The phone REWRITE a slide can carry (`phoneBodyHtml`) is still drawn on a
   handset - below the scaled slide, as body copy, in `.stu-deck__read` - which
   is the same decision the room took for its own student handset
   (`.cl-student__read`, class-app/public/css/app.css).

   ── AND THE IMAGES ARE MIRRORED FOR THE SAME REASON

   A slide body says `src="/img/logos/linux.svg"`, so `public/img/` and
   `public/mascot/` are mirrored here too rather than pointed at the room's
   origin. `StudentSessionDeck.vue` says why that direction was refused.
   ============================================================================ */

/* ============================================================================
   SLIDE TEMPLATES - the shared vocabulary every deck body is composed from.

   WHY THIS FILE EXISTS. The first session-1 deck was 130 slides each carrying
   its own inline <style> block: 130 private definitions of "a card", "a heading",
   "an eyebrow". Two things followed, and both were measured rather than guessed.
   The deck did not look like one deck. And 80 of the 130 slides overflowed the
   1920x1080 canvas, silently, because `.slide__body` scrolls: on a projector the
   surplus is simply not on the screen and nothing anywhere says so.

   So a body now composes template classes and writes almost no CSS of its own.
   A template that fits, fits on every slide that uses it.

   THE COORDINATE SYSTEM is the one slides.css already establishes: a slide lives
   inside `.cl-stage__canvas`, a fixed 1920x1080 box with `container-type:
   inline-size`, transform-scaled to whatever screen it is on. Every rule here
   sizes itself in `cqw` (1cqw = 19.2px) with a `clamp()` floor, never in `vw`,
   never in a `@media` query. A `vw` rule looks perfect on the venue screen and
   breaks the phone preview, which is why it survives casual review.

   THE PHONE SURFACE is ordinary responsive CSS and shares these class names, so
   every template has a `.slide--phone` branch beside it rather than in a second
   file. slides.css says why: the open-day app split one block's rules across two
   files and its own docs now read "change one, change the other".

   NAMING. Everything here is `tpl-`. `cl-` belongs to the app chrome, `s-` to the
   handful of legacy per-slide blocks, and a parallel deck kit owns its own file.

   SKINS ARE OPTED INTO FROM THE BODY. A body cannot set a class on `.slide`, so
   the skins below select upward with `:has()`. `.slide:has(.tpl--dark)` is
   specificity (0,2,0) and beats `.slide` (0,1,0), which is what lets a body
   repaint its own slide, title included.
   ============================================================================ */

/* ── Tokens the templates share ───────────────────────────────────────────── */

.slide {
  /* One scale knob per slide. A template sets its type in `em` off these, so a
     density class can shrink a whole slide by one number rather than by a
     rewrite. This is the lever the overflow pass pulls. */
  --tpl-scale: 1;
  --tpl-gap: calc(1.6cqw * var(--tpl-scale));
  --tpl-pad: calc(2.2cqw * var(--tpl-scale));
  --tpl-radius: 1.1cqw;
  --tpl-line: var(--border-default);
  --tpl-ink: var(--text-color);
  --tpl-ink-soft: var(--text-secondary);
  --tpl-ink-faint: var(--text-tertiary);
  --tpl-surface: #ffffff;
  --tpl-accent: var(--primary-color);
  --tpl-brand: linear-gradient(90deg, #0894ff 0%, #bd32d6 50%, #ffa304 100%);
  /* HOW FAR THE TERMINAL CHEVRON REACHES INTO THE CONTENT COLUMN. The one rule
     about that corner is written off this number, and it is derived, not chosen:

       chevron's far edge, from the canvas's inline-start (right) edge
                        = 3cqw inset + 6.528cqw inline-size = 9.528cqw
       the content column's own inline-start edge
                        = `.slide--display`'s 7cqw padding
       overhang         = 9.528cqw - 7cqw = 2.528cqw = 48.54px on the canvas

     ROUNDED UP TO 2.6cqw, and the 1.4px is the whole point of the rounding: at
     the exact overhang a card's border-box edge lands on the same sub-pixel as
     the mark's box edge (1737.06 against 1737.08), and a zero-clearance
     abutment is how this comes back the next time anything moves by a pixel.

     All three inputs live in slides.css (`.slide--display`,
     `.slide__brandmark--term`, `.slide__brandmark .logo-10x-terminal`). Nothing
     computes this for you: move the chevron or resize it there and this number
     is wrong here, silently, and content starts printing over the mark again.

     IN cqw AND NEVER IN A MEDIA QUERY, like everything else in this file. The
     canvas is a 1920px `container-type: inline-size` box on BOTH surfaces, so
     this is 49.92px on the projector, in the presenter's preview and in the
     student area alike, and the clamp only ever bites somewhere none of them
     goes: the floor is its value at a 462px container, near where the chevron's
     own clamp floors take over from its cqw sizes (6.528cqw = 31px at 475px),
     and the ceiling is its value on the 1920px canvas.

     WHAT IT DOES NOT COVER: a card's ambient shadow. `--shadow-md` is a 16px
     blur, so it reaches about 8px past the border box it belongs to and still
     grazes the outermost pixels of the arm's cap on order 27 - measured at 2
     pixels, 7/255 at worst, against 501 before this rule. Buying those back
     would cost every closing footnote in the deck another 8px of indent, and
     shadows spilling over their neighbours is what this deck's cards do
     everywhere else too. */
  --tpl-mark-reserve: clamp(12px, 2.6cqw, 49.92px);
}

/* Three density steps. `--d2` and `--d3` exist so a slide that is one line too
   tall is fixed by adding a class, not by deleting the line the lecturer wanted.
   Nothing below 0.78: past that the back row cannot read it. */
.tpl--d2 { --tpl-scale: 0.88; }
.tpl--d3 { --tpl-scale: 0.78; }

/* Centre on the CROSS axis too. `.tpl` is a flex column, so its
   `justify-content: center` only centres vertically and every child still
   stretches to the full width - which is why a title card's mascot and lockup
   had to carry `align-self: center` by hand. `tpl--figure` and `tpl--quote`
   centre themselves; this is for the rest. */
.tpl--center {
  align-items: center;
  text-align: center;
}

/* ── The template root ────────────────────────────────────────────────────── */

.tpl {
  position: relative;
  block-size: 100%;
  min-block-size: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--tpl-gap);
  font-family: var(--font-body);
  color: var(--tpl-ink);
  /* The body scrolls by default (slides.css). Inside a template it must not:
     a template that needs to scroll is a template that has been overfilled, and
     the overflow harness is what catches it. Hiding here rather than scrolling
     makes the failure visible in a screenshot instead of only in a measurement. */
  overflow: hidden;
}

.slide--display .slide__body:has(.tpl) {
  /* The template owns the vertical rhythm; the body stops being a scroll port. */
  overflow: visible;
  display: flex;
  min-block-size: 0;
  flex: 1 1 auto;
}

.slide--display .slide__body:has(.tpl) > .tpl {
  inline-size: 100%;
}

.slide--phone .tpl {
  block-size: auto;
  gap: var(--space-md);
  overflow: visible;
}

/* ── Shared parts ─────────────────────────────────────────────────────────── */

/* The kicker over a title: which chapter this slide belongs to. Mono, letter
   spaced, quiet. Hebrew is case-less, so no text-transform - it would only ever
   have shouted at the one Latin word in the line. */
.tpl-eyebrow {
  font-family: var(--font-mono);
  font-size: calc(clamp(12px, 1.25cqw, 25px) * var(--tpl-scale));
  letter-spacing: 0.12em;
  line-height: 1.3;
  color: var(--tpl-ink-faint);
  display: flex;
  align-items: center;
  gap: 0.9cqw;
}

.tpl-eyebrow::before {
  content: '';
  inline-size: calc(2.6cqw * var(--tpl-scale));
  block-size: 2px;
  border-radius: 2px;
  background: var(--tpl-brand);
  flex: none;
}

.slide--phone .tpl-eyebrow { font-size: 11px; }

/* A leading eyebrow belongs to the TITLE, not to the body's centred block. In a
   centred flex column it otherwise drifts to the middle of the slide and reads
   as a subtitle floating in space, which is what the smoke test showed. Pin it
   to the top of the body box and reserve its line so centred content cannot
   ride up into it. */
/* `:not(.tpl-free)` for the same reason the mascot rule carries it, and it was
   found the same way - in a browser, not in a spec. This rule is (0,4,0) and the
   free-placement rule at the foot of this file is (0,3,0), so WITHOUT the
   exclusion this one wins outright on a free eyebrow that happens to be the
   first child of its root. What the owner then sees is not a box that ignored
   its `--tpl-fx`: `inset-inline-start: 0` under `direction: rtl` resolves to
   `right: 0`, so the box pins itself to the OPPOSITE corner from the one the
   numbers name, and the panel and the slide disagree with nothing on screen to
   say why. */
.slide--display .tpl > .tpl-eyebrow:first-child:not(.tpl-free) {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  z-index: 1;
}

.slide--display .tpl:has(> .tpl-eyebrow:first-child:not(.tpl-free)) {
  padding-block-start: calc(3.4cqw * var(--tpl-scale));
}

/* The lede: one sentence under the title that carries the claim. Never a
   paragraph - a paragraph on a projector is read instead of listened to. */
.tpl-lede {
  font-size: calc(clamp(16px, 2.05cqw, 41px) * var(--tpl-scale));
  font-weight: 400;
  line-height: 1.4;
  color: var(--tpl-ink-soft);
  max-inline-size: 62ch;
}

.slide--phone .tpl-lede { font-size: 15px; }

/* The closing line: the principle, stated once, in ink rather than grey. */
.tpl-point {
  font-size: calc(clamp(17px, 2.3cqw, 46px) * var(--tpl-scale));
  font-weight: 400;
  line-height: 1.35;
  color: var(--tpl-ink);
  max-inline-size: 58ch;
}

.slide--phone .tpl-point { font-size: 16px; }

.tpl-note {
  font-size: calc(clamp(13px, 1.5cqw, 30px) * var(--tpl-scale));
  line-height: 1.4;
  color: var(--tpl-ink-faint);
}

.slide--phone .tpl-note { font-size: 13px; }

/* ── The closing block yields the chevron's corner ────────────────────────── */
/*
   THE CORNER BELONGS TO THE MARK, AND CONTENT STEPS AROUND IT. One declaration,
   one number, and the only thing it is ever allowed to touch is the LAST block
   of a template - because that is the only block that can reach the bottom of a
   column `.tpl` centres.

   WHAT IT FIXES. An ink measurement of all 147 rendered states of the session-1
   deck - counting pixels where the mark's own strokes come out different with
   the slide's content drawn over them, not element boxes - found the chevron
   struck on fourteen of them and the 10x mark on none. Eleven were one shape: a
   closing `.tpl-note` or `.tpl-point` on 12/2, 18/1, 42/1, 55/1, 87, 91, 94, 95,
   96, 99/1 and 102, whose last line begins at the column's inline-start edge -
   which in this `dir="rtl"` deck is the physical RIGHT, the very edge the
   chevron is pinned to - and printed straight through its upper arm, 9 to 121
   pixels each. Two more were not prose but a surface landing ON the mark
   and cutting it: 27's layer stack (501px, on all 8 of its steps) and 63's
   closing card row (435px). Same cause, same cure: the block that owns the
   bottom of the slide steps back by the overhang, and the mark is drawn whole
   inside the gap. (The fourteenth, 7, had a different cause and is fixed at
   `.tpl-steps--2col` above.)

   WHY NOT MOVE THE MARK. Its corner is an owner decision of 2026-09-06 and the
   only insets that would clear a full-bleed card are 0.4cqw horizontally or
   0.8cqw vertically - rendered, both read as the mark having fallen off the
   slide rather than sitting in a corner. Fading it where a card covers it fixes
   nothing: an opaque card hides whatever is under it at any opacity, so the mark
   stays cut, only more faintly. Content yielding is also what slides.css already
   says should happen ("a collision an author can see is a collision that gets
   fixed").

   WHY NOT VERTICALLY. Lifting the content instead - `padding-block-end` on
   `.tpl` - is a tempting one-liner and wrong twice over. `.tpl` centres on the
   block axis, so 55.8px of bottom padding moves EVERY display slide up by
   27.9px; and it takes 55.8px out of the height budget of exactly the slides
   that collide, which collide BECAUSE they are already full. 27, 63 and 87
   overflow the moment you try it.

   WHY FOUR NAMED SELECTORS AND NOT `.tpl > :last-child`. The blunt version
   reaches bar scales, tables, comparison columns and section subtitles - content
   whose geometry carries meaning - so each closing shape that can actually reach
   the corner is named instead, and each name resolves to exactly the states that
   need it: `.tpl-layers:last-child` is order 27 alone, and a template that
   stacks a terminal transcript over a closing card row is order 63 alone. The
   nine other `.tpl-grid:last-child` slides close 150px higher and are
   deliberately left flush. Order 7, the fourteenth state, is not here at all:
   its collision was a row rule a two-column step list should never have drawn,
   and it is deleted at `.tpl-steps--2col` above rather than dodged.

   `:last-child` CARRIES THE REST OF THE CONDITION. A block with anything after
   it cannot reach the bottom of a column `.tpl` centres, and the 25 bodies that
   close on a `.tpl-mascot--corner` are out of flow and clear the chevron by
   arithmetic (slides.css).

   `.slide--display` SCOPES IT TO WHERE THE MARK ACTUALLY PAINTS. Both marks are
   `display: none` on `.slide--phone` (slides.css), and the phone branch of this
   file is still live markup on the phone REWRITE a slide can carry, so an
   unscoped rule would indent a phone footnote to clear a mark that is not there.
*/
/* A CENTRED TEMPLATE TAKES NO RESERVE, and this is not a nicety. These five are
   the `.tpl` roots that set `align-items: center`, so their closing block is
   shrink-wrapped and centred in the column rather than stretched to it: it never
   begins at the inline-start edge, so it cannot reach the corner, and padding
   would not indent it - it would widen a centred box and slide its line
   half the reserve OFF CENTRE. Measured before this rule existed: 25px on orders
   1, 73 and 85, whose closing note is centred text, and on 36, 49, 56 and 69,
   whose is a shrink-wrapped block. Zeroing the token rather than bolting
   `:not()` onto all four selectors keeps the rule below readable, and follows what
   `--d2` already does with `--tpl-scale`. A NEW TEMPLATE THAT CENTRES ITS
   CHILDREN BELONGS ON THIS LIST. */
.tpl--center,
.tpl--section,
.tpl--figure,
.tpl--shot,
.tpl--tree {
  --tpl-mark-reserve: 0px;
}

.slide--display .tpl > .tpl-note:last-child,
.slide--display .tpl > .tpl-point:last-child,
.slide--display .tpl > .tpl-layers:last-child,
.slide--display .tpl:has(> .tpl-term) > .tpl-grid:last-child {
  padding-inline-start: var(--tpl-mark-reserve);
}

/* The 3-stop brand hairline, in canvas units. The site's `.brand-hairline` is
   96x2 px; this is the same gradient at the deck's scale. */
.tpl-rule {
  inline-size: calc(9cqw * var(--tpl-scale));
  block-size: 2px;
  border: 0;
  border-radius: 2px;
  background: var(--tpl-brand);
  opacity: 0.9;
  flex: none;
}

.tpl-rule--wide { inline-size: calc(16cqw * var(--tpl-scale)); }
.tpl-rule--full { inline-size: 100%; opacity: 0.55; }

.slide--phone .tpl-rule { inline-size: 72px; }

/* A pill. Counts, labels, "step 4 of 13". */
.tpl-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.5cqw;
  padding: calc(0.35cqw * var(--tpl-scale)) calc(1.1cqw * var(--tpl-scale));
  border-radius: var(--radius-full);
  border: 1px solid var(--primary-border);
  background: var(--primary-light);
  color: var(--tpl-accent);
  font-family: var(--font-mono);
  font-size: calc(clamp(12px, 1.3cqw, 26px) * var(--tpl-scale));
  line-height: 1.2;
}

/* A badge is a pill, and a pill that stretches is a bar. In a flex column the
   default `align-items: stretch` makes it full width, which is what the smoke
   test drew. */
.tpl-badge { align-self: flex-start; }

.tpl-badge--quiet {
  border-color: var(--tpl-line);
  background: transparent;
  color: var(--tpl-ink-faint);
}

.slide--phone .tpl-badge { font-size: 12px; padding: 3px 10px; }

/* Inline code and keys. `unicode-bidi: isolate` is not decoration: a Latin
   identifier inside a Hebrew sentence is reordered without it, and a mis-ordered
   identifier is a line of code nobody in the room can copy. */
.tpl-code {
  font-family: var(--font-mono);
  direction: ltr;
  unicode-bidi: isolate;
  font-size: 0.92em;
  padding: 0.06em 0.34em;
  border-radius: 0.35em;
  background: var(--bg-secondary);
  color: var(--tpl-ink);
}

.tpl-kbd {
  font-family: var(--font-mono);
  direction: ltr;
  unicode-bidi: isolate;
  font-size: 0.9em;
  padding: 0.1em 0.45em;
  border-radius: 0.3em;
  border: 1px solid var(--tpl-line);
  border-block-end-width: 2px;
  background: var(--tpl-surface);
}

/* A definition row: the term on the start side, what it means beside it. The
   chapters on Claude Code and on Git are largely vocabulary, and `tpl--table`
   is a heavy instrument for two columns of short text - it draws rules, reserves
   a header row and asks the author for `<thead>`. This is a grid of pairs.

   The children alternate: k, v, k, v. There is no row element on purpose, so a
   pair cannot drift out of alignment with the pair above it. */
.tpl-kv {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  column-gap: calc(1.4cqw * var(--tpl-scale));
  row-gap: calc(0.6cqw * var(--tpl-scale));
  min-inline-size: 0;
}

/* Four tracks, so twice as many pairs fit the same height. Still k, v, k, v in
   source order; the grid wraps them two pairs to a row. */
.tpl-kv--2col {
  grid-template-columns: auto minmax(0, 1fr) auto minmax(0, 1fr);
  column-gap: calc(1.2cqw * var(--tpl-scale));
}

.tpl-kv__k {
  font-family: var(--font-mono);
  direction: ltr;
  unicode-bidi: isolate;
  font-size: calc(clamp(13px, 1.6cqw, 32px) * var(--tpl-scale));
  line-height: 1.35;
  color: var(--tpl-ink);
  white-space: nowrap;
}

.tpl-kv__v {
  font-size: calc(clamp(13px, 1.55cqw, 31px) * var(--tpl-scale));
  line-height: 1.4;
  color: var(--tpl-ink-soft);
  min-inline-size: 0;
}

.slide--phone .tpl-kv,
.slide--phone .tpl-kv--2col {
  grid-template-columns: minmax(0, 1fr);
  row-gap: 2px;
}

.slide--phone .tpl-kv__k { font-size: 14px; white-space: normal; }
.slide--phone .tpl-kv__k:not(:first-child) { margin-block-start: 10px; }
.slide--phone .tpl-kv__v { font-size: 14px; }

/* The closing slide's heart. Carried over from the open-day deck literally,
   colour included, because the two closers are meant to look like the same
   gesture. Not sized off `--tpl-scale`: it is one glyph on a slide with almost
   nothing else on it, so there is nothing for a density class to rescue. */
.tpl-heart {
  text-align: center;
  font-size: clamp(22px, 2.6cqw, 52px);
  line-height: 1;
  color: #e0245e;
}

.slide--phone .tpl-heart { font-size: 22px; }

/* ── Glass card ───────────────────────────────────────────────────────────── */
/* The main site's fold card, in canvas units: a translucent white pane over the
   slide's own background, capped by the brand hairline. The cap is the site's
   one sanctioned decorative gradient and it never sits behind text. */

.tpl-card {
  position: relative;
  padding: var(--tpl-pad);
  border-radius: var(--tpl-radius);
  border: 1px solid var(--border-light);
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(16px) saturate(160%);
  -webkit-backdrop-filter: blur(16px) saturate(160%);
  box-shadow: var(--shadow-md);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: calc(0.7cqw * var(--tpl-scale));
  min-inline-size: 0;
}

.tpl-card--capped::before {
  content: '';
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  block-size: 2px;
  background: var(--tpl-brand);
}

.tpl-card--flat {
  background: var(--tpl-surface);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: var(--shadow-sm);
}

.tpl-card--tint {
  background: var(--primary-light);
  border-color: var(--primary-border);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: none;
}

.tpl-card__head {
  font-size: calc(clamp(15px, 1.85cqw, 37px) * var(--tpl-scale));
  font-weight: 500;
  line-height: 1.25;
  color: var(--tpl-ink);
}

.tpl-card__body {
  font-size: calc(clamp(13px, 1.55cqw, 31px) * var(--tpl-scale));
  font-weight: 400;
  line-height: 1.45;
  color: var(--tpl-ink-soft);
}

.tpl-card__num {
  font-family: var(--font-display);
  font-size: calc(clamp(24px, 3.6cqw, 72px) * var(--tpl-scale));
  font-weight: 300;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--tpl-accent);
}

.slide--phone .tpl-card { padding: var(--space-md); }
.slide--phone .tpl-card__head { font-size: 15px; }
.slide--phone .tpl-card__body { font-size: 14px; }
.slide--phone .tpl-card__num { font-size: 26px; }

/* ── T1  lead: one claim, optionally built ───────────────────────────────── */

.tpl--lead {
  justify-content: center;
  gap: calc(2cqw * var(--tpl-scale));
}

.tpl--lead .tpl-point {
  font-size: calc(clamp(20px, 3cqw, 60px) * var(--tpl-scale));
  font-weight: 400;
  line-height: 1.28;
}

.slide--phone .tpl--lead .tpl-point { font-size: 19px; }

/* The LAST slide of a deck. Centred, and quiet. Centring is `tpl--center` on the
   body; everything else has to be here, because `.slide__title` is the h1
   OUTSIDE the template and a slide body may not ship a `<style>` block to reach
   it. The selector is (0,2,0) and beats the base `.slide__title` (0,1,0) on all
   four properties.

   Owner decision, 2026-09-06, replacing the blue-and-500 of two days earlier:
   the closing sentence is a courtesy, not the message. The BRAND LOCKUP below it
   is what the room should be left looking at, so the words above it give way -
   near-black instead of the site's blue, weight 200 instead of 500, two thirds
   of the size, and the display face's negative tracking traded for a little air.
   Noto Sans Hebrew is a 100-900 variable face, so 200 is a real drawn weight
   here rather than a synthesised one; at this size 300 still reads as a heading
   and 200 reads as a sign-off, which is the whole point of the change.

   The brand lockup keeps its own colours on purpose: it is an SVG painted with
   `url(#...)` gradient fills, which `color` cannot reach. */
.slide:has(.tpl--closing) .slide__title {
  color: var(--tpl-ink);
  font-weight: 200;
  font-size: clamp(20px, 2.8cqw, 56px);
  letter-spacing: 0.01em;
}

.tpl--closing .tpl-eyebrow,
.tpl--closing .tpl-point { color: var(--tpl-accent); }

/* ── T2  section: the card that opens a module, chapter or subject ───────── */
/* Zero minutes on the clock. It exists so the room can tell a new part of the
   day has started, which the run of show otherwise only tells the lecturer. */

/* CENTRED, like every other slide title in the deck.
   `slides.css` centres `.slide__title`, and this template hides that h1 and
   prints its own title instead - so leaving the card at `flex-start` would make
   the module, chapter and subject openers the only left-ragged headings in a
   deck of centred ones, which is the inconsistency the centring was asked for.
   The kicker, the sub and the badge row centre with it: a title slide whose
   heading is centred over left-aligned metadata reads as a mistake. */
.tpl--section {
  justify-content: center;
  align-items: center;
  gap: calc(1.4cqw * var(--tpl-scale));
  text-align: center;
}

.tpl--section .tpl-section__kicker {
  font-family: var(--font-mono);
  font-size: clamp(13px, 1.5cqw, 30px);
  letter-spacing: 0.16em;
  color: var(--tpl-accent);
}

.tpl--section .tpl-section__title {
  font-family: var(--font-display);
  font-size: clamp(30px, 5.4cqw, 108px);
  font-weight: 300;
  line-height: 1.08;
  letter-spacing: -0.03em;
  color: var(--tpl-ink);
  max-inline-size: 22ch;
}

.tpl--section .tpl-section__sub {
  font-size: clamp(16px, 2.2cqw, 44px);
  font-weight: 400;
  line-height: 1.4;
  color: var(--tpl-ink-soft);
  max-inline-size: 46ch;
}

.tpl--section .tpl-section__meta {
  display: flex;
  gap: 1.2cqw;
  flex-wrap: wrap;
  margin-block-start: 0.6cqw;
}

/* A section card hides the slide's own H1: the card IS the title, at its own
   scale, and the two together read as the same words printed twice. */
.slide--display:has(.tpl--section) .slide__title { display: none; }
.slide--phone:has(.tpl--section) .slide__title { display: none; }

.slide--phone .tpl--section .tpl-section__title { font-size: 26px; }
.slide--phone .tpl--section .tpl-section__sub { font-size: 15px; }

/* ── T3  cards: two to four panes across ─────────────────────────────────── */

.tpl--cards .tpl-grid {
  display: grid;
  grid-template-columns: repeat(var(--tpl-cols, 3), minmax(0, 1fr));
  gap: var(--tpl-gap);
  align-items: stretch;
  min-block-size: 0;
}

.tpl-grid--2 { --tpl-cols: 2; }
.tpl-grid--3 { --tpl-cols: 3; }
.tpl-grid--4 { --tpl-cols: 4; }

.slide--phone .tpl--cards .tpl-grid {
  grid-template-columns: 1fr;
  gap: var(--space-sm);
}

/* ── T4  split: text beside an artefact ──────────────────────────────────── */

.tpl--split .tpl-split {
  display: grid;
  grid-template-columns: var(--tpl-split, 1fr 1fr);
  gap: calc(2.4cqw * var(--tpl-scale));
  align-items: center;
  min-block-size: 0;
}

.tpl-split--wide-start { --tpl-split: 1.35fr 1fr; }
.tpl-split--wide-end { --tpl-split: 1fr 1.35fr; }

.tpl-split__col {
  display: flex;
  flex-direction: column;
  gap: calc(0.9cqw * var(--tpl-scale));
  min-inline-size: 0;
}

.slide--phone .tpl--split .tpl-split {
  grid-template-columns: 1fr;
  gap: var(--space-md);
}

/* ── T5  figure: one number, one caption ─────────────────────────────────── */

.tpl--figure {
  align-items: center;
  text-align: center;
  justify-content: center;
}

.tpl-figure__num {
  font-family: var(--font-display);
  /* 500+ weight is forbidden on the brand gradient, numerals included, so this
     is a solid colour at a light weight rather than a gradient fill. */
  font-size: clamp(56px, 12cqw, 240px);
  font-weight: 300;
  line-height: 0.95;
  letter-spacing: -0.04em;
  color: var(--tpl-accent);
  direction: ltr;
}

/* Its own tracking, overriding the display number's -0.04em. That negative value
   is right for a big Latin numeral and wrong for the Hebrew word glued to it:
   Hebrew letterforms have no side bearings to spare, and on order 73 the two
   vavs of the unit word fused into a single stroke - the word read as a
   different word. Tuned on that slide at 0.06 / 0.08 / 0.10em: 0.06 separates
   them, 0.10 reads best but widens the word enough to start drifting off the
   number, 0.08 is both. Positive, and only just. */
.tpl-figure__unit {
  font-size: 0.34em;
  font-weight: 400;
  letter-spacing: 0.08em;
  color: var(--tpl-ink-soft);
  padding-inline-start: 0.2em;
}

.tpl-figure__cap {
  font-size: clamp(16px, 2.3cqw, 46px);
  font-weight: 400;
  line-height: 1.35;
  color: var(--tpl-ink);
  max-inline-size: 44ch;
}

.slide--phone .tpl-figure__num { font-size: 54px; }
.slide--phone .tpl-figure__cap { font-size: 16px; }

/* ── T6  anatomy: a string taken apart, every part labelled ──────────────── */
/* The template the owner asked for by name: print the URL and map protocol,
   host, domain, TLD, port, path, query. It is also the right shape for a
   command line, a file path and an HTTP request line, so it is generic. */

.tpl--anatomy {
  justify-content: center;
  gap: calc(2cqw * var(--tpl-scale));
}

.tpl-anat {
  direction: ltr;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: center;
  gap: 0;
  font-family: var(--font-mono);
}

.tpl-anat__part {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(0.55cqw * var(--tpl-scale));
  padding-block-end: 0.2cqw;
}

/* The same field written in the base the machine actually stores, printed ABOVE
   the decimal INSIDE the part rather than as a second row of its own: the part is
   already a flex column, so the two writings of one byte share a column and stay
   registered no matter how wide either gets. A separator part carries a blank one
   (`&nbsp;`) for the same reason - `.tpl-anat` aligns to `flex-start`, so a part
   one line shorter floats its dot up out of the row. */
.tpl-anat__bin {
  font-size: calc(clamp(9px, 1.15cqw, 23px) * var(--tpl-scale));
  line-height: 1.25;
  letter-spacing: 0.05em;
  color: var(--tpl-ink-faint);
  white-space: pre;
}

.tpl-anat__part.is-on .tpl-anat__bin { color: var(--tpl-accent); }

.tpl-anat__txt {
  font-size: calc(clamp(18px, 3.1cqw, 62px) * var(--tpl-scale));
  line-height: 1.15;
  color: var(--tpl-ink-faint);
  padding: 0.06em 0.1em;
  border-block-end: 3px solid transparent;
  white-space: pre;
}

/* A lit part. `--on` is the one the slide is talking about at this build step;
   everything else stays grey so the eye has one place to go. */
.tpl-anat__part.is-on .tpl-anat__txt {
  color: var(--tpl-accent);
  border-block-end-color: var(--tpl-accent);
  background: var(--primary-light);
  border-start-start-radius: 0.2em;
  border-start-end-radius: 0.2em;
}

.tpl-anat__lbl {
  font-family: var(--font-body);
  direction: rtl;
  font-size: calc(clamp(11px, 1.25cqw, 25px) * var(--tpl-scale));
  line-height: 1.2;
  color: var(--tpl-ink-faint);
  text-align: center;
  max-inline-size: 14ch;
  opacity: 0;
}

.tpl-anat__part.is-on .tpl-anat__lbl {
  color: var(--tpl-accent);
  opacity: 1;
}

/* Every label showing at once, for the summary step. */
.tpl-anat--all .tpl-anat__lbl { opacity: 1; }

.tpl-anat__legend {
  display: grid;
  grid-template-columns: repeat(var(--tpl-cols, 4), minmax(0, 1fr));
  gap: calc(0.9cqw * var(--tpl-scale)) calc(1.6cqw * var(--tpl-scale));
}

.tpl-anat__legend--2 { --tpl-cols: 2; }
.tpl-anat__legend--3 { --tpl-cols: 3; }
.tpl-anat__legend--4 { --tpl-cols: 4; }
.tpl-anat__legend--5 { --tpl-cols: 5; }

.tpl-anat__row {
  display: flex;
  align-items: baseline;
  gap: 0.7cqw;
  font-size: calc(clamp(12px, 1.45cqw, 29px) * var(--tpl-scale));
  line-height: 1.35;
  color: var(--tpl-ink-soft);
  min-inline-size: 0;
}

.tpl-anat__row b {
  font-family: var(--font-mono);
  direction: ltr;
  unicode-bidi: isolate;
  font-weight: 400;
  color: var(--tpl-ink);
  flex: none;
}

.slide--phone .tpl-anat__bin { font-size: 8px; letter-spacing: 0; }
.slide--phone .tpl-anat__txt { font-size: 15px; }
.slide--phone .tpl-anat__lbl { font-size: 11px; }
.slide--phone .tpl-anat__legend { grid-template-columns: 1fr; }

/* ── T7  steps: a numbered sequence ──────────────────────────────────────── */

.tpl--steps .tpl-steps {
  list-style: none;
  display: grid;
  gap: calc(0.9cqw * var(--tpl-scale));
  padding: 0;
  margin: 0;
  min-block-size: 0;
}

.tpl-steps--2col { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--tpl-gap); }

.tpl-step {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: baseline;
  gap: calc(1.1cqw * var(--tpl-scale));
  padding-block: calc(0.55cqw * var(--tpl-scale));
  border-block-end: 1px solid var(--border-light);
  min-inline-size: 0;
}

.tpl-step:last-child { border-block-end: 0; }

/* A TWO-COLUMN LIST HAS TWO BOTTOM ROWS, and `:last-child` only reaches one of
   them: on order 7 the right-hand column ended on a rule and the left-hand one
   did not, an asymmetry visible from the back of the room. `-n+2` is the last
   cell of each column whether the list is odd or even - with ten steps it is 9
   and 10, with nine it is 8 and 9, and in both cases that is exactly the pair
   that sits at the foot of a column.

   It is also the whole of that slide's collision with the bottom-right
   brandmark: the stray rule ran 13px across the chevron's upper arm at y 948,
   one pixel tall. Deleting a rule that should not have been drawn is the honest
   fix; reserving the mark's corner from a ten-item course outline, the way the
   prose blocks do below, would have inset the entire grid by 50px and left the
   two columns visibly unbalanced. */
.tpl-steps--2col > .tpl-step:nth-last-child(-n + 2) { border-block-end: 0; }

.tpl-step__n {
  font-family: var(--font-mono);
  font-size: calc(clamp(13px, 1.6cqw, 32px) * var(--tpl-scale));
  color: var(--tpl-accent);
  min-inline-size: 2.2ch;
  direction: ltr;
}

.tpl-step__t {
  font-size: calc(clamp(14px, 1.75cqw, 35px) * var(--tpl-scale));
  line-height: 1.35;
  color: var(--tpl-ink);
  min-inline-size: 0;
}

.tpl-step__t small {
  display: block;
  font-size: 0.82em;
  color: var(--tpl-ink-faint);
  line-height: 1.3;
}

.slide--phone .tpl-steps--2col { grid-template-columns: 1fr; }
.slide--phone .tpl-step__t { font-size: 14px; }

/* ── T8  terminal: a command and what it printed ─────────────────────────── */

.tpl-term {
  direction: ltr;
  border-radius: var(--tpl-radius);
  border: 1px solid rgba(255, 255, 255, 0.09);
  background: #14151d;
  overflow: hidden;
  box-shadow: var(--shadow-lg);
  min-inline-size: 0;
}

.tpl-term__bar {
  display: flex;
  align-items: center;
  gap: 0.6cqw;
  padding: calc(0.7cqw * var(--tpl-scale)) calc(1.1cqw * var(--tpl-scale));
  background: rgba(255, 255, 255, 0.04);
  border-block-end: 1px solid rgba(255, 255, 255, 0.07);
}

.tpl-term__dot {
  inline-size: 0.75cqw;
  block-size: 0.75cqw;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.18);
}

.tpl-term__name {
  font-family: var(--font-mono);
  font-size: calc(clamp(11px, 1.2cqw, 24px) * var(--tpl-scale));
  color: rgba(255, 255, 255, 0.45);
  padding-inline-start: 0.6cqw;
}

.tpl-term__body {
  padding: calc(1.2cqw * var(--tpl-scale));
  font-family: var(--font-mono);
  font-size: calc(clamp(13px, 1.6cqw, 32px) * var(--tpl-scale));
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.82);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* The prompt sign and the command the person typed, told apart from the output
   they did not. A room that cannot tell those apart cannot copy either. */
.tpl-term__cmd { color: #ffffff; }
.tpl-term__cmd::before { content: '$ '; color: var(--ccdd-gold, #d4a574); }
.tpl-term__out { color: rgba(255, 255, 255, 0.55); }
.tpl-term__ok { color: #7ddba3; }
.tpl-term__err { color: #ff9d9d; }
.tpl-term__dim { color: rgba(255, 255, 255, 0.34); }

.slide--phone .tpl-term__body { font-size: 12px; padding: 12px; }
.slide--phone .tpl-term__dot { inline-size: 8px; block-size: 8px; }

/* ── T9  compare: two columns, headed ────────────────────────────────────── */

.tpl--compare .tpl-cmp {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--tpl-gap);
  min-block-size: 0;
}

.tpl-cmp__col {
  display: flex;
  flex-direction: column;
  gap: calc(0.7cqw * var(--tpl-scale));
  padding: var(--tpl-pad);
  border-radius: var(--tpl-radius);
  border: 1px solid var(--tpl-line);
  background: var(--tpl-surface);
  min-inline-size: 0;
}

.tpl-cmp__col--on {
  border-color: var(--primary-border);
  background: var(--primary-light);
}

.tpl-cmp__head {
  font-size: calc(clamp(15px, 1.95cqw, 39px) * var(--tpl-scale));
  font-weight: 500;
  line-height: 1.2;
  color: var(--tpl-ink);
  display: flex;
  align-items: baseline;
  gap: 0.6cqw;
}

.tpl-cmp__list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: calc(0.45cqw * var(--tpl-scale));
  font-size: calc(clamp(13px, 1.55cqw, 31px) * var(--tpl-scale));
  line-height: 1.4;
  color: var(--tpl-ink-soft);
}

.tpl-cmp__list li {
  /* RTL list indentation is padding-RIGHT, per the repo convention. */
  padding-inline-start: 1.1em;
  position: relative;
}

.tpl-cmp__list li::before {
  content: '';
  position: absolute;
  inset-inline-start: 0.35em;
  inset-block-start: 0.62em;
  inline-size: 0.32em;
  block-size: 0.32em;
  border-radius: 50%;
  background: var(--tpl-ink-faint);
}

.slide--phone .tpl--compare .tpl-cmp { grid-template-columns: 1fr; }

/* ── T10  table: a compact matrix ────────────────────────────────────────── */

.tpl-table {
  inline-size: 100%;
  border-collapse: collapse;
  font-size: calc(clamp(13px, 1.55cqw, 31px) * var(--tpl-scale));
  line-height: 1.35;
}

.tpl-table th,
.tpl-table td {
  padding: calc(0.7cqw * var(--tpl-scale)) calc(1cqw * var(--tpl-scale));
  text-align: start;
  border-block-end: 1px solid var(--border-light);
  vertical-align: top;
}

.tpl-table th {
  font-weight: 500;
  color: var(--tpl-ink-faint);
  font-size: 0.86em;
  border-block-end-color: var(--tpl-line);
}

.tpl-table td { color: var(--tpl-ink-soft); }
.tpl-table td:first-child { color: var(--tpl-ink); }
.tpl-table tr:last-child td { border-block-end: 0; }

.slide--phone .tpl-table { font-size: 13px; }

/* ── T11  quote: a line somebody said, with their face ───────────────────── */

.tpl--quote { justify-content: center; }

.tpl-quote {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: calc(2.2cqw * var(--tpl-scale));
}

.tpl-quote__portrait {
  inline-size: clamp(56px, 9cqw, 180px);
  block-size: clamp(56px, 9cqw, 180px);
  border-radius: 50%;
  object-fit: cover;
  border: 1px solid var(--tpl-line);
  background: var(--bg-secondary);
  flex: none;
}

.tpl-quote__text {
  font-size: calc(clamp(18px, 2.8cqw, 56px) * var(--tpl-scale));
  font-weight: 300;
  line-height: 1.3;
  color: var(--tpl-ink);
  max-inline-size: 40ch;
}

.tpl-quote__src {
  font-size: calc(clamp(12px, 1.4cqw, 28px) * var(--tpl-scale));
  color: var(--tpl-ink-faint);
  margin-block-start: 0.7cqw;
}

.tpl-quote__src span[dir='ltr'] { unicode-bidi: isolate; }

.slide--phone .tpl-quote { grid-template-columns: 1fr; gap: var(--space-sm); }
.slide--phone .tpl-quote__text { font-size: 17px; }

/* ── T12  timeline: dated stops along one line ───────────────────────────── */
/* A history, drawn: operating systems, the browser, a commit log.

   THE EARLIEST STOP IS ON THE RIGHT. The strip inherits the deck's RTL and a
   grid with `grid-auto-flow: column` fills from the start side, which in RTL is
   the right edge, so source order IS chronological order. Do not add
   `direction: ltr` here to make the gradient run the other way: it would put
   the first year on the left of a Hebrew slide, and the room reads the slide
   before it reads the line. */

.tpl--timeline { justify-content: center; }

.tpl-tl {
  /* The dot diameter, named once: the track aligns to its centre. */
  --tpl-tl-dot: calc(1.5cqw * var(--tpl-scale));
  position: relative;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  align-items: start;
  gap: calc(1.2cqw * var(--tpl-scale));
  padding-block: calc(0.4cqw * var(--tpl-scale));
  min-block-size: 0;
}

/* The rule the stops sit on. Absolutely positioned, so a stop still hidden by a
   build step leaves the line whole instead of punching a hole in it. The offset
   is the block padding plus half a dot; it is written out rather than derived
   because a `var()` holding a `calc()` divided inside another `calc()` is the
   kind of expression that fails silently in one engine and not another. */
.tpl-tl__track {
  position: absolute;
  inset-inline: 0;
  inset-block-start: calc(1.15cqw * var(--tpl-scale) - 1px);
  block-size: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, #0894ff 0%, #bd32d6 50%, #ffa304 100%);
  opacity: 0.9;
}

.tpl-tl__stop {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: calc(0.5cqw * var(--tpl-scale));
  min-inline-size: 0;
}

/* Filled with the slide's own ground, which is what hides the track behind it.
   The dark skin overrides that fill; nothing else needs to. */
.tpl-tl__dot {
  inline-size: var(--tpl-tl-dot);
  block-size: var(--tpl-tl-dot);
  border-radius: 50%;
  border: 2px solid var(--tpl-accent);
  background: var(--tpl-surface);
  flex: none;
}

.tpl-tl__stop.is-on .tpl-tl__dot { background: var(--tpl-accent); }

.tpl-tl__year {
  font-family: var(--font-mono);
  direction: ltr;
  unicode-bidi: isolate;
  font-size: calc(clamp(13px, 1.55cqw, 31px) * var(--tpl-scale));
  line-height: 1.2;
  color: var(--tpl-accent);
}

.tpl-tl__t {
  font-size: calc(clamp(12px, 1.45cqw, 29px) * var(--tpl-scale));
  line-height: 1.35;
  color: var(--tpl-ink-soft);
  max-inline-size: 16ch;
}

.tpl-tl__stop.is-on .tpl-tl__t { color: var(--tpl-ink); }

/* A 9:16 card has no room for six columns, and a horizontal line down a phone
   is a line nobody follows. It becomes a list, oldest first, still in source
   order. */
.slide--phone .tpl-tl { grid-auto-flow: row; gap: var(--space-sm); }
.slide--phone .tpl-tl__track { display: none; }
.slide--phone .tpl-tl__dot { display: none; }
.slide--phone .tpl-tl__stop { flex-direction: row; align-items: baseline; text-align: start; gap: 8px; }
.slide--phone .tpl-tl__year { font-size: 13px; flex: none; min-inline-size: 5ch; }
.slide--phone .tpl-tl__t { font-size: 14px; max-inline-size: none; }

/* ── T13  layers: the abstraction tower, bottom floor first ──────────────── */
/* `column-reverse` so the FIRST child in the source is the BOTTOM floor. A
   tower authored top-down reads backwards to whoever edits it next, and the
   build steps that light one floor at a time have to run in source order:
   transistors, then the language, then the application, upward. */

.tpl--layers { justify-content: center; }

.tpl-layers {
  display: flex;
  flex-direction: column-reverse;
  gap: calc(0.5cqw * var(--tpl-scale));
  min-block-size: 0;
}

/* The glass slab of `.tpl-card`, laid out as one row instead of a pane. The
   padding is tuned to a MEASURED ceiling, not a guessed one: the body box under
   a one-line title is 718px, an eyebrow reserves 65px of it, and eight floors
   have to fit what is left. They do, with about 20px to spare. A ninth floor
   does not; that is what `--d2` is for. */
.tpl-layer {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: baseline;
  gap: calc(1.1cqw * var(--tpl-scale));
  padding: calc(0.68cqw * var(--tpl-scale)) calc(1.5cqw * var(--tpl-scale));
  border-radius: var(--tpl-radius);
  border: 1px solid var(--border-light);
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(16px) saturate(160%);
  -webkit-backdrop-filter: blur(16px) saturate(160%);
  box-shadow: var(--shadow-md);
  min-inline-size: 0;
  transition: border-color 0.25s ease, box-shadow 0.25s ease, background 0.25s ease;
}

/* The floor the lecturer is standing on. Readable from the back row is the
   whole point, so the border is the accent itself rather than the 18%-alpha
   hairline the cards use, and the shadow lifts a step. */
.tpl-layer.is-on {
  border-color: var(--tpl-accent);
  background: rgba(255, 255, 255, 0.94);
  box-shadow: var(--shadow-lg);
}

.tpl-layer__n {
  font-family: var(--font-mono);
  direction: ltr;
  font-size: calc(clamp(12px, 1.4cqw, 28px) * var(--tpl-scale));
  line-height: 1.3;
  color: var(--tpl-accent);
  min-inline-size: 2.2ch;
}

.tpl-layer__t {
  font-size: calc(clamp(14px, 1.7cqw, 34px) * var(--tpl-scale));
  line-height: 1.25;
  color: var(--tpl-ink);
  min-inline-size: 0;
}

/* The concrete instance of the floor, quiet, on the end side. Usually a Latin
   name, so it is isolated. */
.tpl-layer__ex {
  font-family: var(--font-mono);
  direction: ltr;
  unicode-bidi: isolate;
  font-size: calc(clamp(11px, 1.3cqw, 26px) * var(--tpl-scale));
  line-height: 1.3;
  color: var(--tpl-ink-faint);
  justify-self: end;
  text-align: end;
}

.slide--phone .tpl-layers { gap: 6px; }
.slide--phone .tpl-layer { padding: 10px 12px; gap: 10px; border-radius: var(--radius-md); }
.slide--phone .tpl-layer__n { font-size: 12px; }
.slide--phone .tpl-layer__t { font-size: 14px; }
.slide--phone .tpl-layer__ex { font-size: 11px; }

/* ── T14  logos: official marks in a row, each captioned ─────────────────── */
/* THE APP SCALES NOTHING. An `<img>` with no block-size renders at its intrinsic
   pixels, which for a mark downloaded from a vendor is whatever that vendor
   shipped: one press-kit SVG is 512px tall and the next is 24. So the height is
   set here and the width follows it. */

.tpl--logos { justify-content: center; }

.tpl-logos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(calc(13cqw * var(--tpl-scale)), 1fr));
  gap: calc(2.2cqw * var(--tpl-scale)) calc(2.4cqw * var(--tpl-scale));
  align-items: end;
  justify-items: center;
  min-block-size: 0;
}

.tpl-logo {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: calc(0.7cqw * var(--tpl-scale));
  min-inline-size: 0;
}

.tpl-logo__img {
  display: block;
  block-size: calc(7cqw * var(--tpl-scale));
  inline-size: auto;
  max-inline-size: 100%;
  object-fit: contain;
}

.tpl-logo--sm .tpl-logo__img { block-size: calc(5cqw * var(--tpl-scale)); }
.tpl-logo--lg .tpl-logo__img { block-size: calc(9.5cqw * var(--tpl-scale)); }

.tpl-logo__name {
  font-size: calc(clamp(14px, 1.8cqw, 36px) * var(--tpl-scale));
  font-weight: 500;
  line-height: 1.2;
  color: var(--tpl-ink);
}

.tpl-logo__role {
  font-size: calc(clamp(12px, 1.35cqw, 27px) * var(--tpl-scale));
  line-height: 1.35;
  color: var(--tpl-ink-faint);
  max-inline-size: 18ch;
}

.slide--phone .tpl-logos { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-md); }
.slide--phone .tpl-logo { gap: 6px; }
.slide--phone .tpl-logo__img { block-size: 40px; }
.slide--phone .tpl-logo__name { font-size: 14px; }
.slide--phone .tpl-logo__role { font-size: 12px; }

/* ── T15  shot: a framed screenshot, optionally pinned ───────────────────── */

.tpl--shot {
  justify-content: center;
  align-items: center;
}

.tpl-shot {
  position: relative;
  /* The pin coordinates below are the SCREENSHOT's own, and a screenshot of a
     tool is laid out left to right whatever the slide around it does. The
     caption is a sibling, so it keeps the slide's RTL. */
  direction: ltr;
  /* MEASURED, not guessed. The body box under a one-line title is 718px tall
     (1080 canvas, less 2 x 6cqw slide padding, less a 93px title, less the
     slide's 2cqw gap). Take the caption and the template's gap off that and a
     frame has about 649px. A 16:9 capture 649px tall is 1154px wide, so 56cqw
     (1075px) is the widest frame that still leaves headroom for an eyebrow or a
     second caption line. Widen it and the slide fits the canvas while the
     harness reports the template overflowing itself, which is the failure mode
     nobody sees on a projector. 52cqw (998px, so 561px tall at 16:9) is that
     ceiling with an eyebrow and a one-line caption still on the slide: 50cqw
     is 960px, so 540px tall at 16:9, and 44px are left over. */
  max-inline-size: calc(50cqw * var(--tpl-scale));
  border-radius: var(--tpl-radius);
  border: 1px solid var(--border-light);
  box-shadow: var(--shadow-lg);
  background: var(--tpl-surface);
  overflow: hidden;
  /* An `<img>` is an inline box: without this the frame carries a descender's
     worth of ground under the picture. */
  line-height: 0;
  min-inline-size: 0;
}

/* THE ONE PLACE IN THIS FILE WHERE AN IMAGE IS FLUID, and it is deliberate.
   Everywhere else an `<img>` is given an explicit size because the app scales
   nothing and an intrinsic-size image is whatever the vendor shipped. A
   screenshot is the exception: it is a picture of a rectangle, it has no
   correct size of its own, and the frame is what decides how large it is. */
.tpl-shot__img {
  display: block;
  inline-size: 100%;
  block-size: auto;
  /* The guard rail. `overflow: hidden` on the frame would hide a too-tall
     capture from the eye but not from the canvas: the image element itself
     still reaches past the slide, which is exactly what the preview harness
     flags. Capping the image is what actually keeps it inside. A capture taller
     than 16:9 letterboxes here rather than cropping; that is the signal to move
     it to `--tall`. */
  max-block-size: calc(29cqw * var(--tpl-scale));
  object-fit: contain;
}

/* A portrait capture. Keeps the top of the window and drops the bottom, which
   on a long page is where there is least to say. `cover` plus a top
   `object-position` is what makes the crop happen inside the image box instead
   of by clipping an oversized element. */
.tpl-shot--tall {
  max-inline-size: calc(27cqw * var(--tpl-scale));
}

.tpl-shot--tall .tpl-shot__img {
  max-block-size: calc(27cqw * var(--tpl-scale));
  object-fit: cover;
  object-position: top center;
}

/* Placed from the body with `style="--x:42%;--y:18%"`. Those are computed
   values, which is the one thing an inline style is still for. */
.tpl-shot__pin {
  position: absolute;
  inset-inline-start: var(--x, 50%);
  inset-block-start: var(--y, 50%);
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  justify-content: center;
  inline-size: calc(2.6cqw * var(--tpl-scale));
  block-size: calc(2.6cqw * var(--tpl-scale));
  border-radius: 50%;
  /* Solid, never the brand gradient: this numeral is weight 500 and the brand
     rule forbids the gradient at 500 and above, numerals included. The white
     ring is what keeps the pin legible on a dark screenshot as well as a light
     one, so it stays even on the charcoal skin. */
  background: var(--primary-color);
  color: #ffffff;
  font-family: var(--font-mono);
  font-size: calc(clamp(11px, 1.35cqw, 27px) * var(--tpl-scale));
  font-weight: 500;
  line-height: 1;
  box-shadow: 0 0 0 calc(0.22cqw * var(--tpl-scale)) rgba(255, 255, 255, 0.92), var(--shadow-md);
}

.tpl-shot__cap {
  font-size: calc(clamp(13px, 1.5cqw, 30px) * var(--tpl-scale));
  line-height: 1.35;
  color: var(--tpl-ink-faint);
  text-align: center;
  max-inline-size: 52ch;
}

/* Off the canvas there is no container, so a `cqw` cap resolves against the
   small viewport and would crush the picture to a stamp. Every cap the frame
   depends on is restated in px here. */
.slide--phone .tpl-shot,
.slide--phone .tpl-shot--tall { max-inline-size: 100%; }
.slide--phone .tpl-shot__img { max-block-size: none; }
.slide--phone .tpl-shot--tall .tpl-shot__img { max-block-size: 320px; }
.slide--phone .tpl-shot__pin { inline-size: 20px; block-size: 20px; font-size: 11px; box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.92), var(--shadow-md); }
.slide--phone .tpl-shot__cap { font-size: 13px; }

/* ── T16  cmd: one command, the same shape every time ────────────────────── */
/* Used 26 times across the terminal, Claude Code and Git chapters. The value is
   that it never moves: the room learns where the name is, where the gloss is
   and where the example is, and stops re-reading the layout on every slide.

   The name and the form are `justify-self: start` rather than full-width blocks
   with `text-align`. An LTR block inside an RTL column aligns its text to the
   LEFT edge of the slide, which is the wrong side; shrinking the box to its
   content puts it back on the start side where the Hebrew begins. */

.tpl--cmd { justify-content: center; }

.tpl-cmd {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-content: center;
  gap: calc(0.75cqw * var(--tpl-scale));
  min-block-size: 0;
}

.tpl-cmd__name {
  justify-self: start;
  font-family: var(--font-mono);
  direction: ltr;
  unicode-bidi: isolate;
  /* Below the slide's own H1 on purpose. The title names the subject; this
     names the command, and two lines at the same size compete. */
  font-size: calc(clamp(22px, 3.4cqw, 68px) * var(--tpl-scale));
  font-weight: 400;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--tpl-ink);
}

.tpl-cmd__gloss {
  font-size: calc(clamp(16px, 2.1cqw, 42px) * var(--tpl-scale));
  font-weight: 400;
  line-height: 1.35;
  color: var(--tpl-ink-soft);
  max-inline-size: 58ch;
}

.tpl-cmd__form {
  justify-self: start;
  font-family: var(--font-mono);
  direction: ltr;
  unicode-bidi: isolate;
  font-size: calc(clamp(13px, 1.5cqw, 30px) * var(--tpl-scale));
  line-height: 1.35;
  color: var(--tpl-ink-faint);
}

/* The example is a `.tpl-term`, not a second terminal. Put both classes on the
   same element and use `.tpl-term__cmd` / `__out` / `__ok` / `__err` / `__dim`
   inside it; all this adds is the slot's own type size, which is one step down
   so that six output lines still fit under everything above. */
.tpl-cmd__ex { min-inline-size: 0; }

.tpl-cmd__ex .tpl-term__body {
  padding: calc(1cqw * var(--tpl-scale));
  font-size: calc(clamp(12px, 1.4cqw, 28px) * var(--tpl-scale));
  line-height: 1.45;
}

.tpl-cmd__note {
  font-size: calc(clamp(13px, 1.5cqw, 30px) * var(--tpl-scale));
  line-height: 1.4;
  color: var(--tpl-ink-faint);
  max-inline-size: 62ch;
}

.slide--phone .tpl-cmd { gap: var(--space-sm); }
.slide--phone .tpl-cmd__name { font-size: 22px; }
.slide--phone .tpl-cmd__gloss { font-size: 15px; }
.slide--phone .tpl-cmd__form { font-size: 13px; }
.slide--phone .tpl-cmd__ex .tpl-term__body { font-size: 12px; }
.slide--phone .tpl-cmd__note { font-size: 13px; }

/* ── T17  tree: a file system, printed ───────────────────────────────────── */
/* The indentation IS the content, so `white-space: pre` and no wrapping. A row
   that runs past the frame is a row that needs shortening; folding it onto a
   second line would leave something that no longer looks like a tree.

   The `pre` sits on the ROW, not on the block. Put it on the block and the
   newline an author leaves between two `<div class="tpl-tree__row">` tags is
   preserved as a blank line, and a hand-indented tree renders at double
   spacing: measured at 73px of overflow on a 6-row tree, which is a slide the
   preview harness fails and nobody can see the cause of. Between block-level
   rows the whitespace now collapses away as it should. */

/* One tree is an artefact, not a column of text: centred, it sits under a
   centred title instead of hugging the left edge of an RTL slide. A pair of
   trees is a layout and stays full width. */
.tpl--tree {
  justify-content: center;
  align-items: center;
}

.tpl--tree .tpl-trees { align-self: stretch; }

.tpl-tree {
  font-family: var(--font-mono);
  direction: ltr;
  unicode-bidi: isolate;
  font-size: calc(clamp(12px, 1.5cqw, 30px) * var(--tpl-scale));
  line-height: 1.5;
  color: var(--tpl-ink-soft);
  min-inline-size: 0;
  overflow: hidden;
}

.tpl-tree__row {
  display: block;
  white-space: pre;
}

.tpl-tree__dir {
  font-weight: 500;
  color: var(--tpl-ink);
}

.tpl-tree__file { color: var(--tpl-ink-soft); }

/* A Hebrew gloss inside an LTR block. Without the isolate the block's own
   direction reorders it and the punctuation lands on the wrong end, which is
   the same failure `.tpl-code` exists to prevent, running the other way.

   The gap is on inline-END, and that is not a typo. A logical property resolves
   against the ELEMENT's own direction, and this element is RTL inside an LTR
   line: its inline-start is its RIGHT edge, which in the line is the far side,
   away from the file name. Written the obvious way the measured gap is 0px. */
.tpl-tree__note {
  font-family: var(--font-body);
  direction: rtl;
  unicode-bidi: isolate;
  font-size: 0.86em;
  color: var(--tpl-ink-faint);
  padding-inline-end: 1.4em;
}

/* Two trees side by side: Windows beside Unix. Column flow over two tracks, so
   the source reads head, tree, head, tree and needs no wrapper per side. */
.tpl-trees {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: auto minmax(0, 1fr);
  grid-auto-flow: column;
  gap: calc(0.9cqw * var(--tpl-scale)) var(--tpl-gap);
  min-block-size: 0;
}

.tpl-trees__head {
  font-family: var(--font-mono);
  font-size: calc(clamp(12px, 1.4cqw, 28px) * var(--tpl-scale));
  letter-spacing: 0.08em;
  line-height: 1.3;
  color: var(--tpl-ink-faint);
  padding-block-end: calc(0.5cqw * var(--tpl-scale));
  border-block-end: 1px solid var(--tpl-line);
}

.slide--phone .tpl--tree { align-items: stretch; }
.slide--phone .tpl-tree { font-size: 12px; overflow-x: auto; }
.slide--phone .tpl-trees {
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: none;
  grid-auto-flow: row;
  gap: var(--space-sm);
}
.slide--phone .tpl-trees__head { font-size: 12px; padding-block-end: 4px; }

/* ── T18  scale: orders of magnitude, as bars ────────────────────────────── */
/* Logarithmic by construction: the author writes the power of ten AND the bar
   length, because a linear bar for 10 cubed beside 10 to the twelfth is one
   pixel and a blank row. The gradient is allowed on the fill - the brand rule
   is about letterforms, and a bar is not a letter. */

.tpl--scale { justify-content: center; }

.tpl-scale {
  display: grid;
  gap: calc(0.55cqw * var(--tpl-scale));
  min-block-size: 0;
}

.tpl-scale__row {
  display: grid;
  grid-template-columns:
    minmax(0, calc(18cqw * var(--tpl-scale)))
    calc(7cqw * var(--tpl-scale))
    minmax(0, 1fr);
  align-items: center;
  gap: calc(1.2cqw * var(--tpl-scale));
  /* Every row carries the highlight's padding, so lighting one shifts nothing
     on the slide beside it. */
  padding: calc(0.3cqw * var(--tpl-scale)) calc(0.9cqw * var(--tpl-scale));
  border-radius: calc(var(--tpl-radius) * 0.7);
  min-inline-size: 0;
}

.tpl-scale__row.is-on { background: var(--primary-light); }

.tpl-scale__lbl {
  font-size: calc(clamp(13px, 1.6cqw, 32px) * var(--tpl-scale));
  line-height: 1.25;
  color: var(--tpl-ink);
  min-inline-size: 0;
}

.tpl-scale__num {
  font-family: var(--font-mono);
  direction: ltr;
  unicode-bidi: isolate;
  text-align: end;
  font-size: calc(clamp(12px, 1.4cqw, 28px) * var(--tpl-scale));
  line-height: 1.2;
  color: var(--tpl-ink-faint);
}

/* The track. In RTL the fill starts at the right edge and grows leftwards,
   which is the direction the room is already reading in.

   THIN, AND THAT IS THE POINT. The bar was 1.1cqw (21px on the canvas), which
   is a slab, and the fill carries the brand gradient - which the house rule
   allows on hairlines and thin bars and forbids on any large area. 0.22cqw is
   a fifth of the old thickness, so 4.2px on the 1920 canvas: a hairline with a
   colour ramp rather than a painted block. The BAR LENGTHS ARE THE DATA and
   none of them change; this is thinner, never shorter. The phone's hand-set 6px
   below is deliberately NOT re-derived from this number - it is a bar read at
   arm's length rather than from the back of a room, and under about 5px it
   stops reading as a bar there at all. Leave it alone. */
.tpl-scale__bar {
  block-size: calc(0.22cqw * var(--tpl-scale));
  border-radius: var(--radius-full);
  background: var(--bg-secondary);
  overflow: hidden;
  min-inline-size: 0;
}

.tpl-scale__fill {
  display: block;
  block-size: 100%;
  inline-size: var(--w, 0%);
  border-radius: var(--radius-full);
  background: var(--tpl-brand);
}

/* Three columns of a magnitude chart do not survive a phone card, so the bar
   drops to its own full-width row under the label and the number. */
.slide--phone .tpl-scale { gap: 6px; }
.slide--phone .tpl-scale__row { grid-template-columns: minmax(0, 1fr) auto; gap: 6px 10px; padding: 4px 6px; }
.slide--phone .tpl-scale__bar { grid-column: 1 / -1; block-size: 6px; }
.slide--phone .tpl-scale__lbl { font-size: 14px; }
.slide--phone .tpl-scale__num { font-size: 12px; }

/* ── T19  stepper: where the room is in the thirteen-step workshop ───────── */
/* The root exists so a whole slide can be the strip, but the strip is really a
   component: `.tpl-stepper` works as the first child of ANY template root. */

.tpl--stepper { justify-content: center; }

.tpl-stepper {
  display: flex;
  align-items: center;
  gap: calc(0.6cqw * var(--tpl-scale));
  min-inline-size: 0;
}

/* Written first in a body, the strip belongs to the TITLE, exactly as the
   eyebrow does: in a centred flex column it would otherwise drift to the middle
   of the slide and read as content. One class plus one pseudo-class, the same
   weight as the eyebrow's rule above, deliberately. */
/* `:not(.tpl-free)` for the same reason the mascot rule carries it, and it was
   found the same way - in a browser, not in a spec. This rule is (0,4,0) and the
   free-placement rule at the foot of this file is (0,3,0), so WITHOUT the
   exclusion this one wins outright on a free eyebrow that happens to be the
   first child of its root. What the owner then sees is not a box that ignored
   its `--tpl-fx`: `inset-inline-start: 0` under `direction: rtl` resolves to
   `right: 0`, so the box pins itself to the OPPOSITE corner from the one the
   numbers name, and the panel and the slide disagree with nothing on screen to
   say why. */
.slide--display .tpl > .tpl-stepper:first-child:not(.tpl-free) {
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 1;
}

.slide--display .tpl:has(> .tpl-stepper:first-child:not(.tpl-free)) {
  padding-block-start: calc(4cqw * var(--tpl-scale));
}

.tpl-stepper__label {
  flex: none;
  font-family: var(--font-mono);
  font-size: calc(clamp(12px, 1.3cqw, 26px) * var(--tpl-scale));
  letter-spacing: 0.06em;
  line-height: 1.3;
  color: var(--tpl-ink-faint);
  padding-inline-end: calc(0.8cqw * var(--tpl-scale));
}

/* Thirteen pips plus the label is about 640px of a 1651px content column, so
   the strip never needs to wrap on the canvas. */
.tpl-stepper__pip {
  flex: none;
  inline-size: calc(1.6cqw * var(--tpl-scale));
  block-size: calc(0.5cqw * var(--tpl-scale));
  border-radius: var(--radius-full);
  background: var(--border-default);
  transition: inline-size 0.25s ease, background 0.25s ease;
}

.tpl-stepper__pip.is-done {
  background: var(--tpl-accent);
  opacity: 0.45;
}

/* The live step. The gradient is fine here for the same reason as the scale
   bar: it is a pill, not a letterform. */
.tpl-stepper__pip.is-now {
  inline-size: calc(3.4cqw * var(--tpl-scale));
  block-size: calc(0.6cqw * var(--tpl-scale));
  background: var(--tpl-brand);
  opacity: 1;
}

/* On a phone card thirteen dashes are thirteen things nobody counts, so the
   strip becomes what it was standing in for all along: the label on its own
   line and one thin bar of segments under it. */
.slide--phone .tpl-stepper { flex-wrap: wrap; gap: 3px; }
.slide--phone .tpl-stepper__label {
  inline-size: 100%;
  font-size: 12px;
  padding-inline-end: 0;
  margin-block-end: 4px;
}
.slide--phone .tpl-stepper__pip { flex: 1 1 0; inline-size: auto; block-size: 3px; }
.slide--phone .tpl-stepper__pip.is-now { inline-size: auto; block-size: 3px; }

/* ── Mascot ──────────────────────────────────────────────────────────────── */
/* 27 poses live at /mascot/<pose>-<bust|full>-<light|dark>.svg, copied from
   branding/mascot/svg. 240x240 gradient line art, so it reads at any size and
   needs no background behind it. `light` and `dark` name the SURFACE the pose is
   drawn for, not the figure: use `-dark` on the Claude Code skin. */

.tpl-mascot {
  display: block;
  inline-size: clamp(60px, 11cqw, 220px);
  block-size: auto;
  flex: none;
}

.tpl-mascot--sm { inline-size: clamp(44px, 7cqw, 140px); }
.tpl-mascot--lg { inline-size: clamp(90px, 17cqw, 330px); }
.tpl-mascot--xl { inline-size: clamp(120px, 26cqw, 500px); }

/* Parked in a corner of the slide, out of the text's way. Absolute against the
   slide, not the body, so it does not take part in the template's layout and
   therefore cannot push anything off the canvas. */
/* Deliberately smaller than an in-flow mascot. Every corner mascot costs the
   slide a reserved band (below), and at the earlier 11cqw that band was 173px
   out of a roughly 546px content budget - a third of the slide, spent on
   decoration, on a deck whose whole problem was that it did not fit. */
.tpl-mascot--corner {
  position: absolute;
  inset-block-end: 3.2cqw;
  inset-inline-start: 3.2cqw;
  inline-size: clamp(50px, 8cqw, 160px);
  z-index: 0;
  /* Behind the text and quiet enough that the last line of a full slide reads
     cleanly across it. The reserved band below keeps them apart on most slides;
     this is what makes the remaining few look deliberate rather than collided. */
  opacity: 0.7;
  pointer-events: none;
}

/* The mirror of `--corner`: `inline-end` is the LEFT side in this deck's RTL.
   It carries its own size, because a size modifier like `--lg` would otherwise
   win from earlier in the file and put a 326px figure in a corner sized for
   160px - which is what it did, floating it into the middle of the slide. */
.tpl-mascot--corner-end {
  position: absolute;
  inset-block-end: 3.2cqw;
  inset-inline-end: 3.2cqw;
  inline-size: clamp(50px, 8cqw, 160px);
  z-index: 0;
  opacity: 0.7;
  pointer-events: none;
}

/* An IN-FLOW mascot that ends a template column, which is a different animal
   from either corner variant above. `.tpl` is a flex COLUMN, so a child with its
   own `inline-size` is placed at the inline START of its line - the physical
   RIGHT in this RTL deck - and a trailing one therefore lands in the bottom
   right corner, which the terminal brandmark has owned since 2026-09-06
   (slides.css). On order 2 the checklist mascot's clipboard had the chevron's
   arm drawn straight through it, because the brandmark is positioned and an
   in-flow sibling is not, so the mark paints on top.

   Centring is the fix rather than a nudge: it needs no magic number, it cannot
   collide with EITHER bottom corner, and on a template that already centres its
   children (`tpl--section`, `tpl--center`, `tpl--figure`) it changes nothing.
   `align-self` is ignored on an absolutely positioned child, so neither corner
   variant nor the watermark can be reached by this rule. */
.slide--display .tpl > .tpl-mascot:last-child {
  align-self: center;
}

/* A watermark rather than a character: very large, very faint, behind the text. */
.tpl-mascot--watermark {
  position: absolute;
  inset-block-end: -4cqw;
  inset-inline-start: -3cqw;
  inline-size: 34cqw;
  opacity: 0.07;
  z-index: 0;
  pointer-events: none;
}

.slide--phone .tpl-mascot--corner,
.slide--phone .tpl-mascot--corner-end,
.slide--phone .tpl-mascot--watermark { display: none; }

/* Everything a template draws sits above a corner mascot or a watermark.
   `:not(.tpl-mascot)` is load-bearing, not tidiness. `.tpl > *` and
   `.tpl-mascot--corner` are both specificity (0,1,0), and this rule comes later
   in the file, so without the exclusion it OVERRODE the mascot's
   `position: absolute` and dropped it back into the flex column - as a full row,
   pushing the slide's content up and out. Three authors hit it independently and
   each worked around it with an inline style attribute; the bug is here, and
   this is where it is fixed.

   `:not(.tpl-free)` IS THE SAME BUG, ONE VOCABULARY LATER, and it is the first
   of three defences the free-placement section at the end of this file relies
   on. A free box is `position: absolute` too, placed from percentages in its own
   style attribute. Without this exclusion the rule above would drop every one of
   them back into the centred flex column AS A FULL-WIDTH ROW, pushing the
   slide's own content off the canvas, with nothing erroring and nothing on the
   projector to say so. The other two defences are that the free rule is written
   at (0,3,0), which beats this line on specificity, and that it is placed at the
   very end of this file, which beats it on order. Three, because one "tidy-up"
   must not be able to reopen it. tests/editorDeckFreeCss.spec.ts asserts all
   three, in this file and in its mirror. */
.tpl > *:not(.tpl-mascot):not(.tpl-free) { position: relative; z-index: 1; }

/* A template's blocks NEVER shrink below their content.
   This is not tidiness. `.tpl` is a centred flex column, so a direct child with
   the default `flex-shrink: 1` is squashed when the column is over budget - and
   a squashed `.tpl-split` or `.tpl-grid` still lays its own children out at full
   height and, because it centres them, paints them OUTSIDE its box, on top of
   the blocks above and below. Measured on the workshop's `git diff` slide: a
   grid reporting 62px tall holding a 446px terminal, printing over the badge
   above it and the closing line below it. Nothing about that reads as an
   overflow - the slide still fits the canvas - which is why the rule is here
   and why the preview harness now checks for escaping children too. Over
   budget must mean CLIPPED at the bottom, which is visible, never overlapping,
   which is not. */
.tpl > * { flex: 0 0 auto; }

/* `flex: 0 0 auto` needs no mascot exclusion: an absolutely positioned element
   is not a flex item, so this rule cannot reach one. The `position` rule above
   is the one that had to say `:not(.tpl-mascot)`, and does. */

/* A corner mascot occupies a band the centred content must not reach into.
   Reserving it on the template rather than asking every author to remember is
   the whole point: the smoke test put a mascot's head through the bottom card
   of a three-card grid, and nothing about the markup said it would. */
.slide--display:has(.tpl-mascot--corner) .tpl,
.slide--display:has(.tpl-mascot--corner-end) .tpl {
  padding-block-end: calc(6cqw * var(--tpl-scale));
}

/* A watermark sits behind the text on purpose and reserves nothing. */

/* ── Skins ───────────────────────────────────────────────────────────────── */
/* Opted into from the body, so one authored file decides the whole slide. */

/* Soft blue wash - the site's own page ground. */
.slide--display:has(.tpl--tint) {
  background: linear-gradient(180deg, #f7fbff 0%, #eaf2fd 100%);
}

/* A faint grid, for anything structural: layers, files, a network. */
.slide--display:has(.tpl--grid) {
  background-color: var(--bg-surface);
  background-image:
    linear-gradient(rgba(0, 113, 227, 0.055) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0, 113, 227, 0.055) 1px, transparent 1px);
  background-size: 4cqw 4cqw;
}

/* A brand glow in the corner. The deck's one ambient decoration. */
.slide--display:has(.tpl--glow) {
  background:
    radial-gradient(ellipse 60% 55% at 92% 6%, rgba(189, 50, 214, 0.09) 0%, transparent 62%),
    radial-gradient(ellipse 70% 60% at 6% 96%, rgba(8, 148, 255, 0.1) 0%, transparent 60%),
    var(--bg-surface);
}

/* ── The Claude Code skin ────────────────────────────────────────────────── */
/* The palette of the site's Claude Code deep-dive page
   (public/css/claude-code-deep-dive-view.css): charcoal ground, gold accent,
   an opacity scale for ink. Used for the Claude Code chapter and nothing else,
   so that the switch to dark IS the signal that the subject changed. */

.slide--display:has(.tpl--dark),
.slide--phone:has(.tpl--dark) {
  --ccdd-gold: #d4a574;
  --ccdd-ink-100: rgba(255, 255, 255, 0.96);
  --ccdd-ink-80: rgba(255, 255, 255, 0.82);
  --ccdd-ink-60: rgba(255, 255, 255, 0.6);
  --ccdd-ink-45: rgba(255, 255, 255, 0.45);
  --ccdd-line: rgba(255, 255, 255, 0.08);
  background:
    radial-gradient(ellipse 80% 50% at 50% 0%, rgba(212, 165, 116, 0.07) 0%, transparent 60%),
    linear-gradient(180deg, #0b0c12 0%, #13141c 34%, #161722 100%);
  color: var(--ccdd-ink-80);
}

.slide--display:has(.tpl--dark) .slide__title,
.slide--phone:has(.tpl--dark) .slide__title {
  color: var(--ccdd-ink-100);
}

/* The corner brandmarks are GRADIENT-filled, never ink, so they do not vanish
   on charcoal - they only need to sit forward a little, because 0.42 over white
   and 0.42 over #0b0c12 are not the same weight.

   NO `filter: invert(1)`. This rule used to carry one, and it inverted the
   brand ramp itself: #0894FF became #F76B00, #BD32D6 became #42CD29, #FFA304
   became #005CFB, i.e. a green-and-orange mark on the eight Claude Code slides.
   Nobody ever saw it because the brandmark painted nothing at all until the
   sizing bug in slides.css was fixed. Verified by render: at 0.55 with no
   filter both the 5-stop and the 7-stop ramps read correctly on the charcoal. */
.slide--display:has(.tpl--dark) .slide__brandmark { opacity: 0.55; }

.tpl--dark {
  --tpl-ink: var(--ccdd-ink-100);
  --tpl-ink-soft: var(--ccdd-ink-60);
  --tpl-ink-faint: var(--ccdd-ink-45);
  --tpl-line: var(--ccdd-line);
  --tpl-surface: rgba(255, 255, 255, 0.03);
  --tpl-accent: var(--ccdd-gold);
}

.tpl--dark .tpl-card {
  background: rgba(255, 255, 255, 0.035);
  border-color: var(--ccdd-line);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: none;
}

.tpl--dark .tpl-card--tint {
  background: rgba(212, 165, 116, 0.08);
  border-color: rgba(212, 165, 116, 0.18);
}

.tpl--dark .tpl-badge {
  background: rgba(212, 165, 116, 0.08);
  border-color: rgba(212, 165, 116, 0.18);
  color: var(--ccdd-gold);
}

.tpl--dark .tpl-code,
.tpl--dark .tpl-kbd {
  background: rgba(255, 255, 255, 0.06);
  border-color: var(--ccdd-line);
  color: var(--ccdd-ink-100);
}

.tpl--dark .tpl-step { border-block-end-color: var(--ccdd-line); }
.tpl--dark .tpl-table th,
.tpl--dark .tpl-table td { border-block-end-color: var(--ccdd-line); }
.tpl--dark .tpl-cmp__col { background: rgba(255, 255, 255, 0.03); border-color: var(--ccdd-line); }
.tpl--dark .tpl-cmp__col--on { background: rgba(212, 165, 116, 0.07); border-color: rgba(212, 165, 116, 0.2); }
.tpl--dark .tpl-anat__part.is-on .tpl-anat__txt { background: rgba(212, 165, 116, 0.1); }
.tpl--dark .tpl-term { border-color: var(--ccdd-line); background: #0f1017; }

/* T12 to T19. Only the parts that own a ground or a border are listed: every
   type colour above is already a `--tpl-ink*` token and re-points itself. */

/* The dot's fill is what hides the track running behind it, so on charcoal it
   has to be the charcoal rather than the 3%-white surface token. #13141c is the
   middle stop of the skin's own ground gradient. */
.tpl--dark .tpl-tl__dot { background: #13141c; }

.tpl--dark .tpl-layer {
  background: rgba(255, 255, 255, 0.035);
  border-color: var(--ccdd-line);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: none;
}

.tpl--dark .tpl-layer.is-on {
  background: rgba(212, 165, 116, 0.08);
  border-color: rgba(212, 165, 116, 0.45);
  box-shadow: none;
}

/* Opt-in, per logo. Most official marks are monochrome dark and disappear on
   charcoal; a blanket invert would wreck the colourful ones beside them. */
.tpl--dark .tpl-logo--invert .tpl-logo__img { filter: invert(1); }

.tpl--dark .tpl-shot {
  background: rgba(255, 255, 255, 0.03);
  border-color: var(--ccdd-line);
  box-shadow: none;
}

.tpl--dark .tpl-trees__head { border-block-end-color: var(--ccdd-line); }
.tpl--dark .tpl-scale__bar { background: rgba(255, 255, 255, 0.07); }
.tpl--dark .tpl-scale__row.is-on { background: rgba(212, 165, 116, 0.08); }
/* All three pip rules, because `.tpl--dark .tpl-stepper__pip` and
   `.tpl-stepper__pip.is-now` are BOTH (0,2,0) and this one is later in the
   file: without the two states restated here the dark skin repaints every pip
   the same grey, and the strip stops saying anything. Measured on the probe
   deck before it was written. */
.tpl--dark .tpl-stepper__pip { background: rgba(255, 255, 255, 0.13); }
.tpl--dark .tpl-stepper__pip.is-done { background: var(--ccdd-gold); opacity: 0.5; }
.tpl--dark .tpl-stepper__pip.is-now { background: var(--tpl-brand); opacity: 1; }

/* The eyebrow's brand dash and the hairline stay full-colour on charcoal: they
   are the only place the brand gradient appears on this skin, and they read. */

/* The one failure mode of opting a skin in from the body: without `:has()` the
   slide is never repainted, and this skin's white ink would land on a white
   ground - a blank projector, in front of a room. Verified working in the
   Chromium the harness drives, and every browser that will be in the venue
   supports it, but the cost of being wrong is the whole chapter. So when the
   selector is unavailable the template paints its OWN box dark instead. The
   slide's title stays in ink on white above it, which is merely a different
   design rather than an unreadable one. */
@supports not selector(:has(*)) {
  .tpl--dark {
    background: linear-gradient(180deg, #0b0c12 0%, #161722 100%);
    padding: calc(3cqw * var(--tpl-scale));
    border-radius: 1.2cqw;
  }
}

/* ── Logo lockup, for the slides that carry the brand explicitly ─────────── */
/* The renderer already swaps the four brand names for the real logo components
   wherever they appear in a body (SlideHtml.vue). These rules only size a
   deliberate lockup.

   THE BRAND NAMES ARE NEVER SPELLED OUT IN THIS FILE, comments included. That
   swap walks into `<style>` elements too, so a literal here becomes a Vue
   component spliced into the middle of a declaration block. */

.tpl-lockup {
  display: flex;
  align-items: center;
  gap: 1.2cqw;
  font-size: calc(clamp(18px, 2.6cqw, 52px) * var(--tpl-scale));
  font-weight: 300;
  color: var(--tpl-ink);
}

.tpl-lockup--lg { font-size: clamp(24px, 4cqw, 80px); }

/* ── Build-step helpers ──────────────────────────────────────────────────── */
/* slides.css hides `[data-step="N"]` blocks that are still ahead of the live
   step. These are for the cases where the reveal is an emphasis change rather
   than an appearance: a row that lights up, a column that dims. Same one class
   plus one attribute weight as slides.css uses, deliberately - an uneven cascade
   here hides a line with no error and no symptom until the moment it should
   have appeared. */

.tpl-dim { transition: opacity 0.25s ease, color 0.25s ease; }
.slide--step-0 .tpl-dim--from1,
.slide--step-1 .tpl-dim--from2,
.slide--step-2 .tpl-dim--from3 { opacity: 0.32; }

/* ── Free placement ──────────────────────────────────────────────────────── */
/* NOT A TEMPLATE, and deliberately not a `T<n>` section: `tpl-free` is a SHARED
   PART. Any `.tpl` root may carry free boxes beside its own blocks, and a slide
   made entirely of them is just a root with nothing else in it. The roll call in
   tests/slideTemplatesCssSections.spec.ts counts templates by their `T<n>`
   banner, and this is not one.

   Placed from the body with style="--tpl-fx:7;--tpl-fy:14;--tpl-fw:46", which is
   the same sanction `.tpl-shot__pin` carries at T15: computed values, which is
   the one thing an inline style is still for. The numbers are BARE, and the unit
   is supplied here and only here. That is what makes `--tpl-ff: 24px` and
   `--tpl-fw: 200px` unwritable by construction, which is the only honest answer
   to `overflow-never-hand-write-a-font-size` in src/data/slideTemplates.ts.

   PHYSICAL left/top, not the logical pair the rest of this file prefers. These
   are picture coordinates on a fixed stage, taken from a pointer, and a slide
   does not mirror when its Hebrew does. The TEXT inside a box still inherits
   direction: rtl and still aligns to start.

   SCOPED TO .slide--display ON PURPOSE. The projector, both presenter-console
   previews, the student player, the student print sheet and the editor canvas
   all mount `slide slide--display`; the room's student handset and the editor's
   phone preview mount `slide slide--phone`. Nothing here matches a phone, so a
   free box there is simply an ordinary block in the phone's flex column, in DOM
   order, with its role class's own `.slide--phone` type. There is no phone
   branch below because there is nothing for one to fix - and the same scoping is
   what makes a deploy skew benign: a room whose CSS has not shipped yet draws an
   ugly readable stack rather than a heap at the origin.

   NOTHING HERE SETS opacity, transform, pointer-events, visibility, transition
   OR animation. Those are what `.slide--step-N [data-step="M"]` writes at
   (0,2,0) in slides.css, and this file loads AFTER slides.css, so a rule here
   would silently outweigh the build reveal and show a line early on a projector
   with nothing to say so. Rotation below is the INDEPENDENT `rotate` property,
   which composes ahead of `transform` rather than replacing it, so a rotated box
   can still be a build step. Never reach for `transform` here.

   NOR container-type, for the reason the whole file is written in cqw: the one
   container on either page is the 1920px canvas, and a second one re-bases every
   size in this vocabulary while looking plausible at every width. */

@property --tpl-fx { syntax: '<number>';  inherits: false; initial-value: 0; }
@property --tpl-fy { syntax: '<number>';  inherits: false; initial-value: 0; }
@property --tpl-fw { syntax: '<number>';  inherits: false; initial-value: 20; }
@property --tpl-fh { syntax: '<number>';  inherits: false; initial-value: 0; }
@property --tpl-fz { syntax: '<integer>'; inherits: false; initial-value: 1; }
@property --tpl-fr { syntax: '<number>';  inherits: false; initial-value: 0; }
@property --tpl-ff { syntax: '<number>';  inherits: false; initial-value: 2; }
@property --tpl-fo { syntax: '<integer>'; inherits: false; initial-value: 0; }

/* THE PREFIX IS SIX CHARACTERS OF INSURANCE, not a naming preference.
   `@property` registration is DOCUMENT-GLOBAL, and /editor loads the whole admin
   bundle beside this file. `.tpl-shot__pin` above already reads bare `--x` and
   `--y`, and nine live bodies write them. Registering a bare `--x` here would
   retype somebody else's property across the entire document, so every name in
   this section starts `--tpl-f`. */

/* The belt to @property's braces. Firefox before 128 registers nothing, and
   without `inherits: false` a free box nested inside another would inherit its
   parent's geometry. An inline value on the child beats this class rule, so the
   two mechanisms agree; do not delete one because the other is present. */
.tpl-free {
  --tpl-fx: 0; --tpl-fy: 0; --tpl-fw: 20; --tpl-fh: 0;
  --tpl-fz: 1; --tpl-fr: 0; --tpl-ff: 2;  --tpl-fo: 0;
}

/* (0,3,0). DEFENCE TWO of the three named on the mascot exclusion above: this
   beats `.tpl > *:not(.tpl-mascot)` at (0,2,0) on weight alone, so a free box
   stays absolutely positioned even if somebody removes the exclusion. Being the
   last section in the file is defence three. */
.slide--display .tpl > .tpl-free {
  position: absolute;
  box-sizing: border-box;
  left: calc(var(--tpl-fx) * 1%);
  top: calc(var(--tpl-fy) * 1%);
  inline-size: calc(var(--tpl-fw) * 1%);
  /* `.tpl-lede` and `.tpl-point` clamp themselves to 62ch / 58ch. A free box is
     as wide as it was drawn. */
  max-inline-size: none;
  min-inline-size: 0;
  /* A MINIMUM, never a height, and overflow stays visible. A fixed height plus a
     clip is a silent line-eater: `--font-mono` orders 'Noto Sans Hebrew' 2nd in
     class-app/public/css/tokens.css and 5th in public/css/global.css, so a
     Hebrew mono run genuinely measures differently in the room than in the
     editor. A box that grew must grow DOWN, where it can be seen.

     AND NO `block-size` AT ALL, not even the `auto` that stood on the next line
     until 2026-09-08. It read as a harmless restatement of the initial value and
     it was not: this rule is (0,3,0), so it outweighed every role class a placed
     box can carry, and `.tpl-rule`'s own `block-size: 2px` at (0,1,0) lost to it.
     An empty box at `auto` is zero pixels tall, so a "Hairline" placed from the
     panel painted NOTHING on any surface - editor canvas, projector, student
     player, print sheet - with no error anywhere and only the overlay's dashed
     outline, which has a minimum height of its own, to suggest a block was even
     there. `auto` is already the initial value for every other role, so leaving
     it out changes nothing for any of them, and the height a role declares for
     itself is the height it gets. Do not re-add it as a tidy-up: the next role
     with an intrinsic height goes the same way, just as silently.
     tests/editorDeckFreeCss.spec.ts pins this per ROLE rather than per class. */
  min-block-size: calc(var(--tpl-fh) * 1%);
  overflow: visible;
  margin: 0;
  z-index: var(--tpl-fz);
}

/* Opt-in, both of them, so a box that wants neither carries neither side effect:
   a bare font-size here would outweigh every role class at (0,3,0), and `rotate`
   at any value other than `none` establishes a stacking context and a containing
   block. The size is folded through `--tpl-scale` so `tpl--d2` and `tpl--d3`
   still shrink a free slide, which is the rung the overflow ladder relies on. */
.slide--display .tpl > .tpl-free--sized {
  font-size: calc(var(--tpl-ff) * 1cqw * var(--tpl-scale));
}
.slide--display .tpl > .tpl-free--rot {
  rotate: calc(var(--tpl-fr) * 1deg);
}

/* Surface, spacing and ink. Classes, never inline values: a hex in a body
   survives a skin swap and goes invisible on `.tpl--dark`, which redefines
   `--tpl-ink` and `--tpl-surface` above. The brand gradient is a hairline
   (`.tpl-rule`) and is never a fill here. */
.tpl-free--pad          { padding: var(--tpl-pad); }
.tpl-free--boxed        { padding: var(--tpl-pad);
                          border: 1px solid var(--tpl-line);
                          border-radius: var(--tpl-radius); }
.tpl-free--surface      { background: var(--tpl-surface); }
.tpl-free--fill-accent  { background: var(--tpl-accent); }
.tpl-free--ink          { color: var(--tpl-ink); }
.tpl-free--ink-soft     { color: var(--tpl-ink-soft); }
.tpl-free--ink-faint    { color: var(--tpl-ink-faint); }
.tpl-free--accent       { color: var(--tpl-accent); }
.tpl-free--center       { text-align: center; }
.tpl-free--end          { text-align: end; }

/* THE ONE MODIFIER THAT HAS TO BE SCOPED, and the reason is the cascade rather
   than the phone. Every modifier above sets a property the placed-box rule does
   not touch, so a bare class at (0,1,0) is enough. `overflow` is the exception:
   the placed-box rule sets `overflow: visible` at (0,3,0), so a bare
   `.tpl-free--clip` would lose on weight however far down this file it sat, and
   would silently do nothing on every surface that actually places the box. Same
   weight and later in the file is what makes it bite, so KEEP IT BELOW the rule
   it overrides. It also reads correctly on a phone, where nothing places the box
   and clipping a block in a reading column would only lose a line.
   tests/editorDeckFreeCss.spec.ts pins both halves. */
.slide--display .tpl > .tpl-free--clip { overflow: hidden; }
