/* =========================================================
   LECTURE TEMPLATE — Core tokens, typography, layout
   Distill.pub / 3Blue1Brown inspired. 1920×1080 canvas.
   ========================================================= */

/* ----- Design tokens ------------------------------------ */
:root {
  /* Canvas */
  --canvas-w: 1920px;
  --canvas-h: 1080px;

  /* Type scale (tuned for back-row readability) */
  --fs-display: 132px;   /* title slide hero */
  --fs-h1: 96px;         /* section dividers */
  --fs-h2: 72px;         /* slide titles */
  --fs-h3: 52px;         /* sub-headers */
  --fs-lead: 44px;       /* lead paragraph */
  --fs-body: 38px;       /* body text */
  --fs-small: 30px;      /* small body, captions in cards */
  --fs-tiny: 24px;       /* meta, footers */
  --fs-code: 32px;       /* code blocks */
  --fs-code-sm: 28px;    /* dense code */
  --fs-math-big: 96px;   /* hero formula */
  --fs-math: 56px;       /* inline-ish formula display */
  --fs-quote: 64px;      /* pull-quote */

  /* Label sub-scale (kickers, tags, badges, figure callouts) — the T14
     literal→token migration: each value IS the former literal, so swapping
     a literal for its token is render-identical. NOTE: unlike the hall
     scale above, these are NOT multiplied by the tweaks-panel font slider
     (js/tweaks.js BASE) — exactly like the literals they replaced. */
  --fs-label-xl: 36px;   /* emphasized numeral / term (toc-num, readout-value) */
  --fs-caption-lg: 28px; /* big caption / node name (ref-venue, arch-name) */
  --fs-caption: 26px;    /* kicker / secondary caption (slide-kicker, toc-sub) */
  --fs-label: 22px;      /* workhorse fine print (counters, buttons, tags) */
  --fs-label-sm: 20px;   /* small tag / panel head */
  --fs-label-xs: 18px;   /* dense figure label */

  /* Fine-print sub-scale (dense diagram annotations + lecturer chrome:
     presenter / preflight / tweaks panels). Same T14 contract as above:
     values are the former literals; not scaled by the tweaks slider. */
  --fs-fine-lg: 17px;    /* presenter notes, dense diagram text */
  --fs-fine: 16px;       /* chrome default, dense annotation */
  --fs-fine-sm: 14px;    /* chrome secondary */
  --fs-fine-xs: 13px;    /* chrome meta */
  --fs-fine-xxs: 12px;   /* chrome micro-meta */
  --fs-fine-min: 11px;   /* the G20 deck legibility floor — never go below */

  /* Spacing (8pt grid, scaled for 1920 canvas) */
  --sp-1: 8px;
  --sp-2: 16px;
  --sp-3: 24px;
  --sp-4: 32px;
  --sp-5: 48px;
  --sp-6: 64px;
  --sp-7: 96px;
  --sp-8: 128px;

  /* Slide gutters */
  --slide-pad-x: 120px;
  --slide-pad-top: 88px;
  --slide-pad-bottom: 88px;

  /* Radii */
  --r-sm: 8px;
  --r-md: 14px;
  --r-lg: 24px;
  --r-xl: 36px;
  --r-pill: 999px;

  /* Stroke */
  --hairline: 1.5px;
  --line: 2px;
  --line-thick: 4px;

  /* Type families */
  --font-sans: "IBM Plex Sans", "Helvetica Neue", system-ui, sans-serif;
  --font-serif: "Newsreader", "Source Serif 4", Georgia, serif;
  --font-mono: "JetBrains Mono", "IBM Plex Mono", ui-monospace, monospace;

  /* Light theme (default) */
  --bg: #FAF6ED;
  --bg-alt: #F0EBDF;
  --bg-card: #FFFFFF;
  --bg-inset: #EBE7DA;
  --ink: #14181F;
  --ink-2: #3D434E;
  --ink-3: #6B7280;
  --ink-4: #9CA3AF;
  --rule: #DDD7C7;
  --rule-strong: #B8B19E;

  /* Accent palette — colorful but considered */
  --accent: #2A6FDB;      /* primary blue */
  --accent-ink: #1B4FA0;
  --accent-soft: #DCE8F8;

  --c-red: #D7522C;
  --c-red-soft: #FBE3D8;
  --c-green: #3A8A5C;
  --c-green-soft: #DDEDDF;
  --c-amber: #E0A82E;
  --c-amber-soft: #F8ECCB;
  --c-violet: #7D5BA6;
  --c-violet-soft: #E7DEF1;
  --c-cyan: #1AA7B5;
  --c-cyan-soft: #D2EDEF;
  --c-pink: #C9447A;
  --c-pink-soft: #F6DCE5;

  /* Accessible "ink" variants for color-on-soft TEXT (>=4.5:1 on the
     matching *-soft bg). The plain --c-* tokens are tuned as decorative
     fills/borders and fail WCAG AA as text on their soft backgrounds. */
  --c-amber-ink: #8A5A00;
  --c-green-ink: #1F6B40;
  --c-cyan-ink:  #0A6F7A;

  /* Shadows */
  --shadow-sm: 0 2px 6px rgba(16,24,40,.06), 0 1px 2px rgba(16,24,40,.04);
  --shadow-md: 0 8px 24px rgba(16,24,40,.08), 0 2px 6px rgba(16,24,40,.04);
  --shadow-lg: 0 24px 64px rgba(16,24,40,.12), 0 4px 12px rgba(16,24,40,.06);

  /* Motion */
  --ease: cubic-bezier(.22,.61,.36,1);
  --dur-fast: 120ms;
  --dur: 220ms;
  --dur-slow: 420ms;
}

/* Dark theme */
:root[data-theme="dark"] {
  --bg: #14120D;
  --bg-alt: #1D1A14;
  --bg-card: #201C15;
  --bg-inset: #0D0B07;
  --ink: #ECE7D8;
  --ink-2: #BDB7A8;
  --ink-3: #8B8675;
  --ink-4: #5F5B4E;
  --rule: #312C22;
  --rule-strong: #463F31;

  --accent: #6FA8FF;
  --accent-ink: #A8C7FF;
  --accent-soft: #1F2D4D;

  --c-red: #F08664;
  --c-red-soft: #3D2018;
  --c-green: #6BCB8E;
  --c-green-soft: #16321F;
  --c-amber: #F4C975;
  --c-amber-soft: #382A0F;
  --c-violet: #B89BE0;
  --c-violet-soft: #28203B;
  --c-cyan: #5DD3DE;
  --c-cyan-soft: #0E2A2E;
  --c-pink: #E879A8;
  --c-pink-soft: #34182A;

  /* On dark, the bright fills already clear 4.5:1 on the dark *-soft bgs,
     so the text-ink tokens map to the bright tones. */
  --c-amber-ink: #F4C975;
  --c-green-ink: #6BCB8E;
  --c-cyan-ink:  #5DD3DE;

  --shadow-sm: 0 2px 6px rgba(0,0,0,.4), 0 1px 2px rgba(0,0,0,.3);
  --shadow-md: 0 8px 24px rgba(0,0,0,.45), 0 2px 6px rgba(0,0,0,.3);
  --shadow-lg: 0 24px 64px rgba(0,0,0,.6), 0 4px 12px rgba(0,0,0,.3);
}

/* ----- Resets & base ------------------------------------ */
*, *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: #000; /* letterbox bars */
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--fs-fine);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow: hidden;
}

img { max-width: 100%; display: block; }
button { font: inherit; color: inherit; }

/* ----- Keyboard focus ----------------------------------- */
/* A single, high-contrast :focus-visible ring for every interactive
   control. :focus-visible (not bare :focus) so it shows for keyboard
   users without flashing on mouse click. --accent reads well on both
   light and dark themes; the white/ink halo keeps it visible even when
   the control already sits on an accent-tinted surface. */
.toolbar button:focus-visible,
.quiz-option:focus-visible,
.toc-pop-row:focus-visible,
.toc-item:focus-visible,
a.recall-link:focus-visible,
.e2e-arch-block:focus-visible,
.step-controls button:focus-visible,
.e2e-stepper .step-btn:focus-visible,
.e2e-toggle:focus-visible,
.cf-toggle-bar button:focus-visible,
.misc-reveal-btn:focus-visible,
.pause-controls button:focus-visible,
.ha-reveal:focus-visible,
.mw-dismiss:focus-visible,
.pf-toggle:focus-visible,
.pf-close:focus-visible,
input[type="range"]:focus-visible,
.code-runner .runner-editor pre:focus-visible,
.pyodide-runner .py-editor pre:focus-visible,
details > summary:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

/* Bilingual content toggle */
:root[data-lang="ru"] [lang="en"] { display: none !important; }
:root[data-lang="en"] [lang="ru"] { display: none !important; }
/* Safety net: if data-lang is undefined (author removed it from <html>, or
   we're rendering BEFORE JS hydrates the saved pref — e.g. a Cmd+P / PDF
   that beats DOMContentLoaded), never show BOTH languages. Default to ru
   (the shipped default) by hiding en until a language is asserted. */
:root:not([data-lang]) [lang="en"] { display: none !important; }

/* ----- Deck shell --------------------------------------- */
.deck {
  position: fixed;
  inset: 0;
  background: #000;
  overflow: hidden;
}

.stage {
  width: var(--canvas-w);
  height: var(--canvas-h);
  position: absolute;
  top: 50%;
  left: 50%;
  background: var(--bg);
  /* JS applies: transform: translate(-50%, -50%) scale(s).
     translate(-50%, -50%) is in element-local units (so −960px −540px
     for a 1920×1080 stage), placing its center at the viewport center
     regardless of how the viewport is sized. Doesn't fall back to
     "safe" mode like grid place-items does. */
  transform: translate(-50%, -50%) scale(1);
  transform-origin: center center;
  /* No transition on transform — JS reassigns the inline transform during
     init, so scale() is applied directly without animation. */
  overflow: hidden;
}

.slides {
  position: absolute;
  inset: 0;
}

.slide {
  position: absolute;
  inset: 0;
  display: none;
  padding:
    var(--slide-pad-top)
    var(--slide-pad-x)
    var(--slide-pad-bottom)
    var(--slide-pad-x);
  background: var(--bg);
  color: var(--ink);
  overflow: hidden;
}

.slide.is-active { display: block; }

/* ---- Slide transitions (directional) ---- */
/* Default enter — slight rise. */
.slide.is-entering {
  animation: slideIn var(--dur-slow) var(--ease) both;
}
@keyframes slideIn {
  from { transform: translateY(12px); }
  to   { transform: translateY(0); }
}
/* When the deck flags the navigation direction, slides slide in from
   the appropriate side. Set by JS as data-nav-dir="next"|"prev". */
.deck[data-nav-dir="next"] .slide.is-entering {
  animation: slideInRight var(--dur) var(--ease) both;
}
.deck[data-nav-dir="prev"] .slide.is-entering {
  animation: slideInLeft var(--dur) var(--ease) both;
}
@keyframes slideInRight {
  from { transform: translateX(28px); opacity: 0.5; }
  to   { transform: translateX(0); opacity: 1; }
}
@keyframes slideInLeft {
  from { transform: translateX(-28px); opacity: 0.5; }
  to   { transform: translateX(0); opacity: 1; }
}

/* ----- Typography --------------------------------------- */
.slide h1, .slide h2, .slide h3, .slide h4 {
  font-family: var(--font-serif);
  font-weight: 500;
  line-height: 1.05;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 0;
  text-wrap: pretty;
}

.slide h1 { font-size: var(--fs-h1); }
.slide h2 { font-size: var(--fs-h2); }
.slide h3 { font-size: var(--fs-h3); font-weight: 500; }
.slide h4 { font-size: var(--fs-lead); font-weight: 500; }

.slide p {
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: 1.4;
  margin: 0 0 var(--sp-4);
  text-wrap: pretty;
  color: var(--ink);
}

.slide .lead {
  font-size: var(--fs-lead);
  line-height: 1.3;
  color: var(--ink-2);
}

.slide .small { font-size: var(--fs-small); line-height: 1.45; }
.slide .tiny  { font-size: var(--fs-tiny);  line-height: 1.4; color: var(--ink-3); }

.slide ul, .slide ol {
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: 1.45;
  padding-left: 1.2em;
  margin: 0 0 var(--sp-4);
}
.slide li { margin-bottom: var(--sp-3); }
.slide li::marker { color: var(--accent); }

.slide a { color: var(--accent); text-decoration: underline; text-underline-offset: 4px; }

/* Inline code & technical terms */
.slide code, .slide .term {
  font-family: var(--font-mono);
  font-size: 0.85em;
  background: var(--bg-inset);
  padding: 0.08em 0.34em;
  border-radius: 6px;
  border: 1px solid var(--rule);
  color: var(--ink);
}
.slide .term {
  font-family: var(--font-sans);
  font-weight: 600;
  background: var(--accent-soft);
  color: var(--accent-ink);
  border-color: transparent;
}

/* Emphasis */
.slide em  { font-style: italic; color: var(--ink); }
.slide strong { font-weight: 700; color: var(--ink); }
.slide .hi { background: linear-gradient(180deg, transparent 60%, var(--c-amber-soft) 60%); padding: 0 .1em; }

/* Math wrappers — both KaTeX (.katex / .katex-display) and the legacy
   MathJax (mjx-container) are supported so the deck still typesets if
   you swap engines. */
.slide mjx-container,
.slide .katex { color: var(--ink) !important; }
/* Universal contrast safety net: on DARK surfaces (final-result walk/e2e rows,
   section dividers, or any element marked .on-dark) KaTeX/MathJax must FOLLOW
   the container's text colour instead of being force-darkened — otherwise math
   renders dark-on-dark. `inherit` is theme-correct in both light and dark. */
.slide[data-type="divider"] mjx-container,
.slide[data-type="divider"] .katex,
.slide[data-type="divider"] .katex *,
.walk-step[data-final="true"] mjx-container,
.walk-step[data-final="true"] .katex,
.walk-step[data-final="true"] .katex *,
.e2e-step[data-final="true"] mjx-container,
.e2e-step[data-final="true"] .katex,
.e2e-step[data-final="true"] .katex *,
.slide .on-dark mjx-container,
.slide .on-dark .katex,
.slide .on-dark .katex * { color: inherit !important; }
/* …and inline emphasis (em / strong / code / .term / mark / kbd) on the same
   dark surfaces — otherwise an italic or bold word renders dark-on-dark and
   vanishes (it is forced to --ink by `.slide em/strong` above). */
.slide[data-type="divider"] :is(em,strong,b,i,u,mark,kbd,.term,.hi),
.walk-step[data-final="true"] :is(em,strong,b,i,u,mark,kbd,.term,.hi),
.e2e-step[data-final="true"] :is(em,strong,b,i,u,mark,kbd,.term,.hi),
.slide .on-dark :is(em,strong,b,i,u,mark,kbd,.term,.hi) { color: inherit !important; }
/* code chips inside a dark final row: translucent light pill, light text */
.walk-step[data-final="true"] .walk-detail code,
.e2e-step[data-final="true"] code {
  background: rgba(255,255,255,0.16);
  color: inherit;
  border-color: rgba(255,255,255,0.28);
}
.slide .math-display { display: block; text-align: center; margin: var(--sp-5) 0; }
.slide .math-big mjx-container,
.slide .math-big .katex { font-size: 220% !important; }
.slide .katex-display { margin: var(--sp-5) 0; }

/* --- Auto-fit body wrapper ----------------------------- */
/* Wraps regular slide content; JS may scale this down via transform
   when content overflows the slide's content area. */
.slide-body {
  position: relative;
  width: 100%;
  transform-origin: top left;
}

/* ---- Print / PDF export ---- */
@page {
  size: 1920px 1080px;
  margin: 0;
}
@media print {
  html, body {
    background: white;
    overflow: visible;
    height: auto;
    width: auto;
  }
  /* Force the browser to PRINT colored backgrounds. Without this Chrome/WebKit
     drop background fills on paper, so the divider (light text on a dark ink
     fill), title/final gradients, quote/callout/theorem/e2e panels and badges
     all print BLANK (and the divider's light text becomes invisible on white).
     Apply to the slide and every printed colored surface. */
  .slide,
  .slide[data-type="divider"].is-active,
  .slide[data-type="title"].is-active,
  .slide[data-type="final"].is-active,
  .slide[data-type="quote"],
  .thm-card, .callout, .badge,
  .e2e-arch-block, .e2e-math-panel .panel-head, .e2e-example-panel .panel-head,
  .def-tag, .divider-num, .misc-truth, .devil-overlay {
    print-color-adjust: exact !important;
    -webkit-print-color-adjust: exact !important;
  }
  /* Bilingual safety net for the print path: if no language is asserted (PDF
     fired before JS hydrated data-lang), still print exactly one language
     (ru, the shipped default) rather than both stacked. */
  :root:not([data-lang]) [lang="en"] { display: none !important; }
  /* Chrome — every floating UI element is suppressed for print. */
  .toolbar, .progress-bar, .pen-palette, .toc-pop, .presenter,
  .kbd-hint, .step-controls {
    display: none !important;
  }
  .deck {
    position: static !important;
    overflow: visible !important;
    background: white !important;
  }
  .stage {
    position: static !important;
    transform: none !important;
    width: 1920px !important;
    height: auto !important;
    overflow: visible !important;
  }
  .slides {
    position: static !important;
    inset: auto !important;
  }
  /* `--fig-max: NNcqh` resolves against the nearest size container, which on screen is
     `.slides` (absolutely positioned, so 1cqh == 1vh). Print makes `.slides` static, it
     loses its definite height, and every `cqh` cap collapses to ZERO — illustrations
     printed 0px wide, i.e. missing from the PDF. Re-establish the container on the slide
     itself: in print each slide IS exactly 1920×1080, so 1cqh == 10.8px, as on screen. */
  .slides { container-type: normal !important; }
  .slide { container-name: slide; container-type: size; }
  /* On screen a viz slide's kicker may stay `nowrap` because auto-fit scales the whole
     slide down to compensate. Print has no auto-fit (`.stage` transform is removed), so a
     nowrap kicker takes its full natural width — measured 1630px — and squeezes the h2 to
     5px, which then wraps one letter per line UNDER the kicker. Let it wrap on paper. */
  .slide[data-type="viz"] .slide-header .slide-kicker {
    white-space: normal !important;
    flex: 0 1 auto !important;
    max-width: 40% !important;
  }
  /* Every slide is a separate page at full canvas size. */
  .slide {
    display: block !important;
    position: relative !important;
    inset: auto !important;
    width: 1920px !important;
    height: 1080px !important;
    page-break-after: always;
    break-after: page;
    overflow: hidden;
  }
  .slide:last-child {
    page-break-after: auto;
    break-after: auto;
  }
  /* Dynamic / stepped slides stack EVERY step for the handout (see the rules
     below that un-hide steps). That stack can exceed one 1080px page — so let
     these types grow to natural height and paginate instead of being clipped
     by the fixed-height page. Static slides keep their single fixed page. */
  .slide[data-type="walkthrough"],
  .slide[data-type="e2e"],
  .slide[data-type="derivation"],
  .slide[data-type="reverse"],
  .slide[data-type="forward"] {
    height: auto !important;
    min-height: 1080px;
    overflow: visible !important;
  }
  /* Auto-fit transform: KEEP it for static slides so a slide scaled to fit on
     screen (e.g. a dense quiz at 0.79×) still fits one printed page instead of
     overflowing when reset to 1.0×. Only the dynamic/stepped types reset it
     (they expand to show every step and paginate). */
  .slide[data-type="walkthrough"] > .slide-body,
  .slide[data-type="e2e"] > .slide-body,
  .slide[data-type="derivation"] > .slide-body,
  .slide[data-type="reverse"] > .slide-body,
  .slide[data-type="forward"] > .slide-body {
    transform: none !important;
    width: 100% !important;
  }
  .slide-thumb {
    /* If overview was open when print fired, unwrap visually. */
    display: contents !important;
  }
  .pen-layer { display: none !important; }
  /* NOTE: quiz-option.is-revealed is intentionally left untouched in print —
     the on-screen reveal state carries through so authors can print an
     answer key. (No rule needed; documented here to avoid a dead block.) */
  /* Divider prints light-on-dark. Its dark fill + light text live ONLY on
     `.is-active`, but in print every slide is forced visible via display:block
     (not .is-active) — so without these the dark ink fill is dropped and the
     light text lands invisibly on white. Re-assert the colored surface here
     (print-color-adjust:exact above lets the fill actually ink). */
  .slide[data-type="divider"] {
    background: var(--ink) !important;
    color: var(--bg) !important;
    display: grid !important;
    place-items: center !important;
  }
  .slide[data-type="divider"] h1 { color: var(--bg) !important; }
  .slide[data-type="divider"] .divider-num { color: var(--c-amber) !important; }
  /* MathJax / KaTeX containers print best with explicit theme ink.
     A fixed dark color disappears on the dark theme's retained math highlight.
     Exclude dividers, whose foreground is deliberately inverted. */
  .slide:not([data-type="divider"]) mjx-container,
  .slide:not([data-type="divider"]) .katex { color: var(--ink) !important; }
  /* Hidden answers — print expanded for handouts. */
  .hidden-answer .ha-content { display: block !important; }
  .hidden-answer .ha-reveal { display: none !important; }

  /* ---- Print regression: new lab slide types ---- */
  /* Pause slides: timer + autoplay make no sense on paper — show
     the phrase only, no countdown / controls. */
  .slide[data-type="pause"] .pause-timer,
  .slide[data-type="pause"] .pause-controls { display: none !important; }
  .slide[data-type="pause"] { background: white !important; color: #14181F !important; }
  .slide[data-type="pause"] .pause-phrase { color: #14181F !important; }

  /* Misconception: print both the wrong statement AND the truth (handout
     style — student needs to see both to understand the contrast). */
  .slide[data-type="misconception"] .misc-truth { display: block !important; }
  .slide[data-type="misconception"] .misc-reveal-btn { display: none !important; }

  /* Walkthrough / e2e / derivation / reverse: show ALL steps stacked,
     since stepping isn't possible on paper. Reset step-hidden states. */
  .walk-step, .e2e-step, .deriv-step, .reverse-layer {
    display: block !important;
    position: relative !important;
    inset: auto !important;
    opacity: 1 !important;
    transform: none !important;
    page-break-inside: avoid;
    margin-bottom: 12px;
  }
  [data-step].is-step-hidden { opacity: 1 !important; pointer-events: auto !important; }
  /* Walkthrough rows already form an ordered stack. Keep their number/content/
     value grid and let tall stacks paginate; size containment defeats auto height.
     Inverted final rows need inherited foreground, including all KaTeX spans. */
  .slide[data-type="walkthrough"] { container-type: normal !important; }
  .slide[data-type="walkthrough"] .walk-flow {
    transform: none !important;
    width: 100% !important;
    height: auto !important;
  }
  .walk-step { display: grid !important; }
  :root .slide .walk-step[data-final="true"] .katex,
  :root .slide .walk-step[data-final="true"] .katex * { color: inherit !important; }
  /* Derivation: show every step's content sequentially. */
  .slide[data-type="derivation"] .deriv-step[hidden] {
    display: block !important;
    visibility: visible !important;
  }
  .slide[data-type="derivation"] .deriv-form { display: none !important; }
  /* Reverse / forward: stack all layers vertically instead of overlaying. */
  .slide[data-type="reverse"] .reverse-stack,
  .slide[data-type="forward"] .reverse-stack {
    display: block !important;
    min-height: auto !important;
  }
  .slide[data-type="reverse"] .reverse-layer,
  .slide[data-type="forward"] .reverse-layer {
    position: relative !important;
    opacity: 1 !important;
    transform: none !important;
    padding: 16px !important;
    border-bottom: 1px solid #ddd;
  }
  .slide[data-type="reverse"] .reverse-counter,
  .slide[data-type="forward"] .reverse-counter { display: none !important; }

  /* Devil's advocate: print the opposing column inline below content. */
  .devil-overlay {
    position: relative !important;
    width: 100% !important;
    transform: none !important;
    background: #14181F !important;
    color: white !important;
    margin-top: 16px;
    padding: 24px !important;
  }
  .devil-overlay .devil-close { display: none !important; }

  /* Counterfactual: show all variants stacked with labels. */
  .cf-variant { display: block !important; }
  .cf-variant::before {
    content: "→ " attr(data-cf-value) ":";
    display: block;
    font-family: var(--font-mono);
    font-size: var(--fs-fine-sm);
    color: #6B7280;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    margin-bottom: 8px;
  }
  .cf-toggle-bar { display: none !important; }

  /* Typewriter: no animation on paper — every char fully visible. */
  .typewriter .tw-char {
    opacity: 1 !important;
    animation: none !important;
    transform: none !important;
  }

  /* E2E specific: arch strip is fine, but flowing arrows shouldn't
     animate. Stepper hidden (no point on paper). */
  .e2e-arch-arrow.is-flowing { animation: none !important; }
  .e2e-stepper { display: none !important; }
  /* Show both math + example panels regardless of toggle. */
  .slide[data-type="e2e"][data-hide-numeric="true"] .e2e-example-panel { display: block !important; }
  .slide[data-type="e2e"][data-hide-numeric="true"] .e2e-twin {
    grid-template-columns: 0.72fr 1fr !important;
  }

  /* Tooltips, preflight overlay, recall overlay, tweaks panel —
     all transient UI, hide on print. */
  .e2e-tooltip, .preflight-overlay, .recall-overlay, .tweaks-panel { display: none !important; }
}

/* ----- Slide chrome (frame footer/header) --------------- */
.slide__frame {
  position: absolute;
  inset: 0;
  pointer-events: none;
  display: grid;
  grid-template-rows: auto 1fr auto;
  padding: var(--sp-5) var(--slide-pad-x) var(--sp-4);
}
/* The frame is decorative chrome (breadcrumb, logo, page number) laid over the
   slide content. Re-enabling pointer-events on ALL direct children let the empty
   center grid cell (the `1fr` spacer row) swallow clicks meant for the content
   beneath it — demo sliders and quiz options became un-clickable by mouse (only
   keyboard reached them). Scope pointer-events back to genuinely interactive
   chrome only, so the spacer rows / bars stay click-through. */
.slide__frame a,
.slide__frame button { pointer-events: auto; }

.slide__frame-top,
.slide__frame-bot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-5);
  font-family: var(--font-sans);
  font-size: var(--fs-tiny);
  color: var(--ink-3);
  letter-spacing: 0.04em;
  min-width: 0;
}
.slide__frame-top > *,
.slide__frame-bot > * { min-width: 0; }

.slide__frame-bot {
  border-top: var(--hairline) solid var(--rule);
  padding-top: var(--sp-3);
}

.slide__crumb {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
}
.slide__crumb-dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--accent);
}
.slide__crumb-label {
  text-transform: uppercase;
  font-weight: 600;
  letter-spacing: 0.08em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 800px;
}

.slide__pageno {
  font-variant-numeric: tabular-nums;
  font-weight: 500;
  color: var(--ink-2);
}

/* Any element acting as a logo slot — chrome on regular slides AND
   the title slide's own header — gets the same horizontal flex layout. */
.slide__logo-slot,
[data-logo-slot] {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--ink-2);
  white-space: nowrap;
  flex-shrink: 0;
}
.slide__logo-slot .logo-mark,
[data-logo-slot] .logo-mark {
  min-width: 36px; width: auto; height: 36px; padding: 0 9px;
  border-radius: 8px;
  background: var(--accent);
  display: grid; place-items: center;
  color: white; font-family: var(--font-serif); font-weight: 600;
  font-size: var(--fs-label-xs); letter-spacing: 0.02em;
  flex-shrink: 0;
}

/* Hide ALL inline-notes asides from on-screen rendering — they're only
   surfaced through the presenter notes panel. Covers both
   `class="slide-notes"`, `data-notes` (legacy), and
   `data-notes-for-step` (per-step e2e notes). */
.slide-notes,
aside[data-notes],
aside[data-notes-for-step] {
  display: none !important;
}

/* Hide frame on title and divider slides */
.slide[data-type="title"] .slide__frame-bot,
.slide[data-type="title"] .slide__frame-top,
.slide[data-type="divider"] .slide__frame-bot,
.slide[data-type="divider"] .slide__frame-top { display: none; }

/* ----- Handout export (?handout=1) ---------------------- */
/* A flat, scrollable, Cmd+P-friendly answer key built by js/handout.js.
   No transforms, no fixed positioning, no animation — just a document. */
html.is-handout, html.is-handout body {
  height: auto !important;
  overflow: auto !important;
  background: var(--bg) !important;
}
.handout-doc {
  max-width: 900px;
  margin: 0 auto;
  padding: 56px 48px 120px;
  font-family: var(--font-sans);
  color: var(--ink);
  line-height: 1.5;
}
.handout-head { margin-bottom: 40px; padding-bottom: 24px; border-bottom: 2px solid var(--rule-strong); }
.handout-head h1 {
  font-family: var(--font-serif);
  font-size: 40px;
  font-weight: 500;
  line-height: 1.1;
  margin: 0 0 12px;
  color: var(--ink);
}
.handout-head .handout-sub { font-size: var(--fs-fine-lg); color: var(--ink-3); margin: 0; }
.handout-slide {
  padding: 22px 0 26px;
  border-bottom: 1px solid var(--rule);
  break-inside: avoid;
  page-break-inside: avoid;
}
.handout-slide-title {
  font-family: var(--font-serif);
  font-size: var(--fs-caption);
  font-weight: 500;
  line-height: 1.2;
  margin: 0 0 14px;
  color: var(--ink);
}
.handout-slide-title .handout-num {
  font-family: var(--font-mono);
  font-size: var(--fs-label-xs);
  color: var(--accent);
  margin-right: 8px;
}
.handout-notes { font-size: var(--fs-fine-lg); color: var(--ink-2); margin: 0 0 12px; }
.handout-notes :is(p, em, strong, code) { font-size: inherit; }
.handout-step-note {
  border-left: 3px solid var(--rule-strong);
  padding-left: 14px;
  margin-left: 2px;
}
.handout-step-label, .handout-answer-label {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--fs-fine-xxs);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ink-3);
  margin-bottom: 4px;
}
.handout-answer {
  background: var(--c-green-soft);
  border-left: 3px solid var(--c-green);
  border-radius: 6px;
  padding: 12px 16px;
  margin: 0 0 14px;
  font-size: var(--fs-fine-lg);
  color: var(--ink);
}
.handout-answer-label { color: var(--c-green-ink); }
.handout-empty { font-size: 15px; color: var(--ink-4); font-style: italic; margin: 0; }
/* In the handout the cloned <aside class="slide-notes"> would otherwise be
   suppressed by the global hide-notes rule — un-hide it inside the doc. */
.handout-doc .slide-notes,
.handout-doc aside[data-notes],
.handout-doc aside[data-notes-for-step] { display: block !important; }
.handout-doc .handout-answer .misc-truth { display: block !important; }
@media print {
  html.is-handout, html.is-handout body { background: white !important; }
  .handout-doc { max-width: none; padding: 0 24px; }
  .handout-answer { print-color-adjust: exact !important; -webkit-print-color-adjust: exact !important; }
}

/* ---- Reduced motion preference ---- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001s !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001s !important;
    transition-delay: 0s !important;
  }
  .slide.is-entering { animation: none !important; }
}
