/* ============================================================================
   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. Change that one, then copy it here:

     tail -n +N class-app/public/css/slide-templates.css  >> public/css/deck-slide-templates.css

   (N is one past this header's last line. Keep the header, replace the body.)

   ── 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 room's own `slides.css` is deliberately NOT mirrored. It is the projector
   chrome - the 1920x1080 canvas, the brand mark, the ballot indicator - and none
   of it belongs on a page a student reads in a column.

   ── THE STUDENT AREA USES THE `.slide--phone` BRANCH AT EVERY WIDTH

   `StudentSessionDeck` puts `slide slide--phone` on the body wrapper on a phone
   AND on a laptop, which is the one thing here that differs from the room. The
   `.slide--display` branch sizes itself in `cqw` against a canvas that is always
   1920px wide; a reading column is not that canvas, and resolving those rules
   against it gives a slide whose type floats with the width of the page. The
   phone branch is ordinary responsive CSS in px, written for a hand-held column,
   which is exactly what this surface is.

   The `--tpl-*` tokens on `.slide` are still `cqw`-derived, and they resolve
   against `.stu-deck__stage`, which carries `container-type: inline-size` for
   this reason - see the header of `student-session-deck.css`.

   This is the SAME decision the repo already took for `public/img/` and
   `public/mascot/`: a slide body says `src="/img/logos/linux.svg"`, so those
   directories 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%);
}

/* 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. */
.slide--display .tpl > .tpl-eyebrow:first-child {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  z-index: 1;
}

.slide--display .tpl:has(> .tpl-eyebrow:first-child) {
  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 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. Owner decision, 2026-09-04: centred, and its words in
   the site's blue. Centring is `tpl--center` on the body; this class carries only
   the colour, because `.slide__title` is the h1 OUTSIDE the template and a slide
   body may not ship a `<style>` block to reach it.
   The brand lockup keeps its own colours on purpose: it is an SVG painted with
   `url(#...)` gradient fills, which `color` cannot reach. The mark is the
   identity; the words around it are what turns blue. */
.slide:has(.tpl--closing) .slide__title { color: var(--tpl-accent); }

.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;
}

.tpl-figure__unit {
  font-size: 0.34em;
  font-weight: 400;
  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; }

.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. */
.tpl-scale__bar {
  block-size: calc(1.1cqw * 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. */
.slide--display .tpl > .tpl-stepper:first-child {
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 1;
}

.slide--display .tpl:has(> .tpl-stepper:first-child) {
  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;
}

/* 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. */
.tpl > *:not(.tpl-mascot) { 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 brandmark is drawn in ink and disappears on charcoal. */
.slide--display:has(.tpl--dark) .slide__brandmark { opacity: 0.28; filter: invert(1); }

.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; }
