/* =========================================================================
   slaydy — runtime.css
   Fixed asset. Never regenerated per-deck. Themes override tokens only.

   UPSTREAM-OWNED. Every update replaces this file whole, and an edit here
   is skipped by take-update.sh from then on (UPDATING.md §1). Restyle from
   your theme or custom.css instead — they load after this file and win at
   equal specificity — and prefer the tokens (--slide-bg, --section-fg,
   --wash-base, --deck-pad, --sep, --marker-*, --code-*) to selector
   overrides. A rule you cannot reach that way is a gap to report upstream.
   ========================================================================= */

/* ---------- defaults (themes override) ---------- */
:root {
  --slide-w: 1280px;
  --slide-h: 720px;
  --pad: 84px;
  --slide-radius: 14px;
  --deck-pad: 0.94;   /* windowed inset around the deck card; 1 = edge-to-edge (fullscreen forces 1) */

  --font-display: "Inter Tight", "Inter", system-ui, sans-serif;
  --font-body: "Inter", system-ui, sans-serif;
  --font-serif: "Instrument Serif", Georgia, serif;
  --font-mono: "JetBrains Mono", ui-monospace, monospace;

  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  --tx-dur: 450ms;

  /* fallback theme tokens */
  --app-bg: #08080a;
  --bg: #0b0b0f;
  --fg: #f5f4f2;
  --muted: rgba(245, 244, 242, 0.56);
  --faint: rgba(245, 244, 242, 0.13);
  --surface: #15151c;
  --accent: #ff5b2e;
  --accent-2: #7c5cff;
  --accent-fg: #ffffff;

  /* divider drawn between meta items — an em dash, never a middle dot.
     Override in a theme, per deck via <body data-sep>, or with data-sep-text. */
  --sep: "\2014";

  /* code tokens — derived, so a theme only overrides one if it wants to */
  --code-kw: var(--accent);
  --code-str: color-mix(in oklab, var(--accent-2) 78%, var(--fg));
  --code-num: var(--accent-2);
  --code-lit: var(--accent-2);
  --code-key: color-mix(in oklab, var(--accent) 55%, var(--fg));
  --code-fn: color-mix(in oklab, var(--fg) 82%, var(--accent-2));
  --code-attr: color-mix(in oklab, var(--accent) 45%, var(--fg));
  --code-com: color-mix(in oklab, var(--muted) 80%, transparent);
  --code-punc: color-mix(in oklab, var(--fg) 52%, transparent);
}

* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }

body {
  background: var(--app-bg);
  color: var(--fg);
  font-family: var(--font-body);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ---------- stage / scaling ---------- */
.stage {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
}

.deck {
  /* absolute positioning: a grid track would size to 1280px and push the centre off-screen
     on narrow / portrait viewports. fit() computes --deck-x/--deck-y as whole pixels (the
     notes-drawer gutter folded in) and snaps --scale so both scaled dimensions are integers —
     a fractional transform puts every glyph off the device pixel grid and visibly softens
     text on large displays. Integer translate must come BEFORE scale, from origin 0 0. */
  position: absolute;
  left: 0;
  top: 0;
  width: var(--slide-w);
  height: var(--slide-h);
  transform: translate(var(--deck-x, 0px), var(--deck-y, 0px)) scale(var(--scale, 1));
  transform-origin: 0 0;
  border-radius: var(--slide-radius);
  /* --deck-shadow: the drop shadow under the deck in a window. --edit-ring: the colour
     of every edit-mode ring (deck, focused text, image slot, selected sticker). A theme
     sets either on :root; both default to what they always were. */
  box-shadow: var(--deck-shadow, 0 40px 120px -30px rgba(0, 0, 0, 0.7));
}

/* presenting: edge-to-edge, no card, black surround regardless of theme */
html:fullscreen { --slide-radius: 0; background: #000; }
html:fullscreen body { background: #000; }
html:fullscreen .deck { box-shadow: none; }

.slide {
  position: absolute;
  inset: 0;
  padding: var(--pad);
  /* --slide-bg lets a theme compose a per-slide surface (gradient, panel)
     without re-declaring this rule */
  background: var(--slide-bg, var(--bg));
  color: var(--fg);
  border-radius: var(--slide-radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  opacity: 0;
  visibility: hidden;
  /* establishes a stacking context so .slide__wash (z-index: -1) stays behind
     this slide's own content without falling behind the slide's *background*
     or bleeding under a sibling slide */
  isolation: isolate;
}

.slide.is-active { opacity: 1; visibility: visible; transform: none; z-index: 2; }
/* the leaving slide sits ON TOP of the entering one and fades out alone —
   see the transitions block for why (single-sided fade, no background blink) */
.slide.is-leaving { visibility: visible; z-index: 3; }

/* Before the runtime boots — and wherever scripts never run (Quick Look,
   preview panes) — show the title slide, fitted to the viewport, so a
   shared file previews as its cover rather than a blank page. Quick Look
   imposes its own canvas aspect, so paint the whole page in the slide's
   background and drop the card corners/shadow: any crop reads as a
   full-bleed cover instead of a small card on a tall document. */
body:not(.is-ready) { background: var(--bg); }
body:not(.is-ready) .deck .slide:first-child { opacity: 1; visibility: visible; border-radius: 0; }
body:not(.is-ready) .deck {
  left: 50%; top: 50%;
  transform: translate(-50%, -50%) scale(min(100vw / 1280px, 100vh / 720px));
  transform-origin: center center;
  border-radius: 0; box-shadow: none;
}

/* loading cover. Where scripts run, hold the page behind a plain cover with a
   thin progress bar until the runtime adds html.is-loaded (theme, fonts and
   extensions settled), so a brand never shows as the default theme first.
   (scripting: enabled) keeps it out of Quick Look and preview panes, which
   keep the cover-slide preview above. If the runtime itself never runs, the
   cover lifts on its own after 6s rather than hiding the deck for good. */
@media screen and (scripting: enabled) {
  html::before {
    content: ""; position: fixed; inset: 0; z-index: 1000;
    background: var(--bg); pointer-events: none;
    transition: opacity .2s linear;
  }
  html.is-loaded::before { opacity: 0; }
  html:not(.is-loaded)::before { animation: cover-lift 0s linear 6s forwards; }
  html:not(.is-loaded)::after {
    content: ""; position: fixed; left: 50%; top: 50%; z-index: 1001;
    width: min(220px, 40vw); height: 3px; margin: -1px 0 0 calc(min(220px, 40vw) / -2);
    border-radius: 3px; pointer-events: none; opacity: 0;
    background: linear-gradient(var(--accent), var(--accent)) no-repeat -60% 0 / 40% 100%, var(--faint);
    /* a fast load never shows the bar; only a slow one does */
    animation: cover-bar 1.1s var(--ease) .25s infinite, cover-in .2s linear .25s forwards, cover-lift 0s linear 6s forwards;
  }
}
@keyframes cover-bar { from { background-position: -60% 0, 0 0; } to { background-position: 160% 0, 0 0; } }
@keyframes cover-in { to { opacity: 1; } }
@keyframes cover-lift { to { opacity: 0; visibility: hidden; } }
@media (prefers-reduced-motion: reduce) {
  html:not(.is-loaded)::after { background: var(--accent); animation: cover-in .2s linear .25s forwards, cover-lift 0s linear 6s forwards; }
}

/* subtle entrance stagger — "rise" transition only */
@keyframes rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
body:not(.is-editing):not(.is-overview) .slide[data-tx="rise"].is-active > *:not(.slide__chrome):not(.slide__wash) {
  animation: rise .5s var(--ease) backwards;
}
.slide.is-active > *:nth-child(2) { animation-delay: .06s; }
.slide.is-active > *:nth-child(3) { animation-delay: .12s; }
.slide.is-active > *:nth-child(4) { animation-delay: .18s; }
.slide.is-active > *:nth-child(5) { animation-delay: .24s; }

/* =========================================================================
   Slide transitions — data-transition on <body> (deck default) or a
   <section> (per-slide override). Computed effective value lands on
   [data-tx] (derived, stripped on save). Direction follows body[data-dir].

   Motion is applied to slide CONTENT (direct children, minus chrome and
   wash), never to the slide box: a theme may paint imagery behind
   translucent slides, and a travelling box drags its background edge
   across the frame as a visible seam.

   Only ONE layer ever fades: the leaving slide sits on top (z-index 3)
   and fades out over the already fully-opaque entering slide. A symmetric
   cross-fade of two identical backgrounds dips to ~75% coverage at its
   midpoint and reads as a blink; fading one layer over an opaque twin is
   pixel-identical throughout, so between same-surface slides only the
   content appears to change. Wipe instead clips the leaving slide away
   (keyframes), revealing the entering slide beneath — same property, no fade.
   ========================================================================= */
/* fade: a pure dissolve — the old slide fades away over the new one */
.slide[data-tx="fade"].is-leaving { transition: opacity var(--tx-dur) var(--ease); opacity: 0; }

/* rise: entry motion is the child stagger animation above; leaving drifts up as it fades */
.slide[data-tx="rise"].is-leaving { transition: opacity var(--tx-dur) var(--ease); opacity: 0; }
.slide[data-tx="rise"].is-leaving > *:not(.slide__chrome):not(.slide__wash) {
  transition: transform var(--tx-dur) var(--ease); transform: translateY(-10px);
}

/* [data-step] is excluded: the reveal system owns opacity/transform on steps,
   and this rule's higher specificity would force unrevealed steps visible */
.slide[data-tx="zoom"].is-entering > *:not(.slide__chrome):not(.slide__wash):not([data-step]) { opacity: 0; transform: scale(.92); }
.slide[data-tx="zoom"].is-entering.is-active > *:not(.slide__chrome):not(.slide__wash):not([data-step]) {
  transition: opacity var(--tx-dur) var(--ease), transform var(--tx-dur) var(--ease);
  opacity: 1; transform: none;
}
.slide[data-tx="zoom"].is-leaving { transition: opacity var(--tx-dur) var(--ease); opacity: 0; }
.slide[data-tx="zoom"].is-leaving > *:not(.slide__chrome):not(.slide__wash) {
  transition: transform var(--tx-dur) var(--ease); transform: scale(1.06);
}

/* push: content travels a full slide width; the surfaces never move */
.slide[data-tx="push"].is-entering > *:not(.slide__chrome):not(.slide__wash) { transform: translateX(var(--slide-w)); }
body[data-dir="back"] .slide[data-tx="push"].is-entering > *:not(.slide__chrome):not(.slide__wash) { transform: translateX(calc(-1 * var(--slide-w))); }
.slide[data-tx="push"].is-entering.is-active > *:not(.slide__chrome):not(.slide__wash) {
  transition: transform var(--tx-dur) var(--ease); transform: none;
}
.slide[data-tx="push"].is-leaving { transition: opacity var(--tx-dur) var(--ease); opacity: 0; }
.slide[data-tx="push"].is-leaving > *:not(.slide__chrome):not(.slide__wash) {
  transition: transform var(--tx-dur) var(--ease); transform: translateX(calc(-1 * var(--slide-w)));
}
body[data-dir="back"] .slide[data-tx="push"].is-leaving > *:not(.slide__chrome):not(.slide__wash) { transform: translateX(var(--slide-w)); }

/* wipe: the leaving slide clips away (boundary travels right to left, new content on the right).
   The entering slide is never clipped and stays at z-index 2, directly above whatever the theme
   painted, so translucent slide backgrounds composite against the backdrop, not the old slide.
   A keyframes animation, not a transition: show() sets [data-tx] and adds .is-leaving in the
   same tick, so a clip-path transition would have no rendered from-state. opacity: 1 is kept
   because .is-leaving only restores visibility; the slide has just lost the .is-active that
   held opacity up. Thumbnails and print clear clip-path and animation (see those blocks). */
@keyframes wipe-out      { from { clip-path: inset(0 0 0 0); } to { clip-path: inset(0 100% 0 0); } }
@keyframes wipe-out-back { from { clip-path: inset(0 0 0 0); } to { clip-path: inset(0 0 0 100%); } }
.slide[data-tx="wipe"].is-leaving {
  opacity: 1;
  animation: wipe-out var(--tx-dur) var(--ease) both;
}
body[data-dir="back"] .slide[data-tx="wipe"].is-leaving { animation-name: wipe-out-back; }

/* =========================================================================
   Progressive reveal — data-reveal on a container marks its direct children
   as steps ([data-step], derived); a lone element becomes one step itself.

   --step-dim sets how an un-revealed step looks. 0 (default): hidden, and it rises 10px
   into place. A nonzero value (e.g. .28): the step sits dimmed at that opacity with no rise,
   so the audience still sees the shape of the list and that more is coming. A static contrast
   step survives projection and video compression better than a 10px rise. A theme sets it on
   :root or .slide, e.g. --step-dim: .28.
   ========================================================================= */
body:not(.is-editing):not(.is-overview) [data-step] {
  opacity: var(--step-dim, 0);
  transform: translateY(calc(10px * (1 - min(1, var(--step-dim, 0) * 1000))));
  transition: opacity .3s var(--ease), transform .3s var(--ease);
}
body:not(.is-editing):not(.is-overview) [data-step].is-revealed { opacity: 1; transform: none; }

/* =========================================================================
   Content animations
   ========================================================================= */
@keyframes kenburns { from { transform: scale(1); } to { transform: scale(1.08); } }
body:not(.is-editing):not(.is-overview) .slide.is-active figure[data-kenburns] img {
  animation: kenburns 10s linear forwards;
  transform-origin: center center;
}

@media (prefers-reduced-motion: reduce) {
  [data-step] { transition: none !important; }
  figure[data-kenburns] img { animation: none !important; }
}

/* atmospheric wash — one hairline of polish, purely decorative.
   z-index: -1 keeps it below in-flow content (headings, eyebrow) that has no
   z-index of its own — see .slide's `isolation: isolate`, which is what keeps
   this from falling behind the slide's own background instead.
   --wash is a plain custom property, not a selector override: a theme sets
   it directly (and its print counterpart, --wash-print, below) instead of
   fighting this rule's specificity. Fallback here is the stock two-radial
   screen wash, unchanged from before this was made themeable. */
.slide__wash {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: var(--wash,
    radial-gradient(90% 70% at 88% -12%, color-mix(in oklab, var(--accent) 22%, transparent), transparent 62%),
    radial-gradient(70% 60% at -8% 108%, color-mix(in oklab, var(--accent-2) 18%, transparent), transparent 60%));
  opacity: var(--wash-opacity, 1);
}

/* per-slide footer, injected by runtime, not part of source markup */
.slide__chrome {
  position: absolute;
  left: var(--pad);
  right: var(--pad);
  bottom: 34px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font: 500 12px/1 var(--font-mono);
  letter-spacing: .06em;
  color: var(--muted);
  pointer-events: none;
}
.slide[data-bare] .slide__chrome { display: none; }

/* =========================================================================
   Typography primitives
   ========================================================================= */
.eyebrow {
  font: 600 13px/1 var(--font-mono);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0;
}
/* the divided form: wrap the number/tag in <b> — it goes neutral while the
   text keeps the accent. No middle-dot separators; the color shift is the division. */
.eyebrow b { color: var(--muted); font-weight: inherit; margin-inline-end: .45em; }
.eyebrow b:last-child { margin-inline-end: 0; margin-inline-start: .45em; }
.eyebrow b:only-child { margin-inline: 0; }

/* content icons — <svg class="glyph"><use href="#i-name"/></svg>, referencing
   symbols the deck embeds (LAYOUTS.md "Icons"). The icons/ folder ships
   24×24 stroke sources; this rule is the single place their rendering
   contract lives, so a brand swapping in filled icons adjusts it here. */
.glyph {
  width: 26px; height: 26px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
  color: var(--accent);
}
.slide--bento .cell .glyph { width: 22px; height: 22px; }
.slide--bento .cell--accent .glyph { color: var(--accent-fg); }
.slide--stats .stat .glyph { width: 24px; height: 24px; }
.slide--compare .col .glyph { width: 22px; height: 22px; }
/* bullets: a glyph in the marker column replaces that item's counter/dot */
.slide--bullets li > .glyph {
  position: absolute; left: 0; top: 6px;
  width: 21px; height: 21px;
}
.slide--bullets li:has(> .glyph)::before { content: none; }
.display {
  font-family: var(--font-display);
  font-size: 92px;
  line-height: .96;
  letter-spacing: -.035em;
  font-weight: 600;
  margin: 0;
  text-wrap: balance;
}
.h1 {
  font-family: var(--font-display);
  font-size: 62px;
  line-height: 1.02;
  letter-spacing: -.03em;
  font-weight: 600;
  margin: 0;
  text-wrap: balance;
}
.h2 {
  font-family: var(--font-display);
  font-size: 40px;
  line-height: 1.1;
  letter-spacing: -.02em;
  font-weight: 600;
  margin: 0;
}
.h3 {
  font-family: var(--font-display);
  font-size: 24px;
  line-height: 1.2;
  letter-spacing: -.01em;
  font-weight: 600;
  margin: 0;
}
.lead {
  font-size: 25px;
  line-height: 1.45;
  letter-spacing: -.011em;
  color: var(--muted);
  margin: 0;
  max-width: 22ch;
  text-wrap: pretty;
}
.body {
  font-size: 19px;
  line-height: 1.6;
  color: var(--muted);
  margin: 0;
  text-wrap: pretty;
}
.caption {
  font: 500 13px/1.4 var(--font-mono);
  letter-spacing: .04em;
  color: var(--muted);
  margin: 0;
}
.serif {
  font-family: var(--font-serif);
  font-weight: 400;
  letter-spacing: -.015em;
}
code {
  font: 500 .88em/1 var(--font-mono);
  padding: .2em .45em;
  border-radius: 5px;
  background: color-mix(in oklab, var(--fg) 9%, transparent);
  color: var(--fg);
}
/* a link (paste a URL in edit mode) keeps the text's colour and underlines
   in the accent. :where() keeps it at zero specificity, so a theme's own
   `a` rule wins without a fight. */
:where(.slide) a[href] {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-decoration-thickness: .07em;
  text-underline-offset: .18em;
}
.accent { color: var(--accent); }
.dim { color: var(--muted); }

/* <strong> emphasis (⌘B in edit mode): context decides rendering, one
   toggle. Body scale gets weight — 600 matches the deck's own semibold,
   stated explicitly rather than trusting the UA default of 700. Title
   scale is already heavy (600) so weight would do nothing; accent color
   carries the emphasis there instead. .accent stays the explicit color
   primitive regardless of context. */
strong { font-weight: 600; }
h1 strong, h2 strong, h3 strong,
.display strong, .h1 strong, .h2 strong, .h3 strong {
  font-weight: inherit;
  color: var(--accent);
}

.stack { display: flex; flex-direction: column; }
.stack--xs { gap: 8px; }
.stack--sm { gap: 16px; }
.stack--md { gap: 26px; }
.stack--lg { gap: 40px; }
.grow { flex: 1; }
.rule { height: 1px; background: var(--faint); border: 0; margin: 0; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px;
  border: 1px solid var(--faint);
  border-radius: 999px;
  font: 500 13px/1 var(--font-mono);
  letter-spacing: .04em;
  color: var(--muted);
}

/* media / image slots */
.media {
  position: relative;
  margin: 0;
  overflow: hidden;
  border-radius: 12px;
  background: var(--surface);
  min-height: 60px;
}
.media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* authored per-slot override, toggled from the hover chip. The doubled
   attribute lifts specificity over layout defaults like
   .slide--callout .callout__media img { object-fit: contain } */
.media[data-crop="cover"][data-crop] img { object-fit: cover; }
.media[data-crop="contain"][data-crop] img { object-fit: contain; }
/* Grain the runtime stripped out of an SVG asset (degrainImages) comes back
   as this screen-only overlay — same turbulence recipe, but living in CSS so
   @media print can drop it. Baked-in grain rasterizes at print DPI into
   megabytes of incompressible noise per page. z-index 1: above the image,
   below pins (4) and the hover chrome (3). */
.media[data-grain]::after {
  content: "";
  position: absolute; inset: 0; z-index: 1;
  pointer-events: none;
  background: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="240" height="240"%3E%3Cfilter id="g"%3E%3CfeTurbulence type="fractalNoise" baseFrequency="0.8" numOctaves="3"/%3E%3CfeColorMatrix type="saturate" values="0"/%3E%3C/filter%3E%3Crect width="240" height="240" filter="url(%23g)"/%3E%3C/svg%3E');
  opacity: .17;
}

.media:empty::after,
.media[data-empty]::after {
  content: "＋  image";
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font: 500 14px/1 var(--font-mono);
  letter-spacing: .08em;
  color: var(--muted);
  border: 1px dashed var(--faint);
  border-radius: 12px;
}
/* a <figcaption> turns the slot into picture-over-caption: the figure stops
   being the rounded box (the image becomes it) so the caption sits outside
   the crop rather than under it. Excluded are the slots where a caption has
   nowhere to live or would shift a coordinate frame — bleeds run to the slide
   edge, and callout pins are positioned against the whole figure. */
.media:has(> figcaption):not(.bleed, .split__media, .callout__media) {
  display: grid; grid-template-rows: minmax(0, 1fr) auto; gap: 12px;
  overflow: visible; background: none; border-radius: 0;
}
.media:has(> figcaption):not(.bleed, .split__media, .callout__media) > img {
  border-radius: 12px; background: var(--surface);
}
.media:has(> figcaption):not(.bleed, .split__media, .callout__media) > figcaption {
  grid-row: 2;                         /* an empty slot has no img to hold row 1 open */
  font: 500 13px/1.4 var(--font-mono);
  letter-spacing: .04em;
  color: var(--muted);
  text-wrap: pretty;
}
/* the empty-slot placeholder and the edit-mode overlay belong to the picture,
   not the caption — grid placement keeps both absolute boxes in the first row */
.media:has(> figcaption):not(.bleed, .split__media, .callout__media)::after,
.media:has(> figcaption):not(.bleed, .split__media, .callout__media) .media-hover {
  grid-row: 1; border-radius: 12px;
}

body.is-editing .media { cursor: pointer; outline-offset: 3px; }
body.is-editing .media:hover { outline: 2px solid var(--edit-ring, var(--accent)); }

.media-hover {
  position: absolute; inset: 0; z-index: 3;
  display: none;
  align-content: center; justify-items: center; gap: 10px;
  background: rgba(11, 11, 15, .55);
  border-radius: inherit;
}
body.is-editing .media:not([data-empty]):hover .media-hover { display: grid; }
.media-hover span {
  display: flex; gap: 8px; align-items: center;
  padding: 10px 14px; border-radius: 10px;
  background: rgba(20, 20, 26, .9);
  border: 1px solid rgba(255, 255, 255, .14);
  font: 500 13px/1 var(--font-body);
  color: #fff; white-space: nowrap;
}
.media-hover .icon { width: 16px; height: 16px; }

/* =========================================================================
   Layouts  (these are the "master slides")
   ========================================================================= */

/* --- title --- */
.slide--title { justify-content: flex-end; gap: 30px; }
.slide--title .display { font-size: 94px; max-width: 16ch; }
.slide--title .meta {
  display: flex;
  gap: 18px;                 /* the generated separator supplies the rest */
  align-items: center;
  padding-top: 30px;
  border-top: 1px solid var(--faint);
  font: 500 14px/1 var(--font-mono);
  letter-spacing: .05em;
  color: var(--muted);
}

/* Meta separators are generated by the runtime (bare <span>s in the markup,
   an <i class="meta__sep"> between each pair), so a deck file never carries a
   hand-typed divider. The glyph is a token: a theme sets --sep once, a deck
   picks a named value with data-sep, or supplies its own with data-sep-text /
   data-sep-icon. Both attributes work on <body> (deck-wide) or one .slide. */
.meta__sep {
  font-style: normal;
  color: color-mix(in oklab, var(--muted) 62%, transparent);
  user-select: none;
}
.meta__sep::before { content: var(--sep); }
.meta__sep:has(.glyph)::before { content: none; }
.meta__sep .glyph { display: block; width: 13px; height: 13px; }

[data-sep="dash"]    { --sep: "\2014"; }   /* — em dash (default) */
[data-sep="en-dash"] { --sep: "\2013"; }   /* – en dash */
[data-sep="slash"]   { --sep: "/"; }
[data-sep="pipe"]    { --sep: "\2502"; }   /* │ box-drawing bar, lighter than | */
[data-sep="colon"]   { --sep: ":"; }
[data-sep="none"] .meta__sep { display: none; }   /* the runtime also skips generating them */

/* Marker style for every list layout — the bullets list and the compare
   columns read the same tokens, so one data-list on <body> harmonises them.
   Left unset, each layout keeps its own default (numbers / dash). Values go on
   <body> deck-wide, on a slide, or come from data-list-text / data-list-icon. */
[data-list="numbers"] {
  --marker: counter(li, decimal-leading-zero);
  --marker-w: auto; --marker-h: auto; --marker-bg: none; --marker-top: 8px;
}
[data-list="dots"] {
  --marker: ""; --marker-w: 8px; --marker-h: 8px;
  --marker-radius: 999px; --marker-bg: var(--accent);
  --marker-shift: 13px; --marker-top: 10px;
}
[data-list="square"] {
  --marker: ""; --marker-w: 8px; --marker-h: 8px;
  --marker-radius: 2px; --marker-bg: var(--accent);
  --marker-shift: 13px; --marker-top: 10px;
}
[data-list="dash"] {
  --marker: ""; --marker-w: 14px; --marker-h: 2px;
  --marker-bg: var(--accent);
  --marker-shift: 15px; --marker-top: 13px;
}
[data-list="chevron"] {
  --marker: "\203A";                        /* › */
  --marker-w: auto; --marker-h: auto; --marker-bg: none; --marker-top: 8px;
}
[data-list="none"] {
  --marker: ""; --marker-w: 0; --marker-h: 0; --marker-bg: none;
}

/* --- section divider --- */
.slide--section {
  justify-content: center;
  gap: 22px;
  background: var(--slide-bg, var(--accent));
  color: var(--section-fg, var(--accent-fg));
}
.slide--section .eyebrow { color: color-mix(in oklab, var(--section-fg, var(--accent-fg)) 70%, transparent); }
.slide--section .eyebrow b { color: inherit; }   /* accent-on-accent would vanish */
.slide--section .display { font-size: 108px; }
.slide--section .slide__wash { opacity: .25; }
.slide--section .index {
  font: 600 15px/1 var(--font-mono);
  letter-spacing: .16em;
  opacity: .7;
}

/* --- statement / big idea --- */
.slide--statement { justify-content: center; align-items: flex-start; gap: 28px; }
.slide--statement .display { font-size: 86px; max-width: 17ch; }

/* --- quote --- */
.slide--quote { justify-content: center; gap: 40px; }
.slide--quote blockquote {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 62px;
  line-height: 1.12;
  letter-spacing: -.02em;
  max-width: 20ch;
}
.slide--quote figcaption { display: flex; gap: 14px; align-items: center; }
.slide--quote .avatar {
  width: 46px; height: 46px; border-radius: 999px;
  object-fit: cover; background: var(--surface);
}

/* --- bullets --- */
.slide--bullets { gap: 40px; }
.slide--bullets ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 22px; }
/* the marker is absolutely positioned rather than a grid/flex track: an
   editable item must stay a plain inline flow, or an <strong> the user adds
   with cmd+B would become a grid item of its own and break the line apart */
.slide--bullets li {
  position: relative;
  padding-left: 48px;                  /* 30px marker column + 18px gutter */
  font-size: 25px;
  line-height: 1.4;
  letter-spacing: -.01em;
  padding-bottom: 22px;
  border-bottom: 1px solid var(--faint);
}
.slide--bullets li:last-child { border-bottom: 0; padding-bottom: 0; }
.slide--bullets li::before {
  content: var(--marker, counter(li, decimal-leading-zero));
  counter-increment: li;
  position: absolute; left: 0; top: 0;
  width: var(--marker-w, auto); height: var(--marker-h, auto);
  background: var(--marker-bg, none);
  border-radius: var(--marker-radius, 0);
  margin-top: var(--marker-shift, 0);
  font: 600 14px/1.9 var(--font-mono);
  color: var(--accent);
  letter-spacing: .04em;
}
.slide--bullets ul { counter-reset: li; }

/* chevron is a real glyph, not a fixed-size box: match the body text's font
   metrics so its line box lines up exactly with the li's first line instead
   of floating at the small marker font size used by the other list styles */
[data-list="chevron"] .slide--bullets li::before,
.slide--bullets[data-list="chevron"] li::before {
  font: 600 1em/1.4 var(--font-mono);
  top: 0;
  margin-top: 0;
}

/* --- agenda (two column index) --- */
.slide--agenda { gap: 38px; }
.slide--agenda ol {
  margin: 0; padding: 0; list-style: none;
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 0 64px;
}
.slide--agenda li {
  display: flex; justify-content: space-between; align-items: baseline; gap: 20px;
  padding: 18px 0;
  border-bottom: 1px solid var(--faint);
  font-size: 22px;
  letter-spacing: -.01em;
}
.slide--agenda li span { font: 500 13px/1 var(--font-mono); color: var(--muted); }

/* --- split (image bleeding off one edge + text) --- */
.slide--split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 64px;
  align-items: center;
  padding-left: 0;                       /* media bleeds off the left edge */
}
.slide--split .split__media {
  margin: calc(var(--pad) * -1) 0;
  height: var(--slide-h);
  border-radius: 0;
}
.slide--split .split__body { display: flex; flex-direction: column; gap: 24px; }
.slide--split.is-flipped { padding-left: var(--pad); padding-right: 0; }
.slide--split.is-flipped .split__media { order: 2; }

/* --- callout: annotated image — numbered pins keyed to a notes list --- */
.slide--callout {
  display: grid;
  grid-template-columns: 1.55fr 1fr;
  gap: 56px;
  align-items: center;
}
.slide--callout .callout__media {
  align-self: stretch;
  min-height: 0;
  background: var(--surface);
}
.slide--callout .callout__media img { object-fit: contain; }   /* screenshots must never crop */
.slide--callout.is-flipped { grid-template-columns: 1fr 1.55fr; }
.slide--callout.is-flipped .callout__media { order: 2; }
.slide--callout .callout__body { display: flex; flex-direction: column; gap: 22px; min-width: 0; }
.callout__notes {
  margin: 0; padding: 0; list-style: none;
  display: grid; gap: 14px;
  counter-reset: cnote;
}
.callout__notes li {
  position: relative;
  padding-left: 38px;                  /* 26px marker + 12px gutter */
  font-size: 17px; line-height: 1.5; color: var(--muted);
}
.callout__notes li::before {
  content: counter(cnote); counter-increment: cnote;
  position: absolute; left: 0; top: 0;
  width: 26px; height: 26px; border-radius: 50%;
  background: color-mix(in oklab, var(--accent) 16%, transparent);
  border: 1px solid color-mix(in oklab, var(--accent) 50%, transparent);
  color: var(--accent);
  font: 600 12px/24px var(--font-mono); text-align: center;
}

/* --- callout-full: the annotated image IS the slide; notes live in a
   floating box (add callout__box--left to dock it left) --- */
.slide--callout-full { padding: 0; }
.slide--callout-full .callout__media {
  position: absolute; inset: 0;
  border-radius: 0;
  background: var(--surface);
}
.slide--callout-full .callout__media img { object-fit: contain; }
.slide--callout-full .callout__box {
  position: absolute; right: 44px; bottom: 44px; z-index: 5;
  width: 360px; max-height: calc(100% - 88px); overflow: hidden;
  background: color-mix(in srgb, var(--bg) 80%, transparent);
  backdrop-filter: blur(14px);
  border: 1px solid var(--faint);
  border-radius: 16px;
  padding: 24px 26px;
  display: flex; flex-direction: column; gap: 14px;
}
.slide--callout-full .callout__box--left { left: 44px; right: auto; }
.slide--callout-full .callout__box .h3 { margin: 0; }
.slide--callout-full .callout__notes { gap: 12px; }
.slide--callout-full .callout__notes li { font-size: 15px; line-height: 1.45; }

/* numbered pins — position is the ONLY inline style a deck may carry
   (left/top in % of the media box); numbering is a CSS counter, so
   add / delete / reorder renumber themselves */
.media { counter-reset: pin; }
.media .pin {
  position: absolute; z-index: 4;
  width: 34px; height: 34px; margin: -17px 0 0 -17px;   /* centred on the stored point */
  border-radius: 50%;
  background: var(--accent); color: var(--accent-fg);
  font: 600 15px/34px var(--font-mono); font-style: normal; text-align: center;
  box-shadow: 0 0 0 4px color-mix(in oklab, var(--accent) 30%, transparent), 0 6px 18px rgba(0, 0, 0, .35);
  counter-increment: pin;
}
.media .pin::before { content: counter(pin); }
body.is-editing .pin { cursor: grab; }
body.is-editing .pin:active { cursor: grabbing; }
body.is-editing .pin.is-sel { outline: 2px solid #fff; outline-offset: 2px; }

/* on a callout media the hover overlay must not swallow pin drags or the
   double-click-to-add gesture — only its buttons stay clickable */
.callout__media .media-hover { pointer-events: none; }
.callout__media .media-hover span { pointer-events: auto; }
.media-hover .media-hover__crop {
  font: 500 12px/1 var(--font-mono);
  letter-spacing: .04em;
  padding: 7px 11px;
  cursor: pointer;
}
.media-hover .media-hover__crop:hover { border-color: rgba(255, 255, 255, .4); }
.media-hover .media-hover__hint {
  font: 500 12px/1 var(--font-mono);
  color: rgba(255, 255, 255, .75);
  background: rgba(20, 20, 26, .75);
  border: 0;
  padding: 7px 10px;
}

/* --- three-up cards --- */
.slide--cards { gap: 44px; }
.slide--cards .cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; flex: 1; }
.slide--cards .card {
  background: var(--surface);
  border: 1px solid var(--faint);
  border-radius: 16px;
  padding: 30px;
  display: flex; flex-direction: column; gap: 12px;
}
.slide--cards .card .num {
  font: 600 13px/1 var(--font-mono);
  letter-spacing: .14em;
  color: var(--accent);
}

/* --- stats --- */
.slide--stats { gap: 44px; }
.slide--stats .stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0; flex: 1; align-items: center; }
.slide--stats .stat { padding: 0 40px; border-left: 1px solid var(--faint); display: flex; flex-direction: column; gap: 12px; }
.slide--stats .stat:first-child { border-left: 0; padding-left: 0; }
.slide--stats .stat__num {
  font-family: var(--font-display);
  font-size: 88px; line-height: .9; letter-spacing: -.045em; font-weight: 600;
}

/* --- full bleed image --- */
.slide--full { padding: 0; justify-content: flex-end; }
.slide--full .bleed { position: absolute; inset: 0; border-radius: 0; }
.slide--full .bleed::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, color-mix(in oklab, var(--bg) 92%, transparent) 8%, transparent 62%);
}
.slide--full .overlay { position: relative; z-index: 1; padding: var(--pad); display: flex; flex-direction: column; gap: 18px; }

/* --- bento grid: 3×2 tile mosaic; span cells with .cell--wide / .cell--tall --- */
.slide--bento { gap: 34px; }
.slide--bento .bento {
  flex: 1; min-height: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 1fr;
  gap: 16px;
}
.slide--bento .cell {
  background: var(--surface);
  border: 1px solid var(--faint);
  border-radius: 16px;
  padding: 24px 26px;
  display: flex; flex-direction: column; gap: 8px;
  justify-content: flex-end;
  position: relative; overflow: hidden;
  min-height: 0;
}
.slide--bento .cell--wide { grid-column: span 2; }
.slide--bento .cell--tall { grid-row: span 2; }
.slide--bento .cell--accent { background: var(--accent); border-color: transparent; color: var(--accent-fg); }
.slide--bento .cell--accent .body,
.slide--bento .cell--accent .caption { color: color-mix(in oklab, var(--accent-fg) 78%, transparent); }
.slide--bento .cell .stat__num {
  font-family: var(--font-display);
  font-size: 54px; line-height: .95; letter-spacing: -.03em; font-weight: 600;
  margin: 0;
}
.slide--bento .cell--media { padding: 0; }
.slide--bento .cell--media .media { position: absolute; inset: 0; border-radius: 0; min-height: 0; }

/* --- number: one hero figure --- */
.slide--number { justify-content: center; gap: 24px; }
.slide--number .stat__num {
  font-family: var(--font-display);
  font-size: 210px; line-height: .88; letter-spacing: -.05em; font-weight: 600;
  margin: 0;
}
.slide--number .lead { max-width: 30ch; }

/* --- compare: two panels side by side --- */
.slide--compare { gap: 36px; }
.slide--compare .compare { flex: 1; min-height: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.slide--compare .col {
  background: var(--surface); border: 1px solid var(--faint); border-radius: 16px;
  padding: 30px; display: flex; flex-direction: column; gap: 18px; min-height: 0;
}
.slide--compare .col--accent {
  border-color: color-mix(in oklab, var(--accent) 55%, transparent);
  background: color-mix(in oklab, var(--accent) 9%, var(--surface));
}
.slide--compare .col ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 14px; align-content: start; }
.slide--compare .col li {
  position: relative; padding-left: 24px;
  font-size: 19px; line-height: 1.5; color: var(--muted);
}
.slide--compare .col ul { counter-reset: li; }
.slide--compare .col li::before {
  content: var(--marker, "");
  counter-increment: li;
  position: absolute; left: 0; top: var(--marker-top, 13px);
  width: var(--marker-w, 12px); height: var(--marker-h, 2px);
  background: var(--marker-bg, var(--accent));
  border-radius: var(--marker-radius, 0);
  font: 600 12px/1 var(--font-mono);
  color: var(--accent);
  letter-spacing: .04em;
}
.slide--compare .col li > .glyph {
  position: absolute; left: 0; top: .25em;
  width: 15px; height: 15px; margin: 0;
}
.slide--compare .col li:has(> .glyph)::before { content: none; }

/* a panel may hold a picture instead of a list — before/after screenshots,
   two directions side by side. The slot takes the leftover column height. */
.slide--compare .col > .media { flex: 1; min-height: 0; }

/* --- table: a matrix of short cells — pricing, feature grid, RACI.
   Hairline rows and no vertical rules; the first column reads as the row
   label. data-key="N" on the <table> tints the Nth column end to end, which
   is how you point at the plan you are arguing for. --- */
.slide--table { gap: 34px; }
.slide--table table {
  width: 100%; border-collapse: collapse; table-layout: fixed;
}
.slide--table th,
.slide--table td {
  text-align: left; vertical-align: top;
  padding: 15px 16px;
  border-bottom: 1px solid var(--faint);
  font-size: 18px; line-height: 1.4; color: var(--muted);
}
.slide--table thead th {
  font: 600 13px/1.4 var(--font-mono);
  letter-spacing: .06em; text-transform: uppercase;
  color: var(--accent);
  border-bottom-color: color-mix(in oklab, var(--fg) 22%, transparent);
}
.slide--table tbody tr:last-child > * { border-bottom: 0; }
.slide--table tbody th,
.slide--table tbody td:first-child { color: var(--fg); font-weight: 500; }
.slide--table .glyph { width: 19px; height: 19px; margin: 0; }
/* one tinted column. CSS cannot read an attribute into :nth-child, so the
   five a table can hold are spelled out. */
.slide--table table[data-key="1"] tr > :nth-child(1),
.slide--table table[data-key="2"] tr > :nth-child(2),
.slide--table table[data-key="3"] tr > :nth-child(3),
.slide--table table[data-key="4"] tr > :nth-child(4),
.slide--table table[data-key="5"] tr > :nth-child(5) {
  background: color-mix(in oklab, var(--accent) 9%, transparent);
}
/* --- timeline: horizontal steps on a hairline --- */
.slide--timeline { gap: 56px; }
.slide--timeline .timeline {
  flex: 1; min-height: 0;
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  gap: 32px; align-content: start;
  padding-top: 34px; position: relative;
}
.slide--timeline .timeline::before {
  content: ""; position: absolute; top: 5px; left: 6px; right: 6px;
  height: 2px; background: var(--faint);
}
.slide--timeline .step { position: relative; display: flex; flex-direction: column; gap: 8px; }
.slide--timeline .step::before {
  content: ""; position: absolute; top: -34px; left: 0;
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in oklab, var(--accent) 25%, transparent);
}
.slide--timeline .step .caption { color: var(--accent); }

/* --- image: the picture IS the slide; optional .credit bottom-right --- */
.slide--image { padding: 0; justify-content: flex-end; align-items: flex-end; }
.slide--image .bleed { position: absolute; inset: 0; border-radius: 0; }
.slide--image .credit {
  position: relative; z-index: 1; margin: 0 34px 30px 0;
  font: 500 12px/1.4 var(--font-mono); letter-spacing: .04em;
  color: #fff; padding: 8px 12px; border-radius: 8px;
  background: rgba(10, 10, 14, .55); backdrop-filter: blur(8px);
}
.slide--image .credit:empty { display: none; }

/* --- gallery: a grid of images (3 across; .gallery--2 / .gallery--4 for 2 across) --- */
.slide--gallery { gap: 34px; }
.slide--gallery .gallery {
  flex: 1; min-height: 0;
  display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: 1fr;
  gap: 16px;
}
.slide--gallery .gallery--2,
.slide--gallery .gallery--4 { grid-template-columns: repeat(2, 1fr); }
.slide--gallery .media { min-height: 0; }

/* --- code / mono block ---
   The <pre> paints itself as a small terminal window: a title bar with three
   monochrome rings (deliberately not the traffic-light colours — this should
   read as "a terminal", not "macOS"), an optional data-label caption, and the
   code below. All of it is ::before/::after, so the saved markup stays a bare
   <pre> and edit mode has nothing extra to trip over. */
.slide--code { gap: 34px; }
.slide--code pre {
  position: relative;
  margin: 0; flex: 1; overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--faint);
  border-radius: 14px;
  padding: 62px 34px 30px;
  font: 400 17px/1.65 var(--font-mono);
  color: var(--fg);
  white-space: pre-wrap;
  box-shadow: 0 24px 60px -34px rgba(0, 0, 0, .55),
              inset 0 1px 0 color-mix(in oklab, var(--fg) 6%, transparent);
}
/* title bar — carries the optional caption, centred like a window title */
.slide--code pre::before {
  content: attr(data-label);
  position: absolute; inset: 0 0 auto 0; height: 42px;
  display: flex; align-items: center; justify-content: center;
  padding: 0 92px;
  font: 500 12px/1 var(--font-mono);
  letter-spacing: .07em;
  color: color-mix(in oklab, var(--muted) 85%, transparent);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  background: color-mix(in oklab, var(--fg) 4%, transparent);
  border-bottom: 1px solid var(--faint);
  border-radius: 13px 13px 0 0;
}
/* three hollow rings, one gradient each — monochrome, so the window belongs
   to no particular OS and takes the theme's ink instead of a fixed palette */
.slide--code pre::after {
  content: "";
  position: absolute; left: 20px; top: 16px;
  width: 46px; height: 10px;
  --ring: color-mix(in oklab, var(--fg) 34%, transparent);
  background:
    radial-gradient(circle at 5px 5px,  transparent 0 2.6px, var(--ring) 2.6px 4.1px, transparent 4.3px),
    radial-gradient(circle at 23px 5px, transparent 0 2.6px, var(--ring) 2.6px 4.1px, transparent 4.3px),
    radial-gradient(circle at 41px 5px, transparent 0 2.6px, var(--ring) 2.6px 4.1px, transparent 4.3px);
}
/* data-chrome="none" drops the window and goes back to a plain fill */
.slide--code pre[data-chrome="none"] { padding-top: 30px; box-shadow: none; }
.slide--code pre[data-chrome="none"]::before,
.slide--code pre[data-chrome="none"]::after { display: none; }

/* syntax tokens. Derived from the theme's own accents so any brand gets a
   coherent scheme for free; a theme can override any --code-* it dislikes. */
.slide--code pre .tok--com  { color: var(--code-com); font-style: italic; }
.slide--code pre .tok--str  { color: var(--code-str); }
.slide--code pre .tok--key  { color: var(--code-key); }
.slide--code pre .tok--num  { color: var(--code-num); }
.slide--code pre .tok--lit  { color: var(--code-lit); }
.slide--code pre .tok--kw   { color: var(--code-kw); font-weight: 500; }
.slide--code pre .tok--fn   { color: var(--code-fn); }
.slide--code pre .tok--tag  { color: var(--code-kw); font-weight: 500; }
.slide--code pre .tok--attr { color: var(--code-attr); }
.slide--code pre .tok--punc { color: var(--code-punc); }

/* --- end card --- */
.slide--end { justify-content: center; align-items: center; text-align: center; gap: 26px; }
.slide--end .display { font-size: 96px; }
.slide--end .lead { max-width: 34ch; }

/* --- placeholder: a slide left for a later Claude pass --- */
.slide--placeholder {
  justify-content: center; align-items: center; text-align: center;
  border: 2px dashed var(--faint);
  border-radius: var(--slide-radius);
}
.slide--placeholder .slide__wash { display: none; }
/* data-clean (the "Blank canvas" option): a placeholder used as a surface to
   stack stickers on — prompt and dashed frame get out of the way */
.slide--placeholder[data-clean] { border-color: transparent; }
.slide--placeholder[data-clean] .slide__placeholder-note { display: none; }
/* ...and on a split slide it blanks the image half: the picture (or its empty
   slot) is hidden but kept in the file, so the option can be switched back off.
   The column stays, so the text doesn't jump. */
.slide--split[data-clean] .split__media { visibility: hidden; pointer-events: none; }
.slide__placeholder-note {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  max-width: 32ch;
  font: 500 22px/1.5 var(--font-display);
  color: var(--muted);
}
.slide__placeholder-note .icon { width: 22px; height: 22px; opacity: .6; }
.slide__placeholder-note.has-note .icon { display: none; }
body.is-editing .slide__placeholder-note {
  cursor: pointer; padding: 18px 22px; border-radius: 14px;
  transition: background-color .15s, outline-color .15s;
  outline: 2px solid transparent; outline-offset: 6px;
}
body.is-editing .slide__placeholder-note:hover {
  background: rgba(124, 92, 255, .08); outline-color: rgba(124, 92, 255, .4);
}
body.is-editing .slide__placeholder-note:not(.has-note) { color: #cdbfff; }

/* --- deck logo (data-logo), replaces the text label in the footer --- */
.slide__logo { display: block; height: 18px; width: auto; object-fit: contain; }

/* =========================================================================
   Runtime chrome (never serialized — all marked [data-runtime])
   ========================================================================= */
.icon { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; flex: none; }

/* --- toolbar: fades after 1s of stillness (body.is-idle, set in JS);
   hovering the bottom edge of the window brings it back and pins it.
   Edit mode pins it unconditionally. --- */
.toolbar {
  --tb-h: 36px;   /* button height; icon-only and collapsed buttons are square on it */
  position: fixed; z-index: 45;
  bottom: 22px; left: 50%; transform: translateX(-50%);
  display: flex; align-items: center; gap: 2px;
  padding: 6px;
  background: rgba(16, 16, 20, .84);
  backdrop-filter: blur(18px);
  border: 1px solid rgba(255, 255, 255, .1);
  border-radius: 14px;
  box-shadow: 0 16px 48px -8px rgba(0, 0, 0, .6);
  white-space: nowrap;
  transition: opacity .25s var(--ease), transform .25s var(--ease);
  /* safety net at every width: the pill would rather scroll its own
     contents than push past the viewport edge */
  max-width: calc(100vw - 16px);
  overflow-x: auto;
}
body.is-idle:not(.is-editing) .toolbar {
  opacity: 0; pointer-events: none;
  transform: translateX(-50%) translateY(8px);
}
html:fullscreen body.is-idle { cursor: none; }

.tb-btn {
  display: inline-flex; align-items: center; gap: 8px;
  height: var(--tb-h); padding: 0 12px; border: 0; border-radius: 9px;
  background: none; color: rgba(246, 245, 243, .84);
  font: 500 13px/1 var(--font-body); cursor: pointer;
  transition: background .15s, color .15s;
}
.tb-btn.icon-only { width: var(--tb-h); padding: 0; justify-content: center; }
.tb-btn:hover { background: rgba(255, 255, 255, .08); color: #fff; }
.tb-btn.is-on { background: rgba(255, 255, 255, .1); color: #fff; }
.tb-btn.is-disabled { opacity: .35; pointer-events: none; }
.tb-btn.primary { background: var(--accent); color: var(--accent-fg); }
.tb-btn.primary .icon { stroke: var(--accent-fg); }
/* unsaved changes: a dot on Download copy, never a colour change. --tb-unsaved recolours it */
.tb-btn.has-unsaved { position: relative; }
.tb-btn.has-unsaved::after {
  content: ""; position: absolute; top: 5px; right: 5px;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--tb-unsaved, var(--accent));
}
.tb-count {
  font: 500 13px/1 var(--font-mono); font-variant-numeric: tabular-nums;
  min-width: 66px; text-align: center; color: rgba(246, 245, 243, .7);
}
.tb-sep { width: 1px; height: 20px; background: rgba(255, 255, 255, .12); margin: 0 5px; }
.tb-tag { font: 500 12px/1 var(--font-body, system-ui); color: rgba(255, 255, 255, .55); padding: 0 10px; white-space: nowrap; }

/* --- narrow viewports: the edit-mode bar is the widest (restart, prev,
   counter, next, sep, add slide, dup, del, sep, options, request change,
   speaker notes, sep, undo, download, sep, done — ~1010px of buttons+gaps+
   padding at 13px/500 body type). Icon-collapse labelled buttons first;
   tooltips (data-tip) still name every button, so nothing is lost. --- */
@media (max-width: 1080px) {
  .tb-btn:not(.icon-only) > span { display: none; }
  .tb-btn:not(.icon-only) { width: var(--tb-h); padding: 0; justify-content: center; }
  .tb-count { min-width: 52px; }
}
@media (max-width: 480px) {
  .toolbar { padding: 4px; gap: 0; }
  .tb-sep { margin: 0 2px; }
  .tb-count { min-width: 40px; }
}

/* --- tooltips: replace native title="" on every bar button --- */
.tip {
  position: fixed; z-index: 70;
  display: flex; gap: 10px; align-items: center;
  padding: 7px 10px; border-radius: 8px;
  background: #f6f5f3; color: #0b0b0f;
  font: 500 12px/1 var(--font-body);
  width: max-content; max-width: 260px; line-height: 1.4;   /* long hints wrap */
  box-shadow: 0 8px 24px rgba(0, 0, 0, .4);
  pointer-events: none;
}
.tip[data-below]::after { display: none; }
.tip kbd { font: 500 11px/1 var(--font-mono); padding: 3px 6px; border-radius: 5px; background: rgba(11, 11, 15, .1); }
.tip::after {
  content: ""; position: absolute; left: calc(50% + var(--ax, 0px)); bottom: -5px;
  width: 10px; height: 10px; background: #f6f5f3;
  transform: translateX(-50%) rotate(45deg);
}

/* --- shortcuts sheet (?) --- */
.sheet {
  position: fixed; inset: 0; z-index: 65;
  background: rgba(8, 8, 11, .7); backdrop-filter: blur(6px);
  display: flex; align-items: center; justify-content: center;
}
.sheet__card {
  width: min(560px, 90vw); max-height: 80vh; overflow: auto;
  background: #15151b; border: 1px solid rgba(255, 255, 255, .12);
  border-radius: 16px; padding: 24px 26px; color: #f6f5f3;
  box-shadow: 0 30px 80px -20px rgba(0, 0, 0, .7);
}
.sheet__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.sheet__head h3 { margin: 0; font: 600 18px/1.2 var(--font-display); letter-spacing: -.01em; }
.icon-btn {
  width: 32px; height: 32px; display: grid; place-items: center;
  border: 0; border-radius: 8px; background: rgba(255, 255, 255, .06);
  color: rgba(246, 245, 243, .7); cursor: pointer;
}
.icon-btn:hover { background: rgba(255, 255, 255, .12); color: #fff; }
.sheet__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 24px; }
.sheet__row { display: flex; align-items: center; gap: 12px; padding: 6px 0; font-size: 13.5px; color: rgba(246, 245, 243, .85); }
.sheet__row kbd {
  min-width: 60px; text-align: center; font: 500 12px/1 var(--font-mono);
  padding: 4px 8px; border-radius: 6px; background: rgba(255, 255, 255, .08); color: rgba(246, 245, 243, .9);
}

/* --- toast: top-centre, optional action button + key hint --- */
.toast {
  position: fixed; top: 22px; left: 50%; transform: translateX(-50%) translateY(-10px);
  z-index: 60; pointer-events: none;
  display: flex; align-items: center; gap: 14px;
  padding: 10px 10px 10px 16px; border-radius: 12px;
  background: rgba(16, 16, 20, .9);
  border: 1px solid rgba(255, 255, 255, .12);
  backdrop-filter: blur(14px);
  color: #f6f5f3; font: 500 13px/1.4 var(--font-body);
  max-width: min(460px, calc(100vw - 24px));
  opacity: 0; transition: opacity .25s, transform .25s var(--ease);
}
.toast.is-shown { opacity: 1; transform: translateX(-50%); pointer-events: auto; }
.toast__action {
  height: 28px; padding: 0 10px; border-radius: 7px; border: 0;
  background: rgba(255, 255, 255, .1); color: #fff;
  font: 600 12px/1 var(--font-body); cursor: pointer; flex: none;
}
.toast kbd { font: 500 11px/1 var(--font-mono); color: rgba(246, 245, 243, .5); flex: none; }

/* --- popovers: request-change, add-slide picker --- */
.pop-layer { position: fixed; inset: 0; z-index: 55; pointer-events: none; }
.pop-layer .pop { pointer-events: auto; }
.pop {
  position: fixed; transform: translateX(-50%);
  background: #15151b; border: 1px solid rgba(255, 255, 255, .12);
  border-radius: 14px; box-shadow: 0 24px 60px -10px rgba(0, 0, 0, .7);
  color: #f6f5f3; padding: 18px 18px 16px; width: min(420px, calc(100vw - 24px));
}
.pop h4 { margin: 0; font: 600 15px/1.2 var(--font-display); letter-spacing: -.01em; }
.pop__sub { color: rgba(246, 245, 243, .5); font-size: 12.5px; margin: 4px 0 0; }
.pop__ta {
  display: block; width: 100%; min-height: 92px; margin-top: 14px; resize: vertical;
  padding: 12px 14px; border-radius: 10px; border: 1px solid rgba(255, 255, 255, .12);
  background: #0e0e13; color: #f6f5f3; font: 400 14px/1.5 var(--font-body);
}
.pop__row { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-top: 14px; }
/* the options popover is wider than the base .pop so common rows fit on
   one line. Options live in collapsible groups under a pinned heading; the
   list scrolls, so a brand registry can declare any number of options and
   nothing is cropped. Rows wrap as the structural guarantee. */
#opts-pop { width: min(560px, calc(100vw - 32px)); max-height: var(--pop-max, calc(100vh - 110px)); display: flex; flex-direction: column; }
.opt-head { margin-top: 0; }
.opt-head .btn { height: 28px; padding: 0 10px; font-size: 12px; }
.opt-find {
  display: block; width: 100%; margin-top: 12px; height: 32px; padding: 0 12px; box-sizing: border-box;
  border-radius: 8px; border: 1px solid rgba(255, 255, 255, .12); background: #0e0e13; color: #f6f5f3; font: 400 13px/1 var(--font-body);
}
.opt-find::placeholder { color: rgba(246, 245, 243, .4); }
.opt-rows { display: flex; flex-direction: column; gap: 4px; margin-top: 12px; min-width: 340px; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
.opt-group[hidden], .opt-row[hidden], .opt-empty[hidden] { display: none; }
.opt-group + .opt-group { border-top: 1px solid rgba(255, 255, 255, .08); }
.opt-group__head {
  display: flex; align-items: center; gap: 6px; width: 100%; padding: 8px 2px; border: 0; background: none; cursor: pointer;
  color: rgba(246, 245, 243, .55); font: 600 11px/1 var(--font-body); letter-spacing: .08em; text-transform: uppercase; text-align: left;
}
.opt-group__head:hover { color: #fff; }
.opt-group__head .icon { width: 12px; height: 12px; transition: transform .15s; }
.opt-group.is-open > .opt-group__head .icon { transform: rotate(90deg); }
.opt-group__head small { margin-left: auto; font: 500 11px/1 var(--font-body); letter-spacing: 0; text-transform: none; color: var(--accent); }
.opt-group__rows { display: none; flex-direction: column; gap: 12px; padding: 4px 2px 14px; }
.opt-group.is-open > .opt-group__rows { display: flex; }
.opt-row { display: flex; align-items: center; justify-content: space-between; gap: 8px 18px; }
.opt-row__text { display: flex; align-items: center; gap: 7px; min-width: 0; flex: 1 1 180px; }
.opt-row__label { font-size: 13px; color: rgba(246, 245, 243, .88); }
.opt-help {
  flex: none; width: 16px; height: 16px; padding: 0; border: 1px solid rgba(255, 255, 255, .22); border-radius: 50%; cursor: help;
  background: none; color: rgba(246, 245, 243, .5); font: 600 10px/1 var(--font-body);
}
.opt-help:hover, .opt-help:focus-visible { color: #fff; border-color: rgba(255, 255, 255, .6); outline: none; }
.opt-empty { margin: 8px 2px; font-size: 12.5px; color: rgba(246, 245, 243, .5); }
.opt-row__seg { display: flex; flex-wrap: wrap; gap: 3px; background: rgba(255, 255, 255, .06); border-radius: 8px; padding: 3px; }
.opt-select {
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' fill='none' stroke='%23f6f5f3' stroke-opacity='.7' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 10px center;
  height: 30px; padding: 0 28px 0 10px; border: 0; border-radius: 7px; cursor: pointer; max-width: 55%;
  background-color: rgba(255, 255, 255, .08); color: rgba(246, 245, 243, .92); font: 500 12px/1 var(--font-body);
}
.opt-switch {
  position: relative; flex: none; width: 34px; height: 20px; padding: 0; border: 0; border-radius: 10px; cursor: pointer;
  background: rgba(255, 255, 255, .16); transition: background .15s;
}
.opt-switch i { position: absolute; left: 2px; top: 2px; width: 16px; height: 16px; border-radius: 50%; background: #fff; transition: transform .15s; }
.opt-switch.is-on { background: var(--accent); }
.opt-switch.is-on i { transform: translateX(14px); }
.opt {
  border: 0; background: transparent; color: rgba(246, 245, 243, .6);
  font: 500 12px/1 var(--font-body); padding: 6px 10px; border-radius: 6px; cursor: pointer;
}
.opt:hover { color: #fff; }
.opt.is-on { background: rgba(255, 255, 255, .16); color: #fff; }
.pop__actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 16px; }
.pop__row small { color: rgba(246, 245, 243, .45); font-size: 12px; }
.pop::after {
  content: ""; position: absolute; left: 50%; bottom: -7px; width: 13px; height: 13px;
  background: #15151b; border-right: 1px solid rgba(255, 255, 255, .12); border-bottom: 1px solid rgba(255, 255, 255, .12);
  transform: translateX(-50%) rotate(45deg);
}
.pop--menu { padding: 6px; width: auto; min-width: 230px; }
.menu-item {
  display: flex; align-items: center; gap: 10px; width: 100%; height: 36px; padding: 0 10px; border: 0; border-radius: 8px;
  background: none; color: #f6f5f3; font: 500 13px/1 var(--font-body); text-align: left; cursor: pointer;
}
.menu-item:hover, .menu-item:focus-visible { background: rgba(255, 255, 255, .08); outline: none; }
.menu-item .icon { width: 16px; height: 16px; opacity: .7; }
.menu-item kbd { margin-left: auto; opacity: .45; font: 500 11px/1 var(--font-body); }
.btn {
  height: 34px; padding: 0 14px; border-radius: 9px; border: 1px solid rgba(255, 255, 255, .14);
  background: none; color: #f6f5f3; font: 500 13px/1 var(--font-body); cursor: pointer;
}
.btn--primary { background: var(--accent); border-color: var(--accent); color: var(--accent-fg); font-weight: 600; }
.btn--quiet { border-color: transparent; color: rgba(246, 245, 243, .6); }
.btn--ghost { border-color: rgba(124, 92, 255, .6); color: #cdbfff; }

/* the picker grows upward from the toolbar, so it is the one popover that
   can outgrow the viewport. positionPopover sets --pop-max to the room
   above the toolbar; the heading and the describe row stay pinned while
   only the grid scrolls, so a brand fork can register any number of
   layouts and nothing is ever cropped. Six columns keep the common case
   (~20 layouts) at four rows, which fits a 13" laptop without scrolling. */
.picker {
  width: min(720px, calc(100vw - 32px)); padding: 20px;
  max-height: var(--pop-max, calc(100vh - 110px));
  display: flex; flex-direction: column;
}
.picker__grid {
  display: grid; grid-template-columns: repeat(6, 1fr); gap: 8px; margin-top: 16px;
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
}
@media (max-width: 640px) { .picker__grid { grid-template-columns: repeat(4, 1fr); } }
.tile {
  appearance: none; text-align: left; cursor: pointer;
  border: 1px solid rgba(255, 255, 255, .1); border-radius: 9px; padding: 6px 6px 7px; background: #0e0e13;
}
.tile:hover { border-color: var(--accent); background: color-mix(in oklab, var(--accent) 8%, transparent); }
.tile .wf {
  --tile-card: #22222b;
  aspect-ratio: 16 / 9; border-radius: 4px; background: #17171e;
  position: relative; overflow: hidden; margin-bottom: 7px;
}
.tile .wf i { position: absolute; background: rgba(246, 245, 243, .55); border-radius: 2px; }
.tile .wf i.a { background: var(--accent); }
.tile .wf i.img { background: linear-gradient(135deg, rgba(124, 92, 255, .7), rgba(255, 91, 46, .7)); }
.tile span { display: block; font: 500 12px/1 var(--font-body); color: rgba(246, 245, 243, .85); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.describe {
  margin-top: 14px; display: flex; align-items: center; gap: 12px; padding: 12px 14px;
  border-radius: 10px; border: 1px dashed rgba(124, 92, 255, .6); background: rgba(124, 92, 255, .08);
}
.describe .icon { stroke: #cdbfff; }
.describe__input { flex: 1; background: none; border: 0; color: #f6f5f3; font-size: 14px; outline: none; }
.describe__input::placeholder { color: rgba(246, 245, 243, .45); }

/* --- short viewports (13" laptops with browser chrome are ~790px tall):
   the whole chrome comes down a notch — toolbar hugs the edge, buttons and
   popover padding tighten — so the bar plus an open picker leave the slide
   visible. Widths are untouched; the toolbar's icon-collapse handles those. --- */
@media (max-height: 860px) {
  .toolbar { --tb-h: 32px; bottom: 12px; padding: 5px; }
  .tb-btn { font-size: 12.5px; }
  .tb-count { font-size: 12.5px; }
  .pop { padding: 14px 14px 12px; }
  .pop h4 { font-size: 14px; }
  .pop__sub { font-size: 12px; }
  .picker { padding: 16px; }
  .picker__grid { margin-top: 12px; gap: 7px; }
  .tile { padding: 5px 5px 6px; }
  .tile .wf { margin-bottom: 6px; }
  .describe { margin-top: 12px; padding: 9px 12px; }
  .btn { height: 30px; }
  .drawer { bottom: 64px; }
}

/* --- speaker notes drawer --- */
.drawer {
  position: fixed; right: 0; top: 0; bottom: 84px; z-index: 46; width: min(340px, 100vw);
  background: #101015; border-left: 1px solid rgba(255, 255, 255, .1);
  border-radius: 0 0 0 14px;
  padding: 24px; display: flex; flex-direction: column; gap: 12px;
  box-shadow: -20px 0 60px -20px rgba(0, 0, 0, .5);
}
.drawer h4 { margin: 0; font: 600 15px/1.2 var(--font-display); display: flex; justify-content: space-between; align-items: center; color: #f6f5f3; }
.drawer h4 small { font: 500 11px/1 var(--font-mono); letter-spacing: .06em; color: rgba(246, 245, 243, .45); }
.drawer__body {
  flex: 1; overflow: auto; outline: none; white-space: pre-wrap;
  font: 400 14.5px/1.55 var(--font-body); color: rgba(246, 245, 243, .85);
}
.drawer__body:empty::before { content: "Speaker notes…"; color: rgba(246, 245, 243, .3); }
.drawer__hint { font-size: 12px; color: rgba(246, 245, 243, .4); }
body.has-notes-drawer .stage { padding-right: var(--notes-gutter, 340px); } /* the deck offset lives in .deck left */

/* --- overview / table of contents --- */
.overview {
  position: fixed; inset: 0; z-index: 50;
  /* opaque, no backdrop blur: re-blurring the deck on every scroll made
     keyboard navigation feel laggy */
  background: #08080b;
  overflow: auto; padding: 60px 56px 90px;
  display: none;
}
body.is-overview { overflow: auto; }
body.is-overview .overview { display: block; }
.overview__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 20px; margin-bottom: 32px;
}
.overview__head h2 { font: 600 22px/1 var(--font-display); letter-spacing: -.02em; margin: 0; color: #fff; }
.overview__head span { font: 500 12px/1 var(--font-mono); letter-spacing: .1em; color: rgba(255,255,255,.4); }
.overview__head .ov-hint { margin-left: auto; text-transform: none; letter-spacing: .04em; }
.overview__head .ov-hint kbd {
  font: inherit; color: rgba(255,255,255,.7);
  border: 1px solid rgba(255,255,255,.2); border-radius: 4px; padding: 2px 5px;
}
.overview__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 26px; }
.thumb { cursor: pointer; }
.thumb__frame {
  position: relative; width: 100%; aspect-ratio: 16 / 9;
  overflow: hidden; border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, .14);
  contain: content;                       /* thumbs never paint outside their frame */
  transition: border-color .2s, transform .2s var(--ease);
}
.thumb:hover .thumb__frame { border-color: var(--accent); transform: translateY(-3px); }
.thumb.is-current .thumb__frame { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); }
/* keyboard cursor — ring only, no transitioned properties, so it lands the
   same frame the key is pressed (the hover lift animates; this must not) */
.thumb.is-selected .thumb__frame {
  transition: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent), 0 14px 34px -14px rgba(0, 0, 0, .7);
}
.thumb__check { display: none; }
.thumb__scaler { pointer-events: none; }
body.is-editing .thumb { cursor: grab; }
.thumb { position: relative; }
.thumb.is-dragging { opacity: .35; }
.thumb.is-drop-before::before, .thumb.is-drop-after::before {
  content: ""; position: absolute; top: -6px; bottom: 26px; width: 4px; border-radius: 2px;
  background: var(--accent); z-index: 4; pointer-events: none;
}
.thumb.is-drop-before::before { left: -15px; }
.thumb.is-drop-after::before { right: -15px; }
.ov-btn {
  font: 500 12px/1 var(--font-body, system-ui); letter-spacing: .02em; color: rgba(255,255,255,.85);
  background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.18);
  border-radius: 8px; padding: 9px 13px; cursor: pointer; white-space: nowrap;
}
.ov-btn:hover { background: rgba(255,255,255,.14); color: #fff; }
.ov-btn.is-on, .ov-btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.ov-btn.is-disabled { opacity: .35; pointer-events: none; }
.ov-bar {
  position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%);
  display: none; align-items: center; gap: 10px; padding: 10px 12px 10px 18px;
  background: #17171d; border: 1px solid rgba(255,255,255,.16); border-radius: 12px;
  box-shadow: 0 18px 40px -12px rgba(0,0,0,.7); z-index: 2;
}
.ov-bar span { font: 500 12px/1 var(--font-mono); letter-spacing: .06em; color: rgba(255,255,255,.6); margin-right: 8px; white-space: nowrap; }
body.is-picking .ov-bar { display: flex; }
body.is-picking .overview { padding-bottom: 120px; }
body.is-picking .thumb__frame { opacity: .55; }
body.is-picking .thumb.is-picked .thumb__frame { opacity: 1; border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); }
body.is-picking .thumb__check {
  display: block; position: absolute; top: 10px; left: 10px; width: 22px; height: 22px;
  border-radius: 6px; border: 2px solid rgba(255,255,255,.7); background: rgba(0,0,0,.45);
  z-index: 3; box-sizing: border-box;
}
body.is-picking .thumb.is-picked .thumb__check {
  background: var(--accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E") center / 14px no-repeat;
  border-color: var(--accent);
}
.thumb__scaler {
  position: absolute; top: 0; left: 0;
  width: var(--slide-w); height: var(--slide-h);
  transform-origin: top left;
}
.thumb__scaler .slide {
  opacity: 1 !important; visibility: visible !important;
  transform: none !important; transition: none !important;
  border-radius: 0;
}
.thumb__scaler .slide { clip-path: none !important; animation: none !important; }
.thumb__scaler .slide * { animation: none !important; }
/* thumbnails (overview + presenter) are their own scale context: ignore the
   live slide's data-fit zoom and overflow badge, both already baked into scale */
.thumb__scaler .slide[data-fit] > *,
.presenter .slide[data-fit] > * { zoom: 1 !important; }
.thumb__scaler .slide__overflow-badge,
.presenter .slide__overflow-badge,
.thumb__scaler .media-hover,
.presenter .media-hover,
.thumb__scaler .handle,
.presenter .handle { display: none !important; }
.thumb__label {
  margin-top: 10px; display: flex; gap: 10px; align-items: baseline;
  font: 500 12px/1.3 var(--font-mono); letter-spacing: .04em;
  color: rgba(255, 255, 255, .5);
}
.thumb__label b { color: rgba(255, 255, 255, .85); font-weight: 500; }

/* --- edit mode: editable affordances --- */
body.is-editing .deck { box-shadow: 0 0 0 2px var(--edit-ring, var(--accent)), var(--deck-shadow, 0 40px 120px -30px rgba(0,0,0,.7)); }
[contenteditable="true"], [contenteditable="plaintext-only"] {
  outline: 1px dashed transparent;
  outline-offset: 6px;
  border-radius: 4px;
  transition: outline-color .15s, background .15s;
}
body.is-editing [contenteditable]:hover { outline-color: var(--faint); cursor: text; }
body.is-editing [contenteditable]:focus {
  position: relative; z-index: 3;
  outline: 2px solid var(--edit-ring, var(--accent)); outline-offset: 6px;
  background: color-mix(in oklab, var(--edit-ring, var(--accent)) 8%, transparent);
}

/* pasted / dropped images, freely positioned, with corner resize handles */
.sticker {
  position: absolute;
  z-index: 4;
  border-radius: 8px;
  box-shadow: 0 20px 50px -18px rgba(0, 0, 0, .55);
  user-select: none;
}
.sticker img { display: block; width: 100%; height: auto; border-radius: inherit; -webkit-user-drag: none; pointer-events: none; }
body.is-editing .sticker { cursor: grab; }
body.is-editing .sticker:active { cursor: grabbing; }
body.is-editing .sticker.is-sel { outline: 2px solid var(--edit-ring, var(--accent)); outline-offset: 3px; }
/* behind text: below the slide's in-flow content, above its wash (which is
   first in the DOM at the same z-index). Selected or not, it stays behind —
   what the editor shows is what the slide is. */
.sticker[data-layer="back"] { z-index: -1; }

/* drag guides: snap lines for margins (--pad) and centre, drawn only while dragging */
.guide { position: absolute; z-index: 6; pointer-events: none; background: #ff3d8b; opacity: .9; }
.guide[data-axis="x"] { top: 0; bottom: 0; width: 1px; margin-left: -.5px; }
.guide[data-axis="y"] { left: 0; right: 0; height: 1px; margin-top: -.5px; }
.guide.is-centre { background: var(--accent); }
.guide.is-object { background: #35c8ff; }   /* aligned with another object on the slide, not the slide itself */
@media print { .guide { display: none; } }
.handle {
  position: absolute; width: 12px; height: 12px; z-index: 5;
  background: #fff; border: 1.5px solid #0b0b0f; border-radius: 3px;
  display: none;
}
body.is-editing .sticker.is-sel .handle { display: block; }
.handle[data-c="nw"] { left: -6px; top: -6px; cursor: nwse-resize; }
.handle[data-c="ne"] { right: -6px; top: -6px; cursor: nesw-resize; }
.handle[data-c="sw"] { left: -6px; bottom: -6px; cursor: nesw-resize; }
.handle[data-c="se"] { right: -6px; bottom: -6px; cursor: nwse-resize; }

/* text box: a sticker whose body is a <p> wearing one of the deck's own type
   classes (display, h1…eyebrow). It carries no font, size or colour of its own. */
.sticker--text {
  box-shadow: none; border-radius: 4px; padding: 2px 6px;
  user-select: text;
}
.sticker--text > p { outline: none; overflow-wrap: anywhere; min-height: 1.3em; max-width: none; }
.sticker--text[data-align="center"] { text-align: center; }
.sticker--text[data-align="right"]  { text-align: right; }
body.is-editing .sticker--text { cursor: text; }
body.is-editing .sticker--text > p:empty::before { content: "Type something"; opacity: .4; }
.handle--grip { top: -30px; left: 50%; margin-left: -24px; width: 48px; height: 18px; border-radius: 9px; cursor: grab; }
.handle--grip:active { cursor: grabbing; }
.handle--rot {
  left: 50%; bottom: -34px; margin-left: -8px; width: 16px; height: 16px; border-radius: 50%;
  background: var(--accent); border-color: #fff; cursor: grab;
}
.handle--rot::before {              /* stem back to the box */
  content: ""; position: absolute; left: 50%; bottom: 100%; width: 1.5px; height: 18px;
  margin-left: -.75px; background: var(--accent);
}
.handle--rot:active { cursor: grabbing; }
.tbx-bar select {
  height: 30px; padding: 0 8px; border: 0; border-radius: 7px; cursor: pointer;
  background: rgba(255,255,255,.08); color: rgba(246,245,243,.92); font: 600 12px/1 var(--font-body);
}
.tbx-bar {
  position: fixed; z-index: 60; display: none; align-items: center; gap: 2px; padding: 4px;
  background: #17171d; border: 1px solid rgba(255,255,255,.16); border-radius: 10px;
  box-shadow: 0 14px 34px -12px rgba(0,0,0,.7);
}
.tbx-bar.is-shown { display: flex; }
.tbx-bar[data-kind="image"] [data-for="text"], .tbx-bar[data-kind="text"] [data-for="image"] { display: none; }
.sticker[data-shadow="off"] { box-shadow: none; }
.tbx-bar button {
  min-width: 30px; height: 30px; padding: 0 6px; border: 0; border-radius: 7px; cursor: pointer;
  display: inline-grid; place-items: center;
  background: none; color: rgba(246,245,243,.84); font: 600 12px/1 var(--font-body);
}
.tbx-bar button:hover { background: rgba(255,255,255,.1); color: #fff; }
.tbx-bar button.is-on { background: rgba(255,255,255,.16); color: #fff; }
.tbx-bar svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.tbx-bar i { width: 1px; height: 18px; margin: 0 4px; background: rgba(255,255,255,.16); }
body.is-presenter .tbx-bar, body.is-overview .tbx-bar { display: none !important; }
@media print { .tbx-bar { display: none !important; } }

/* --- badges: request-change chip + overflow warning, edit-mode only --- */
.badge {
  position: absolute; top: 28px; right: 28px; z-index: 5;
  display: none; align-items: center; gap: 8px; height: 28px;
  padding: 0 12px 0 10px; border-radius: 8px;
  font: 500 12px/1 var(--font-body);
}
body.is-editing .badge.req, body.is-overview .badge.req { display: flex; }
.badge.req {
  background: rgba(124, 92, 255, .16); border: 1px solid rgba(124, 92, 255, .5);
  color: #cdbfff; max-width: 420px;
}
.badge.req span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.slide__overflow-badge {
  position: absolute; top: 28px; right: 28px; z-index: 6;
  display: none; align-items: center; gap: 8px; height: 28px;
  padding: 0 12px 0 10px; border-radius: 8px;
  background: color-mix(in oklab, var(--accent) 16%, #1a1a1a);
  border: 1px solid color-mix(in oklab, var(--accent) 50%, transparent);
  color: var(--accent);
  font: 500 12px/1 var(--font-body);
}
body.is-editing .slide[data-overflow] .slide__overflow-badge { display: flex; }
.slide[data-note][data-overflow] .slide__overflow-badge { top: 66px; }

/* =========================================================================
   Overflow guard — data-fit / data-overflow are derived, stripped on save
   ========================================================================= */
.slide[data-fit="0.95"] > :not(.slide__wash):not(.slide__chrome):not(.sticker):not(.guide) { zoom: .95; }
.slide[data-fit="0.9"]  > :not(.slide__wash):not(.slide__chrome):not(.sticker):not(.guide) { zoom: .9; }
.slide[data-fit="0.85"] > :not(.slide__wash):not(.slide__chrome):not(.sticker):not(.guide) { zoom: .85; }
.slide[data-fit="0.8"]  > :not(.slide__wash):not(.slide__chrome):not(.sticker):not(.guide) { zoom: .8; }

/* =========================================================================
   Speaker notes — hidden by default; only shown in print with ?notes.
   Edited through the right-hand drawer (#notes-drawer) in edit mode.
   ========================================================================= */
.notes { display: none; }

/* =========================================================================
   Presenter view — a second window opened with ?presenter
   ========================================================================= */
body.is-presenter .stage, body.is-presenter .toolbar, body.is-presenter .toast,
body.is-presenter .overview, body.is-presenter .drawer, body.is-presenter .pop-layer,
body.is-presenter .sheet, body.is-presenter .tip { display: none !important; }
body.is-presenter { background: #000; }

body.is-black::after { content: ""; position: fixed; inset: 0; background: #000; z-index: 999; }

.presenter {
  position: fixed; inset: 0; z-index: 5;
  display: grid; grid-template-columns: 1fr 360px; grid-template-rows: 1fr auto; gap: 24px;
  padding: 28px;
  /* always white on black, whatever theme the deck is in — the presenter is
     backstage chrome, not part of the deck's look, and must stay readable */
  background: #000; color: #fff; color-scheme: dark;
}
.presenter::selection, .presenter *::selection { background: rgba(255, 255, 255, .25); }
.presenter__alert {
  position: fixed; top: 28px; left: 50%; transform: translateX(-50%);
  z-index: 6; max-width: 640px; padding: 12px 18px; border-radius: 10px;
  background: rgba(200, 82, 58, .18); border: 1px solid rgba(200, 82, 58, .5);
  color: #f6f5f3; font: 400 14px/1.5 var(--font-body);
}
.presenter__alert code { font: 500 12px/1 var(--font-mono); background: rgba(255, 255, 255, .1); padding: 2px 5px; border-radius: 4px; }
.presenter__main { display: flex; align-items: center; min-width: 0; }
.presenter__frame { width: 100%; border-radius: 10px; cursor: pointer; }
.presenter__side { display: flex; flex-direction: column; gap: 18px; min-width: 0; }
.presenter__block--grow { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.presenter__frame--sm { width: 100%; opacity: .9; cursor: pointer; }
.presenter__lbl {
  font: 500 11px/1 var(--font-mono); letter-spacing: .1em; text-transform: uppercase;
  color: rgba(255, 255, 255, .4); margin-bottom: 8px;
}
.presenter__notes {
  flex: 1; overflow: auto;
  font: 400 18px/1.55 var(--font-body); color: rgba(246, 245, 243, .88);
}
.presenter__no-notes { color: rgba(255, 255, 255, .4); font-style: italic; margin: 0; }
.presenter__foot {
  grid-column: 1 / -1;
  display: flex; align-items: center; gap: 28px;
  font: 500 14px/1 var(--font-body); color: rgba(246, 245, 243, .6);
}
.presenter__foot b { font: 600 34px/1 var(--font-display); letter-spacing: -.02em; color: #f6f5f3; font-variant-numeric: tabular-nums; }
.presenter__foot .presenter__count { font-size: 22px; }
.presenter__spacer { flex: 1; }
.presenter__restart { display: flex; align-items: center; gap: 8px; }
.presenter__restart button { font: 500 12px/1 var(--font-body); padding: 6px 10px; border-radius: 6px; border: 1px solid rgba(255, 255, 255, .16); background: transparent; color: rgba(246, 245, 243, .75); cursor: pointer; }
.presenter__restart button:hover, .presenter__restart button:focus-visible { background: rgba(255, 255, 255, .1); color: #f6f5f3; }
.presenter__restart [data-yes] { background: var(--accent, #ff5a1f); border-color: transparent; color: #fff; }
.presenter__confirm { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
.presenter__confirm[hidden], #pv-restart-btn[hidden] { display: none; }
.presenter__keys { display: flex; align-items: center; gap: 8px; }
.presenter__keys kbd { font: 500 12px/1 var(--font-mono); padding: 4px 7px; border-radius: 5px; background: rgba(255, 255, 255, .08); color: rgba(246, 245, 243, .7); }

/* =========================================================================
   Print → PDF.  Chrome: Print, margins None, Background graphics ON.
   ========================================================================= */
@media print {
  @page { size: 1280px 720px; margin: 0; }   /* exact: 13.333in floors 100vw to 1279px, leaving a 1px white edge */
  html, body { overflow: visible; background: #fff; height: auto; }
  /* data-runtime="print" opts a chrome element into the PDF (it is still
     stripped from saves like any other [data-runtime] node) */
  [data-runtime]:not([data-runtime="print"]) { display: none !important; }
  .stage { position: static; display: block; padding: 0 !important; }
  .deck {
    position: static; left: auto; top: auto;      /* the screen layout centres it absolutely; print must flow */
    width: auto; height: auto; transform: none;
    border-radius: 0; box-shadow: none;
  }
  .slide {
    position: relative; inset: auto;
    width: var(--slide-w); height: var(--slide-h);
    opacity: 1 !important; visibility: visible !important;
    transform: none !important; clip-path: none !important; border-radius: 0;
    break-after: page; page-break-after: always;
    break-inside: avoid; page-break-inside: avoid;
  }
  /* transition state lives on slide CHILDREN now — printing inside the 450ms
     window must not emit a page with its content translated/faded away */
  .slide.is-entering > *, .slide.is-leaving > * { opacity: 1 !important; transform: none !important; }
  .slide:last-child { break-after: auto; page-break-after: auto; }
  /* Chrome honours the @page size above (page box = 1280px wide → calc() is exactly 1, slide prints 1:1). Safari ignores
     @page size and prints on whatever paper the dialog says, so scale the slide to the page width.
     The calc() form is exact wherever unit division is supported; the two fixed values are the
     fallback for older engines (portrait paper ≈ 800px, landscape ≈ 1100px). */
  .slide { zoom: 0.6; }
  @media (orientation: landscape) { .slide { zoom: 0.8; } }
  .slide { zoom: calc(100vw / 1280px); }
  .badge, .slide__overflow-badge, .media-hover, .handle, .slide__placeholder-note { display: none !important; }
  .media[data-grain]::after { display: none !important; }   /* grain is screen-only — printed, it rasterizes huge */
  .slide { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  [data-step] { opacity: 1 !important; transform: none !important; }
  * {
    animation: none !important; transition: none !important;
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }

  /* Vector shadings, not bitmaps: an OPAQUE gradient exports from Chrome's
     print path as a native PDF shading; a translucent one gets rasterized
     into a full-page bitmap + soft mask. The screen wash is two translucent
     radial layers — cheap on screen, expensive in print (4 bitmaps/slide).
     Rebuild it here as opaque layers that fade to the slide's own base color
     instead of to transparent — same silhouette, since compositing a
     translucent stop over an opaque base is visually identical to baking
     that base into the stop (premultiplied sRGB). Each layer is confined via
     background-size/position to a non-overlapping band so the two opaque
     radials never stack on top of each other. */
  /* Themeable via --wash-print, same contract as --wash on screen (base
     rule, ~line 235): a theme sets the custom property, not a selector
     override, so it never has to fight this rule's specificity the way a
     downstream fork previously had to (carrying its own print-flatten copy
     of this block just to swap the wash). Fallback is the stock flattened
     wash, unchanged from before this was tokenized — folded into ONE
     shorthand value per layer (gradient, position, size, repeat together)
     so a theme's override doesn't also have to restate the separate
     background-repeat/size/position declarations this rule used to carry. */
  .slide__wash {
    /* must mirror .slide's own `background: var(--slide-bg, var(--bg))`
       (line ~97) fallback-for-fallback, or the wash's bands won't fade to
       the color they're actually sitting on */
    --wash-base: var(--slide-bg, var(--bg));
    background: var(--wash-print,
      radial-gradient(90% 175% at 88% -30%, color-mix(in srgb, var(--accent) 22%, var(--wash-base)), var(--wash-base) 62%) top / 100% 40% no-repeat,
      radial-gradient(70% 200% at -8% 126.67%, color-mix(in srgb, var(--accent-2) 18%, var(--wash-base)), var(--wash-base) 60%) bottom / 100% 30% no-repeat);
    background-color: var(--wash-base);
    opacity: 1;
  }
  /* .slide--section overrides its OWN background to fall back to --accent
     instead of --bg (line ~522) — the wash's base has to follow that same
     override or its bands fade to the wrong color and the whole slide
     visibly shifts hue. (Caught by pixel-diffing this exact slide type.) */
  .slide--section .slide__wash {
    --wash-base: var(--slide-bg, var(--accent));
  }
  /* .slide--section's `opacity: .25` (base rule, ~line 520) is a uniform
     element-level alpha over an already-opaque gradient — measured: this
     stays a vector group (a constant /ca alpha wrapping a shading), it does
     NOT get rasterized the way a spatially-varying transparent-to-color
     gradient stop does. No print override needed here. (.slide--placeholder
     .slide__wash { display: none } already applies here — base rule, line
     ~939 — no print override needed for that either.) */

  /* pin ring: the blurred half of its box-shadow forces a transparency group
     per pin in print. Preview.app then draws that group's bounding box as an
     opaque square. Two crisp (non-blurred) rings read the same at slide size
     and export as vector fills — no bitmap, no square. */
  .media .pin {
    box-shadow:
      0 0 0 4px color-mix(in oklab, var(--accent) 30%, transparent),
      0 0 0 5px rgba(8, 8, 8, 0.45);
  }

  /* Audited, no override needed — each is a translucent layer with no
     spatial variation (a uniform fill, or a crisp non-blurred box-shadow
     ring, same shape as the pin ring above), which composites as a
     constant-alpha vector fill rather than a rasterized gradient. Measured
     directly against this file's own PDF output, with a real raster photo
     substituted for every stock SVG image to rule out the image format
     itself being what kept these vector: zero /PatternType 1 either way.
       - .slide--timeline .step::before ring (base rule ~line 834) — solid
         0-blur box-shadow, same construction as the pin ring.
       - .callout__notes li::before marker (base rule ~line 641) — solid
         fill + solid border, no gradient.
       - .slide--callout-full .callout__box (base rule ~line 659) and
         .slide--image .credit (base rule ~line 848) — uniform rgba/color-mix
         fills; their backdrop-filter blur is print-inert (Chrome's print
         path doesn't rasterize it), so no extra compositing gets triggered
         beyond the flat fill either.
     Two more items this task's brief named — a gallery "tile frame/
     treatment" and a split-slide "media frame treatment" — don't exist as
     CSS in this file: .slide--gallery tiles and .slide--split .split__media
     carry no decoration beyond the shared .media rule (border-radius +
     overflow: hidden, both opaque). A "quote mark" treatment likewise isn't
     present — .slide--quote has no pseudo-element or background tied to the
     mark glyph. Full-text search plus the same before/after PDF scan (SVG
     and raster) turned up nothing to flatten for any of the three. */

  /* code panel: the blurred outer shadow (0 24px 60px -34px, base rule
     ~line 881) rasterizes into a full-page bitmap + soft mask per code
     slide — measured, it's the single largest offender in the deck. At
     slide scale that soft shadow reads as nothing under a code panel, so
     drop it outright rather than crisping it into a ring (there's no edge
     here that needs one, unlike the pin). The inset hairline is kept: it's
     confined to the pre's own box (not blurred past its edge) and the pre
     paints an opaque background, so it exports as a vector fill, not a
     mask — measured separately, adding/removing it made no difference to
     the bitmap count. Lower specificity than
     `.slide--code pre[data-chrome="none"]` (line ~911, one extra attribute
     selector), so that rule's `box-shadow: none` still wins there and this
     doesn't resurrect a shadow on the chromeless variant. */
  .slide--code pre {
    box-shadow: inset 0 1px 0 color-mix(in oklab, var(--fg) 6%, transparent);
  }

  /* title bar + terminal ring (base rules ~line 885 and ~900): the title
     bar's own fill is a UNIFORM color-mix(…, transparent) — a flat,
     spatially-constant alpha, which composites as a constant-alpha fill and
     already exports as a vector fill; measured, it contributes nothing to
     the pattern count and needs no print override. The ring is three
     radial-gradients whose stops fade through `transparent` — each one IS
     spatially varying, and each rasterizes into its own tiling pattern
     (confirmed: toggling the ring off alone dropped this slide's pattern
     count from 3 to 0, the title bar and inset hairline both contributing
     zero on their own). This chrome is new this release, so catch it now:
     bake all three gradients against the backdrop the ring actually paints
     over — not the pre's --surface directly, but the title bar's OWN fill
     flattened onto --surface first, since the ring sits on top of the title
     bar, not directly on the pre. Same identity as the wash, applied twice
     in sequence because there are two translucent layers stacked here, each
     with a known opaque backdrop once the one below it is resolved.
     The three gradients can't just swap `transparent` for the opaque
     backdrop in place, though: on screen each ring is drawn by its own
     gradient LAYER that spans the pseudo's full 46×10 box (only the "at Npx
     5px" offset moves where its circle sits within that shared box) — with
     transparent stops that's invisible everywhere but the ring itself, so
     the three layers show through each other. Baked opaque, the topmost
     layer (first-listed — background layers stack first-on-top) would
     paint its now-opaque "elsewhere" over the whole box and blot out the
     other two rings entirely (caught by pixel-diffing this exact slide:
     two of three rings vanished). Each gradient is confined to its own
     18px-wide slot via background-position/size instead — same local
     offset (5px, 5px) in every slot, since 18px spacing between ring
     centers was already uniform — so the three opaque layers tile
     side-by-side rather than stacking on top of one another. */
  .slide--code pre::after {
    --ring-bg: color-mix(in srgb, var(--fg) 4%, var(--surface));
    --ring: color-mix(in srgb, var(--fg) 34%, var(--ring-bg));
    background:
      radial-gradient(circle at 5px 5px, var(--ring-bg) 0 2.6px, var(--ring) 2.6px 4.1px, var(--ring-bg) 4.3px) 0    0 / 18px 10px no-repeat,
      radial-gradient(circle at 5px 5px, var(--ring-bg) 0 2.6px, var(--ring) 2.6px 4.1px, var(--ring-bg) 4.3px) 18px 0 / 18px 10px no-repeat,
      radial-gradient(circle at 5px 5px, var(--ring-bg) 0 2.6px, var(--ring) 2.6px 4.1px, var(--ring-bg) 4.3px) 36px 0 / 18px 10px no-repeat;
  }

  /* full-bleed scrim (.slide--full .bleed::after, base rule ~line 740): a
     translucent linear-gradient sitting directly on a photo, whose backdrop
     varies per pixel — it can't be baked opaque the way the wash and the
     code ring were. Ladder tried, in order, and the result of each:
       1. Gutter clipping (oversize the pseudo past its clipping ancestor so
          the BBox/XStep gutter falls outside the visible clip) — TESTED
          AND FALSIFIED. .bleed clips its own ::after via its own `overflow:
          hidden` (base rule ~line 385): the generating box and the clip are
          the SAME box here, not a smaller child inside a bigger clipped
          parent (unlike a gallery tile). Measured directly against this
          file's exported PDF: with the pseudo's `inset` pushed to -4px and
          separately to -40px !important, the pattern's own BBox/XStep in
          the PDF objects stayed pinned to 960×540 / 962×542 either way —
          Chrome sizes this pattern's BBox to the clipped/visible paint
          region, not to the generating box, so oversizing it has no effect
          on the object actually written. Reverted; nothing to keep here.
       2. Inline-SVG data-URI in place of the CSS gradient — BLOCKED by the
          theme contract, not attempted in the file. The scrim's tint has to
          track var(--bg) (a theme sets it — see .slide's own `background:
          var(--slide-bg, var(--bg))`, ~line 97), but an SVG loaded through
          background-image: url(data:...) is rendered in "image mode",
          isolated from the host document's cascade: currentColor inside it
          resolves to black, not the host element's `color`. Verified
          directly — a currentColor SVG rect painted black against a
          differently-colored host in a standalone Chrome screenshot, not
          the host's color. The only way around that is baking a literal
          color into the data URI, which would silently paint the wrong
          tint under any non-stock theme; rejected as a worse regression
          than the seam it would fix.
     Adopting rung 3: left translucent. One /PatternType 1 remains on the
     full-bleed slide, BBox 960×540 == XStep-2/YStep-2 — the full slide, so
     the 2pt gutter sits exactly at the page edge; whether that reads as a
     seam depends on the viewer the same way the wash's did before it was
     flattened. No CSS change on this rule — this is the runtime's one
     accepted residual, and BRANDING.md's PDF section says so out loud. */

  /* speaker notes print under their slide only when ?notes is in the URL */
  body.show-notes .slide {
    height: auto; min-height: var(--slide-h); overflow: visible;
    break-after: auto; page-break-after: auto;
  }
  /* the slide's data-fit zoom is for its content, not the notes under it */
  body.show-notes .slide[data-fit] > .notes { zoom: 1 !important; }
  body.show-notes .notes {
    display: block; position: static;
    margin: 20px 0 50px; padding-top: 20px;
    border-top: 1px dashed #999;
    font: 400 13px/1.55 var(--font-body); color: #222;
    break-after: page; page-break-after: always;
  }
  body.show-notes .notes p { margin: 0 0 8px; }
  body.show-notes .slide:last-child .notes { break-after: auto; page-break-after: auto; }
}

.pop__ta:focus, .describe__input:focus, .drawer__body:focus { outline: none; border-color: var(--accent, #ff5b2e); box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent, #ff5b2e) 30%, transparent); }
