/* ============================================================
   Diagramium — app chrome
   Brand system: "Midnight iris" — near-black blue-tinted surfaces and
   hairline borders do the work; a single electric iris signal marks the
   one thing that matters. The white diagram sheet is the star.
   Dark-first; light chrome is a manual toggle ([data-ui-theme="light"]).
   ============================================================ */

/* Typography rides the system stack in --sans (SF Pro / Segoe UI Variable /
   Inter where installed) — no font file ships, so nothing is fetched. */

/* TOKENS BEGIN — single source of truth. scripts/build_pages.py copies this
   block into the generated pages/*.html at build time; edit only here. */
:root {
  /* ---- Brand tokens (dark ground, the default chrome) ---- */
  --ink: #eef0f8;
  --ink-2: #c9cde0;
  --paper: #08090f;
  --panel: #0e1018;
  --panel-2: #161927;
  /* Default signal: Sapphire — familiar enough to feel trustworthy, deep
     enough to feel editorial rather than playful. White on #315efb clears
     large-control contrast; #8fa7ff remains comfortably readable on paper. */
  --iris: #315efb;
  --iris-bright: #6d8dff;
  --iris-deep: #2146c7;
  --iris-ink: #8fa7ff;   /* accent used as text on --paper */
  --iris-soft: #101a3b;
  --on-signal: #ffffff;
  --slate: #9aa2c2;
  --slate-2: #7d84a3;
  --edge: #1e2233;
  --dot: #1c2030;
  --faint: #8189a4;

  /* ---- Scales ---- */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 24px;
  --sp-6: 32px; --sp-7: 48px; --sp-8: 64px; --sp-9: 96px;
  --r-1: 6px; --r-2: 10px; --r-3: 14px; --r-4: 20px; --r-full: 999px;
  --radius: var(--r-2);
  --radius-lg: 16px;
  --shadow: 0 1px 2px rgba(2, 3, 10, .5), 0 18px 48px -14px rgba(2, 3, 10, .7);
  --shadow-sm: 0 1px 2px rgba(2, 3, 10, .5), 0 6px 18px -8px rgba(2, 3, 10, .55);
  --shadow-lg: 0 1px 3px rgba(2, 3, 10, .55), 0 32px 80px -20px rgba(2, 3, 10, .8);
  --glow-accent: 0 0 0 1px color-mix(in srgb, var(--iris) 38%, transparent),
                 0 8px 32px -8px color-mix(in srgb, var(--iris) 45%, transparent);
  /* Glass recipe — pair --glass-bg with backdrop-filter: var(--glass-blur)
     and a hairline var(--edge) border. Used by .lp-top, modals, floating UI. */
  --glass-bg: color-mix(in srgb, var(--panel) 72%, transparent);
  --glass-blur: blur(14px) saturate(1.4);
  /* Motion — one vocabulary: quick hovers (--dur-1), component moves
     (--dur-2), entrances (--dur-3). */
  --ease-out: cubic-bezier(.22, 1, .36, 1);
  --ease-spring: cubic-bezier(.34, 1.3, .5, 1);
  --dur-1: 120ms; --dur-2: 200ms; --dur-3: 320ms;

  --sans: "InterVariable", "Inter", "SF Pro Display", "Segoe UI Variable", "Segoe UI", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, "Liberation Mono", monospace;
  /* Product diagram spectrum — semantic node colors, never brand accents. */
  --d-indigo: #6d7cff; --d-sky: #0ea5e9; --d-green: #10b981; --d-amber: #f59e0b;
  --d-coral: #ef4444; --d-violet: #8b5cf6; --d-pink: #ec4899;
  /* Legacy warm accent (favicon amber) — a few editor rules still read it. */
  --marigold: #f2a31c; --marigold-deep: #d98a0e;

  /* ---- Legacy aliases: the app's existing rules read these names.
     Mapping them onto the brand tokens re-skins the whole app at once. ---- */
  --chrome: var(--paper);
  --chrome-2: var(--panel);
  --chrome-3: var(--panel-2);
  --border: var(--edge);
  --border-soft: #181b2a;
  --text: var(--ink);
  --muted: #9aa1bd;
  --accent: var(--iris);
  --accent-ink: var(--iris-ink);
  --on-accent: var(--on-signal);
  --wordmark: #ffffff; /* brand wordmark — max-bright on the dark chrome */
  --accent-soft: color-mix(in srgb, var(--accent) 16%, transparent);
  --accent-ring: color-mix(in srgb, var(--accent) 32%, transparent);
  --danger: #f0546a;
  --canvas-bg: #d9dcea;
  --font: var(--sans);
}

/* App-chrome light mode — a manual toggle (js/app.js), separate from the
   per-diagram canvas themes (Classic/Ocean/.../Midnight) in js/render.js,
   which only color the rendered SVG and are unaffected by this. */
[data-ui-theme="light"] {
  --ink: #111320;
  --ink-2: #2a2d40;
  --paper: #f5f6fa;
  --panel: #ffffff;
  --panel-2: #eceef5;
  /* Light half of Sapphire: mature blue with a darker text ramp. */
  --iris: #1d4ed8;
  --iris-bright: #315efb;
  --iris-deep: #1e3a8a;
  --iris-ink: #1e40af;
  --iris-soft: #dbeafe;
  --on-signal: #ffffff;
  --slate: #4b5270;
  --slate-2: #6b7290;
  --edge: #e2e4f0;
  --dot: #d5d9e8;
  --faint: #676d86;
  --shadow: 0 1px 2px rgba(17, 19, 32, .05), 0 12px 32px -12px rgba(17, 19, 32, .14);
  --shadow-sm: 0 1px 2px rgba(17, 19, 32, .06), 0 4px 12px -6px rgba(17, 19, 32, .1);
  --shadow-lg: 0 1px 3px rgba(17, 19, 32, .07), 0 28px 64px -18px rgba(17, 19, 32, .2);

  --wordmark: #14161c; /* strong ink on the light chrome */
  --muted: #5d6480;
  --border-soft: #e8eaf4;
  --accent-soft: color-mix(in srgb, var(--accent) 12%, transparent);
  --accent-ring: color-mix(in srgb, var(--accent) 26%, transparent);
  --danger: #d8324a;
  --canvas-bg: #e7e9f2;
}

/* ---- ACCENT PALETTES ---------------------------------------------------
   Inside the TOKENS block ON PURPOSE: scripts/build_pages.py copies only this
   region into the generated marketing pages, which inline their own CSS
   rather than linking styles.css. Defined below the block (where they started
   out) the app went orange and /pages/about.html stayed iris — the site split
   in two the moment anyone picked a colour. */

/* ---- dark ground (default chrome) ---- */
:root[data-accent="orange"] { --iris:#e8590c; --iris-bright:#ff8a3d; --iris-deep:#c2410c; --iris-ink:#ff9d5c; --iris-soft:#2a1a0f; }
:root[data-accent="blue"]   { --iris:#2563eb; --iris-bright:#60a5fa; --iris-deep:#1d4ed8; --iris-ink:#7cb3ff; --iris-soft:#0f1b33; }
:root[data-accent="teal"]   { --iris:#0d9488; --iris-bright:#2dd4bf; --iris-deep:#0f766e; --iris-ink:#5eead4; --iris-soft:#0c2321; }
:root[data-accent="rose"]   { --iris:#e11d48; --iris-bright:#fb7185; --iris-deep:#be123c; --iris-ink:#ff8da1; --iris-soft:#2b1018; }
:root[data-accent="amber"]  { --iris:#f59e0b; --iris-bright:#fbbf24; --iris-deep:#d97706; --iris-ink:#fcd34d; --iris-soft:#2a1f08;
  /* Amber is the one accent light enough that WHITE fails on it — #fff on
     #f59e0b is 2.15:1, unreadable on a button. Every other palette keeps the
     white default (iris itself is 3.92, the bar this site already sets for
     large/UI text). Flipping the label to near-black instead of darkening the
     amber is what keeps it amber. */
  --on-signal: #1a1205; }
/* Same problem as amber: #fff on lime is 2.7:1. Dark label, keep the hue. */
:root[data-accent="lime"] { --on-signal: #101705; }
:root[data-accent="green"]  { --iris:#16a34a; --iris-bright:#4ade80; --iris-deep:#15803d; --iris-ink:#6ee7a0; --iris-soft:#0d2417; }
:root[data-accent="violet"] { --iris:#7c3aed; --iris-bright:#a78bfa; --iris-deep:#6d28d9; --iris-ink:#c4b5fd; --iris-soft:#1e1633; }
:root[data-accent="magenta"]{ --iris:#c026d3; --iris-bright:#e879f9; --iris-deep:#a21caf; --iris-ink:#f0abfc; --iris-soft:#280a2c; }
:root[data-accent="cyan"]   { --iris:#0891b2; --iris-bright:#22d3ee; --iris-deep:#0e7490; --iris-ink:#67e8f9; --iris-soft:#082530; }
:root[data-accent="indigo"] { --iris:#4f46e5; --iris-bright:#818cf8; --iris-deep:#4338ca; --iris-ink:#a5b4fc; --iris-soft:#141733; }
:root[data-accent="lime"]   { --iris:#65a30d; --iris-bright:#a3e635; --iris-deep:#4d7c0f; --iris-ink:#bef264; --iris-soft:#182210; }
:root[data-accent="slate"]  { --iris:#64748b; --iris-bright:#94a3b8; --iris-deep:#475569; --iris-ink:#cbd5e1; --iris-soft:#161a24; }
/* Midnight Indigo is 1.74:1 on the near-black chrome — as a base it would be a
   smudge, so the DARK ramp runs a step brighter and keeps #312E81 for the
   pressed state, where deep is the point. Its light ramp uses the named hex
   directly (10.58:1 there). Midnight Slate #0F172A is its tint: unusable as an
   accent, exactly right as a surface. */
:root[data-accent="midnight"] { --iris:#4338ca; --iris-bright:#6366f1; --iris-deep:#312e81; --iris-ink:#a5b4fc; --iris-soft:#0f172a; }
:root[data-accent="sky"]      { --iris:#0ea5e9; --iris-bright:#38bdf8; --iris-deep:#0284c7; --iris-ink:#7dd3fc; --iris-soft:#07202e;
  /* White on #0ea5e9 is 2.77:1 — same problem as amber and lime. */
  --on-signal: #04121c; }
:root[data-accent="emerald"]  { --iris:#10b981; --iris-bright:#34d399; --iris-deep:#059669; --iris-ink:#6ee7b7; --iris-soft:#062a20;
  /* White on #10b981 is 2.54:1. */
  --on-signal: #04180f; }
:root[data-accent="coral"]    { --iris:#f43f5e; --iris-bright:#fb7185; --iris-deep:#e11d48; --iris-ink:#fda4af; --iris-soft:#2c0f16; }

/* ---- light ground: deeper bases so the accent stays readable AS TEXT ---- */
[data-ui-theme="light"][data-accent="orange"] { --iris:#c2410c; --iris-bright:#e8590c; --iris-deep:#9a3412; --iris-ink:#9a3412; --iris-soft:#ffedd5; }
[data-ui-theme="light"][data-accent="blue"]   { --iris:#1d4ed8; --iris-bright:#2563eb; --iris-deep:#1e40af; --iris-ink:#1e40af; --iris-soft:#dbeafe; }
[data-ui-theme="light"][data-accent="teal"]   { --iris:#0f766e; --iris-bright:#0d9488; --iris-deep:#115e59; --iris-ink:#115e59; --iris-soft:#ccfbf1; }
[data-ui-theme="light"][data-accent="rose"]   { --iris:#be123c; --iris-bright:#e11d48; --iris-deep:#9f1239; --iris-ink:#9f1239; --iris-soft:#ffe4e6; }
/* Amber is the one that cannot keep its own hue on white — #f59e0b is 2.1:1
   there. The light ramp drops to bronze so the text stays legible. */
[data-ui-theme="light"][data-accent="amber"]  { --iris:#b45309; --iris-bright:#d97706; --iris-deep:#92400e; --iris-ink:#92400e; --iris-soft:#fef3c7; }
[data-ui-theme="light"][data-accent="green"]  { --iris:#15803d; --iris-bright:#16a34a; --iris-deep:#166534; --iris-ink:#166534; --iris-soft:#dcfce7; }
[data-ui-theme="light"][data-accent="violet"] { --iris:#7c3aed; --iris-bright:#8b5cf6; --iris-deep:#6d28d9; --iris-ink:#6d28d9; --iris-soft:#ede9fe; }
[data-ui-theme="light"][data-accent="magenta"]{ --iris:#a21caf; --iris-bright:#c026d3; --iris-deep:#86198f; --iris-ink:#86198f; --iris-soft:#fae8ff; }
[data-ui-theme="light"][data-accent="cyan"]   { --iris:#0e7490; --iris-bright:#0891b2; --iris-deep:#155e75; --iris-ink:#155e75; --iris-soft:#cffafe; }
[data-ui-theme="light"][data-accent="indigo"] { --iris:#4338ca; --iris-bright:#4f46e5; --iris-deep:#3730a3; --iris-ink:#3730a3; --iris-soft:#f5f3ff; }
/* Lime, like amber, is far too light to be text on white — #65a30d is 3.3:1
   there. The light ramp drops to olive so labels stay legible. */
[data-ui-theme="light"][data-accent="lime"]   { --iris:#4d7c0f; --iris-bright:#65a30d; --iris-deep:#3f6212; --iris-ink:#3f6212; --iris-soft:#ecfccb; }
[data-ui-theme="light"][data-accent="slate"]  { --iris:#475569; --iris-bright:#64748b; --iris-deep:#334155; --iris-ink:#334155; --iris-soft:#e2e8f0; }
/* Lavender Mist #F5F3FF is 1.02:1 on the light ground — invisible as an
   accent, and a perfect tint. It is the soft fill for both indigo families. */
[data-ui-theme="light"][data-accent="midnight"] { --iris:#312e81; --iris-bright:#4338ca; --iris-deep:#1e1b4b; --iris-ink:#1e1b4b; --iris-soft:#f5f3ff; }
[data-ui-theme="light"][data-accent="sky"]      { --iris:#0369a1; --iris-bright:#0284c7; --iris-deep:#075985; --iris-ink:#075985; --iris-soft:#e0f2fe; --on-signal:#ffffff; }
[data-ui-theme="light"][data-accent="emerald"]  { --iris:#047857; --iris-bright:#059669; --iris-deep:#065f46; --iris-ink:#065f46; --iris-soft:#d1fae5; --on-signal:#ffffff; }
[data-ui-theme="light"][data-accent="coral"]    { --iris:#be123c; --iris-bright:#e11d48; --iris-deep:#9f1239; --iris-ink:#9f1239; --iris-soft:#ffe4e6; }

/* TOKENS END */
/* A few spots use hand-picked colors instead of the variables above (tuned
   for the dark chrome specifically) and need their own light-mode override. */
[data-ui-theme="light"] .toast { background: #ffffff; box-shadow: 0 10px 30px rgba(20, 24, 40, 0.16); }
[data-ui-theme="light"] .modal-backdrop { background: rgba(60, 66, 90, 0.35); }
[data-ui-theme="light"] ::-webkit-scrollbar-thumb { background: rgba(60, 66, 90, 0.22); }
[data-ui-theme="light"] ::-webkit-scrollbar-thumb:hover { background-color: rgba(60, 66, 90, 0.35); }
[data-ui-theme="light"] #canvasWrap::-webkit-scrollbar-thumb { background-color: rgba(60, 66, 90, 0.18); }

* { box-sizing: border-box; margin: 0; padding: 0; }
[hidden] { display: none !important; }

/* Pre-paint routing — the inline script in index.html's <head> sets these on
   <html> based on the URL, and boot() removes them. They stop the editor chrome
   from flashing before boot() runs on window 'load': on the home route the
   static #homePage overlay is shown right away (it covers the editor); on
   home/explore the editor chrome is kept invisible until the overlay is ready.
   More specific than [hidden]{display:none} so it wins for #homePage. */
.preboot-overlay #topbar,
.preboot-overlay #main { visibility: hidden; }
.preboot-home #homePage { display: flex !important; }
/* Same trick for /search: show its (fixed, full-page) view before the first
   paint so the editor underneath is never seen. js/search.js takes over at
   DOMContentLoaded and boot() clears the class. */
.preboot-search #searchPage { display: flex !important; }
html, body { height: 100%; }
/* Mobile browsers used to report the largest possible viewport for `100vh`,
   even while their address bar or on-screen keyboard covered part of the
   app.  The editor and Present stage are full-height flex layouts, so that
   stale measurement translated directly into cropped controls / a diagram
   fitted for pixels the viewer could not see.  Modern dynamic viewport units
   track the actually visible window; the preceding 100% remains the portable
   fallback for older offline browsers. */
@supports (height: 100dvh) {
  html, body { height: 100dvh; }
}
body {
  font-family: var(--font);
  font-size: 13px;
  color: var(--text);
  background: var(--chrome);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}

button { font-family: inherit; font-size: inherit; color: inherit; background: none; border: none; cursor: pointer; }
input, textarea, select { font-family: inherit; font-size: inherit; color: inherit; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ============ Top bar ============ */
#topbar {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 52px;
  padding: 0 14px;
  background: color-mix(in srgb, var(--paper) 82%, transparent);
  -webkit-backdrop-filter: saturate(1.2) blur(10px);
  backdrop-filter: saturate(1.2) blur(10px);
  border-bottom: 1px solid var(--border);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02) inset, var(--shadow-sm);
  flex: none;
  z-index: 20;
}
.brand {
  font-weight: 700;
  font-size: 14px;
  letter-spacing: -0.01em;
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 4px 8px 4px 4px;
  border-radius: 8px;
  transition: background .12s;
}
button.brand:hover { background: var(--chrome-2); }
.tool-chip {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .09em;
  color: var(--accent-ink);
  background: var(--accent-soft);
  border: 1px solid var(--accent-ring);
  padding: 3px 8px;
  border-radius: 6px;
  white-space: nowrap;
}
.brand {
  font-family: var(--sans);
}
/* Editor topbar wordmark — the artifact lockup, sized down for the dense bar. */
.brand .wordmark { font-size: 17px; font-weight: 750; letter-spacing: -0.03em; color: var(--wordmark); }
.brand .wordmark .um { color: var(--accent); }
.brand:hover .wordmark { color: var(--wordmark); }
.brand-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px; height: 40px;
  color: var(--slate);
}
.brand-mark svg { width: 40px; height: 40px; display: block; }
#docTitle {
  background: transparent;
  border: 1px solid transparent;
  border-radius: 7px;
  padding: 6px 10px;
  width: clamp(120px, 16vw, 240px); /* yields space before bar buttons wrap */
  min-width: 0;
  font-size: 13px;
  color: var(--text);
  transition: border-color .15s, background .15s;
}
#docTitle:hover { background: var(--chrome-2); }
#docTitle:focus { background: var(--chrome-2); border-color: var(--border); outline: none; }
#docTitle::placeholder { color: var(--muted); }

.tb-group {
  display: flex;
  align-items: center;
  gap: 2px;
  background: var(--chrome-2);
  border: 1px solid var(--border-soft);
  border-radius: 8px;
  padding: 2px;
}
.tb-btn {
  padding: 5px 9px;
  border-radius: 6px;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.2;
  white-space: nowrap; /* Inter runs wider than SF Pro — never wrap bar labels */
  transition: background .12s, color .12s;
}
.tb-btn:hover:not(:disabled) { background: var(--chrome-3); color: var(--text); }
.tb-btn:disabled { opacity: .35; cursor: default; }
/* Save button: an iris dot appears while the doc has unsaved changes */
#btnSaveDoc { position: relative; }
#btnSaveDoc.dirty::after {
  content: "";
  position: absolute; top: 3px; right: 3px;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--iris-bright);
}
/* Save-to-file status chip (same treatment as the kids app): amber while the
   work only lives in this browser, green once it's safely in a file. */
.save-state {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  font-size: 11.5px; font-weight: 650; white-space: nowrap;
  padding: 4px 10px; border-radius: 999px; border: 1px solid var(--edge);
  color: var(--muted);
  margin-right: 2px;
}
.save-state[hidden] { display: none !important; }
.save-state .ss-dot { width: 8px; height: 8px; border-radius: 999px; background: currentColor; flex: none; }
.save-state.is-dirty { color: #d97706; border-color: color-mix(in srgb, #f59e0b 45%, var(--edge)); background: color-mix(in srgb, #f59e0b 12%, transparent); }
.save-state.is-saved { color: #16a34a; border-color: color-mix(in srgb, #22c55e 45%, var(--edge)); background: color-mix(in srgb, #22c55e 12%, transparent); }
[data-ui-theme="light"] .save-state.is-dirty { color: #92400e; }
[data-ui-theme="light"] .save-state.is-saved { color: #15803d; }
.tb-autosave {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 5px 6px;
  color: var(--muted); font-size: 12px; white-space: nowrap;
  cursor: pointer; user-select: none;
}
.tb-autosave input { accent-color: var(--iris); margin: 0; }
.tb-btn.wide { padding: 6px 12px; }
.tb-btn.on { background: var(--accent-soft); color: var(--accent-ink); }
/* Labeled "◑ Theme" toggle in the editor topbar — matches the home/explore pill. */
.tb-btn .glyph { font-family: var(--mono); font-size: 13px; line-height: 1; margin-right: 1px; }
.zoom-label { min-width: 48px; text-align: center; font-variant-numeric: tabular-nums; }
.spacer { flex: 1; }

.btn-primary {
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 640;
  padding: 7px 16px;
  border-radius: 9px;
  box-shadow: 0 1px 0 rgba(255, 255, 255, .25) inset, 0 8px 20px -10px var(--accent);
  transition: filter .12s, transform .12s, box-shadow .2s;
}
.btn-primary:hover { filter: brightness(1.06); transform: translateY(-1px); box-shadow: 0 1px 0 rgba(255, 255, 255, .3) inset, 0 12px 26px -10px var(--accent); }
.btn-primary:active { transform: none; }
.btn-ghost {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 6px 12px;
  color: var(--text);
  transition: background .12s, border-color .12s;
}
.btn-ghost:hover { background: var(--chrome-3); border-color: var(--accent); }
.btn-danger {
  border: 1px solid rgba(240, 84, 106, .4);
  color: var(--danger);
  border-radius: 8px;
  padding: 6px 12px;
  width: 100%;
  transition: background .12s;
}
.btn-danger:hover { background: rgba(240, 84, 106, .12); }

/* ============ Main layout ============ */
#main { flex: 1; display: flex; min-height: 0; }

/* ---- Palette (left) ---- */
#palette {
  width: 212px;
  flex: none;
  background: var(--chrome);
  border-right: 1px solid var(--border);
  padding: 14px 12px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.pal-title {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
  margin-top: 6px;
}
.pal-title:first-child { margin-top: 0; }
.pal-hint { font-size: 11px; color: var(--muted); opacity: .75; margin-top: -4px; }
.pal-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 7px;
}

/* collapsible shape groups */
#palGroups { display: flex; flex-direction: column; gap: 4px; }
.pal-group { border-bottom: 1px solid var(--border-soft); padding-bottom: 8px; }
.pal-group:last-child { border-bottom: none; padding-bottom: 0; }
.pal-group > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 2px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
  user-select: none;
  -webkit-user-select: none;
}
.pal-group > summary::-webkit-details-marker { display: none; }
.pal-group > summary:hover { color: var(--text); }
.pal-group > summary .pal-chevron {
  display: inline-flex;
  transition: transform .15s ease;
  font-size: 9px;
  color: var(--muted);
}
.pal-group[open] > summary .pal-chevron { transform: rotate(90deg); }
.pal-group > summary .pal-count {
  margin-left: auto;
  font-size: 10px;
  font-weight: 600;
  color: var(--muted);
  opacity: .7;
  letter-spacing: 0;
  text-transform: none;
}
.pal-group-body { padding-top: 2px; display: flex; flex-direction: column; gap: 7px; }
.pal-group-hint { font-size: 11px; color: var(--muted); opacity: .75; }
.pal-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: 10px 4px 8px;
  background: var(--chrome-2);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius);
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
  transition: border-color .12s, background .12s, transform .08s;
  text-align: center;
}
.pal-item:hover { border-color: var(--accent); background: var(--chrome-3); transform: translateY(-1px); }
.pal-item:active { cursor: grabbing; transform: none; }
.pal-glyph { font-size: 19px; line-height: 1; }
.pal-glyph svg { display: block; }
.pal-name { font-size: 11px; color: var(--muted); }
.pal-item:hover .pal-name { color: var(--text); }
.pal-footer {
  margin-top: auto;
  padding-top: 12px;
  font-size: 11px;
  line-height: 1.5;
  color: var(--muted);
  opacity: .8;
  border-top: 1px solid var(--border-soft);
}

/* ---- Canvas (center) ---- */
#canvasWrap {
  flex: 1;
  min-width: 0;
  position: relative;
  overflow: auto;
  background: var(--canvas-bg);
  background-image: radial-gradient(rgba(20, 22, 31, 0.10) 1px, transparent 1px);
  background-size: 22px 22px;
}
#zoomLayer {
  position: relative;
  display: inline-block;
  padding: 34px;
  min-width: 100%;
  min-height: 100%;
}
#svgHost { display: inline-block; }
#svgHost svg {
  display: block;
  border-radius: 10px;
  box-shadow: 0 2px 6px rgba(15, 18, 32, 0.14), 0 14px 44px rgba(15, 18, 32, 0.16);
}

/* interactive affordances inside the SVG */
#svgHost [data-hit] { cursor: pointer; }
#svgHost [data-hit="lifeline"] { cursor: crosshair; }
#svgHost [data-hit="lifeline"]:hover { fill: rgba(109, 124, 255, 0.10); }
#svgHost [data-hit="actor"] { cursor: grab; }
#svgHost [data-hit="item"]:hover { fill: rgba(109, 124, 255, 0.08); }
#svgHost [data-sm="port"], #svgHost [data-fc="port"], #svgHost [data-mm="port"], #svgHost [data-sl="port"] { cursor: crosshair; }
#svgHost [data-sm="node"], #svgHost [data-fc="node"], #svgHost [data-mm="node"], #svgHost [data-sl="node"], #svgHost [data-oc="person"], #svgHost [data-oc-note] { cursor: grab; }
#svgHost [data-tl="card"], #svgHost [data-tl="marker"] { cursor: pointer; }
#svgHost [data-tl="cardResize"] { cursor: ew-resize; }
#svgHost [data-jm="stageHeader"], #svgHost [data-jm="rowLabel"], #svgHost [data-jm="cell"] { cursor: pointer; }
#svgHost [data-sm="resize"], #svgHost [data-fc="resize"], #svgHost [data-mm="resize"], #svgHost [data-sl="resize"], #svgHost [data-oc-resize], #svgHost [data-oc-note-resize] { cursor: nwse-resize; }
#svgHost [data-sm="bend"], #svgHost [data-fc="bend"], #svgHost [data-mm="bend"], #svgHost [data-sl="bend"] { cursor: grab; }
#svgHost [data-mm="boundary"] { cursor: pointer; }

/* Free-floating text elements — borderless by default, so give them a
   PowerPoint-style dashed outline on hover to stay discoverable/selectable. */
#svgHost [data-hit="float"], #svgHost [data-fc="text"], #svgHost [data-sm="text"], #svgHost [data-mm="text"], #svgHost [data-sl="text"], #svgHost [data-tl="text"], #svgHost [data-jm="text"], #svgHost [data-oc-text] { cursor: grab; }
#svgHost [data-hit="floatResize"], #svgHost [data-fc="textResize"], #svgHost [data-sm="textResize"], #svgHost [data-mm="textResize"], #svgHost [data-sl="textResize"], #svgHost [data-tl="textResize"], #svgHost [data-jm="textResize"], #svgHost [data-oc-text-resize] { cursor: nwse-resize; }
#svgHost [data-hit="float"]:hover, #svgHost [data-fc="text"]:hover, #svgHost [data-sm="text"]:hover, #svgHost [data-mm="text"]:hover, #svgHost [data-sl="text"]:hover, #svgHost [data-tl="text"]:hover, #svgHost [data-jm="text"]:hover, #svgHost [data-oc-text]:hover {
  stroke: rgba(109, 124, 255, 0.4); stroke-width: 1; stroke-dasharray: 4 3;
}

/* Connection ports stay hidden until you hover the shape or it's selected —
   pure CSS (no per-pointermove JS) since :hover on the group already fires from
   hovering the always-hit-testable shape body. */
#svgHost .fc-node .fc-port, #svgHost .sm-node .sm-port, #svgHost .mm-node .mm-port, #svgHost .sl-node .sl-port { opacity: 0; pointer-events: none; transition: opacity .12s ease; }
/* While a connector is being drawn, EVERY shape shows every magnet, so the
   drop target is visible before the pointer gets there. Hovering one grows
   it, which is the "leave the arrow here" affordance. */
body.dgm-connecting #svgHost :is(.fc-port, .sm-port, .sl-port, .mm-port) {
  opacity: 1; pointer-events: auto;
}
#svgHost :is(.fc-port, .sm-port, .sl-port, .mm-port):hover {
  r: 6.5; filter: drop-shadow(0 0 5px rgba(109, 124, 255, .9));
}
#svgHost .fc-node:hover .fc-port, #svgHost .fc-node.selected .fc-port,
#svgHost .sm-node:hover .sm-port, #svgHost .sm-node.selected .sm-port,
#svgHost .mm-node:hover .mm-port, #svgHost .mm-node.selected .mm-port,
#svgHost .sl-node:hover .sl-port, #svgHost .sl-node.selected .sl-port { opacity: 1; pointer-events: auto; }
/* Connector ENDPOINT handles — the two rings on a selected connector. They
   are the "unplug this end" affordance, so they read as grabbable and grow
   under the pointer the same way a magnet does. Drawn only while the edge is
   selected, so they need no visibility rule of their own. */
#svgHost :is(.fc-endpoint, .sm-endpoint, .sl-endpoint) {
  cursor: grab; transition: r .12s ease, filter .12s ease;
}
#svgHost :is(.fc-endpoint, .sm-endpoint, .sl-endpoint):hover {
  r: 8; filter: drop-shadow(0 0 6px rgba(109, 124, 255, .95));
}
body.dgm-connecting #svgHost :is(.fc-endpoint, .sm-endpoint, .sl-endpoint) { cursor: grabbing; }
#svgHost [data-sl="laneResize"] { cursor: row-resize; }
#svgHost [data-sl="laneHeader"] { cursor: pointer; }

/* Timeline card resize handle stays hidden until the card is hovered or selected. */
#svgHost .tl-card .tl-resize { opacity: 0; pointer-events: none; transition: opacity .12s ease; }
#svgHost .tl-card:hover .tl-resize, #svgHost .tl-card.selected .tl-resize { opacity: 1; pointer-events: auto; }

#inlineEdit {
  position: absolute;
  z-index: 10;
  border: 1.5px solid var(--accent);
  border-radius: 5px;
  padding: 2px 6px;
  background: #fff;
  color: #1f2430;
  box-shadow: 0 4px 14px rgba(15, 18, 32, 0.25);
  text-align: center;
}

#inlineEditMulti {
  position: absolute;
  z-index: 10;
  border: 1.5px solid var(--accent);
  border-radius: 5px;
  padding: 3px 6px;
  background: #fff;
  color: #1f2430;
  box-shadow: 0 4px 14px rgba(15, 18, 32, 0.25);
  text-align: center;
  font-family: inherit;
  line-height: 1.3;
  resize: none;
  overflow-y: auto;
}

#emptyState {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.empty-card {
  pointer-events: auto;
  text-align: center;
  color: #4a5065;
  padding: 30px 40px;
}
.empty-glyph {
  width: 54px; height: 54px;
  margin: 0 auto 14px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 14px;
  background: linear-gradient(135deg, var(--marigold), var(--marigold-deep));
  color: var(--on-accent);
  font-size: 26px;
  box-shadow: 0 10px 24px -8px color-mix(in srgb, var(--marigold) 60%, transparent);
}
.empty-card h2 { font-size: 17px; color: #262b3d; margin-bottom: 6px; }
.empty-card p { font-size: 13px; line-height: 1.55; margin-bottom: 16px; }

/* ============================================================
   Present mode — "Stagecraft". Cinema-dark glass chrome that stays
   deliberately theme-constant (film-projector logic: the sheet is the
   screen, the chrome is the theater), floating clusters, tooltips on
   everything, auto-hiding during playback (body.pr-idle), an intro and
   end slide, popovers for narration/music/motion settings, laser & pen
   overlays and a step spotlight. JS contracts preserved: element ids,
   .show/.on/.pos-*/.speak/.pnl-* state classes, inline style hooks on
   #prProgress/#prPresenter/#prBrand/#prNarration/#prTitleCard*.
   ============================================================ */
#prBrand { display: none; }
.presenting #prBrand {
  display: flex; align-items: center; gap: 8px;
  position: absolute; bottom: calc(18px + var(--pr-brand-lift, 0px)); left: 18px; z-index: 5;
  pointer-events: none;
  padding: 6px 13px 6px 10px; border-radius: 999px;
  /* Near-solid black: the pill must read as a crisp badge on ANY sheet color —
     at 55% alpha it dissolved into light canvases. */
  background: rgba(6, 7, 11, 0.94);
  -webkit-backdrop-filter: saturate(1.3) blur(12px);
  backdrop-filter: saturate(1.3) blur(12px);
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow: 0 8px 24px -10px rgba(0, 0, 0, 0.75);
  opacity: 0;
  animation: prBrandIn .5s ease .15s forwards;
  transition: opacity .35s ease, bottom .25s ease;
}
#prBrand svg { width: 24px; height: 24px; display: block; color: #c3cbdb; }
.pr-brand-word { font-family: var(--sans); font-weight: 700; font-size: 14.5px; letter-spacing: -0.03em; color: #ffffff; }
.pr-brand-word .um { color: var(--iris-bright); }
@keyframes prBrandIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .presenting #prBrand { animation: none; opacity: 1; } }

/* ---- Branded show frame: a slim iris ring around the whole stage. ----
   Gradient border via the padding + mask-composite trick so the middle stays
   fully transparent; z-index 3 keeps it under the spotlight (4) and every
   control. Deliberately thin — chrome, not furniture. */
#prFrame { display: none; }
.presenting #prFrame {
  display: block;
  position: absolute; inset: 8px; z-index: 3;
  pointer-events: none;
  border-radius: 18px;
  box-shadow: inset 0 0 60px -18px rgba(124, 108, 248, 0.16);
  opacity: 0;
  animation: prBrandIn .5s ease .1s forwards;
}
.presenting #prFrame::before {
  content: '';
  position: absolute; inset: 0;
  border-radius: 18px;
  padding: 1.5px;
  background: linear-gradient(135deg,
    rgba(156, 141, 255, 0.85), rgba(124, 108, 248, 0.35) 30%,
    rgba(124, 108, 248, 0.22) 60%, rgba(156, 141, 255, 0.75));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
}
@media (prefers-reduced-motion: reduce) { .presenting #prFrame { animation: none; opacity: 1; } }

/* ---- The TV unit ----
   While presenting, #stage itself becomes the branded container: dark bezel
   (real padding, so fitPresent() sizes the diagram into the smaller screen),
   a header row inside the bezel, and the diagram sheet as a rounded "screen"
   card. The notes panel turns into a floating card INSIDE the unit (below,
   overriding its full-height dock). The show is the content; Diagramium is
   the set it plays on. */
body.presenting #stage {
  background: #0b0d14;
  /* --pr-plate is the nameplate's height; the top lane is sized from it. */
  --pr-plate: 36px;
  /* viewport-fit=cover lets the show paint under the cutout and the home
     indicator, so the stage owes the insets on EVERY edge — and it owes them
     at every width, not inside a phone breakpoint: the orientation people
     actually present in is LANDSCAPE, which is precisely when the notch
     moves to a side edge and the 18px gutter stops clearing it. max() keeps
     the original numbers everywhere the insets are 0, which is every desktop
     and every phone without a cutout. */
  padding:
    calc(var(--pr-plate, 36px) + 9px + env(safe-area-inset-top, 0px))
    max(10px, env(safe-area-inset-right, 0px))
    max(10px, env(safe-area-inset-bottom, 0px))
    max(10px, env(safe-area-inset-left, 0px));
}
body.presenting #canvasWrap { background: transparent; }
/* Lanes: a whisper of air under the nameplate, and a reserved band at the
   bottom for the numbered stepper (the old top progress track is hidden
   while presenting — the stepper replaces it). Constant, not toggle-aware,
   so the framing never jumps. */
/* Two reserved lanes, and both reserve their FULL height.
   Top: the stepper, which moved up here from the bottom band.
   Bottom: --pr-band is the measured height the control bar actually occupies
   (its own bottom offset + its height, so a bar that wraps to two rows on a
   narrow canvas reports a taller band). Reserving `band - N` was the old
   behaviour and it meant the bar always sat ON the drawing — 33px of it on a
   wrapped two-row bar. Reserve the whole band plus a gutter so the diagram
   gets a clean lane and the bar docks below it rather than over it. */
body.presenting #canvasWrap {
  /* ONE definition of the show's lane, so everything that has to line up with
     the diagram reads the same two numbers. The notes panel used its own
     hand-tuned offsets and drifted 24px high at the top and 63px short at the
     bottom; now it cannot, because it inherits these. */
  --pr-lane-top: 28px;
  --pr-lane-bottom: calc(var(--pr-band, 66px) + 8px);
  padding-top: var(--pr-lane-top);
  padding-bottom: var(--pr-lane-bottom);
}
body.presenting #prProgress { display: none; }

/* ---- The stepper: numbered, clickable step dots, under the nameplate. ----
   It used to sit at the bottom, floating over the diagram: the canvas is
   centred in the remaining space, so on a tall diagram the dots landed ON the
   drawing. Anchored to the top instead, it sits in the band between the
   nameplate and the canvas, which is chrome either way — nothing to overlap.
   #prStepper is a child of #canvasWrap, whose box already starts just below
   the 45px nameplate, so `top` here is measured from that edge. */
#prStepper { display: none; }
body.presenting #prStepper:not([hidden]) {
  display: flex;
  align-items: center;
  gap: 16px;
  position: absolute;
  left: 30px;
  /* Spans the DIAGRAM column only: JS sets --pr-step-right to the notes
     panel's footprint so the dots never run under it. */
  right: var(--pr-step-right, 30px);
  top: 4px;
  z-index: 6;
}
#prStepDots {
  display: flex;
  align-items: center;
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  padding: 4px 0;
}
.pr-step-dot {
  position: relative;
  flex: none;
  width: 27px;
  height: 27px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.16);
  background: rgba(255, 255, 255, 0.06);
  color: rgba(233, 236, 255, 0.55);
  font: 600 11.5px var(--sans);
  cursor: pointer;
  padding: 0;
  transition: background .2s ease, border-color .2s ease, box-shadow .2s ease;
}
.pr-step-dot + .pr-step-dot { margin-left: var(--pr-dot-gap, 26px); }
.pr-step-dot + .pr-step-dot::before {
  content: '';
  position: absolute;
  right: 100%;
  top: 50%;
  width: var(--pr-dot-gap, 26px);
  height: 2px;
  transform: translateY(-50%);
  background: rgba(255, 255, 255, 0.12);
}
/* A step that carries an authored note — the stepper's chapter marker. */
.pr-step-dot.chapter::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -7px;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  transform: translateX(-50%);
  background: color-mix(in srgb, var(--iris) 75%, #fff);
}
.pr-step-dot.done {
  background: color-mix(in srgb, var(--iris) 42%, #14162a);
  border-color: color-mix(in srgb, var(--iris) 65%, transparent);
  color: #fff;
}
.pr-step-dot.done::before, .pr-step-dot.now::before { background: color-mix(in srgb, var(--iris) 70%, transparent); }
.pr-step-dot.now {
  background: #10131f;
  border-color: #43e0b0;
  color: #fff;
  box-shadow: 0 0 0 3px rgba(67, 224, 176, 0.22);
}
.pr-step-dot:hover { border-color: rgba(255, 255, 255, 0.45); }
/* Compact mode for long decks: plain dots, tighter run. */
#prStepDots.mini .pr-step-dot { width: 11px; height: 11px; font-size: 0; }
#prStepDots.mini .pr-step-dot + .pr-step-dot::before { height: 1.5px; }
#prStepDots.mini .pr-step-dot.chapter::after { bottom: -6px; }
/* Lives in the nameplate now, immediately before the step arrows.
   The auto margin has to sit on THIS element, and #prTvNav's has to be
   cancelled: with one on each, flexbox splits the free space between them and
   the clock parks in the middle of the header; with it only on the nav, the
   clock stays glued to the title. Collecting the slack here pushes the clock
   and the arrows to the right edge as one group. When the clock is empty the
   nav keeps its own auto margin and still right-aligns on its own. */
#prStepTime { display: none; }
body.presenting #prStepTime:not(:empty) + #prTvNav { margin-left: 0; }
body.presenting #prStepTime:not(:empty) {
  display: block;
  flex: none;
  margin-left: auto;
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  color: rgba(233, 236, 255, 0.55);
  white-space: nowrap;
}
/* The stepper left the bottom band, so the caption and karaoke subtitle can
   drop back down to just above the control bar. */
body.presenting #prCaption, body.presenting #prSubtitle { bottom: calc(var(--pr-band, 66px) + 18px); }

/* ---- Premium notes-panel structure (Present mode only). ---- */
#prNoteStep, #prNoteIcon, #prNoteTitle, #prNoteTakeaway { display: none; }
body.presenting #prNoteStep:not([hidden]) {
  display: block;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: .15em;
  text-transform: uppercase;
  color: var(--iris-bright);
  margin: 0 0 12px;
}
body.presenting #prNoteIcon:not([hidden]) {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: 13px;
  background: color-mix(in srgb, var(--iris) 15%, transparent);
  border: 1px solid color-mix(in srgb, var(--iris) 36%, transparent);
  color: var(--iris-bright);
  margin: 0 0 10px;
}
#prNoteIcon svg { width: 26px; height: 26px; display: block; }
body.presenting #prNoteTitle:not([hidden]) {
  display: block;
  font-size: calc(19px * var(--pr-text-k, 1));
  font-weight: 750;
  line-height: 1.25;
  color: #fff;
  margin: 0 0 10px;
}
body.presenting #prNoteTakeaway:not([hidden]) {
  display: block;
  flex: none;
  margin-top: 12px;
  padding: 11px 13px;
  border-radius: 12px;
  background: color-mix(in srgb, #43e0b0 9%, transparent);
  border: 1px solid color-mix(in srgb, #43e0b0 28%, transparent);
}
.pr-tk-head {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: #43e0b0;
  margin-bottom: 5px;
}
#prNoteTakeawayText { font-size: calc(13.5px * var(--pr-text-k, 1)); line-height: 1.5; color: rgba(233, 236, 255, 0.92); }

/* The stage holds still through a show — only the spotlight glides — for as
   long as the whole drawing is READABLE where it stands. When it is not (see
   the smart camera in app.js) the sheet becomes a window onto the drawing and
   turns like a page between regions, which is the only way a 4:1 diagram of
   13px labels is a presentation rather than a picture of one. */
body.presenting #prSpotlight {
  transition: left .55s cubic-bezier(0.25, 0.65, 0.3, 1), top .55s cubic-bezier(0.25, 0.65, 0.3, 1),
              width .55s cubic-bezier(0.25, 0.65, 0.3, 1), height .55s cubic-bezier(0.25, 0.65, 0.3, 1);
}
@media (prefers-reduced-motion: reduce) {
  body.presenting #prSpotlight { transition: none; }
  body.presenting #prTvHead { animation: none; opacity: 1; }
}

/* ---- Header step navigation: ‹ Step n of m › ---- */
#prTvNav {
  margin-left: auto;
  flex: none;
  display: flex;
  align-items: center;
  gap: 2px;
  pointer-events: auto;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 999px;
  padding: 3px;
}
#prTvNav button {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 0;
  background: transparent;
  color: rgba(233, 236, 255, 0.75);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}
#prTvNav button:hover { background: rgba(255, 255, 255, 0.12); color: #fff; }
#prTvNav button svg { width: 15px; height: 15px; }
/* Notes-panel head + takeaway icons */
.pr-nh-label { display: inline-flex; align-items: center; gap: 6px; }
.pr-nh-label svg, .pr-tk-head svg { width: 13px; height: 13px; flex: none; }
.pr-tk-head { display: flex; align-items: center; gap: 6px; }
#prNarrationHeadBtns svg { width: 15px; height: 15px; display: block; }

/* ---- The animated presenter ----
   Lives at the top of the notes panel: chrome, never over the diagram or the
   note text. The stage is transparent by default; `glow` lights a soft pool
   behind the character and `card` frames it. Motion is entirely inside the
   SVG (transform attributes on grouped parts), so the browser composites it
   without touching layout — that is what holds 60fps. */
#prPresenterStage { display: none; }
body.presenting #prPresenterStage:not([hidden]) {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  flex: none;
  position: relative;
  margin: -4px 0 12px;
  border-radius: 14px;
  overflow: hidden;
  contain: layout paint;
}
#prPresenterStage[data-align="left"] { justify-content: flex-start; }
#prPresenterStage[data-align="right"] { justify-content: flex-end; }
/* The stage owns a DETERMINISTIC height per size preset. Without it the
   WebGL canvas falls back to its own intrinsic size (260px), which squeezes
   the note card out of a short panel and strands the figure at the bottom
   edge. The renderer fits the character into whatever box it is given. */
#prPresenterStage[data-size="s"] { height: 112px; }
#prPresenterStage[data-size="m"] { height: 150px; }
#prPresenterStage[data-size="l"] { height: 196px; }
/* Ceiling so the notes always keep the majority of the panel — but loose
   enough that Large still reads as larger than Medium on a short window. */
body.presenting #prPresenterStage:not([hidden]) { max-height: 40%; }
#prPresenterStage > canvas { width: 100%; height: 100%; display: block; }
/* The SVG fallback rig still sizes itself. */
#prPresenterStage[data-size="s"] .pz-svg { width: 96px; height: 106px; }
#prPresenterStage[data-size="m"] .pz-svg { width: 132px; height: 145px; }
#prPresenterStage[data-size="l"] .pz-svg { width: 172px; height: 189px; }
.pz-svg { display: block; overflow: visible; will-change: transform; }
.pz-portrait { overflow: hidden; will-change: auto; }
/* Library cards accept either the SVG fallback rig or a manifest portrait. */
img.pz-portrait { display: block; border-radius: 10px; object-fit: cover; }
.pz-root, .pz-head, .pz-body, .pz-arm, .pz-upper, .pz-lid, .pz-iris, .pz-brow { will-change: transform; }
#prPresenterStage[data-bg="glow"]::before {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -18%;
  width: 78%;
  height: 118%;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(ellipse at 50% 55%, color-mix(in srgb, var(--iris) 30%, transparent), transparent 68%);
  pointer-events: none;
}
#prPresenterStage[data-bg="card"] {
  background: linear-gradient(180deg, rgba(124, 108, 248, 0.14), rgba(255, 255, 255, 0.03));
  border: 1px solid rgba(255, 255, 255, 0.08);
  padding-top: 6px;
}
#prPresenterStage[data-align="left"][data-bg="glow"]::before { left: 34%; }
#prPresenterStage[data-align="right"][data-bg="glow"]::before { left: 66%; }
/* Fallback when the rig cannot be built: a still brand mark, never an empty box. */
#prPresenterStage.pz-fallback { min-height: 74px; }
#prPresenterStage.pz-fallback::after {
  content: '';
  width: 54px; height: 54px;
  border-radius: 50%;
  background: radial-gradient(circle at 38% 32%, var(--iris-bright), var(--iris-deep));
  opacity: .85;
}
@media (prefers-reduced-motion: reduce) {
  #prPresenterStage[data-bg="glow"]::before { background: radial-gradient(ellipse at 50% 55%, color-mix(in srgb, var(--iris) 22%, transparent), transparent 68%); }
}

/* The animated presenter supersedes the corner emoji avatar — one host per
   show. The legacy element and its toggle stay wired (app.js owns them);
   only the old face is retired. */
body.presenting #prPresenter { display: none; }

/* ---- Presenter controls in the settings popover ---- */
.pr-pres-row { display: flex; gap: 6px; }
.pr-pres-thumb {
  width: 26px; height: 26px;
  border-radius: 8px;
  overflow: hidden;
  display: inline-flex;
  align-items: flex-end;
  justify-content: center;
  background: rgba(124, 108, 248, 0.18);
  flex: none;
}
.pr-pres-thumb svg { width: 26px; height: 26px; }
#prPresPick { display: flex; align-items: center; gap: 8px; justify-content: flex-start; }

/* ---- Presenter library modal ---- */
.modal.modal-wide { max-width: 860px; width: min(860px, 94vw); }
.pl-tools { display: flex; flex-direction: column; gap: 10px; margin-bottom: 14px; }
.pl-cats { display: flex; flex-wrap: wrap; gap: 6px; }
.pl-cat {
  border: 1px solid var(--border-soft);
  background: var(--chrome-2);
  color: var(--text);
  border-radius: 999px;
  padding: 5px 13px;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
}
.pl-cat.on {
  background: color-mix(in srgb, var(--iris) 26%, var(--chrome-2));
  border-color: color-mix(in srgb, var(--iris) 55%, var(--border));
  color: var(--text);
}
.pl-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(158px, 1fr));
  gap: 12px;
  max-height: min(58vh, 520px);
  overflow-y: auto;
  padding: 2px;
}
.pl-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 14px 10px 12px;
  border-radius: 16px;
  border: 1px solid var(--border-soft);
  background: linear-gradient(180deg, color-mix(in srgb, var(--iris) 9%, transparent), var(--chrome-2));
  cursor: pointer;
  text-align: center;
  transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
}
.pl-card:hover { transform: translateY(-2px); border-color: color-mix(in srgb, var(--iris) 50%, var(--border)); box-shadow: 0 12px 28px -16px rgba(0,0,0,.6); }
.pl-card.on { border-color: var(--iris); box-shadow: 0 0 0 2px color-mix(in srgb, var(--iris) 45%, transparent); }
.pl-card svg { display: block; }
.pl-name { font-size: 13.5px; font-weight: 700; color: var(--text); }
.pl-role { font-size: 11.5px; color: var(--muted); }
.pl-meta { display: flex; gap: 5px; flex-wrap: wrap; justify-content: center; margin-top: 2px; }
.pl-chip {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 999px;
  background: rgba(124, 108, 248, 0.16);
  color: var(--iris-ink);
}
.pl-chip.pl-voice { background: rgba(56, 224, 176, 0.15); color: #3bbf98; }
.pl-preview {
  margin-top: 6px;
  font-size: 11.5px;
  font-weight: 600;
  border-radius: 8px;
  border: 1px solid var(--border-soft);
  background: var(--chrome-3);
  color: var(--text);
  padding: 4px 10px;
  cursor: pointer;
}
.pl-preview:hover { border-color: color-mix(in srgb, var(--iris) 50%, var(--border)); }
.pl-empty { padding: 26px 4px; text-align: center; color: var(--muted); font-size: 13px; }

/* ---- Deep-dive rows: "Why this matters" / "Technical detail" ---- */
.pr-note-x { display: none; }
body.presenting .pr-note-x:not([hidden]) {
  display: block;
  flex: none;
  margin-top: 10px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.045);
  border: 1px solid rgba(255, 255, 255, 0.09);
  overflow: hidden;
}
.pr-note-x-h {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 11px 13px;
  background: none;
  border: 0;
  color: rgba(233, 236, 255, 0.9);
  font: 600 13.5px var(--sans);
  cursor: pointer;
  text-align: left;
}
.pr-note-x-h > svg:first-child { width: 16px; height: 16px; flex: none; color: var(--iris-bright); }
.pr-note-x-h span { flex: 1 1 auto; }
.pr-note-x-chev { width: 14px; height: 14px; flex: none; opacity: .6; transition: transform .3s ease; }
.pr-note-x.open .pr-note-x-chev { transform: rotate(180deg); }
.pr-note-x-b {
  display: none;
  padding: 0 13px 12px 38px;
  font-size: 13px;
  line-height: 1.55;
  color: rgba(233, 236, 255, 0.78);
}
.pr-note-x.open .pr-note-x-b { display: block; }

/* ---- Control bar: the play control becomes the glowing centerpiece. ---- */
body.presenting #prPlay {
  border-radius: 999px;
  min-width: 66px;
  font-weight: 750;
  background: linear-gradient(180deg, var(--iris-bright), var(--iris-deep));
  color: #fff;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--iris) 22%, transparent),
              0 10px 26px -10px var(--iris);
}
body.presenting #svgHost {
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 20px 56px -30px rgba(0, 0, 0, 0.72), 0 0 0 1px rgba(255, 255, 255, 0.08);
}
/* The pre-roll is a clean title/splash surface.  Keep the already-mounted
   editor SVG and guide reservation out of sight until app.js releases the
   first real step; visibility preserves the measured stage geometry. */
body.pr-opening-hold #svgHost,
body.pr-opening-hold #prNarration,
body.pr-opening-hold #prCaption,
body.pr-opening-hold #prPresenter,
body.pr-opening-hold #prTvHead,
body.pr-opening-hold #presentBar,
body.pr-opening-hold #prProgress,
body.pr-opening-hold #prStepper {
  visibility: hidden;
}
/* The clean sheet title is the second opening screen.  It is timed and
   navigable like the cover, while the document remains completely held back. */
body.pr-sheet-title-hold #svgHost,
body.pr-sheet-title-hold #prNarration,
body.pr-sheet-title-hold #prCaption,
body.pr-sheet-title-hold #prPresenter,
body.pr-sheet-title-hold #prTvHead,
body.pr-sheet-title-hold #presentBar,
body.pr-sheet-title-hold #prProgress,
body.pr-sheet-title-hold #prStepper {
  visibility: hidden;
}
#prTvHead { display: none; }
/* The unit's nameplate: a tinted label bar hugging the ring's top edge, so
   the whole container reads as one branded appliance. It carries the document
   title, and a title belongs at the head. (Briefly docked to the foot; that
   was a misread of a note about the TITLE SLIDE's logo, not this bar.) */
body.presenting #prTvHead {
  display: flex;
  align-items: center;
  gap: 9px;
  position: absolute;
  top: 5px;
  left: 5px;
  right: 5px;
  height: var(--pr-plate, 36px);
  padding: 0 12px;
  z-index: 5;
  pointer-events: none;
  color: #c3cbdb;
  background: linear-gradient(180deg, rgba(124, 108, 248, 0.20), rgba(124, 108, 248, 0.07) 78%, rgba(124, 108, 248, 0.03));
  border-bottom: 1px solid rgba(156, 141, 255, 0.22);
  border-radius: 11px 11px 0 0;
  opacity: 0;
  animation: prBrandIn .5s ease .12s forwards;
}
body.presenting #prTvHead svg { width: 18px; height: 18px; flex: none; }
#prTvHome {
  appearance: none;
  width: 24px;
  height: 24px;
  padding: 3px;
  border: 0;
  border-radius: 7px;
  color: inherit;
  background: transparent;
  cursor: pointer;
  pointer-events: auto;
}
#prTvHome:hover:not(:disabled) { background: rgba(255, 255, 255, .10); }
#prTvHome:focus-visible { outline: 2px solid var(--iris-bright); outline-offset: 2px; }
#prTvHome:disabled { cursor: default; opacity: .72; }
.pr-tv-word { font-family: var(--sans); font-weight: 700; font-size: 14px; letter-spacing: -0.03em; color: #fff; white-space: nowrap; }
.pr-tv-word .um { color: var(--iris-bright); }
#prTvTitle {
  font-size: 12.5px;
  color: rgba(233, 236, 255, 0.55);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  flex: 0 1 auto;
}
#prTvTitle:not(:empty)::before { content: '·'; margin-right: 9px; color: rgba(233, 236, 255, 0.35); }
/* Lives inside the #prTvNav pill, which supplies the chrome. */
#prTvStep {
  flex: none;
  font-size: 12.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: rgba(233, 236, 255, 0.82);
  padding: 0 9px;
  white-space: nowrap;
}
#prTvNav[hidden] { display: none; }
/* The header carries the brand now — retire the corner pill while presenting.
   NOTE: the present overlays (progress, notes, caption…) live inside
   #canvasWrap, which the stage's bezel padding already pushes below the
   header — their own offsets are bezel-relative. */
body.presenting #prBrand { display: none; }
/* Notes: from full-height dock to a floating card inside the unit. The
   background/color/left/right land inline from positionNotesPanel(), hence
   the !importants — everything else the stylesheet owns. Present chrome is
   dark in both themes (intentional), so the card is too; the note-style
   presets keep styling the inner text card. */
body.presenting #prNarration {
  /* Exactly the diagram's lane — same top, same bottom, same height. The panel
     is a child of #canvasWrap, so it inherits both lane variables and stays
     flush with the sheet whatever the control bar does (a wrapped two-row bar
     moves both edges together). */
  top: var(--pr-lane-top, 34px);
  bottom: var(--pr-lane-bottom, calc(var(--pr-band, 66px) + 12px));
  max-height: none;
  border-radius: 12px;
  min-width: 250px;
  background: rgba(15, 17, 27, 0.92) !important;
  color: #e9ecff !important;
  border: 1px solid rgba(255, 255, 255, 0.1) !important;
  box-shadow: 0 22px 60px -24px rgba(0, 0, 0, 0.8) !important;
  -webkit-backdrop-filter: blur(12px) saturate(1.2);
  backdrop-filter: blur(12px) saturate(1.2);
}
body.presenting #prNarration.pos-right { right: 26px !important; left: auto !important; }
body.presenting #prNarration.pos-left { left: 26px !important; right: auto !important; }

/* ---- The control bar: one floating glass pill, clustered. ---- */
#presentBar {
  position: absolute; left: 50%; bottom: 16px; transform: translateX(-50%);
  display: flex; align-items: stretch; gap: 10px;
  max-width: calc(100% - 24px);
  background: rgba(13, 15, 24, 0.86);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 18px;
  padding: 8px;
  z-index: 7;
  box-shadow: 0 1px 0 rgba(255, 255, 255, .08) inset, 0 18px 48px -16px rgba(0, 0, 0, 0.65);
  -webkit-backdrop-filter: blur(18px) saturate(1.5);
  backdrop-filter: blur(18px) saturate(1.5);
  transition: opacity .35s ease, transform .35s ease;
}
.prc { display: flex; align-items: center; gap: 2px; position: relative; }
.prc + .prc::before {
  content: ""; align-self: center; width: 1px; height: 22px;
  background: rgba(255, 255, 255, 0.12); margin-right: 8px; flex: none;
}
.prc-status { gap: 10px; padding: 0 6px; }
#prCount { font-size: 12.5px; color: rgba(232, 234, 244, .92); font-variant-numeric: tabular-nums; white-space: nowrap; font-weight: 600; }
#prTime { font-family: var(--mono); font-size: 11px; color: rgba(232, 234, 244, .82); font-variant-numeric: tabular-nums; white-space: nowrap; }
.pr-btn {
  position: relative;
  padding: 7px 11px; border-radius: 10px;
  color: rgba(232, 234, 244, .9);
  font-size: 13px; white-space: nowrap; cursor: pointer;
  transition: background var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.pr-btn:hover { background: rgba(255, 255, 255, .1); color: #fff; }
.pr-btn:active { transform: scale(.94); }
.pr-ico { width: 36px; height: 34px; padding: 0; display: inline-flex; align-items: center; justify-content: center; }
.pr-ico svg { width: 17px; height: 17px; display: block; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.pr-play {
  background: var(--iris); color: #fff; font-weight: 640; min-width: 66px;
  margin: 0 3px; border-radius: 999px; padding: 7px 16px;
  box-shadow: 0 1px 0 rgba(255,255,255,.22) inset, 0 6px 18px -6px color-mix(in srgb, var(--iris) 70%, transparent);
  transition: filter var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-spring);
}
.pr-play:hover { background: var(--iris); filter: brightness(1.1); transform: scale(1.03); }
.pr-btn.on, .pr-btn[aria-pressed="true"] { background: color-mix(in srgb, var(--iris) 30%, transparent); color: #cfc8ff; box-shadow: 0 0 0 1px color-mix(in srgb, var(--iris) 45%, transparent) inset; }
.pr-exit { color: rgba(232, 234, 244, .85); font-weight: 560; }
.pr-exit:hover { color: #fff; background: rgba(240, 84, 106, .22); }
.pr-rec-dot { fill: currentColor; stroke: none; }
#prRecord:hover .pr-rec-dot { fill: #f0546a; }
/* Legacy select kept for app.js's speed contract — visually replaced by the
   pace slider in settings. (Hidden stub wrapper in markup.) */

/* ---- Tooltips: every control explains itself. ---- */
[data-tip] { position: relative; }
[data-tip]::after {
  content: attr(data-tip);
  position: absolute; left: 50%; bottom: calc(100% + 10px); transform: translateX(-50%) translateY(3px);
  background: rgba(10, 12, 20, 0.94); color: #e8eaf4;
  font-size: 11.5px; font-weight: 500; letter-spacing: .01em; line-height: 1.35;
  padding: 6px 10px; border-radius: 8px; border: 1px solid rgba(255,255,255,.1);
  white-space: nowrap; pointer-events: none; opacity: 0; z-index: 30;
  box-shadow: 0 8px 24px -8px rgba(0,0,0,.6);
  transition: opacity .18s ease .3s, transform .18s ease .3s;
}
[data-tip]:hover::after, [data-tip]:focus-visible::after { opacity: 1; transform: translateX(-50%) translateY(0); }
[data-tip]::after { max-width: min(260px, 60vw); white-space: normal; width: max-content; text-align: center; }
/* Edge buttons anchor their tooltip to the near edge so it can't spill out. */
.prc:first-child > [data-tip]:first-child::after { left: 0; transform: translateX(0) translateY(3px); }
.prc:first-child > [data-tip]:first-child:hover::after,
.prc:first-child > [data-tip]:first-child:focus-visible::after { transform: translateX(0) translateY(0); }
.prc:last-child > [data-tip]:last-child::after { left: auto; right: 0; transform: translateX(0) translateY(3px); }
.prc:last-child > [data-tip]:last-child:hover::after,
.prc:last-child > [data-tip]:last-child:focus-visible::after { transform: translateX(0) translateY(0); }
/* Inside the scrolling popovers a floating tooltip would be clipped by the
   panel and (on flex rows) would become a stray flex item, so it is suppressed
   there — those controls carry visible labels already. Buttons keep theirs. */
.pr-pop .pr-slider-row[data-tip]::after,
.pr-pop .pr-check[data-tip]::after,
.pr-pop .pr-avatar-row[data-tip]::after,
.pr-pop .pr-voice-row[data-tip]::after { content: none; }
.pr-pop button[data-tip]::after { max-width: 200px; }

/* ---- Auto-hide during playback (body.pr-idle set by js/present-plus.js).
   The brand lockup deliberately stays: every slide keeps the Diagramium mark. */
body.pr-idle #presentBar, body.pr-idle .pr-pop { opacity: 0; transform: translateX(-50%) translateY(10px); pointer-events: none; }
body.pr-idle #canvasWrap, body.pr-idle #canvasWrap * { cursor: none; }

/* ---- Zen mode (body.pr-zen, set by setPresentZen in js/app.js): a genuine
   full-bleed diagram, not a smaller version of the podium. The frame, brand,
   header, guide and notes all leave; only the existing auto-hidden transport
   dock returns on pointer movement so touch users still have a way out. */
body.pr-zen #prTvHead, body.pr-zen #prStepper,
body.pr-zen #prProgress, body.pr-zen #prCaption, body.pr-zen #prBrand,
body.pr-zen #prFrame, body.pr-zen #prSegBar { display: none !important; }
body.pr-zen:not(.pr-zen-notes) #prNarration { display: none !important; }
/* The karaoke strip STAYS in zen — it is narration, not podium — but it
   cannot ride the collapsed band: every bottom strip stacks off --pr-band,
   which zen pins to 10px so the woken dock may overlay the sheet, and that
   drops the subtitle underneath the dock. --pr-plate-lift is the bar's real
   occupied height, published on #stage by syncBottomBand from LAYOUT metrics
   (so a faded pr-idle bar still reports its slot and the strip never hops). */
body.pr-zen #prSubtitle { bottom: calc(var(--pr-plate-lift, 66px) + 12px); }
body.pr-zen #canvasWrap, body.pr-zen #stage { --pr-band: 10px !important; }
/* Zen is a single continuous sheet, including the title lane — a dark stage
   behind a light diagram makes the title feel like leftover chrome. */
body.pr-zen.presenting #stage { padding: 0; background: var(--pr-sheet, #fff); }
body.pr-zen:not(.pr-zen-notes):not(.pr-zen-title).presenting #canvasWrap {
  --pr-lane-top: 0px;
  --pr-lane-bottom: 0px;
  padding: 0 !important;
  background: var(--pr-sheet, #fff);
}
body.pr-zen #zoomLayer { padding: 0; }
body.pr-zen #svgHost, body.pr-zen #svgHost svg {
  border-radius: 0;
  box-shadow: none;
}
/* The authored document title is often centred inside the drawing and can be
   cropped by the readability camera. Zen owns one quiet, always-visible
   sheet-native title bar instead. Its mark and count are deliberately small:
   they orient the audience without competing with the diagram. */
#prZenTitle { display: none; }
body.pr-zen #svgHost g[data-role="doc-title"] { display: none; }
body.pr-zen-title #prZenTitle {
  display: flex;
  align-items: center;
  justify-content: center;
  position: absolute;
  z-index: 6;
  top: 0;
  left: 0;
  right: 0;
  box-sizing: border-box;
  min-height: clamp(60px, 8vh, 80px);
  padding: clamp(12px, 2vh, 20px) clamp(16px, 5vw, 72px) clamp(10px, 1.5vh, 15px);
  color: var(--pr-sheet-ink, #1b1b22);
  background: color-mix(in srgb, var(--iris) 5%, var(--pr-sheet, #fff));
  pointer-events: none;
  text-shadow: none;
}
/* The title itself gets a protected middle lane. The two positioned bookends
   stay genuinely flush to the bar's sides, while a long title cannot overlap
   either one on a narrow screen. */
#prZenTitleText {
  display: block;
  min-width: 0;
  max-width: min(76vw, calc(100% - 148px));
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: inherit;
  font: 700 clamp(18px, 2vw, 30px)/1.2 var(--sans);
  letter-spacing: -.025em;
  text-align: center;
}
#prZenMark {
  position: absolute;
  left: clamp(16px, 3vw, 40px);
  width: clamp(20px, 2.15vw, 27px);
  height: clamp(20px, 2.15vw, 27px);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: color-mix(in srgb, var(--pr-sheet-ink, #1b1b22) 82%, var(--iris));
  appearance: none;
  padding: 0;
  border: 0;
  background: transparent;
  border-radius: 8px;
  cursor: pointer;
  pointer-events: auto;
}
#prZenMark svg { display: block; width: 100%; height: 100%; }
#prZenMark:hover:not(:disabled) { background: color-mix(in srgb, var(--iris) 10%, transparent); }
#prZenMark:focus-visible { outline: 2px solid var(--iris); outline-offset: 3px; }
#prZenMark:disabled { cursor: default; opacity: .72; }
#prZenStep {
  position: absolute;
  right: clamp(16px, 3vw, 40px);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 50px;
  padding: 4px 8px;
  border-radius: 999px;
  color: color-mix(in srgb, var(--pr-sheet-ink, #1b1b22) 78%, var(--pr-sheet, #fff));
  background: color-mix(in srgb, var(--pr-sheet-ink, #1b1b22) 6%, var(--pr-sheet, #fff));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--pr-sheet-ink, #1b1b22) 10%, transparent) inset;
  font: 650 clamp(10.5px, 1vw, 12px)/1 var(--mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: .035em;
  white-space: nowrap;
}
/* This is a layout lane, not a visual frame: the diagram's fit is measured
   against the remaining space, so the title never sits on top of a node. */
body.pr-zen-title.presenting #canvasWrap {
  padding-top: clamp(60px, 8vh, 80px) !important;
}
/* Zen's optional Step Guide is a first-class split layout, not an overlay.
   Keep it beneath the bare title, while the normal reserve logic handles its
   side/bottom footprint and preserves the full note treatment (image + why). */
body.pr-zen-notes.pr-zen-title:not(.pr-notes-bottom) #prNarration {
  top: clamp(60px, 8vh, 80px) !important;
}
/* Opening and closing cards are still show content in Zen — never a reason
   to bring back the old dark theatre overlay. The card title itself already
   inherits the document sheet inline; these two modal surfaces need the same
   treatment so the whole show reads as one continuous canvas. */
body.pr-zen #prIntro,
body.pr-zen #prEnd {
  background: var(--pr-sheet, #fff);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
body.pr-zen #prIntro .pr-intro-card,
body.pr-zen #prEnd .pr-end-card { color: var(--pr-sheet-ink, #1b1b22); }
body.pr-zen #prIntro .pr-intro-mark,
body.pr-zen #prEnd .pr-end-mark { color: color-mix(in srgb, var(--pr-sheet-ink, #1b1b22) 76%, var(--pr-sheet, #fff)); }
body.pr-zen #prIntro .pr-brandin-word { color: var(--pr-sheet-ink, #1b1b22); }
body.pr-zen #prIntro .pr-intro-meta span,
body.pr-zen #prIntro .pr-intro-foot,
body.pr-zen #prEnd .pr-end-sub,
body.pr-zen #prEnd .pr-end-h,
body.pr-zen #prEnd .pr-end-takeaways li,
body.pr-zen #prEnd .pr-end-share-txt small,
body.pr-zen #prEnd .pr-end-brand { color: color-mix(in srgb, var(--pr-sheet-ink, #1b1b22) 68%, var(--pr-sheet, #fff)); }
/* The wordmark inside that line has to follow it. Zen paints the end slide on
   the SHEET (opaque, and usually white), while the base rule below sets the
   wordmark to a near-white that only makes sense on the dark scrim a non-zen
   end slide draws for itself — measured at 1.11 contrast on a white sheet,
   i.e. "Made with" legible and "Diagram" gone. Emphasis here means CLOSER TO
   THE INK, not brighter: mixing against the sheet's own ink is what makes
   that flip correctly on a chalkboard or blueprint sheet too. */
body.pr-zen #prEnd .pr-end-brand .dbrand { color: color-mix(in srgb, var(--pr-sheet-ink, #1b1b22) 88%, var(--pr-sheet, #fff)); }
body.pr-zen #prIntro .pr-intro-meta span,
body.pr-zen #prIntro .pr-mini,
body.pr-zen #prEnd .pr-end-btn:not(.pr-end-primary) {
  border-color: color-mix(in srgb, var(--pr-sheet-ink, #1b1b22) 18%, var(--pr-sheet, #fff));
}
body.pr-zen #prIntro .pr-mini,
body.pr-zen #prEnd .pr-end-btn:not(.pr-end-primary) {
  color: color-mix(in srgb, var(--pr-sheet-ink, #1b1b22) 86%, var(--pr-sheet, #fff));
  background: color-mix(in srgb, var(--pr-sheet-ink, #1b1b22) 6%, var(--pr-sheet, #fff));
}
body.pr-zen #prIntro .pr-mini:hover,
body.pr-zen #prEnd .pr-end-btn:not(.pr-end-primary):hover {
  background: color-mix(in srgb, var(--pr-sheet-ink, #1b1b22) 11%, var(--pr-sheet, #fff));
}
#prZenToggle.on { background: color-mix(in srgb, var(--iris) 30%, transparent); color: #cfc8ff; }

/* ---- Tap-zone feedback (#prTapFx, js/app.js gesture layer): a soft
   directional chevron that breathes once where the tap landed. Pure
   feedback — pointer-events none, aria-hidden, removed from flow when
   hidden. ---- */
#prTapFx {
  position: absolute; top: 50%; z-index: 8;
  width: 56px; height: 56px; margin-top: -28px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: rgba(13, 15, 24, 0.5);
  color: rgba(240, 242, 252, 0.95);
  pointer-events: none;
  opacity: 0;
}
#prTapFx[hidden] { display: none; }
#prTapFx svg { width: 26px; height: 26px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
/* Offset from the DIAGRAM column, not the wrap: absolute offsets resolve
   against the padding box, and the notes reserve is paid as padding — so a
   plain `right: 18px` put the forward chevron on top of the notes panel.
   The lanes are published by reserveNotesColumn (publishTapLanes). */
#prTapFx.left { left: calc(18px + var(--pr-tap-left, 0px)); }
#prTapFx.right { right: calc(18px + var(--pr-tap-right, 0px)); }
#prTapFx.left svg { transform: rotate(180deg); }
#prTapFx.show { animation: prTapPulse .38s cubic-bezier(0, 0, 0.2, 1); }
@keyframes prTapPulse {
  0% { opacity: 0; transform: scale(.82); }
  30% { opacity: .95; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.06); }
}
@media (prefers-reduced-motion: reduce) {
  #prTapFx.show { animation: none; opacity: .8; transition: opacity .25s ease; }
}

/* ---- Segmented step progress (#prSegBar, present-plus.js) ---------------
   A slim strip of cells over the top of the stage: filled behind you, bright
   where you are, faint ahead. Yields to the opt-in numbered stepper. */
body.presenting #prSegBar {
  position: absolute; top: 10px; left: 50%; transform: translateX(-50%);
  z-index: 24; display: flex; gap: 4px; padding: 4px 6px;
  max-width: min(680px, 72vw);
  background: color-mix(in srgb, #0b0d14 55%, transparent);
  border-radius: 999px;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  transition: opacity .35s ease;
}
#prSegBar[hidden] { display: none; }
#prSegBar .pr-seg {
  flex: 1 1 0; min-width: 10px; height: 4px; padding: 0; border: 0;
  border-radius: 999px; cursor: pointer;
  background: rgba(233, 236, 255, .22);
  transition: background .3s ease, transform .2s ease;
}
#prSegBar .pr-seg:hover { transform: scaleY(1.8); }
#prSegBar .pr-seg.is-done { background: color-mix(in srgb, var(--iris, #6a5cff) 60%, transparent); }
#prSegBar .pr-seg.is-active {
  background: var(--iris-bright, #8b80ff);
  box-shadow: 0 0 8px color-mix(in srgb, var(--iris-bright, #8b80ff) 70%, transparent);
}
body.pr-idle #prSegBar { opacity: .35; }

/* ---- Completed-step dim (svg.pr-step-dim, present-plus.js) --------------
   Already-revealed elements rest while the active step and its direct
   neighbours speak. Render-time classes on the step's own svg — the finale
   and every export stay untouched. */
body.presenting #svgHost svg.pr-step-dim .pr-done-dim {
  opacity: var(--pr-context-opacity, .9);
  transition: opacity .45s ease;
}

/* ---- Step Guide collapse (body.pr-guide-collapsed) ----------------------
   The panel folds to a slim rail; content is hidden, never removed.
   layoutPresentChrome measures the panel's REAL rect, so the sheet takes
   the reclaimed width with no second geometry. */
#prGuideCollapse svg { transition: transform .25s ease; }
body.pr-guide-collapsed #prNarration { width: 46px !important; min-width: 46px !important; cursor: pointer; overflow: hidden; }
body.pr-guide-collapsed #prNarration > :not(#prNarrationHead) { display: none !important; }
body.pr-guide-collapsed #prNarrationHead { flex-direction: column; gap: 10px; }
body.pr-guide-collapsed .pr-nh-label { writing-mode: vertical-rl; }
body.pr-guide-collapsed .pr-nh-label svg { display: none; }
body.pr-guide-collapsed #prNarrationEditBtn, body.pr-guide-collapsed #prNarrationFlip { display: none; }
body.pr-guide-collapsed #prGuideCollapse svg { transform: rotate(180deg); }

@media (prefers-reduced-motion: reduce) {
  #prSegBar, #prSegBar .pr-seg, body.presenting #svgHost svg.pr-step-dim .pr-done-dim, #prGuideCollapse svg { transition: none; }
}

/* ---- Captions & live subtitles. ----
   Everything in the bottom band stacks off --pr-band: the height #presentBar
   actually occupies, measured by js/present-plus.js (syncBottomBand). The old
   fixed 78px assumed a single-row bar, but the bar wraps to two rows whenever
   the CANVAS is narrow (side panels open, not just small screens) and then sat
   right on top of the caption. The 66px fallback reproduces the original
   offsets exactly, so nothing moves before the first measurement. */
/* ---- The reading sizes -------------------------------------------------
   Every piece of type an audience READS during a show — the note, the
   subtitle strip, the step caption, the why bubble — is written as
   `calc(<size> * var(--pr-text-k, 1))` so smart stage can lift the lot with
   one number (body.pr-smart below) and leave every relationship between them
   intact. A multiplier rather than a second set of sizes on purpose: the
   audience skins already set their own sizes on a heavier selector, and
   another absolute rule would have to out-specify them one at a time and then
   stay in step forever. Unset it is exactly 1, which is what "intelligence
   off" has to mean.
   ---------------------------------------------------------------------- */
body.pr-smart { --pr-text-k: 1.12; }

#prCaption {
  position: absolute; left: 50%; bottom: calc(12px + var(--pr-band, 66px)); transform: translateX(-50%);
  width: max-content; max-width: min(760px, 88%);
  background: rgba(12, 14, 22, 0.8);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  color: #fff; border: 1px solid rgba(255,255,255,.08);
  border-radius: 12px; padding: 10px 20px;
  font-size: calc(16px * var(--pr-text-k, 1)); font-weight: 520; line-height: 1.35; text-align: center;
  z-index: 6; opacity: 0; transition: opacity .25s ease, transform .25s ease;
  pointer-events: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
#prCaption.show { opacity: 1; }
/* `width: max-content` for the same reason #presentBar needs it: an
   absolutely-positioned box with only `left: 50%` shrink-to-fits into the
   space to the RIGHT of that 50%, so max-width never got to do its job and a
   wrapping subtitle was stuck at half the canvas (three lines where two fit).
   The caption escaped this only because `nowrap` makes its min-content huge. */
#prSubtitle {
  position: absolute; left: 50%; bottom: calc(12px + var(--pr-band, 66px)); transform: translateX(-50%);
  width: max-content; max-width: min(820px, 90%);
  background: rgba(12, 14, 22, 0.86);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  color: #fff; border: 1px solid color-mix(in srgb, var(--iris) 35%, transparent);
  border-left: 3px solid var(--iris);
  border-radius: 12px; padding: 11px 20px;
  font-size: calc(16.5px * var(--pr-text-k, 1)); line-height: 1.45; text-align: left;
  z-index: 6; pointer-events: none;
}
#prSubtitle b { color: var(--iris-bright); font-weight: inherit; }

/* ---- Audience skins ----------------------------------------------------
   body.pr-aud-<key> is stamped by the Audience preset (present-plus.js).
   Present-only re-tints: the chrome stays DARK in every skin (deliberate,
   same rule as the base stage) — these shift its hue and scale the reading
   surfaces, they never lighten the room. The notes-text bumps repeat the
   full #prNarration[data-note-style] chain because that selector's
   specificity is what beats the per-style font rules below it. */
body.presenting.pr-aud-kids #stage {
  background: linear-gradient(180deg, #221046 0%, #2c1258 58%, #3a1766 100%);
}
body.pr-aud-kids #prCaption {
  background: rgba(255, 209, 102, 0.94); color: #3a2500;
  border-color: rgba(212, 150, 0, 0.8); font-weight: 750; font-size: calc(17.5px * var(--pr-text-k, 1));
}
body.pr-aud-kids #prSubtitle { font-size: calc(19px * var(--pr-text-k, 1)); line-height: 1.5; }
body.pr-aud-kids #prNarration[data-note-style] #prNarrationText { font-size: calc(17.5px * var(--pr-text-k, 1)); line-height: 1.62; }
body.presenting.pr-aud-tween #stage {
  background: linear-gradient(180deg, #0b1f1b 0%, #0f2c26 58%, #123a30 100%);
}
body.pr-aud-tween #prCaption { border-color: rgba(52, 211, 153, 0.45); }
body.pr-aud-tween #prSubtitle { font-size: calc(17.5px * var(--pr-text-k, 1)); }
body.pr-aud-tween #prNarration[data-note-style] #prNarrationText { font-size: calc(16px * var(--pr-text-k, 1)); line-height: 1.55; }

/* ---- Title card (legacy quick card, still used in watch mode). ---- */
#prTitleCard {
  position: absolute; inset: 0; z-index: 7;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; text-align: center; padding: 24px;
  opacity: 0; transition: opacity .3s ease; pointer-events: none;
}
#prTitleCard.show { opacity: 1; }
#prTitleCardHeader, #prTitleCardFooter { font-size: 13px; max-width: 80%; }
#prTitleCardTitle { font-size: clamp(22px, 4vw, 40px); font-weight: 700; max-width: 86%; }

/* ---- Intro screen. ---- */
#prIntro, #prEnd {
  position: absolute; inset: 0; z-index: 8;
  display: flex; align-items: center; justify-content: center; padding: 24px;
  background:
    radial-gradient(900px 480px at 50% 20%, color-mix(in srgb, var(--iris) 10%, transparent), transparent 70%),
    rgba(8, 9, 15, 0.78);
  -webkit-backdrop-filter: blur(14px) saturate(1.2); backdrop-filter: blur(14px) saturate(1.2);
}
.pr-intro-card, .pr-end-card {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  max-width: min(620px, 92%); max-height: 100%; overflow-y: auto; padding: 10px;
  color: #eef0f8;
  animation: prCardIn .5s var(--ease-out) both;
}
@keyframes prCardIn { from { opacity: 0; transform: translateY(14px) scale(.97); } to { opacity: 1; transform: none; } }
.pr-intro-eyebrow {
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .16em; text-transform: uppercase;
  color: #8b80ff; margin-bottom: 16px;
}
.pr-intro-title { font-size: clamp(28px, 4.6vw, 52px); font-weight: 680; letter-spacing: -0.03em; line-height: 1.08; text-wrap: balance; }
.pr-intro-meta { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; margin-top: 18px; }
.pr-intro-meta span {
  font-family: var(--mono); font-size: 11.5px; color: rgba(238,240,248,.72);
  border: 1px solid rgba(255,255,255,.16); border-radius: 999px; padding: 5px 12px;
}
.pr-intro-play {
  margin-top: 34px;
  display: inline-flex; align-items: center; gap: 12px;
  font-size: 17px; font-weight: 640; color: #fff; cursor: pointer;
  background: var(--iris); border: none; border-radius: 999px; padding: 14px 34px 14px 24px;
  box-shadow: 0 1px 0 rgba(255,255,255,.24) inset, 0 16px 44px -12px color-mix(in srgb, var(--iris) 85%, transparent);
  transition: transform var(--dur-2) var(--ease-spring), filter var(--dur-1) var(--ease-out);
  position: relative;
}
.pr-intro-play:hover { transform: scale(1.045); filter: brightness(1.08); }
.pr-intro-play::after { content: ""; position: absolute; inset: 0; border-radius: 999px; border: 1.5px solid var(--iris-bright); opacity: 0; pointer-events: none; }
.pr-intro-play:hover::after { animation: lp-play-ring .9s var(--ease-out); }
.pr-intro-play-ico {
  width: 34px; height: 34px; border-radius: 50%; background: rgba(255,255,255,.2);
  display: inline-flex; align-items: center; justify-content: center; font-size: 13px; padding-left: 2px;
}
.pr-intro-row { display: flex; gap: 10px; margin-top: 16px; }
.pr-intro-foot { margin-top: 26px; font-family: var(--mono); font-size: 11px; color: rgba(238,240,248,.45); }
.pr-mini {
  font: inherit; font-size: 12.5px; font-weight: 560; color: rgba(238,240,248,.85); cursor: pointer;
  background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.14); border-radius: 9px;
  padding: 7px 13px;
  transition: background var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out);
}
.pr-mini:hover { background: rgba(255,255,255,.15); border-color: rgba(255,255,255,.3); }
.pr-mini-wide { width: 100%; margin-top: 8px; text-align: center; }

/* ---- End slide. ---- */
.pr-end-mark { width: 46px; height: 46px; color: #8b93ad; margin-bottom: 14px; }
.pr-end-mark svg { width: 100%; height: 100%; }
.pr-end-title { font-size: clamp(30px, 4.6vw, 50px); font-weight: 680; letter-spacing: -0.03em; }
.pr-end-sub { margin-top: 10px; font-size: 14.5px; color: rgba(238,240,248,.7); }
.pr-end-takeaways { margin-top: 22px; width: 100%; max-width: 460px; text-align: left; }
.pr-end-h { font-family: var(--mono); font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase; color: rgba(238,240,248,.5); margin-bottom: 8px; }
.pr-end-takeaways ol { margin: 0; padding: 0 0 0 20px; display: flex; flex-direction: column; gap: 6px; }
.pr-end-takeaways li { font-size: 13.5px; line-height: 1.45; color: rgba(238,240,248,.85); }
.pr-end-share { margin-top: 22px; display: flex; align-items: center; gap: 16px; }
.pr-end-share svg { width: 92px; height: 92px; border-radius: 10px; background: #fff; padding: 6px; color: #10121a; }
.pr-end-share-txt { text-align: left; display: flex; flex-direction: column; gap: 8px; }
.pr-end-share-txt small { font-size: 11.5px; color: rgba(238,240,248,.55); max-width: 220px; line-height: 1.45; }
.pr-end-ctas { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin-top: 26px; }
.pr-end-btn {
  font: inherit; font-size: 13.5px; font-weight: 580; color: rgba(238,240,248,.9); cursor: pointer;
  background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.14); border-radius: 11px;
  padding: 10px 18px;
  transition: background var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.pr-end-btn:hover { background: rgba(255,255,255,.16); transform: translateY(-1px); }
.pr-end-primary { background: var(--iris); border-color: transparent; color: #fff; box-shadow: 0 10px 26px -10px color-mix(in srgb, var(--iris) 80%, transparent); }
.pr-end-primary:hover { background: var(--iris); filter: brightness(1.08); }
.pr-end-brand { margin-top: 28px; font-size: 12px; color: rgba(238,240,248,.5); }
.pr-end-brand .dbrand { color: rgba(238,240,248,.8); }

/* ---- Popovers (settings / share / record). ---- */
.pr-pop {
  position: absolute; bottom: 76px; left: 50%; transform: translateX(-50%);
  width: min(340px, calc(100% - 28px)); max-height: min(66vh, 560px); overflow-y: auto;
  background: rgba(13, 15, 24, 0.9);
  -webkit-backdrop-filter: blur(20px) saturate(1.5); backdrop-filter: blur(20px) saturate(1.5);
  border: 1px solid rgba(255,255,255,.12); border-radius: 16px;
  box-shadow: 0 1px 0 rgba(255,255,255,.07) inset, 0 24px 64px -20px rgba(0,0,0,.7);
  padding: 16px; z-index: 9; color: #e8eaf4;
  animation: prPopIn .22s var(--ease-out) both;
}
@keyframes prPopIn { from { opacity: 0; transform: translateX(-50%) translateY(8px); } to { opacity: 1; transform: translateX(-50%) translateY(0); } }
/* Phones: the popover becomes a full-width bottom sheet — thumb-reachable,
   rounded top, riding above the home indicator. Same element, same wiring. */
@media (max-width: 760px), (max-height: 560px) and (max-width: 1024px) {
  .pr-pop {
    left: 0; right: 0; bottom: 0; transform: none;
    width: auto; max-height: 72vh;
    border-radius: 18px 18px 0 0; border-left: 0; border-right: 0; border-bottom: 0;
    padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
    animation: prSheetIn .26s var(--ease-out) both;
  }
  @keyframes prSheetIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
  body.pr-idle .pr-pop { transform: translateY(10px); }
}
.pr-pop-sec { padding: 12px 2px; }
.pr-pop-sec + .pr-pop-sec { border-top: 1px solid rgba(255,255,255,.08); }
.pr-pop-h { font-family: var(--mono); font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase; color: rgba(232,234,244,.78); margin-bottom: 10px; }
.pr-voice-row { display: flex; gap: 8px; }
.pr-voice-row select, .pr-pop select {
  flex: 1; min-width: 0; font: inherit; font-size: 13px; color: #e8eaf4;
  background: rgba(255,255,255,.07); border: 1px solid rgba(255,255,255,.14); border-radius: 9px; padding: 7px 9px;
}
.pr-pop select option { background: #14161f; color: #e8eaf4; }
.pr-slider-row { display: flex; align-items: center; gap: 10px; margin-top: 10px; font-size: 12.5px; color: rgba(232,234,244,.8); }
.pr-slider-row span { width: 52px; flex: none; }
.pr-slider-row em { font-style: normal; font-family: var(--mono); font-size: 11px; width: 36px; text-align: right; color: rgba(232,234,244,.85); }
.pr-slider-row input[type="range"] { flex: 1; appearance: none; -webkit-appearance: none; height: 20px; background: transparent; cursor: pointer; min-width: 0; }
.pr-slider-row input[type="range"]::-webkit-slider-runnable-track { height: 3px; border-radius: 3px; background: rgba(255,255,255,.18); }
.pr-slider-row input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; width: 14px; height: 14px; border-radius: 50%; background: var(--iris-bright);
  margin-top: -5.5px; box-shadow: 0 0 0 3px color-mix(in srgb, var(--iris) 30%, transparent);
}
.pr-slider-row input[type="range"]::-moz-range-track { height: 3px; border-radius: 3px; background: rgba(255,255,255,.18); }
.pr-slider-row input[type="range"]::-moz-range-thumb { width: 14px; height: 14px; border: none; border-radius: 50%; background: var(--iris-bright); }
.pr-check { display: flex; align-items: center; gap: 9px; font-size: 13px; color: rgba(232,234,244,.85); margin-top: 10px; cursor: pointer; }
.pr-check input { accent-color: var(--iris); width: 15px; height: 15px; }
.pr-music-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px; }
.pr-music-card {
  display: flex; align-items: center; gap: 8px; cursor: pointer;
  font-size: 12.5px; font-weight: 540; color: rgba(232,234,244,.85);
  background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.1); border-radius: 10px;
  padding: 8px 10px; text-align: left;
  transition: background var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out);
}
.pr-music-card:hover { background: rgba(255,255,255,.1); }
.pr-music-card.on { border-color: var(--iris); background: color-mix(in srgb, var(--iris) 22%, transparent); color: #fff; }
.pr-seg-row { display: flex; gap: 4px; background: rgba(255,255,255,.06); border-radius: 10px; padding: 3px; }
.pr-seg {
  flex: 1; font: inherit; font-size: 12.5px; font-weight: 560; color: rgba(232,234,244,.7); cursor: pointer;
  background: transparent; border: none; border-radius: 8px; padding: 6px 4px;
  transition: background var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}
.pr-seg:hover { color: #fff; }
.pr-seg.on { background: var(--iris); color: #fff; }
.pr-avatar-row { display: flex; gap: 6px; margin-top: 10px; }
.pr-avatar {
  width: 38px; height: 38px; font-size: 19px; cursor: pointer;
  background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.1); border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center;
  transition: transform var(--dur-1) var(--ease-spring), border-color var(--dur-1) var(--ease-out);
}
.pr-avatar:hover { transform: scale(1.1); }
.pr-avatar.on { border-color: var(--iris); background: color-mix(in srgb, var(--iris) 22%, transparent); }
.pr-pop-share { width: min(360px, calc(100% - 28px)); }
.pr-pop-rec { width: min(300px, calc(100% - 28px)); }
.pr-share-preview {
  display: grid; grid-template-columns: 76px minmax(0, 1fr); gap: 11px; align-items: center;
  margin: 8px 0 10px; padding: 8px; border: 1px solid rgba(255,255,255,.12);
  border-radius: 12px; background: rgba(255,255,255,.055);
}
.pr-share-preview img, .pr-share-preview-mark {
  width: 76px; height: 48px; object-fit: cover; border-radius: 8px;
  background: color-mix(in srgb, var(--iris) 26%, #0c1020);
}
.pr-share-preview-mark { display: grid; place-items: center; color: #fff; font: 750 22px/1 var(--sans); }
.pr-share-preview span:last-child { display: flex; min-width: 0; flex-direction: column; gap: 4px; }
.pr-share-preview b { color: #f4f6ff; font-size: 13px; line-height: 1.3; }
.pr-share-preview small { color: rgba(232,234,244,.65); font-size: 10.5px; line-height: 1.3; }
.pr-share-summary { margin: 0 0 8px; color: rgba(232,234,244,.78); font-size: 11.5px; line-height: 1.5; }
.pr-share-link { display: flex; gap: 6px; margin: 8px 0 12px; }
.pr-share-link input {
  flex: 1; min-width: 0; font-family: var(--mono); font-size: 11.5px; color: rgba(232,234,244,.85);
  background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.12); border-radius: 8px; padding: 7px 9px;
}
.pr-share-qr { display: flex; justify-content: center; padding: 6px 0 2px; }
.pr-share-qr svg { width: 132px; height: 132px; background: #fff; border-radius: 10px; padding: 8px; color: #10121a; }
.pr-share-note { font-size: 11.5px; line-height: 1.5; color: rgba(232,234,244,.78); margin-top: 8px; }
.pr-share-native {
  width: 100%; min-height: 36px; margin: -3px 0 6px; border: 1px solid color-mix(in srgb, var(--iris-bright) 50%, transparent);
  border-radius: 9px; background: color-mix(in srgb, var(--iris) 24%, transparent); color: #fff;
  font: 650 12px/1 var(--sans); cursor: pointer;
}
.pr-share-native:hover { background: color-mix(in srgb, var(--iris) 38%, transparent); }
.pr-rec-opt {
  display: flex; flex-direction: column; gap: 2px; width: 100%; text-align: left; cursor: pointer;
  background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.1); border-radius: 11px;
  padding: 11px 13px; margin-top: 8px; color: #e8eaf4; font: inherit;
  transition: background var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out);
}
.pr-rec-opt:hover { background: rgba(255,255,255,.1); border-color: rgba(255,255,255,.25); }
.pr-rec-opt b { font-size: 13.5px; font-weight: 620; }
.pr-rec-opt span { font-size: 11.5px; color: rgba(232,234,244,.75); }

/* ---- Q&A panel. ---- */
#prQa {
  position: absolute; right: 16px; bottom: 76px; z-index: 9;
  width: min(340px, calc(100% - 28px)); max-height: min(60vh, 480px);
  display: flex; flex-direction: column;
  background: rgba(13, 15, 24, 0.92);
  -webkit-backdrop-filter: blur(20px) saturate(1.5); backdrop-filter: blur(20px) saturate(1.5);
  border: 1px solid rgba(255,255,255,.12); border-radius: 16px;
  box-shadow: 0 24px 64px -20px rgba(0,0,0,.7);
  color: #e8eaf4; overflow: hidden;
  animation: prPopIn .22s var(--ease-out) both; transform: none;
}
#prQa.pr-pop-in { animation: none; }
.pr-qa-head { display: flex; align-items: center; justify-content: space-between; padding: 12px 14px; font-size: 13px; font-weight: 620; border-bottom: 1px solid rgba(255,255,255,.08); }
.pr-qa-log { flex: 1; overflow-y: auto; padding: 12px 14px; display: flex; flex-direction: column; gap: 8px; }
.pr-qa-hint { font-size: 12px; line-height: 1.5; color: rgba(232,234,244,.75); }
.pr-qa-q { align-self: flex-end; max-width: 85%; background: var(--iris); color: #fff; border-radius: 12px 12px 3px 12px; padding: 7px 12px; font-size: 13px; }
.pr-qa-a { align-self: flex-start; max-width: 90%; background: rgba(255,255,255,.08); border-radius: 12px 12px 12px 3px; padding: 8px 12px; font-size: 13px; line-height: 1.5; }
#prQaForm { display: flex; gap: 6px; padding: 10px 12px; border-top: 1px solid rgba(255,255,255,.08); }
#prQaInput {
  flex: 1; min-width: 0; font: inherit; font-size: 13px; color: #fff;
  background: rgba(255,255,255,.07); border: 1px solid rgba(255,255,255,.14); border-radius: 9px; padding: 8px 10px;
}
#prQaInput::placeholder { color: rgba(232,234,244,.4); }

/* ---- Keyboard shortcuts sheet. ---- */
#prShortcuts {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 10;
  width: min(400px, calc(100% - 32px));
  background: rgba(13, 15, 24, 0.94);
  -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px);
  border: 1px solid rgba(255,255,255,.12); border-radius: 16px; padding: 20px 22px;
  color: #e8eaf4; box-shadow: 0 30px 80px -24px rgba(0,0,0,.8);
  animation: prCardIn .25s var(--ease-out) both;
}
.pr-keys-head { font-size: 15px; font-weight: 640; margin-bottom: 14px; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.pr-keys-grid { display: grid; grid-template-columns: auto 1fr; gap: 9px 16px; font-size: 13px; color: rgba(232,234,244,.8); align-items: center; }
.pr-keys-grid kbd {
  font-family: var(--mono); font-size: 11px; color: #e8eaf4;
  background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.16); border-bottom-width: 2px;
  border-radius: 6px; padding: 2.5px 7px;
}

/* ---- Laser / pen canvas + spotlight. ---- */
#prToolsCanvas { position: absolute; inset: 0; z-index: 5; pointer-events: none; }
body.pr-tool-laser #prToolsCanvas, body.pr-tool-pen #prToolsCanvas { pointer-events: auto; }
body.pr-tool-laser #prToolsCanvas { cursor: none; }
body.pr-tool-pen #prToolsCanvas { cursor: crosshair; }
#prSpotlight {
  /* This is a focus lens, not a blackout. The older 9999px shadow dimmed the
     entire sheet — especially unpleasant over white Zen canvases — while the
     exact node trace already tells the audience what is new. */
  position: absolute; z-index: 4; pointer-events: none; box-sizing: border-box;
  border-radius: var(--pr-focus-radius, 18px);
  border: 1.5px solid var(--pr-focus-edge, color-mix(in srgb, var(--pr-focus, var(--iris, #4f46e5)) 72%, #1f2937));
  background: radial-gradient(ellipse at center, var(--pr-focus-fill, color-mix(in srgb, var(--pr-focus, var(--iris, #4f46e5)) 7%, transparent)) 0%, transparent 72%);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--pr-focus-edge, var(--pr-focus, #4f46e5)) 16%, transparent),
              0 0 18px 2px var(--pr-focus-halo, color-mix(in srgb, var(--pr-focus, #4f46e5) 20%, transparent));
  transition: left .5s var(--ease-out), top .5s var(--ease-out), width .5s var(--ease-out), height .5s var(--ease-out), opacity .3s ease;
}
/* Automatic step focus has one visual owner: the animated SVG outline that
   follows the real silhouette. Keep this positioned lens as the geometry/
   camera anchor, but do not draw a second border around the same subject.
   The deliberate cursor-spotlight tool below still paints its circular lens. */
body.presenting:not(.pr-tool-spot) #prSpotlight {
  border-color: transparent;
  background: transparent;
  box-shadow: none;
}
/* The diagram sheet, not the dark Present shell, determines focus contrast.
   `current` can be any authored palette, so app.js stamps this from its actual
   sheet colour instead of assuming a visual-theme name. */
#stage[data-pr-sheet-tone="light"] {
  --pr-focus-edge: color-mix(in srgb, var(--pr-focus, var(--iris, #4f46e5)) 76%, #17213b);
  --pr-focus-halo: color-mix(in srgb, var(--pr-focus, var(--iris, #4f46e5)) 20%, transparent);
  --pr-focus-fill: color-mix(in srgb, var(--pr-focus, var(--iris, #4f46e5)) 6%, transparent);
  /* A paper sheet needs a richer, inked glint rather than a pale neon wash.
     The base is supplied by the active Present/document theme in app.js. */
  --pr-connector-flow-core: color-mix(in srgb, var(--pr-motion-base, var(--iris-bright, #8b80ff)) 68%, #17213b);
  --pr-connector-flow-spark: color-mix(in srgb, var(--pr-motion-spark-base, var(--iris-bright, #8b80ff)) 70%, #fff);
  --pr-connector-flow-halo: color-mix(in srgb, var(--pr-motion-base, var(--iris-bright, #8b80ff)) 24%, transparent);
  --pr-context-opacity: .94;
}
#stage[data-pr-sheet-tone="dark"] {
  --pr-focus-edge: color-mix(in srgb, var(--pr-focus, var(--iris-bright, #8b80ff)) 72%, #fff);
  --pr-focus-halo: color-mix(in srgb, var(--pr-focus, var(--iris-bright, #8b80ff)) 34%, transparent);
  --pr-focus-fill: color-mix(in srgb, var(--pr-focus, var(--iris-bright, #8b80ff)) 12%, transparent);
  /* On midnight sheets, lift the same theme hue toward white and give the
     small companion dash a brighter glint without turning the whole map neon. */
  --pr-connector-flow-core: color-mix(in srgb, var(--pr-motion-base, var(--iris-bright, #8b80ff)) 84%, #fff);
  --pr-connector-flow-spark: color-mix(in srgb, var(--pr-motion-spark-base, var(--iris-bright, #8b80ff)) 88%, #fff);
  --pr-connector-flow-halo: color-mix(in srgb, var(--pr-motion-base, var(--iris-bright, #8b80ff)) 42%, transparent);
  --pr-context-opacity: .87;
}
body.pr-tool-spot #prSpotlight { border-radius: 50%; }
@media (forced-colors: active) {
  #prSpotlight { background: transparent; border: 2px solid CanvasText; box-shadow: none; }
}

/* ---- The step's OWN shape arriving ----
   The sequence tool tweens its draw-on, but every other tool used to hard-cut
   a new node into place: one frame it is absent, the next it simply exists.
   present-plus.js stamps .pr-shape-in on just the element(s) this step
   reveals, so the eye is led to what actually changed.
   transform-box: fill-box is required — without it an SVG transform-origin is
   the whole canvas, and the shape swings in from the corner. */
body.presenting #svgHost .pr-shape-in {
  animation: prShapeIn .45s cubic-bezier(.22, 1, .36, 1) both;
  transform-box: fill-box;
  transform-origin: 50% 50%;
}
@keyframes prShapeIn {
  from { opacity: 0; transform: scale(.82); }
  70% { opacity: 1; transform: scale(1.03); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  body.presenting #svgHost .pr-shape-in { animation: prShapeFade .25s ease both; }
  @keyframes prShapeFade { from { opacity: 0; } to { opacity: 1; } }
}

/* ---- Whole-frame transitions (Fade / Zoom / Morph — Flow is the
   per-element default). js/present-plus.js stamps .pr-svg-in on the fresh
   SVG only on whole-step renders, so the sequence tool's fractional tween
   never retriggers these. ---- */
.pr-trans-fade #svgHost .pr-svg-in { animation: prSvgFade .38s ease both; }
.pr-trans-zoom #svgHost .pr-svg-in { animation: prSvgZoom .5s var(--ease-out) both; transform-origin: 50% 50%; }
.pr-trans-morph #svgHost .pr-svg-in { animation: prSvgMorph .6s var(--ease-spring) both; transform-origin: 50% 50%; }
@keyframes prSvgFade { from { opacity: 0; } to { opacity: 1; } }
/* The outgoing sheet, frozen at its exact on-screen box and dissolving out
   over the incoming frame (renderPresentAt's cross-fade). pointer-events off
   so a viewer's tap mid-dissolve still reaches the gesture layer. */
#canvasWrap > .pr-frame-ghost { position: absolute; z-index: 3; pointer-events: none; animation: prGhostOut .42s ease forwards; }
@keyframes prGhostOut { from { opacity: 1; } to { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { #canvasWrap > .pr-frame-ghost { display: none; } }
@keyframes prSvgZoom { from { opacity: 0; transform: scale(.965); } to { opacity: 1; transform: scale(1); } }
@keyframes prSvgMorph { 0% { opacity: 0; transform: scale(.94) translateY(8px); } 60% { opacity: 1; } 100% { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .pr-trans-fade #svgHost .pr-svg-in, .pr-trans-zoom #svgHost .pr-svg-in, .pr-trans-morph #svgHost .pr-svg-in { animation: none; }
}

/* ---- Progress: slim gradient + chapter ticks + times. ---- */
#prProgress {
  position: absolute; left: 16px; right: 16px; top: 16px; z-index: 6;
  pointer-events: none;
  transition: left .3s ease, right .3s ease, opacity .35s ease;
}
body.pr-idle #prProgress { opacity: 0; }
#prProgressTrack {
  position: relative; height: 5px; border-radius: 999px;
  background: rgba(255, 255, 255, .14);
  box-shadow: 0 1px 6px rgba(0, 0, 0, .3);
  cursor: pointer; pointer-events: auto;
}
#prProgressTrack:hover { height: 7px; }
#prProgressFill {
  position: absolute; top: 0; left: 0; bottom: 0; width: 0%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--iris-deep), var(--iris-bright));
  box-shadow: 0 0 10px color-mix(in srgb, var(--iris) 55%, transparent);
  transition: width .3s cubic-bezier(.4, 0, .2, 1);
}
#prProgressPuck {
  position: absolute; top: 50%; left: 0%;
  width: 11px; height: 11px; border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--iris) 55%, transparent), 0 2px 6px rgba(0, 0, 0, .4);
  transform: translate(-50%, -50%);
  transition: left .3s cubic-bezier(.4, 0, .2, 1);
}
#prChapters { position: absolute; inset: 0; pointer-events: none; }
.pr-chap {
  position: absolute; top: 50%; width: 9px; height: 9px; border-radius: 50%;
  background: rgba(255,255,255,.6); border: 1.5px solid rgba(10,12,20,.5);
  transform: translate(-50%, -50%);
  pointer-events: auto; cursor: pointer;
}
/* Invisible 26px hit area — the dot itself must stay small on a 5px track. */
.pr-chap::before { content: ""; position: absolute; inset: -9px; border-radius: 50%; }
/* The bar lives at the top of the canvas, so its tooltip opens downward. */
.pr-chap::after { bottom: auto; top: calc(100% + 12px); }
.pr-chap:hover { background: #fff; transform: translate(-50%, -50%) scale(1.3); }
.pr-chap.done { background: var(--iris-bright); }
.pr-progress-times {
  display: flex; justify-content: space-between; margin-top: 7px;
  font-family: var(--mono); font-size: 10.5px; color: rgba(255,255,255,.85);
}
.pr-progress-times span {
  background: rgba(12,14,22,.6); border-radius: 6px; padding: 2px 7px;
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
.pr-progress-times span:empty { display: none; }

/* ---- Presenter avatar. ----
   The avatar and the caption/subtitle strip are laid out independently but
   share the bottom band, so a long caption ran straight under the bobbing
   emoji — and the avatar, being later in the DOM at the same z-index, won.
   --pr-strip-lift is measured by js/present-plus.js (syncBottomBand) and
   raises the avatar clear of the strip, but only when the two actually
   overlap horizontally, so on a wide canvas it keeps its low corner perch.
   The +18 vs the strip's +12 is the 6px the avatar always sat higher. */
#prPresenter {
  position: absolute; right: 28px; z-index: 6;
  bottom: calc(18px + var(--pr-band, 66px) + var(--pr-strip-lift, 0px));
  display: flex; align-items: center; justify-content: center;
  animation: pr-presenter-bob 3.2s ease-in-out infinite;
  transition: left .3s ease, right .3s ease, bottom .25s ease;
}
@keyframes pr-presenter-bob {
  0%, 100% { transform: translateY(0) rotate(-2deg); }
  50% { transform: translateY(-9px) rotate(2deg); }
}
#prPresenterFace {
  font-size: 64px; line-height: 1;
  padding: 12px;
  background: rgba(12, 14, 22, 0.35);
  border: 1px solid rgba(255,255,255,.14);
  border-radius: 24px;
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  filter: drop-shadow(0 8px 16px rgba(0, 0, 0, .35));
}
#prPresenterFace.speak { animation: pr-presenter-speak .45s ease-in-out; }
@keyframes pr-presenter-speak {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.12); }
}
@media (prefers-reduced-motion: reduce) {
  #prPresenter { animation: none; }
  #prPresenterFace.speak { animation: none; }
  .pr-intro-card, .pr-end-card, .pr-pop, #prShortcuts, #prQa { animation: none; }
  .pr-intro-play:hover::after { animation: none; }
  #prSpotlight { transition: opacity .3s ease; }
  #presentBar, #prBrand, #prProgress { transition: opacity .2s ease; }
  body.pr-idle #presentBar { transform: translateX(-50%); }
  body.pr-idle .pr-pop { transform: none; }
}
/* Sizing: `max-content` keeps the row from shrink-to-fitting the space right
   of left:50% (an absolutely-positioned flex box would otherwise wrap early),
   while `max-width` + `wrap` let it fold into a second row whenever the CANVAS
   is narrow — which happens with side panels open, not just on small screens,
   so this must be container-relative rather than a viewport media query. */
#presentBar { width: max-content; max-width: calc(100% - 24px); flex-wrap: wrap; justify-content: center; row-gap: 6px; }
.prc { min-width: 0; }

/* ---- The bar's diet: transport + sound + exit, everything else earns its
   place per screen class. Buttons are HIDDEN, never removed — app.js binds
   every id at parse, and each hidden control keeps a twin: First/Last =
   Home/End + the progress strip; SpeedCycle = the pace slider in Settings;
   SmartToggle = the Settings checkbox (#prSmartStage). ---- */
#prFirst, #prLast, #prSpeedCycle, #prSmartToggle { display: none; }

@media (max-width: 1024px) {
  /* TABLETS keep the presenter's kit. An earlier cut hid laser/pen/spot
     here and that was wrong twice over: the bar has room at this width
     (measured, one row), and a tablet is exactly the device someone
     PRESENTS from — where L/D/S do not exist because there is no keyboard.
     What does stand down: Restart (the end slide's Replay and the seg
     bar's first cell both reach it) and Pin (an idle-mouse concept — on
     touch any tap wakes the dock) plus the running clock. */
  #prRestart, #prPin, #prTime { display: none; }
}
@media (max-width: 760px), (max-height: 560px) and (max-width: 1024px) {
  /* PHONES: ONE ROW, or it is not a pill — it is a second notes panel.
     MEASURED at 390px: 8 controls (374px of buttons) + gaps + two dividers
     needed 432px against 374px available, so the bar wrapped to two rows and
     ate 116px of an 844px screen. Seven controls, no internal rules:
     Prev · Play · Next · mute · settings · zen · exit, each a 44px target,
     riding above the home indicator. Voice keeps its Settings checkbox (and
     mute silences it in one tap); like/share live on the end slide, which is
     where a phone viewer reaches for them anyway. */
  #presentBar { gap: 4px; padding: 6px; bottom: max(10px, env(safe-area-inset-bottom)); }
  .prc + .prc::before { display: none; }
  /* Voice keeps its Settings checkbox, captions gained one (#prShowCaptions),
     like/share live on the end slide. The ink tools are the one honest loss:
     they are a desk-and-stylus feature, their keys need a keyboard, and a
     finger drawing on a 390px stage is not the gesture anyone wants — they
     return the moment the screen does. */
  #prNarrationToggle, #prAudienceCycle, #prFull, .prc-status,
  #prVoiceToggle, #prCaptionToggle, #prLikeBtn,
  #prToolLaser, #prToolPen, #prToolSpot, #prConnectorMotionToggle { display: none !important; }
  /* Phones are touch-first: taps navigate, drags pan and pinches zoom. Do not
     advertise a keyboard-only help surface there. */
  #prShortcutsBtn { display: none !important; }
  .pr-play { min-width: 58px; padding: 7px 12px; }
  /* .pr-pop is the full-width bottom sheet on phones (rule beside .pr-pop's
     own definition) — only the Q&A panel keeps a lifted bottom here. */
  #prQa { bottom: calc(66px + env(safe-area-inset-bottom, 0px)); }
}
@media (max-width: 400px) {
  #prExit { padding: 7px 8px; }
}

/* A true flush split-pane docked to the canvas edge — background/color/
   width/side are all set inline from JS (layoutPresentChrome) to match
   the diagram's own active theme and the 85/15 diagram/notes split, so it
   reads as part of the same sheet rather than a dark tooltip floating on
   top of the diagram (which could cover nodes underneath it). */
#prNarration {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 6;
  display: flex;
  flex-direction: column;
  padding: 22px;
  opacity: 0;
  /* Opacity keeps the guide available for its short exit transition, but an
     invisible full-height panel must never sit over the newly expanded sheet
     and steal taps from the diagram. */
  pointer-events: none;
  transition: opacity .3s ease, left .3s ease, right .3s ease, width .3s ease;
}
#prNarration.pos-left { border-right: 1px solid rgba(0, 0, 0, .12); box-shadow: 6px 0 20px rgba(0, 0, 0, .08); }
#prNarration.pos-right { border-left: 1px solid rgba(0, 0, 0, .12); box-shadow: -6px 0 20px rgba(0, 0, 0, .08); }
#prNarration.show { opacity: 1; pointer-events: auto; }
#prNarrationHead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  opacity: .6;
  margin-bottom: 14px;
  flex: none;
}
#prNarrationHeadBtns { display: flex; gap: 2px; }
#prNarrationFlip, #prNarrationEditBtn {
  font-size: 14px;
  padding: 3px 7px;
  border-radius: 6px;
  text-transform: none;
  letter-spacing: 0;
  font-weight: 600;
  opacity: .8;
}
#prNarrationFlip:hover, #prNarrationEditBtn:hover { background: rgba(128, 128, 128, .18); opacity: 1; }
#prNarrationText {
  font-size: calc(19px * var(--pr-text-k, 1));
  line-height: 1.6;
  overflow-y: auto;
  opacity: 0;
  transition: opacity .25s ease;
}
#prNarrationText.show { opacity: 1; }
#prNarrationText.hidden-editing { display: none; }

/* ---- Notes panel: style presets, entrance, word-by-word highlight ----
   The preset is a data attribute set from Present settings (present-plus.js
   setNoteStyle). All four are tuned for the dark presentation chrome; Paper
   is deliberately light for editorial contrast. */
#prNarration[data-note-style] #prNarrationText {
  padding: 16px 18px;
  border-radius: 14px;
  flex: 0 1 auto;
}
#prNarration[data-note-style="sleek"] #prNarrationText{
  background: rgba(255, 255, 255, .05);
  border-left: 3px solid #7a8aff;
  border-radius: 6px 14px 14px 6px;
  box-shadow: 0 12px 34px rgba(0, 0, 0, .35);
}
#prNarration[data-note-style="glass"] #prNarrationText{
  background: rgba(255, 255, 255, .07);
  -webkit-backdrop-filter: blur(16px) saturate(1.35);
  backdrop-filter: blur(16px) saturate(1.35);
  border: 1px solid rgba(255, 255, 255, .16);
  border-radius: 16px;
  box-shadow: 0 18px 50px rgba(0, 0, 0, .5), inset 0 1px 0 rgba(255, 255, 255, .12);
}
#prNarration[data-note-style="aurora"] #prNarrationText{
  border: 1px solid transparent;
  border-radius: 16px;
  color: #e9ecff;   /* dark card — the panel's inherited ink is dark on light sheets */
  background:
    linear-gradient(rgba(18, 20, 30, .88), rgba(18, 20, 30, .88)) padding-box,
    linear-gradient(135deg, #7a8aff, #22d3ee 45%, #a78bfa) border-box;
  box-shadow: 0 0 36px rgba(122, 138, 255, .22), 0 14px 40px rgba(0, 0, 0, .45);
}
#prNarration[data-note-style="paper"] #prNarrationText{
  background: linear-gradient(180deg, #faf4e7, #f1e8d4);
  color: #33302c;
  font-family: Georgia, 'Iowan Old Style', 'Times New Roman', serif;
  font-size: 20px;
  line-height: 1.65;
  border-radius: 12px;
  box-shadow: 0 14px 36px rgba(0, 0, 0, .4), inset 0 0 0 1px rgba(120, 100, 60, .18);
}

/* ---- Audience presets: one premium template per segment ---- */

/* Corporate — boardroom-crisp white card, navy meter, tight ink. */
/* ---- Audience presets: one premium template per segment ---- */

/* Corporate — boardroom-crisp white card, navy meter, tight ink. */
#prNarration[data-note-style="corporate"] #prNarrationText{
  background: #ffffff;
  color: #1f2937;
  border: 1px solid #dde2ec;
  border-top: 4px solid transparent;
  border-radius: 10px;
  background-clip: padding-box;
  position: relative;
  box-shadow: 0 14px 34px rgba(15, 23, 42, .18);
  font-size: 17.5px;
  line-height: 1.55;
  letter-spacing: -.005em;
}
#prNarration[data-note-style="corporate"] #prNarrationText::after {
  content: ""; position: absolute; top: -4px; left: -1px; right: -1px; height: 4px;
  border-radius: 10px 10px 0 0;
  background: linear-gradient(90deg, #1e3a8a, #3b82f6 60%, #93c5fd);
}
#prNarration[data-note-style="corporate"] #prNarrationText::before {
  content: "STEP " attr(data-step);
  /* BLOCK, not inline-block: as an inline box the note's first words flowed
     up beside the chip and sat against it, and the bottom margin separated
     nothing. width:max-content keeps the pill hugging its own text instead of
     stretching the full column. */
  display: block; width: max-content; max-width: 100%; margin: 0 0 10px;
  font-size: 10px; font-weight: 800; letter-spacing: .14em;
  color: #1e3a8a; background: #eef2ff; border: 1px solid #c7d2fe;
  padding: 3px 8px; border-radius: 5px;
}
#prNarration[data-note-style="corporate"] #prNarrationText.karaoke .kw.now { color: #2563eb; }

/* Kids — sunny, bubbly, friendly round type, a touch of tilt. */
/* Kids — sunny, bubbly, friendly round type, a touch of tilt. */
#prNarration[data-note-style="kids"] #prNarrationText{
  background: linear-gradient(160deg, #fff6d8, #ffe7ee 85%);
  color: #4a3510;
  border: 3px solid #ffb703;
  border-radius: 22px;
  transform: rotate(-.6deg);
  box-shadow: 0 12px 30px rgba(180, 100, 0, .22);
  font-family: 'Chalkboard SE', 'Comic Sans MS', 'Segoe Print', cursive;
  font-size: 20px;
  line-height: 1.55;
}
#prNarration[data-note-style="kids"] #prNarrationText::before {
  content: "✨ Step " attr(data-step);
  display: block; width: max-content; max-width: 100%; margin: 0 0 10px;
  font-size: 12px; font-weight: 700;
  color: #b45309; background: #ffffffaa;
  padding: 3px 10px; border-radius: 999px; border: 2px dashed #fcbf49;
}
#prNarration[data-note-style="kids"] #prNarrationText.karaoke .kw { opacity: .45; }
#prNarration[data-note-style="kids"] #prNarrationText.karaoke .kw.now { color: #e11d48; }

/* Classroom — chalk on a green board in a wooden frame. */
/* Classroom — chalk on a green board in a wooden frame. */
#prNarration[data-note-style="classroom"] #prNarrationText{
  background:
    radial-gradient(120% 90% at 30% 20%, rgba(255, 255, 255, .06), transparent 60%),
    #24463a;
  color: #f2fbf4;
  border: 7px solid #8b5e34;
  border-radius: 8px;
  box-shadow: inset 0 0 24px rgba(0, 0, 0, .35), 0 14px 34px rgba(0, 0, 0, .4);
  font-family: 'Chalkboard SE', 'Bradley Hand', 'Segoe Print', cursive;
  font-size: 19px;
  line-height: 1.6;
}
#prNarration[data-note-style="classroom"] #prNarrationText::before {
  content: "Step " attr(data-step);
  display: block; width: max-content; max-width: 100%; margin: 0 0 10px; padding-bottom: 2px;
  font-size: 13px; color: #ffe066;
  border-bottom: 2px dashed rgba(255, 224, 102, .55);
}
#prNarration[data-note-style="classroom"] #prNarrationText.karaoke .kw { opacity: .4; }
#prNarration[data-note-style="classroom"] #prNarrationText.karaoke .kw.now { color: #ffe066; }

/* Tech — dark terminal, mono type, cyan cursor line. */
/* Tech — dark terminal, mono type, cyan cursor line. */
#prNarration[data-note-style="tech"] #prNarrationText{
  background: #0b1020;
  color: #d2e3ff;
  border: 1px solid #1e293b;
  border-left: 3px solid #22d3ee;
  border-radius: 8px;
  box-shadow: 0 14px 40px rgba(0, 0, 0, .5);
  font-family: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 15.5px;
  line-height: 1.65;
}
#prNarration[data-note-style="tech"] #prNarrationText::before {
  content: "▸ step_" attr(data-step);
  display: block; margin: 0 0 8px;
  font-size: 11px; letter-spacing: .06em; color: #22d3ee; opacity: .9;
}
#prNarration[data-note-style="tech"] #prNarrationText.karaoke .kw { opacity: .38; }
#prNarration[data-note-style="tech"] #prNarrationText.karaoke .kw.now { color: #22d3ee; }

/* The style row wraps to two lines now. */
.pr-seg-wrap { flex-wrap: wrap; }

/* Note image — rides above the styled text card, follows the same sticky
   key as the text, and joins the entrance animation. */
#prNarrationImg {
  width: 100%; max-height: 34%; object-fit: cover; flex: none;
  border-radius: 12px; margin-bottom: 10px;
  box-shadow: 0 10px 26px rgba(0, 0, 0, .3);
}
#prNarration[data-note-style="kids"] #prNarrationImg { border: 3px solid #ffb703; border-radius: 18px; transform: rotate(.5deg); }
#prNarration[data-note-style="classroom"] #prNarrationImg { border: 5px solid #8b5e34; border-radius: 6px; }
#prNarration[data-note-style="corporate"] #prNarrationImg { border: 1px solid #dde2ec; border-radius: 8px; }
#prNarration[data-note-style="tech"] #prNarrationImg { border: 1px solid #1e293b; border-radius: 6px; }

/* Notes editor: per-step image row */
.pnl-img-row { display: flex; align-items: center; gap: 8px; margin: 4px 0 2px; }
.pnl-img-thumb { width: 54px; height: 38px; object-fit: cover; border-radius: 6px; border: 1px solid var(--border-soft); }
.pnl-img-btn { font-size: 11px; font-weight: 600; color: var(--muted); padding: 4px 8px; border: 1px dashed var(--border-soft); border-radius: 7px; }
.pnl-img-btn:hover { color: var(--text); background: var(--chrome-2, rgba(128, 128, 128, .08)); }

/* Entrance: a new note glides in from the panel's own edge and settles. */
#prNarration.pr-note-in #prNarrationText,
#prNarration.pr-note-in #prNarrationImg { animation: prNoteInR .55s cubic-bezier(.22, 1, .36, 1) both; }
#prNarration.pos-left.pr-note-in #prNarrationText,
#prNarration.pos-left.pr-note-in #prNarrationImg { animation-name: prNoteInL; }
@keyframes prNoteInR {
  from { opacity: 0; transform: translateX(26px); }
  to { opacity: 1; transform: none; }
}
@keyframes prNoteInL {
  from { opacity: 0; transform: translateX(-26px); }
  to { opacity: 1; transform: none; }
}
/* The step's picture gets its OWN arrival rather than sliding with the text.
   It is the thing the eye goes to, so it lifts and focuses into place a beat
   after the words start — which reads as deliberate instead of as one block
   of panel sliding sideways. */
#prNarration.pr-note-in #prNarrationImg,
#prNarration.pos-left.pr-note-in #prNarrationImg {
  animation: prNoteImgIn .62s cubic-bezier(.22, 1, .36, 1) .07s both;
}
@keyframes prNoteImgIn {
  from { opacity: 0; transform: scale(.93) translateY(12px); filter: blur(7px); }
  60%  { opacity: 1; filter: blur(0); }
  to   { opacity: 1; transform: none; filter: none; }
}
@media (prefers-reduced-motion: reduce) {
  #prNarration.pr-note-in #prNarrationText,
  #prNarration.pr-note-in #prNarrationImg,
  #prNarration.pos-left.pr-note-in #prNarrationText,
  #prNarration.pos-left.pr-note-in #prNarrationImg { animation: prNoteFade .25s ease both; }
  @keyframes prNoteFade { from { opacity: 0; } to { opacity: 1; } }
}

/* While the voice reads the note, its words fill in like a progress bar —
   same .kw/.said/.now contract as the subtitle strip (present-plus.js).
   The current word pools a wash of its own accent behind itself: the pill
   is painted in currentColor, so every note style's .now hue (corporate
   blue, kids rose, tech cyan…) recolours the liquid for free. */
#prNarrationText.karaoke .kw {
  opacity: .38;
  transition: opacity .16s ease, color .16s ease;
  border-radius: 4px;
}
#prNarrationText.karaoke .kw.said { opacity: 1; }
#prNarrationText.karaoke .kw.now {
  opacity: 1; color: #9fb0ff;
  background-image: linear-gradient(90deg,
    color-mix(in srgb, currentColor 26%, transparent),
    color-mix(in srgb, currentColor 12%, transparent));
  background-repeat: no-repeat;
  background-size: 0% 100%;
  animation: kwLiquid .34s ease-out forwards;
  box-shadow: 0 0 0 3px color-mix(in srgb, currentColor 14%, transparent);
}
#prNarration[data-note-style="paper"] #prNarrationText.karaoke .kw.now { color: #6d28d9; }
#prNarrationEdit {
  flex: 1;
  width: 100%;
  resize: none;
  font: inherit;
  font-size: 17px;
  line-height: 1.55;
  color: inherit;
  background: rgba(128, 128, 128, .1);
  border: 1px solid rgba(128, 128, 128, .3);
  border-radius: 8px;
  padding: 10px;
}
#prNarrationEditBtns { display: flex; gap: 8px; margin-top: 10px; flex: none; }
#prNarrationList { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 14px; }
.pnl-item { display: flex; flex-direction: column; gap: 6px; }
.pnl-empty { font-size: 13px; opacity: .6; line-height: 1.5; padding: 8px 2px; }
.pnl-label { font-size: 12px; font-weight: 700; opacity: .55; }
.pnl-text {
  width: 100%;
  min-height: 64px;
  resize: vertical;
  font: inherit;
  font-size: 15px;
  line-height: 1.5;
  color: inherit;
  background: rgba(128, 128, 128, .1);
  border: 1px solid rgba(128, 128, 128, .3);
  border-radius: 8px;
  padding: 8px 10px;
}
.pnl-save {
  align-self: flex-start;
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 6px;
  font-weight: 600;
  background: var(--accent);
  color: var(--on-accent);
}
.pnl-save.saved { background: #10b981; }
/* Editor-only: the notes pane has no room on a phone. Present is EXCLUDED —
   the show has its own notes layouts (side column / bottom band / collapsed
   rail) and a blanket display:none here made reserveNotesColumn measure a
   zero rect and pay a stage-sized padding for a panel that wasn't there. */
@media (max-width: 760px) { body:not(.presenting) #prNarration { display: none; } }

body.presenting #hintBar { display: none; }
/* Presenting is FULL-BLEED: the editor chrome leaves entirely, same as watch
   mode below ("no other layout change needed" — #canvasWrap is flex:1 in
   #main's row). The old treatment kept palette/side at 0.45 opacity as a
   "you're still in the editor" hint, which cost ~500px of stage on a laptop
   and left a phone a 36px-wide show. Exit restores everything by dropping
   the class. */
body.presenting #topbar, body.presenting #palette, body.presenting #side { display: none; }
body.presenting #svgHost [data-hit] { cursor: default; }
/* The gesture layer owns the diagram surface: without touch-action the
   browser claims a horizontal swipe for its own scrolling/navigation, and a
   400ms hold summons text selection and the iOS callout instead of pausing.
   Scoped to #zoomLayer ON PURPOSE — the settings sheet (#prSettings) and
   the notes panel scroll by touch and live outside it. */
/* BOTH declarations belong to the diagram surface, not to the wrap. Every
   Present overlay — the Q&A transcript, the end slide's takeaways, the notes
   panel, the shortcut sheet — is a DESCENDANT of #canvasWrap, and
   `user-select: auto` on a child computes to `none` under a `none` parent, so
   scoping this to the wrap quietly made every word in the show unselectable.
   #zoomLayer holds only the sheet, which is exactly what the hold gesture
   touches and the only thing that must not select or raise a callout. */
body.presenting #zoomLayer {
  touch-action: none;
  -webkit-user-select: none; user-select: none;
  -webkit-touch-callout: none;
}

/* clean stage while a narrated video is being recorded */
body.recording #topbar, body.recording #palette, body.recording #side,
body.recording #hintBar, body.recording #presentBar, body.recording #toasts,
body.recording #prProgress, body.recording #prPresenter, body.recording #prNarration { display: none !important; }
body.recording #canvasWrap { background-image: none; }

/* Watch mode (Present triggered from the Explore gallery) — a YouTube-style
   clean player: hide all editor chrome but, unlike .recording above, KEEP
   #presentBar visible as the play/pause/scrubber bar. #canvasWrap is
   flex:1 inside #main's flex row, so with #palette/#side gone it fills
   the full viewport width with no other layout change needed. */
body.watching #topbar, body.watching #palette, body.watching #side { display: none; }

#canvasWrap:fullscreen { background-color: var(--canvas-bg); }

#hintBar {
  position: absolute;
  left: 50%;
  bottom: 12px;
  transform: translateX(-50%);
  background: rgba(20, 22, 31, 0.82);
  color: #c6cadb;
  font-size: 11px;
  padding: 6px 14px;
  border-radius: 99px;
  pointer-events: none;
  white-space: nowrap;
  backdrop-filter: blur(4px);
  z-index: 5;
}

/* ---- Side panel (right) ---- */
#side {
  width: 292px;
  transition: width .18s ease;
  flex: none;
  background: var(--chrome);
  border-left: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  min-height: 0;
}
#side.code-mode { width: 430px; }
#tabs {
  display: flex;
  gap: 4px;
  padding: 10px 12px 0;
  border-bottom: 1px solid var(--border);
}
.tab {
  padding: 7px 12px 9px;
  color: var(--muted);
  font-weight: 600;
  font-size: 12.5px;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color .12s;
}
.tab:hover { color: var(--text); }
.tab.active { color: var(--text); border-bottom-color: var(--accent); }
.tabpane { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 14px; min-height: 0; }

/* inspector fields */
.insp-head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
  font-size: 13px;
}
.insp-head .chip {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--accent-ink);
  background: var(--accent-soft);
  padding: 3px 7px;
  border-radius: 5px;
}
.field { display: flex; flex-direction: column; gap: 6px; }
.field > label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); }
.field input[type="text"], .field input[type="number"], .field select, .field textarea {
  background: var(--chrome-2);
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 7px 9px;
  color: var(--text);
  width: 100%;
}
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--accent); outline: none; }
.field textarea.insp-multiline { min-height: 54px; line-height: 1.35; resize: vertical; }
.field select { appearance: none; -webkit-appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%); background-position: calc(100% - 16px) 55%, calc(100% - 11px) 55%; background-size: 5px 5px; background-repeat: no-repeat; padding-right: 26px; }

.inline-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.inline-row > * { flex: none; }
.muted { color: var(--muted); font-size: 11.5px; line-height: 1.5; }

.seg { display: flex; background: var(--chrome-2); border: 1px solid var(--border); border-radius: 8px; padding: 2px; gap: 2px; }
.seg-btn { flex: 1; padding: 6px 4px; border-radius: 6px; color: var(--muted); font-size: 12px; text-align: center; transition: background .12s, color .12s; }
.seg-btn:hover { color: var(--text); }
.seg-btn.active { background: var(--chrome-3); color: var(--text); box-shadow: inset 0 0 0 1px var(--border); }

/* Account-gated formats seen by a guest (js/app.js `syncExpFormatLocks`).
   Dimmed and padlocked, never `disabled` — the click is what reveals
   #expGateHint and the way in, and Present's ⏺ Record buttons reach this
   modal by clicking the seg, so a dead button would break Record outright.
   --faint + 🔒 is the lock convention this app already uses for locked
   layers (.ck-layer.locked); both tokens are real and theme-aware, which
   matters because an invented one fails SILENTLY to its fallback and would
   look right in dark and wrong in light. */
.seg-btn.locked { color: var(--faint); }
.seg-btn.locked:hover { color: var(--muted); }
.seg-btn.locked.active { color: var(--muted); }
.seg-btn.locked::before { content: "🔒"; font-size: 8.5px; margin-right: 3px; opacity: .8; vertical-align: 1px; }

.swatches { display: flex; flex-wrap: wrap; gap: 6px; }
.swatch {
  width: 22px; height: 22px;
  border-radius: 6px;
  border: 2px solid transparent;
  cursor: pointer;
  transition: transform .08s;
  padding: 0;
}
.swatch:hover { transform: scale(1.12); }
.swatch.active { border-color: #fff; box-shadow: 0 0 0 2px var(--accent); }
.swatch.none {
  background: var(--chrome-2);
  border: 1px solid var(--border);
  position: relative;
  color: var(--muted);
  font-size: 13px;
  line-height: 1;
}
.swatch-custom { width: 22px; height: 22px; padding: 0; border: 1px solid var(--border); border-radius: 6px; background: none; cursor: pointer; }
.swatch-custom::-webkit-color-swatch-wrapper { padding: 2px; }
.swatch-custom::-webkit-color-swatch { border: none; border-radius: 4px; }

.emoji-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.emoji-btn { font-size: 16px; padding: 4px 0; border-radius: 6px; text-align: center; transition: background .1s; line-height: 1.3; }
.emoji-btn:hover { background: var(--chrome-3); }
.emoji-btn.active { background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent); }

.theme-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; }
.theme-card {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 9px;
  background: var(--chrome-2);
  border: 1px solid var(--border-soft);
  border-radius: 8px;
  font-size: 12px;
  color: var(--muted);
  transition: border-color .12s, color .12s;
}
.theme-card:hover { color: var(--text); border-color: var(--accent); }
.theme-card.active { color: var(--text); border-color: var(--accent); background: var(--accent-soft); }
.theme-dot { width: 14px; height: 14px; border-radius: 4px; flex: none; border: 1px solid rgba(0,0,0,.15); }

/* ---- Quick decoration presets (js/decor-presets.js). Each card draws its
   own preset at thumbnail scale, so a preview can never drift from what
   clicking it actually does. ---- */
.dp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(74px, 1fr)); gap: 7px; margin-bottom: 6px; }
.dp-card {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 6px 5px 5px;
  background: var(--chrome-2);
  border: 1px solid var(--border-soft);
  border-radius: 8px;
  color: var(--muted);
  cursor: pointer;
  transition: border-color .12s, color .12s, background .12s;
}
.dp-card:hover { color: var(--text); border-color: var(--accent); }
.dp-card.active { color: var(--text); border-color: var(--accent); background: var(--accent-soft); }
.dp-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.dp-prev { width: 100%; height: auto; display: block; border-radius: 4px; }
.dp-name { font-size: 11px; font-weight: 600; letter-spacing: -0.01em; }

.check { display: flex; align-items: center; gap: 8px; cursor: pointer; font-size: 12.5px; color: var(--text); text-transform: none; letter-spacing: 0; font-weight: 500; }
.check input { accent-color: var(--accent); width: 15px; height: 15px; }

.row-btns { display: flex; gap: 7px; }
.row-btns .btn-ghost { flex: 1; text-align: center; padding: 6px 4px; font-size: 12px; }
.insp-sep { border-top: 1px solid var(--border-soft); margin: 2px 0; }

/* code pane */
#codePane { padding: 12px; gap: 10px; }
.code-tools { display: flex; align-items: center; gap: 7px; }
.btn-ghost.sm { padding: 4px 10px; font-size: 12px; }
#codeWrap {
  flex: 1;
  min-height: 220px;
  display: flex;
  background: var(--chrome-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
}
#codeWrap:focus-within { border-color: var(--accent); }
#codeGutter {
  flex: none;
  width: 34px;
  padding: 10px 6px 10px 0;
  text-align: right;
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.65;
  color: var(--muted);
  opacity: .55;
  background: var(--chrome);
  border-right: 1px solid var(--border-soft);
  overflow: hidden;
  user-select: none;
  -webkit-user-select: none;
  white-space: pre;
}
#codeText {
  flex: 1;
  min-width: 0;
  resize: none;
  background: transparent;
  border: none;
  padding: 10px;
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.65;
  color: var(--text);
  white-space: pre;
  overflow: auto;
}
#codeText:focus { outline: none; }
#codeStatus { font-size: 11.5px; }
#codeStatus.ok { color: #57c98b; }
#codeStatus.err { color: var(--danger); }
#codeErrors { display: flex; flex-direction: column; gap: 3px; font-size: 11.5px; color: var(--danger); }
#cheat { border-top: 1px solid var(--border-soft); padding-top: 8px; }
#cheat summary { cursor: pointer; color: var(--muted); font-size: 12px; font-weight: 600; }
#cheat pre {
  margin-top: 8px;
  background: var(--chrome-2);
  border: 1px solid var(--border-soft);
  border-radius: 8px;
  padding: 10px;
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.7;
  color: var(--muted);
  overflow-x: auto;
}

/* ============ Modals ============ */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(10, 12, 20, 0.62);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  backdrop-filter: blur(3px);
}
.modal {
  width: 400px;
  max-width: calc(100vw - 40px);
  max-height: calc(100vh - 60px);
  overflow-y: auto;
  background: var(--chrome-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}
.modal.wide { width: 620px; }
.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 18px 0;
}
.modal-head h3 { font-size: 15px; }
.modal-x { font-size: 20px; color: var(--muted); line-height: 1; padding: 2px 6px; border-radius: 6px; }
.modal-x:hover { color: var(--text); background: var(--chrome-3); }
.modal-body { padding: 14px 18px 18px; display: flex; flex-direction: column; gap: 14px; }
.modal-actions { display: flex; gap: 8px; justify-content: flex-end; }
.modal-sep { border-top: 1px solid var(--border-soft); }

/* ---- Export hub (#modalExportHub) ----
   The one dialog for GIF / animated SVG / movie. Same token discipline as
   every modal here: only names this file defines. */
.modal.xh { width: 520px; }
.xh-formats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.xh-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  padding: 10px 11px; text-align: left; cursor: pointer;
  background: var(--chrome-3); border: 1px solid var(--border-soft);
  border-radius: 10px; color: var(--text); font: inherit;
}
.xh-card b { font-size: 13px; }
.xh-card .xh-ico { font-size: 17px; line-height: 1; }
.xh-card .xh-sub { font-size: 10.5px; line-height: 1.35; color: var(--muted); }
.xh-card:hover { border-color: var(--border); }
.xh-card.active {
  border-color: #6d7cff;
  box-shadow: 0 0 0 1px #6d7cff inset;
  background: rgba(109, 124, 255, 0.08);
}
.xh-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.xh-foot .muted { font-size: 11px; line-height: 1.4; }
.xh-progress { display: flex; flex-direction: column; gap: 6px; }
.xh-bar { height: 6px; border-radius: 3px; background: var(--chrome-3); overflow: hidden; }
.xh-bar i { display: block; height: 100%; width: 0; border-radius: 3px; background: #6d7cff; transition: width 0.2s ease; }
.xh #xhVoiceWrap { display: flex; flex-direction: column; gap: 6px; }
.xh .xh-studio {
  display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px;
  color: var(--text); background: rgba(16, 185, 129, 0.1);
  border: 1px solid rgba(16, 185, 129, 0.4); border-radius: 8px; padding: 6px 10px;
}
/* ---- Design-panel group headings (CommonSettings.grp) ----
   A small uppercase label with a rule that fades out beside it — the same
   quiet divider language as the diagram's own title underline, so the
   inspector reads as sections rather than one long column of fields. */
.cs-grp { display: flex; align-items: center; gap: 8px; margin: 18px 0 4px; }
/* The accent tick is what separates a SECTION heading from the field labels
   under it — those are muted uppercase too, so without it the two read as
   the same level and the panel is still one long column. */
.cs-grp::before {
  content: ""; flex: none; width: 3px; height: 11px;
  border-radius: 2px; background: #6d7cff;
}
.cs-grp span {
  font-size: 10.5px; font-weight: 750; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--text); white-space: nowrap;
}
.cs-grp::after {
  content: ""; flex: 1; height: 1px; border-radius: 1px;
  background: linear-gradient(to right, var(--border), transparent);
}
.cs-grp:first-child { margin-top: 4px; }

.xh-adv { text-align: center; margin-top: -6px; }
.xh-adv .btn-ghost { font-size: 11.5px; color: var(--muted); }
/* Guest lock: the format cards stay selectable (the lock is decoration,
   never a dead button — same doctrine as the legacy modal) but read as
   locked, with one honest message underneath. */
.xh-card.xh-locked { opacity: 0.55; }
.xh-card.xh-locked b::after { content: ' 🔒'; font-size: 11px; }
.xh-lock {
  font-size: 12.5px; color: var(--text); line-height: 1.45;
  background: rgba(109, 124, 255, 0.08);
  border: 1px solid rgba(109, 124, 255, 0.3);
  border-radius: 8px; padding: 8px 11px; margin-top: -6px;
}

/* ---- Embed dialog (#modalEmbed) ----
   Tokens copied from the neighbouring modal styles above rather than invented:
   an unknown custom property in var(--x, fallback) fails SILENTLY to its
   fallback, so a panel styled with a made-up name looks right in dark and is
   still that hard-coded colour in light. --chrome-2 / --border-soft / --text /
   --muted are the ones this file actually defines. */
.emb-opts { display: flex; flex-wrap: wrap; gap: 10px 18px; }
.emb-opt { display: flex; align-items: center; gap: 7px; font-size: 13.5px; color: var(--text); cursor: pointer; }
.emb-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px 12px; }
.emb-field { display: flex; flex-direction: column; gap: 5px; font-size: 12.5px; color: var(--muted); }
.emb-field select,
.emb-field input {
  font: inherit; font-size: 13.5px; color: var(--text);
  padding: 7px 9px; border-radius: 8px;
  background: var(--chrome-2, rgba(128, 128, 128, .08));
  border: 1px solid var(--border-soft);
}
.emb-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px; line-height: 1.5; color: var(--text);
  padding: 11px 12px; border-radius: 10px; resize: vertical;
  background: var(--chrome-2, rgba(128, 128, 128, .08));
  border: 1px solid var(--border-soft);
  white-space: pre; overflow-x: auto;
}
.emb-note { font-size: 12.5px; line-height: 1.5; }

/* ---- Icon library modal ---- */
.il-modal { width: min(860px, 94vw); max-height: min(84vh, 780px); display: flex; flex-direction: column; }
.il-toolbar { display: flex; gap: 10px; align-items: center; padding: 12px 18px 4px; }
.il-toolbar input[type="search"] {
  flex: 1; min-width: 0; font-size: 14px; padding: 10px 14px; border-radius: 10px;
  background: var(--chrome-2, rgba(128, 128, 128, .08)); border: 1px solid var(--border-soft);
}
.il-tools { display: flex; gap: 6px; }
.il-tool { padding: 8px 10px; border-radius: 8px; border: 1px solid var(--border-soft); font-size: 13px; color: var(--text); }
.il-tool:hover { background: var(--chrome-3, rgba(128, 128, 128, .14)); }
.il-tool.on { background: color-mix(in srgb, #6d7cff 18%, transparent); border-color: #6d7cff; }
.il-upload { font-weight: 600; }
.il-chips { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 18px; align-items: center; }
.il-chip {
  font-size: 11.5px; font-weight: 600; padding: 5px 10px; border-radius: 999px;
  border: 1px solid var(--border-soft); color: var(--text); white-space: nowrap;
}
.il-chip em { font-style: normal; opacity: .55; margin-left: 3px; }
.il-chip:hover { background: var(--chrome-3, rgba(128, 128, 128, .14)); }
.il-chip.on { background: color-mix(in srgb, #6d7cff 22%, transparent); border-color: #6d7cff; }
.il-chip-sep { width: 1px; height: 16px; background: var(--border-soft); margin: 0 3px; }
.il-grid { flex: 1; overflow-y: auto; padding: 6px 18px 14px; }
.il-view-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; }
.il-view-list { display: flex; flex-direction: column; gap: 4px; }
.il-item {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 12px 6px 9px; border-radius: 12px; border: 1px solid transparent; color: var(--text);
  cursor: pointer; text-align: center;
}
.il-item:hover { background: var(--chrome-2, rgba(128, 128, 128, .08)); border-color: var(--border-soft); }
.il-item:focus-visible { outline: 2px solid #6d7cff; outline-offset: 1px; }
.il-view-list .il-item { flex-direction: row; justify-content: flex-start; gap: 12px; padding: 7px 10px; text-align: left; }
.il-glyph { width: 34px; height: 34px; flex: none; display: flex; align-items: center; justify-content: center; }
.il-glyph svg { width: 100%; height: 100%; }
.il-view-list .il-glyph { width: 24px; height: 24px; }
.il-name { font-size: 11px; font-weight: 600; line-height: 1.25; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.il-view-list .il-name { font-size: 12.5px; flex: 1; white-space: nowrap; }
.il-meta { font-size: 11px; color: var(--muted); white-space: nowrap; }
.il-fav {
  position: absolute; top: 4px; right: 6px; font-size: 12px; line-height: 1;
  color: var(--muted); opacity: 0; transition: opacity .12s ease, color .12s ease; padding: 3px;
}
.il-view-list .il-fav { position: static; order: 3; opacity: .5; }
.il-item:hover .il-fav, .il-fav.on { opacity: 1; }
.il-fav.on { color: #f59e0b; }
.il-empty { padding: 40px 10px; text-align: center; color: var(--muted); font-size: 13px; }
.il-foot {
  display: flex; gap: 12px; align-items: center; padding: 9px 18px;
  border-top: 1px solid var(--border-soft); font-size: 11px; color: var(--muted);
}
.il-note { color: #f59e0b; font-weight: 600; }
.il-lic { margin-left: auto; text-align: right; }

/* Export modal: "the shared audio can't carry the narration" recovery panel */
.exp-audio-retry {
  display: flex; flex-direction: column; gap: 10px;
  padding: 10px 12px; border-radius: 10px;
  border: 1px solid color-mix(in srgb, #f59e0b 45%, var(--border-soft));
  background: color-mix(in srgb, #f59e0b 9%, transparent);
  font-size: 12.5px; line-height: 1.5; color: var(--text);
}
/* Narration-source picker inside the export modal: premium studio clips vs
   the device's live voice, plus the studio persona dropdown. */
.exp-voice-src { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 2px 0 4px; }
.exp-voice-src select { flex: 0 1 auto; min-width: 190px; }
.exp-voice-src .seg-btn[data-val="studio"].active {
  background: linear-gradient(135deg, color-mix(in srgb, var(--iris) 28%, var(--chrome-2)), var(--chrome-2));
  border-color: color-mix(in srgb, var(--iris) 55%, var(--border));
}
/* "Present look" block in the export modal — the stagecraft options mirrored
   from Present mode's settings panel. Its segmented rows carry more choices
   than a modal row fits on one line, so they wrap into a chip grid. */
.exp-look-sub {
  font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); margin: 8px 0 -2px;
}
.exp-look-sub:first-of-type { margin-top: 2px; }
.seg.seg-wrap { flex-wrap: wrap; }
.seg.seg-wrap .seg-btn { flex: 1 0 auto; min-width: 74px; }

/* The animated presenter character is retired (see PRESENTER_ENABLED in
   js/app.js). These nodes stay in the DOM for the code that binds them, but
   nothing may ever paint them — belt and braces alongside the `hidden`
   attributes, so a stray `.hidden = false` cannot put the character back. */
#prPresenter, #prPresenterStage, #prPresenterSec, #prShowPresenterWrap,
#prAvatarRow, #modalPresenter { display: none !important; }

.provider-disabled { font-size: 11.5px; cursor: default; }
.file-recent-list { display: flex; flex-direction: column; gap: 4px; max-height: 190px; overflow-y: auto; }
.file-recent-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 9px;
  border-radius: 8px;
  background: var(--chrome-3);
  border: 1px solid transparent;
  font-size: 12.5px;
  cursor: pointer;
}
.file-recent-item:hover { border-color: var(--accent); }
.file-recent-item .fri-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-recent-item .fri-meta { flex: none; color: var(--muted); font-size: 11px; }
.file-recent-item .fri-remove { flex: none; color: var(--muted); padding: 0 4px; font-size: 15px; line-height: 1; border-radius: 5px; }
.file-recent-item .fri-remove:hover { color: var(--danger); background: var(--chrome); }

/* Type picker modal — "What do you want to make?" Tiles are cloned from the
   home type cards at runtime, so the 16 types live in exactly one place. */
.tp-modal { width: 760px; }
.tp-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
@media (max-width: 680px) { .tp-grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 480px) { .tp-grid { grid-template-columns: repeat(2, 1fr); } }
.tp-tile {
  display: flex; flex-direction: column; gap: 9px; padding: 10px;
  background: var(--chrome); border: 1px solid var(--border-soft); border-radius: 12px;
  cursor: pointer; text-align: left; color: inherit; font: inherit;
  transition: border-color .12s, transform .1s ease, box-shadow .15s;
}
.tp-tile:hover { border-color: var(--accent); transform: translateY(-2px); box-shadow: var(--shadow-sm); }
.tp-prev {
  background: linear-gradient(160deg, #ffffff, #eef1f8);
  border: 1px solid var(--border-soft); border-radius: 8px; padding: 9px 10px; line-height: 0;
}
.tp-prev svg { width: 100%; height: auto; display: block; }
.tp-name { font-size: 12.5px; font-weight: 600; letter-spacing: -0.01em; }
.tp-foot { margin-top: 6px; font-size: 13px; color: var(--muted); }
.tp-foot .link-btn { color: var(--accent-ink); font-weight: 600; background: none; border: none; cursor: pointer; padding: 0; font: inherit; }
.tp-foot .link-btn:hover { text-decoration: underline; }

.tpl-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.tpl-cat {
  grid-column: 1 / -1;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--muted);
  margin-top: 8px;
}
.tpl-cat:first-child { margin-top: 0; }
.tpl-card {
  display: flex;
  gap: 11px;
  align-items: flex-start;
  text-align: left;
  padding: 12px;
  background: var(--chrome);
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  transition: border-color .12s, transform .08s;
}
.tpl-card:hover { border-color: var(--accent); transform: translateY(-1px); }
.tpl-emoji { font-size: 22px; line-height: 1.2; }
.tpl-name { font-weight: 700; font-size: 12.5px; margin-bottom: 3px; }
.tpl-desc { font-size: 11.5px; color: var(--muted); line-height: 1.45; }

.help-cols { display: flex; flex-direction: row; gap: 22px; }
.help-cols > div { flex: 1; min-width: 0; }
.help-cols h4 { font-size: 12px; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); margin-bottom: 8px; }
.help-cols h4 + .help-list + h4 { margin-top: 16px; }
.help-list { list-style: none; display: flex; flex-direction: column; gap: 7px; font-size: 12.5px; line-height: 1.5; }
.help-list b { color: var(--text); font-weight: 600; }
.help-list li { color: var(--muted); }
.help-h { font-size: 12px; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); margin-bottom: 10px; }

/* notation legend */
.note-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 18px; }
.note-item { display: flex; gap: 11px; align-items: flex-start; }
.note-item svg { flex: none; margin-top: 1px; }
.note-item b { display: block; font-size: 12.5px; margin-bottom: 2px; }
.note-item span { font-size: 11.5px; color: var(--muted); line-height: 1.45; }
.note-item span b { display: inline; font-size: inherit; margin: 0; color: var(--text); font-weight: 600; }
.note-item code, .legal code {
  font-family: var(--mono);
  font-size: 11px;
  background: var(--chrome-3);
  border-radius: 4px;
  padding: 1px 5px;
}

/* legal modal */
.legal { gap: 4px; }
.legal section { padding: 10px 0 14px; border-bottom: 1px solid var(--border-soft); }
.legal section:last-child { border-bottom: none; }
.legal h4 { font-size: 13.5px; margin-bottom: 8px; display: flex; align-items: center; gap: 8px; }
.legal p, .legal ul { font-size: 12.5px; color: var(--muted); line-height: 1.6; margin-bottom: 8px; }
.legal ul { padding-left: 18px; display: flex; flex-direction: column; gap: 5px; }
.legal b { color: var(--text); font-weight: 600; }
.legal .fineprint { font-size: 11px; opacity: .8; font-style: italic; }
.tag-dummy {
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: #f59e0b;
  background: rgba(245, 158, 11, .14);
  padding: 2px 6px;
  border-radius: 4px;
}

/* ============ Home page ============ */
#homePage {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: var(--chrome);
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  /* flex-start (not center) is deliberate: centering a flex item taller than its
     container clips the overflow above the fold with no way to scroll to it. */
  align-items: flex-start;
  justify-content: center;
}

.home-inner {
  position: relative;
  z-index: 1;
  max-width: 900px;
  width: 100%;
  padding: clamp(96px, 12vh, 128px) 22px 40px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  margin: auto 0;
}
.home-anim {
  animation: home-fade-up .55s cubic-bezier(.2, .7, .3, 1) both;
  animation-delay: var(--d, 0s);
}
@keyframes home-fade-up {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ---- Scroll-reveal (js/scroll-fx.js): sections rise + fade in as they enter
   the viewport, so moving between sections feels smooth. The `sr` class is
   added by JS, so content is always visible without it. ---- */
.sr { opacity: 0; transform: translateY(14px); transition: opacity .38s var(--ease-out), transform .42s var(--ease-out); }
.sr.sr-in { opacity: 1; transform: none; }
/* Card-grid stagger: js/scroll-fx.js stamps --sr-i on each grid child; when
   the section reveals, its cards cascade in a beat apart. */
.sr .home-card, .sr .lp-tpl-card, .sr .price-card, .sr .lp-who .card { opacity: 0; transform: translateY(10px); }
.sr.sr-in .home-card, .sr.sr-in .lp-tpl-card, .sr.sr-in .price-card, .sr.sr-in .lp-who .card {
  opacity: 1; transform: none;
  transition: opacity .34s var(--ease-out), transform .38s var(--ease-out);
  transition-delay: calc(var(--sr-i, 0) * 45ms);
}
/* When JS drives the reveal, don't also run the one-shot load animation. */
.home-anim.sr { animation: none; }
/* Smooth programmatic/anchor scrolls. */
#homePage, #explorePage { scroll-behavior: smooth; }
#homePage { scroll-padding-top: 84px; }
@media (prefers-reduced-motion: reduce) {
  .sr, .sr.sr-in { opacity: 1; transform: none; transition: none; }
  #homePage, #explorePage { scroll-behavior: auto; scroll-snap-type: none; }
}

/* ============ CanvasKit — floating toolbar, context menu, ⌘K ============ */
/* All fixed-position glass chrome, shared across every canvas tool. */

/* The stage guide (toggle F) is drawn INTO the rulers now — an accent band
   on the top and left ruler marking the 16:9 working area (canvas-kit's
   renderRulers). The old #ckFrame on-canvas rectangle and its "16:9" tag are
   gone: they crossed lines through the middle of people's diagrams to say
   what the margins can say. */

#ckToolbar {
  position: fixed; z-index: 40; display: flex; align-items: center; gap: 2px;
  padding: 4px; border-radius: var(--r-2);
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--border); box-shadow: var(--shadow-sm);
  animation: ck-pop var(--dur-1) var(--ease-out);
}
@keyframes ck-pop { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
.ck-btn {
  min-width: 28px; height: 28px; padding: 0 6px; border-radius: 7px;
  font-size: 14px; line-height: 1; color: var(--muted); display: inline-flex;
  align-items: center; justify-content: center;
  transition: background var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}
.ck-btn:hover { background: var(--chrome-3); color: var(--text); }
.ck-sep { width: 1px; height: 18px; background: var(--border); margin: 0 4px; flex: none; }
.ck-swatches { display: inline-flex; align-items: center; gap: 3px; padding: 0 2px; }
.ck-swatch {
  width: 16px; height: 16px; border-radius: 50%; border: 1px solid rgba(255, 255, 255, .25);
  cursor: pointer; padding: 0; transition: transform var(--dur-1) var(--ease-out);
}
.ck-swatch:hover { transform: scale(1.2); }
.ck-swatch.active { box-shadow: 0 0 0 2px var(--accent-ring); }
.ck-swatch-none { background: var(--chrome-3); color: var(--muted); font-size: 9px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center; border-color: var(--border); }

#ckMenu {
  position: fixed; z-index: 41; min-width: 190px; padding: 5px; border-radius: var(--r-2);
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--border); box-shadow: var(--shadow);
  animation: ck-pop var(--dur-1) var(--ease-out);
}
.ck-mi {
  display: flex; align-items: center; justify-content: space-between; gap: 18px;
  width: 100%; padding: 7px 10px; border-radius: 7px; font-size: 12.5px; color: var(--text);
  text-align: left;
}
.ck-mi:hover:not(:disabled) { background: var(--accent-soft); }
.ck-mi:disabled { color: var(--faint); cursor: default; }
.ck-mi kbd { font-family: var(--mono); font-size: 10.5px; color: var(--faint); }
.ck-msep { height: 1px; background: var(--border); margin: 4px 6px; }

#canvasWrap.space-pan { cursor: grab; }
#canvasWrap.space-pan:active { cursor: grabbing; }

/* Magic Insert launcher — the round on-canvas button (kids-app style),
   the visible twin of ⌘K. Sits above the canvas, bottom-left. */
#miLauncher {
  position: absolute; left: 16px; bottom: 14px; z-index: 12;
  width: 46px; height: 46px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  color: var(--on-accent);
  background: linear-gradient(160deg, var(--iris-bright), var(--iris-deep));
  box-shadow: 0 8px 24px -8px color-mix(in srgb, var(--iris) 70%, transparent),
    0 1px 0 rgba(255, 255, 255, .25) inset;
  transition: transform var(--dur-1) var(--ease-spring), box-shadow var(--dur-2) var(--ease-out);
}
#miLauncher:hover { transform: scale(1.1) rotate(8deg); box-shadow: var(--glow-accent), 0 1px 0 rgba(255, 255, 255, .25) inset; }
#miLauncher:active { transform: scale(.96); }
#miLauncher svg { display: block; }
body.presenting #miLauncher, body.watching #miLauncher, body.recording #miLauncher { display: none !important; }
@media (prefers-reduced-motion: reduce) { #miLauncher, #miLauncher:hover { transition: none; transform: none; } }
/* Presenting/recording owns the screen — no editing chrome. Belt for the JS
   guard, which only re-evaluates on the next render. */
body.presenting #ckToolbar, body.watching #ckToolbar, body.recording #ckToolbar,
body.presenting #ckMenu, body.watching #ckMenu, body.recording #ckMenu { display: none !important; }

/* ⌘K command palette */
#ckPalette {
  position: fixed; inset: 0; z-index: 90;
  background: color-mix(in srgb, var(--paper) 55%, transparent);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  display: flex; align-items: flex-start; justify-content: center; padding-top: 14vh;
}
.ckp {
  width: min(560px, 92vw); border-radius: var(--r-3); overflow: hidden;
  background: var(--panel);
  border: 1px solid var(--border); box-shadow: var(--shadow-lg), var(--glow-accent);
  animation: ckp-in var(--dur-2) var(--ease-out);
}
@keyframes ckp-in { from { opacity: 0; transform: translateY(-8px) scale(.985); } to { opacity: 1; transform: none; } }
#ckpInput {
  width: 100%; padding: 15px 18px; font-size: 15px; background: none; border: none; outline: none;
  border-bottom: 1px solid var(--border); color: var(--text);
}
#ckpInput::placeholder { color: var(--faint); }
.ckp-list { max-height: 46vh; overflow-y: auto; padding: 6px; }
.ckp-item {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 9px 10px;
  border-radius: 8px; font-size: 13px; color: var(--text); text-align: left;
}
.ckp-item.hi { background: var(--accent-soft); }
.ckp-kind {
  flex: none; font-family: var(--mono); font-size: 9.5px; letter-spacing: .07em; text-transform: uppercase;
  color: var(--accent-ink); background: var(--accent-soft); border-radius: 5px; padding: 3px 6px;
}
.ckp-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ckp-item kbd { font-family: var(--mono); font-size: 11px; color: var(--faint); }
.ckp-empty { padding: 18px; text-align: center; color: var(--faint); font-size: 13px; }
/* Magic Insert extensions: category chips, group headers, star/delete, footer */
.ckp-chips { display: flex; gap: 6px; padding: 9px 12px; border-bottom: 1px solid var(--border); overflow-x: auto; scrollbar-width: none; }
.ckp-chip {
  flex: none; font-size: 12px; font-weight: 550; color: var(--muted); cursor: pointer;
  padding: 5px 12px; border-radius: var(--r-full); border: 1px solid var(--border);
  transition: color var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out), background var(--dur-1) var(--ease-out);
}
.ckp-chip:hover { color: var(--text); border-color: var(--accent); }
.ckp-chip.on { color: var(--on-accent); background: var(--accent); border-color: var(--accent); }
.ckp-group {
  font-family: var(--mono); font-size: 10px; letter-spacing: .09em; text-transform: uppercase;
  color: var(--faint); padding: 10px 12px 3px;
}
.ckp-item { cursor: pointer; user-select: none; }
.ckp-glyph { flex: none; width: 26px; text-align: center; font-size: 16px; }
.ckp-desc { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11.5px; color: var(--faint); text-align: right; }
.ckp-item .ckp-label { flex: none; max-width: 45%; }
.ckp-star, .ckp-del {
  flex: none; width: 22px; height: 22px; border-radius: 6px; font-size: 13px; line-height: 1;
  color: var(--faint); opacity: 0; transition: opacity var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}
.ckp-item:hover .ckp-star, .ckp-item.hi .ckp-star,
.ckp-item:hover .ckp-del, .ckp-item.hi .ckp-del { opacity: 1; }
.ckp-star:hover, .ckp-star.on { color: var(--d-amber); opacity: 1; }
.ckp-del:hover { color: var(--danger); }
.ckp-foot {
  display: flex; gap: 16px; justify-content: center; padding: 8px 12px;
  border-top: 1px solid var(--border); font-family: var(--mono); font-size: 10.5px; color: var(--faint);
}

/* Layers panel */
#ckLayers {
  position: fixed; left: 232px; top: 76px; z-index: 39; width: 216px;
  border-radius: var(--r-2); overflow: hidden;
  background: var(--glass-bg); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--border); box-shadow: var(--shadow);
  animation: ck-pop var(--dur-1) var(--ease-out);
}
.ck-layers-head {
  display: flex; align-items: center; justify-content: space-between;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); padding: 9px 6px 7px 12px; border-bottom: 1px solid var(--border);
}
.ck-layers-x { width: 22px; height: 22px; border-radius: 6px; color: var(--faint); font-size: 14px; }
.ck-layers-x:hover { color: var(--text); background: var(--chrome-3); }
.ck-layers-list { max-height: 46vh; overflow-y: auto; padding: 5px; }
.ck-layer {
  display: flex; align-items: center; gap: 6px; padding: 6px 8px; border-radius: 7px;
  font-size: 12.5px; color: var(--text); cursor: pointer;
}
.ck-layer:hover { background: var(--chrome-3); }
.ck-layer.sel { background: var(--accent-soft); }
.ck-layer.drop { box-shadow: 0 -2px 0 var(--accent); }
.ck-layer.locked .ck-layer-name { color: var(--faint); }
.ck-layer-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ck-layer-lock { flex: none; font-size: 11px; opacity: .35; }
.ck-layer:hover .ck-layer-lock, .ck-layer.locked .ck-layer-lock { opacity: 1; }
.ck-layers-empty { padding: 14px; text-align: center; color: var(--faint); font-size: 12px; }

/* Minimap */
#ckMinimap {
  position: fixed; z-index: 39; width: 186px; border-radius: var(--r-2); overflow: hidden;
  background: var(--glass-bg); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--border); box-shadow: var(--shadow);
  animation: ck-pop var(--dur-1) var(--ease-out);
}
.ck-mini-head {
  font-family: var(--mono); font-size: 10px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); padding: 7px 12px 5px; border-bottom: 1px solid var(--border);
}
.ck-mini-stage { position: relative; margin: 8px; cursor: crosshair; border-radius: 6px; overflow: hidden; background: var(--canvas-bg); }
.ck-mini-stage svg { display: block; }
.ck-mini-view {
  position: absolute; border: 1.5px solid var(--iris); border-radius: 3px;
  background: color-mix(in srgb, var(--iris) 14%, transparent); pointer-events: none;
}
body.presenting #ckLayers, body.watching #ckLayers, body.recording #ckLayers,
body.presenting #ckMinimap, body.watching #ckMinimap, body.recording #ckMinimap { display: none !important; }

/* Animated connector flow — marching dashes in the editor and Present mode
   (raster exports draw the static dash pattern). */
@keyframes fc-flow-dash { to { stroke-dashoffset: -30; } }
.fc-flow { animation: fc-flow-dash 1s linear infinite; }

/* Live Present's optional arrow motion is a travelling accent overlay, never
   the real shaft. The base arrow therefore keeps its authored dash, colour,
   width and arrowhead; labels remain readable even in a moving diagram.
   `pathLength=100` on the cloned runner makes its comet-and-spark rhythm
   proportionate on both tiny local links and long diagram-spanning routes. */
@keyframes pr-connector-flow-glint {
  0% { stroke-dashoffset: 100; stroke-opacity: .62; }
  14% { stroke-opacity: .98; }
  62% { stroke-opacity: .86; }
  84% { stroke-opacity: .96; }
  100% { stroke-dashoffset: 0; stroke-opacity: .68; }
}
body.presenting #svgHost .pr-connector-motion-trace {
  fill: none !important;
  /* The runner is the Present theme's kinetic accent. It is intentionally
     separate from the authored shaft (and from the branch-coloured one-shot
     arrival trace), so changing Current/Aurora/Gallery immediately changes
     the live flow treatment without restyling the diagram itself. */
  stroke: var(--pr-connector-flow-color,
    color-mix(in srgb,
      var(--pr-connector-flow-core, var(--iris-bright, #8b80ff)) 84%,
      var(--pr-connector-flow-spark, #fff))) !important;
  stroke-width: var(--pr-connector-flow-width, 3.4) !important;
  stroke-opacity: .92;
  /* A long rounded comet followed by a pin-point sparkle. `pathLength=100`
     on the clone keeps the treatment proportional across local and long
     connectors, without adding a particle layer over the diagram. */
  stroke-dasharray: 12 3 1.35 83.65 !important;
  stroke-dashoffset: 100;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  filter: drop-shadow(0 0 1px var(--pr-connector-flow-spark, transparent))
          drop-shadow(0 0 3px var(--pr-connector-flow-halo, transparent));
  animation: pr-connector-flow-glint var(--pr-connector-flow-duration, 3.4s) linear infinite;
  animation-delay: var(--pr-connector-flow-delay, 0s);
}
body.presenting #prConnectorMotionToggle.on,
body.presenting #prConnectorMotionToggle[aria-pressed="true"] {
  color: var(--pr-stage-ink, #e9ecff);
  background: color-mix(in srgb, var(--pr-motion-base, var(--iris-bright, #8b80ff)) 25%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--pr-motion-base, var(--iris-bright, #8b80ff)) 48%, transparent) inset,
              0 0 14px -8px var(--pr-motion-base, var(--iris-bright, #8b80ff));
}
@media (prefers-reduced-motion: reduce) {
  body.presenting #svgHost [data-pr-connector-motion] { display: none !important; animation: none !important; }
}

/* Rulers + user guides (toggle R) */
#ckRulerH, #ckRulerV {
  position: fixed; z-index: 38; background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  cursor: crosshair;
}
#ckRulerH { height: 22px; border-bottom: 1px solid var(--border); }
#ckRulerV { width: 22px; border-right: 1px solid var(--border); }
#ckGuides { position: fixed; inset: 0; z-index: 37; pointer-events: none; }
.ck-guide { position: fixed; pointer-events: auto; }
.ck-guide-x { width: 9px; margin-left: -4.5px; cursor: ew-resize; }
.ck-guide-y { height: 9px; margin-top: -4.5px; cursor: ns-resize; }
.ck-guide::after { content: ""; position: absolute; background: color-mix(in srgb, var(--iris) 75%, transparent); }
.ck-guide-x::after { left: 4px; top: 0; bottom: 0; width: 1px; }
.ck-guide-y::after { top: 4px; left: 0; right: 0; height: 1px; }
body.presenting #ckRulerH, body.presenting #ckRulerV, body.presenting #ckGuides,
body.watching #ckRulerH, body.watching #ckRulerV, body.watching #ckGuides { display: none !important; }

/* Present-mode entrance animations — the element revealed by the current step
   carries one of these (see graphtool buildSvg). Classes only have rules in
   the live document, so exported SVG/GIF/video stay static by design. */
@keyframes prFade { from { opacity: 0; } }
@keyframes prGrow { from { opacity: 0; transform: scale(.55); } }
@keyframes prSlide { from { opacity: 0; transform: translateY(20px); } }
@keyframes prPulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.06); } }
@keyframes prWipe { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0); } }
/* dasharray lives inside the keyframes with fill-mode "backwards", so once the
   draw finishes the edge's own dash pattern (dashed/dotted/flow) reasserts. */
@keyframes prDrawPath {
  from { stroke-dasharray: 100; stroke-dashoffset: 100; }
  to { stroke-dasharray: 100; stroke-dashoffset: 0; }
}
.pr-anim-fade { animation: prFade .5s var(--ease-out) both; }
.pr-anim-grow { animation: prGrow .5s var(--ease-spring) both; transform-box: fill-box; transform-origin: center; }
.pr-anim-slide { animation: prSlide .5s var(--ease-out) both; }
.pr-anim-pulse { animation: prFade .35s var(--ease-out) both, prPulse 1.5s .4s ease-in-out 2; transform-box: fill-box; transform-origin: center; }
.pr-anim-draw { animation: prWipe .55s var(--ease-out) both; }
.pr-anim-drawpath { animation: prDrawPath .55s var(--ease-out) backwards; }
/* Pencil sketch: the INK holds back while an injected tracer (PencilTrace in
   app.js) draws the shape's outline with a pencil riding the tip, then the
   ink fades in as the pencil lifts. The hold and the trace share one clock —
   PencilTrace reads this animation's duration off the element, so retiming
   the entrance retimes the pencil with it. */
.pr-anim-sketch { animation: prSketchInk 2.6s var(--ease-out) both; }
@keyframes prSketchInk {
  /* Hold to 50% — the trace runs exactly the first half (PencilTrace uses
     duration/2) — then arrive fast: the ink lands AS the pencil lifts, which
     is what makes it read as drawn rather than revealed. */
  0%, 50% { opacity: 0; }
  66% { opacity: 1; }
  100% { opacity: 1; }
}
.pr-pencil-line {
  fill: none; stroke-linecap: round; stroke-linejoin: round;
  pointer-events: none;
}
/* Ink & paper — the document-wide font/background override in Settings. */
.cs-inkpaper { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.cs-ip { display: inline-flex; align-items: center; gap: 5px; }
.cs-ip input[type="color"] {
  width: 34px; height: 24px; padding: 1px; border: 1px solid var(--border);
  border-radius: 7px; background: var(--panel-2, var(--panel)); cursor: pointer;
}
.cs-ip small { color: var(--muted); font-size: 10.5px; }
.cs-ip-x {
  border: 1px solid var(--border); background: none; color: var(--muted);
  border-radius: 6px; width: 18px; height: 18px; line-height: 1; cursor: pointer;
  font-size: 12px; padding: 0;
}
.cs-ip-x:hover { color: var(--text); border-color: var(--text); }

/* The HAND chrome skin lives at the END of this file — it must outrank the
   Present polish sections' `font:` shorthands, which come after this line. */

@media (prefers-reduced-motion: reduce) {
  .fc-flow, .pr-anim-fade, .pr-anim-grow, .pr-anim-slide, .pr-anim-pulse, .pr-anim-draw, .pr-anim-drawpath, .pr-anim-sketch { animation: none; }
}
@media (prefers-reduced-motion: reduce) {
  #ckToolbar, #ckMenu, #ckLayers, #ckMinimap, .ckp { animation: none; }
}

/* ============ Explore gallery page ============ */
#explorePage {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: var(--chrome);
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  align-items: flex-start;
  justify-content: center;
}
/* ============================================================
   Explore — template marketplace. Discovery-first surface built on the
   existing brand tokens (marigold signal, graphite/paper neutrals, dot grid).
   Class prefix .mkt-*. Pagination reuses .explore-page-* below.
   ============================================================ */
.mkt {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: none;
  margin: 0 auto;
  padding: clamp(76px, 8vh, 90px) clamp(18px, 4vw, 64px) 34px;
  text-align: left;
}
/* Full-width explore: header spans the viewport to match the body. */
#explorePage .lp-top-inner { max-width: none; padding: 0 clamp(18px, 4vw, 64px); }
/* ---- Hero + search (minimal, marketplace-style) ---- */
.mkt-hero { text-align: center; padding: 8px 0 6px; }
.mkt-title {
  font-size: clamp(26px, 3.6vw, 38px); font-weight: 800; letter-spacing: -.03em;
  line-height: 1.06; margin: 0; text-wrap: balance;
}
.mkt-sub { color: var(--muted); font-size: clamp(13.5px, 1.4vw, 15px); line-height: 1.5; max-width: 54ch; margin: 10px auto 0; }
.mkt-sub b { color: var(--text); font-weight: 600; }
.mkt-search {
  position: relative; display: flex; align-items: center; gap: 12px;
  max-width: 620px; margin: 22px auto 0; padding: 0 14px 0 18px; height: 54px;
  background: var(--chrome-2); border: 1px solid var(--border); border-radius: 14px;
  box-shadow: var(--shadow-sm); transition: border-color .16s ease, box-shadow .16s ease;
}
.mkt-search:focus-within { border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-ring), var(--shadow-sm); }
.mkt-search-ico { width: 21px; height: 21px; color: var(--faint); flex: none; }
.mkt-search input {
  flex: 1; min-width: 0; height: 100%; border: none; background: none; outline: none;
  color: var(--text); font-size: 15.5px; font-family: inherit;
}
.mkt-search input::placeholder { color: var(--faint); }
.mkt-kbd {
  font-family: var(--mono); font-size: 12px; color: var(--muted); flex: none;
  background: var(--chrome-3); border: 1px solid var(--border-soft); border-bottom-width: 2px;
  border-radius: 6px; padding: 2px 8px;
}
.mkt-suggests { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 7px; margin-top: 14px; }
.mkt-sugg-label { font-size: 12.5px; color: var(--faint); }
.mkt-sugg {
  font-size: 12.5px; font-weight: 550; color: var(--muted); cursor: pointer;
  background: var(--chrome-2); border: 1px solid var(--border-soft); border-radius: 999px; padding: 5px 12px;
  transition: color .12s, border-color .12s, transform .12s;
}
.mkt-sugg:hover { color: var(--text); border-color: var(--accent); transform: translateY(-1px); }

/* ---- Category (diagram type) tabs ---- */
/* Wrapper owns the divider line + a right-edge fade so a partially-scrolled tab
   reads as "scroll for more" instead of an abrupt clip. */
.mkt-tabs-wrap { position: relative; margin: 18px 0 2px; border-bottom: 1px solid var(--border); }
.mkt-tabs-wrap::after {
  content: ""; position: absolute; top: 0; right: 0; bottom: 1px; width: 46px; pointer-events: none;
  background: linear-gradient(to right, transparent, var(--chrome));
}
.mkt-tabs {
  display: flex; gap: 7px; padding: 2px 2px 14px; overflow-x: auto; scrollbar-width: none;
  scroll-snap-type: x proximity; scroll-padding-right: 46px;
}
.mkt-tabs::-webkit-scrollbar { display: none; }
.mkt-tab {
  display: inline-flex; align-items: center; gap: 7px; flex: none; scroll-snap-align: start;
  font-size: 13px; font-weight: 600; color: var(--muted); cursor: pointer; white-space: nowrap;
  background: var(--chrome-2); border: 1px solid var(--border-soft); border-radius: 999px; padding: 8px 14px;
  transition: color .12s, background .12s, border-color .12s, transform .12s;
}
.mkt-tab svg { width: 17px; height: 17px; flex: none; }
.mkt-tab:hover { color: var(--text); border-color: var(--accent); transform: translateY(-1px); }
.mkt-tab.active { color: var(--on-accent); background: var(--accent); border-color: var(--accent); }

/* ---- Toolbar: filters + sort + count ---- */
/* Sticky glass toolbar — sort + result count stay in reach while scrolling
   the grid. Sits just under the fixed .lp-top header. */
.mkt-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  position: sticky; top: 64px; z-index: 5; margin: 12px 0 4px; padding: 8px 10px;
  background: var(--glass-bg); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--border-soft); border-radius: var(--r-2); }
.mkt-filters { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.mkt-select {
  font: inherit; font-size: 12.5px; font-weight: 600; color: var(--muted); cursor: pointer;
  background: var(--chrome-2); border: 1px solid var(--border-soft); border-radius: 999px;
  padding: 8px 30px 8px 13px; appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2398a0ae' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 11px center;
  transition: border-color .12s, color .12s;
}
.mkt-select:hover { border-color: var(--accent); color: var(--text); }
.mkt-select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.mkt-select.is-set { color: var(--text); border-color: var(--accent); background-color: var(--accent-soft); }
.mkt-toggle {
  font: inherit; font-size: 12.5px; font-weight: 600; color: var(--muted); cursor: pointer;
  background: var(--chrome-2); border: 1px solid var(--border-soft); border-radius: 999px; padding: 8px 13px;
  transition: color .12s, background .12s, border-color .12s;
}
.mkt-toggle:hover { color: var(--text); border-color: var(--accent); }
.mkt-toggle.on { color: var(--on-accent); background: var(--accent); border-color: var(--accent); }
.mkt-sort { margin-left: 2px; }
.mkt-clear { font: inherit; font-size: 12.5px; font-weight: 600; color: var(--danger); cursor: pointer; background: none; border: none; padding: 8px 6px; }
.mkt-clear:hover { text-decoration: underline; }
.mkt-count { font-family: var(--mono); font-size: 12.5px; color: var(--faint); flex: none; }

/* ---- Quick access: continue / recent / favorites ---- */
.mkt-quick { display: flex; flex-wrap: wrap; gap: 14px 22px; align-items: flex-start; margin: 8px 0 18px; }
.mkt-continue {
  display: inline-flex; align-items: center; gap: 12px; cursor: pointer; text-align: left;
  background: linear-gradient(120deg, var(--accent-soft), transparent 70%), var(--chrome-2);
  border: 1px solid var(--accent-ring); border-radius: 14px; padding: 12px 16px;
  transition: transform .12s, box-shadow .15s;
}
.mkt-continue:hover { transform: translateY(-2px); box-shadow: var(--shadow-sm); }
.mkt-continue-ico { width: 38px; height: 38px; flex: none; display: grid; place-items: center; color: var(--accent-ink); background: var(--chrome); border: 1px solid var(--border-soft); border-radius: 10px; }
.mkt-continue-ico svg { width: 20px; height: 20px; }
.mkt-continue-txt { display: flex; flex-direction: column; }
.mkt-continue-k { font-family: var(--mono); font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase; color: var(--accent-ink); }
.mkt-continue-t { font-size: 14px; font-weight: 650; color: var(--text); max-width: 30ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mkt-continue-go { font-size: 12.5px; font-weight: 600; color: var(--accent-ink); margin-left: 4px; }
.mkt-mini-block { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.mkt-mini-head { font-family: var(--mono); font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--faint); }
.mkt-mini-rail { display: flex; flex-wrap: wrap; gap: 8px; }
.mkt-mini {
  display: inline-flex; align-items: center; gap: 8px; flex: none; cursor: pointer; max-width: 220px;
  background: var(--chrome-2); border: 1px solid var(--border-soft); border-radius: 10px; padding: 5px 11px 5px 5px;
  transition: border-color .12s, transform .12s;
}
.mkt-mini:hover { border-color: var(--accent); transform: translateY(-1px); }
.mkt-mini-thumb { width: 40px; height: 26px; flex: none; border-radius: 6px; overflow: hidden; background: var(--canvas-bg); }
.mkt-mini-thumb svg { width: 100%; height: 100%; display: block; }
.mkt-mini-name { font-size: 12.5px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- Sections + rails ---- */
.mkt-sections { display: flex; flex-direction: column; gap: 26px; margin-top: 6px; }
.mkt-section-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 12px; }
.mkt-section-head h2 { font-size: clamp(16px, 2vw, 19px); font-weight: 760; letter-spacing: -.02em; display: flex; align-items: center; gap: 8px; }
.mkt-section-head p { font-size: 13px; color: var(--muted); margin-top: 3px; }
.mkt-h-ico { display: inline-grid; place-items: center; width: 24px; height: 24px; border-radius: 7px; font-size: 13px; background: var(--accent-soft); color: var(--accent-ink); }
.mkt-h-ico--ai { background: color-mix(in srgb, var(--d-violet) 20%, transparent); color: var(--d-violet); }
.mkt-seeall { font-family: var(--mono); font-size: 12.5px; font-weight: 600; color: var(--accent-ink); background: none; border: none; cursor: pointer; white-space: nowrap; flex: none; }
.mkt-seeall span { display: inline-block; transition: transform .15s; }
.mkt-seeall:hover span { transform: translateX(3px); }
/* One row of cards — the count is sliced in JS to exactly fill the width, so
   there is no horizontal scroll and no wrapping; the rest is behind "See more". */
.mkt-row { display: grid; grid-template-columns: repeat(auto-fill, minmax(205px, 1fr)); gap: 14px; }

/* ---- Collections ---- */
.mkt-collections { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.mkt-coll {
  display: flex; align-items: center; gap: 10px; text-align: left; cursor: pointer;
  background: var(--chrome-2); border: 1px solid var(--border-soft); border-radius: 12px; padding: 9px 11px;
  transition: transform .12s, border-color .12s, box-shadow .15s;
}
.mkt-coll:hover { transform: translateY(-2px); border-color: color-mix(in srgb, var(--coll) 60%, var(--border-soft)); box-shadow: var(--shadow-sm); }
.mkt-coll-ico {
  width: 34px; height: 34px; flex: none; display: grid; place-items: center; font-size: 17px; border-radius: 9px;
  background: color-mix(in srgb, var(--coll) 18%, transparent); border: 1px solid color-mix(in srgb, var(--coll) 34%, transparent);
}
.mkt-coll-txt { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.mkt-coll-name { font-size: 13px; font-weight: 680; color: var(--text); }
.mkt-coll-blurb { font-size: 11px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mkt-coll-count { font-family: var(--mono); font-size: 11.5px; font-weight: 700; color: var(--faint); flex: none; }

/* ---- Card ---- */
.mkt-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(205px, 1fr)); gap: 14px; }
.mkt-card {
  position: relative; display: flex; flex-direction: column; text-align: left; cursor: pointer;
  background: var(--chrome-2); border: 1px solid var(--border-soft); border-radius: 14px; overflow: hidden;
  transition: transform .14s ease, border-color .14s ease, box-shadow .16s ease;
  /* Off-screen cards skip layout/paint entirely; the intrinsic size keeps the
     scrollbar stable while they're skipped. */
  content-visibility: auto;
  contain-intrinsic-size: 240px 260px;
}
/* A soft, neutral lift — no colored glow, so the card reads calm. */
.mkt-card:hover {
  transform: translateY(-3px); border-color: color-mix(in srgb, var(--slate) 32%, var(--border-soft));
  box-shadow: var(--shadow);
}
.mkt-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.mkt-thumb { position: relative; aspect-ratio: 16 / 10; background: var(--canvas-bg); overflow: hidden; }
/* The card's only artwork: the catalog row's own `icon_svg`, drawn inline.
   There is no per-template raster/SVG file layered on top — see renderExploreCard. */
.mkt-thumb > svg { width: 100%; height: 100%; display: block; padding: 8px 12px; }
/* One restrained frosted label — no bright fills competing with the artwork. A
   small colored dot carries the only hue. */
.mkt-badge {
  position: absolute; top: 9px; left: 9px; z-index: 2; display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--mono); font-size: 9.5px; font-weight: 650; letter-spacing: .05em; text-transform: uppercase;
  color: #f4f5f8; padding: 3px 8px 3px 7px; border-radius: 6px;
  background: color-mix(in srgb, #05070c 58%, transparent); border: 1px solid rgba(255, 255, 255, .14);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}
.mkt-badge::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: var(--badge-dot, var(--marigold)); }
.mkt-badge--trending { --badge-dot: var(--marigold); }
.mkt-badge--new { --badge-dot: #34d399; }

/* ---- Engagement strip (js/engagement.js) --------------------------------
   Views · watch time (text) + like · share (live buttons), one quiet row.
   Shared by Explore cards, search tiles and the quick view; empty spans stay
   hidden so an untouched diagram wears no zeros. */
.eng-strip {
  display: flex; align-items: center; gap: 10px;
  margin-top: 8px; padding-top: 8px;
  border-top: 1px solid var(--border-soft);
  font-size: 11.5px; color: var(--faint);
}
.eng-strip svg { width: 14px; height: 14px; flex: none; }
.eng-stat { display: inline-flex; align-items: center; gap: 4px; }
.eng-stat b { font-weight: 600; font-variant-numeric: tabular-nums; }
.eng-like, .eng-share {
  display: inline-flex; align-items: center; gap: 4px;
  font: inherit; font-weight: 600; color: var(--faint);
  background: none; border: 0; padding: 3px 6px; margin: -3px 0;
  border-radius: 8px; cursor: pointer;
  transition: color var(--dur-1) ease, background var(--dur-1) ease, transform var(--dur-1) ease;
}
.eng-like { margin-left: auto; }   /* actions live on the right edge */
.eng-like:hover, .eng-share:hover { color: var(--text); background: var(--accent-soft); }
.eng-like:active, .eng-share:active { transform: scale(.92); }
.eng-like.is-on { color: var(--accent-ink); }
.eng-like.is-on svg { fill: color-mix(in srgb, var(--accent) 30%, transparent); }
.eng-strip--qv { font-size: 12.5px; margin-top: 10px; }
.eng-strip--qv svg { width: 16px; height: 16px; }
/* Search tiles pad their own body; keep the strip inside that gutter. */
.srch-tile .eng-strip { margin: 10px 12px 0; padding-top: 8px; }
/* Home demo tiles: tighter, and no extra gutter — the body already pads. */
/* min-height reserves the line the counts will occupy. The strip is mounted
   empty (its totals arrive from diagram_stats a moment later, and it stays
   empty below the display thresholds by design), so without a reserve every
   tile grew by ~14px the instant the numbers landed — a layout shift across
   the whole band, on a page whose CLS this work exists to fix. Matches the
   filled height: 11px font on the strip's line box. */
.lp-sample .eng-strip { margin-top: 7px; padding-top: 7px; font-size: 11px; gap: 8px; min-height: 14px; }
.lp-sample .eng-strip svg { width: 13px; height: 13px; }

/* ---- Watch-link boot veil (#watchBoot) ----------------------------------
   A player's loading screen, not an app's: the Present stage's own dark
   ground from the first paint, the mark, one progress bar. Deliberately dark
   in BOTH themes — it hands off to Present, whose chrome is dark in both.
   Shown only under html.preboot-watch; sample-present.js widens the fill per
   load stage and removes the class (the fade below) when the show is up. */
#watchBoot {
  display: none;
  position: fixed; inset: 0; z-index: 400;
  background: radial-gradient(60rem 40rem at 50% -10%, #171a2c, #0b0d14 70%);
  flex-direction: column; align-items: center; justify-content: center; gap: 22px;
  color: #e9ecff;
  transition: opacity .45s ease;
}
.preboot-watch #watchBoot { display: flex; }
#watchBoot.wb-out { opacity: 0; pointer-events: none; }
#watchBoot .wb-mark { width: 74px; height: 74px; color: #e9ecff; }
#watchBoot .wb-mark svg { width: 100%; height: 100%; }
#watchBoot .wb-bar {
  width: min(320px, 60vw); height: 4px; border-radius: 999px;
  background: rgba(233, 236, 255, .14); overflow: hidden;
}
#watchBoot .wb-bar i {
  display: block; height: 100%; width: 12%;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--iris, #6a5cff), var(--iris-bright, #8b80ff));
  transition: width .5s cubic-bezier(.22, 1, .36, 1);
}
#watchBoot .wb-label {
  font-size: 13px; letter-spacing: .02em; color: rgba(233, 236, 255, .72);
}
@media (prefers-reduced-motion: reduce) {
  #watchBoot, #watchBoot .wb-bar i { transition: none; }
}

.mkt-fav {
  position: absolute; top: 8px; right: 8px; z-index: 2; width: 30px; height: 30px; cursor: pointer;
  display: grid; place-items: center; border-radius: 8px; border: 1px solid var(--border-soft);
  background: color-mix(in srgb, var(--chrome-2) 78%, transparent);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  color: var(--slate); opacity: 0; transition: color .12s, transform .12s, border-color .12s, opacity .14s;
}
/* Quiet at rest — only appears on hover, or stays if it's a saved favorite. */
.mkt-card:hover .mkt-fav, .mkt-card:focus-within .mkt-fav, .mkt-fav.is-on { opacity: 1; }
.mkt-fav svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; }
.mkt-fav:hover { color: var(--danger); transform: scale(1.08); border-color: var(--danger); }
.mkt-fav.is-on { color: var(--danger); border-color: var(--danger); }
.mkt-fav.is-on svg { fill: var(--danger); stroke: var(--danger); }
.mkt-thumb-hover {
  position: absolute; inset: 0; z-index: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 7px;
  background: color-mix(in srgb, var(--paper) 46%, transparent); opacity: 0; transition: opacity .16s ease;
}
.mkt-card:hover .mkt-thumb-hover, .mkt-card:focus-within .mkt-thumb-hover { opacity: 1; }
.mkt-thumb-hover button {
  font: inherit; font-size: 12.5px; font-weight: 650; cursor: pointer; padding: 7px 14px; border-radius: 9px; min-width: 118px; text-align: center;
  transform: translateY(6px); transition: transform .16s ease, filter .12s;
}
.mkt-card:hover .mkt-thumb-hover button { transform: translateY(0); }
.mkt-use { color: var(--on-accent); background: var(--accent); border: 1px solid var(--accent); box-shadow: 0 6px 18px -8px var(--marigold); }
.mkt-use:hover { filter: brightness(1.05); }
/* One-touch Present — warm marigold so it reads as a distinct "play" next to the
   iris Use button; dark ink keeps it high-contrast on the fixed fill in both themes. */
.mkt-present { color: #241a06; background: var(--marigold); border: 1px solid var(--marigold-deep); box-shadow: 0 6px 18px -8px var(--marigold); }
.mkt-present:hover { filter: brightness(1.05); }
.mkt-eye { color: var(--text); background: var(--chrome-2); border: 1px solid var(--border-soft); }
.mkt-eye:hover { border-color: var(--accent); }
.mkt-card-body { display: flex; flex-direction: column; gap: 5px; padding: 10px 12px 11px; flex: 1; }
.mkt-type { display: flex; align-items: center; gap: 6px; font-size: 10px; font-weight: 650; letter-spacing: .03em; text-transform: uppercase; color: var(--faint); }
.mkt-typeico { width: 14px; height: 14px; display: inline-grid; place-items: center; color: var(--slate); }
.mkt-typeico svg { width: 14px; height: 14px; }
.mkt-card-title { font-size: 13.5px; font-weight: 700; line-height: 1.28; color: var(--text); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.mkt-card-desc { font-size: 12px; color: var(--muted); line-height: 1.45; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.mkt-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 3px 6px; margin-top: auto; padding-top: 4px; font-size: 11px; color: var(--muted); }
/* "Read the guide" — the narrated tour's article version (pages/diagram/).
   Sits under the meta row so it reads as a second action, not a stat. */
.mkt-guide { display: inline-flex; align-items: center; gap: 5px; margin-top: 6px; font-size: 11.5px;
  font-weight: 650; color: var(--iris-bright, #8b80ff); text-decoration: none; }
.mkt-guide:hover { text-decoration: underline; }
.mkt-guide span { transition: transform .15s ease; }
.mkt-guide:hover span { transform: translateX(3px); }
.mkt-diff { display: inline-flex; align-items: center; gap: 4px; font-weight: 550; }
.mkt-diff::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.mkt-diff--beg { color: var(--d-green); }
.mkt-diff--int { color: var(--d-amber); }
.mkt-diff--adv { color: var(--d-coral); }
.mkt-sep { color: var(--faint); }
.mkt-uses { margin-left: auto; font-family: var(--mono); font-size: 10.5px; color: var(--faint); }
.mkt-uses::before { content: "▲"; font-size: 7px; margin-right: 3px; vertical-align: middle; opacity: .7; }
/* The content-review stamp, on the meta line after the use count. Quiet —
   it is a provenance mark, not a promotion; the accent-ink text token keeps
   it readable in both themes without shouting. */

/* ---- completeness meter: Diagram / Notes / Images / Premium voice ----
   Four single-letter pips (D N I V). A filled pip means the axis is present;
   the first is always filled (every entry is a diagram). Compact by design —
   the full axis names live in the title/aria-label and in the filter. Shared
   by Explore cards (.dg-meter) and /search tiles. */
.dg-meter {
  display: inline-flex;
  gap: 3px;
  margin-top: 8px;
  vertical-align: middle;
}
.dg-pip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 15px;
  height: 15px;
  border-radius: 4px;
  font-size: 9px;
  font-weight: 700;
  line-height: 1;
  /* Absent: a faint outline, no fill — legible in both themes off --faint. */
  color: var(--faint);
  border: 1px solid var(--border-soft);
  background: transparent;
  font-family: var(--mono, ui-monospace, monospace);
}
.dg-pip.on {
  color: var(--on-signal, #fff);
  background: var(--accent);
  border-color: var(--accent);
}
:root[data-ui-theme="light"] .dg-pip.on { color: #fff; }

/* On a search tile the meter sits on its own line under the meta text. */
.srch-meter-wrap { display: block; margin-top: 6px; }
.srch-tile .dg-meter { margin-top: 0; }
.mkt-card--featured .mkt-thumb { aspect-ratio: 16 / 10; }

/* ---- Results ---- */
.mkt-results-head { margin: 6px 0 16px; }
.mkt-results-head h2 { font-size: clamp(19px, 2.4vw, 24px); font-weight: 780; letter-spacing: -.02em; }
.mkt-empty { margin: 30px 0; color: var(--muted); font-size: 13.5px; text-align: center; }

/* ---- Pagination (reused engine class names) ---- */
.explore-pagination { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 6px; margin-top: 32px; }
.explore-page-btn {
  min-width: 36px; height: 36px; padding: 0 10px; border-radius: 9px; border: 1px solid var(--border-soft);
  background: var(--chrome-2); color: var(--text); font-size: 13px; font-weight: 600; cursor: pointer;
}
.explore-page-btn:hover:not(:disabled) { border-color: var(--accent); }
.explore-page-btn.active { background: var(--accent); border-color: var(--accent); color: var(--on-accent); cursor: default; }
.explore-page-btn:disabled { opacity: .4; cursor: default; }
.explore-page-ellipsis { color: var(--muted); font-size: 13px; padding: 0 4px; }

/* ---- Quick-view modal ---- */
.mkt-qv-backdrop {
  position: fixed; inset: 0; z-index: 80; display: flex; align-items: center; justify-content: center; padding: 22px;
  background: color-mix(in srgb, #05070c 62%, transparent); -webkit-backdrop-filter: blur(7px); backdrop-filter: blur(7px);
  opacity: 0; transition: opacity .18s ease;
}
.mkt-qv-backdrop.is-open { opacity: 1; }
.mkt-qv {
  position: relative; width: min(760px, 96vw); max-height: 88vh; overflow-y: auto;
  background: var(--chrome-2); border: 1px solid var(--border-soft); border-radius: 20px;
  box-shadow: 0 30px 80px -20px rgba(0,0,0,.6); padding: 22px; transform: translateY(10px) scale(.98);
  transition: transform .2s cubic-bezier(.16,.84,.28,1);
}
.mkt-qv-backdrop.is-open .mkt-qv { transform: translateY(0) scale(1); }
.mkt-qv-x { position: absolute; top: 14px; right: 14px; z-index: 3; width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--border-soft); background: var(--chrome); color: var(--muted); font-size: 15px; cursor: pointer; }
.mkt-qv-x:hover { color: var(--text); border-color: var(--accent); }
.mkt-qv-body { display: grid; grid-template-columns: minmax(0, 260px) 1fr; gap: 22px; }
.mkt-qv-preview { position: relative; aspect-ratio: 5 / 3; background: var(--canvas-bg); border-radius: 14px; overflow: hidden; border: 1px solid var(--border-soft); }
.mkt-qv-preview svg { width: 100%; height: 100%; padding: 14px 18px; }
.mkt-qv-main { min-width: 0; }
.mkt-qv-title { font-size: 22px; font-weight: 780; letter-spacing: -.02em; margin: 8px 0 6px; line-height: 1.2; }
.mkt-qv-desc { font-size: 14px; color: var(--muted); line-height: 1.6; }
.mkt-qv-facts { display: flex; flex-wrap: wrap; gap: 7px; margin: 15px 0 0; }
.mkt-fact { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 600; color: var(--muted); background: var(--chrome-3); border: 1px solid var(--border-soft); border-radius: 8px; padding: 5px 10px; }
.mkt-fact.mkt-diff::before { content: ""; }
.mkt-fact--ai { color: var(--d-violet); }
.mkt-qv-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.mkt-qv-tag { font-size: 11px; color: var(--muted); background: var(--chrome-3); border-radius: 6px; padding: 3px 9px; }
.mkt-qv-actions { display: flex; flex-wrap: wrap; gap: 9px; margin-top: 20px; }
.mkt-btn { display: inline-flex; align-items: center; gap: 7px; font: inherit; font-size: 13px; font-weight: 650; cursor: pointer; color: var(--text); background: var(--chrome-3); border: 1px solid var(--border-soft); border-radius: 11px; padding: 10px 16px; transition: border-color .12s, filter .12s, transform .12s; }
.mkt-btn:hover { border-color: var(--accent); transform: translateY(-1px); }
.mkt-btn svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; }
.mkt-btn--primary { color: var(--on-accent); background: var(--accent); border-color: var(--accent); box-shadow: 0 8px 22px -10px var(--marigold); }
.mkt-btn--primary:hover { filter: brightness(1.05); }
.mkt-btn.qv-fav.is-on { color: var(--danger); border-color: var(--danger); }
.mkt-btn.qv-fav.is-on svg { fill: var(--danger); stroke: var(--danger); }
.mkt-qv-similar { grid-column: 1 / -1; margin-top: 22px; border-top: 1px solid var(--border); padding-top: 18px; }
.mkt-qv-similar h3 { font-size: 14px; font-weight: 700; margin-bottom: 12px; }
.mkt-qv-sim-rail { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.mkt-sim { display: flex; flex-direction: column; gap: 6px; text-align: left; cursor: pointer; background: var(--chrome); border: 1px solid var(--border-soft); border-radius: 11px; overflow: hidden; padding-bottom: 8px; transition: border-color .12s, transform .12s; }
.mkt-sim:hover { border-color: var(--accent); transform: translateY(-2px); }
.mkt-sim-thumb { aspect-ratio: 5 / 3; background: var(--canvas-bg); overflow: hidden; }
.mkt-sim-thumb svg { width: 100%; height: 100%; padding: 6px 9px; }
.mkt-sim-name { font-size: 11.5px; font-weight: 600; color: var(--text); line-height: 1.3; padding: 0 9px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

/* ---- Responsive ---- */
@media (max-width: 900px) {
  .mkt-collections { grid-template-columns: repeat(2, 1fr); }
  .mkt-qv-body { grid-template-columns: 1fr; }
  .mkt-qv-preview { max-height: 200px; }
  .mkt-qv-sim-rail { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 560px) {
  .mkt-collections { grid-template-columns: 1fr; }
  .mkt-grid { grid-template-columns: 1fr; }
  .mkt-toolbar { flex-direction: column; align-items: stretch; }
  .mkt-count { align-self: flex-end; }
}
/* ---- Reduced motion ---- */
@media (prefers-reduced-motion: reduce) {
  .mkt-card, .mkt-coll, .mkt-tab, .mkt-sugg, .mkt-continue, .mkt-mini, .mkt-btn, .mkt-sim, .mkt-fav,
  .mkt-thumb-hover, .mkt-thumb-hover button, .srch-hover, .srch-hover button, .mkt-qv, .mkt-qv-backdrop, .mkt-seeall span { transition: none; transform: none !important; }
}

.link-btn { color: inherit; font: inherit; text-decoration: underline; text-underline-offset: 2px; cursor: pointer; }
.link-btn:hover { color: var(--accent-ink); }
@media (prefers-reduced-motion: reduce) {
  .home-anim { animation: none; }
}

/* ============ Toasts ============ */
#toasts {
  position: fixed;
  /* CLEAR THE BOTTOM PLATE. Toasts used to sit at bottom:40px, which is
     inside the canvas dock's own footprint (bottom:14px + ~46px tall), so
     every message landed on the dock and was hard to read and to dismiss.
     --toast-lift is the height of whatever bottom furniture is showing;
     each mode below sets it, so a message always floats just above. */
  bottom: calc(var(--toast-lift, 40px) + 18px);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  z-index: 200;
  pointer-events: none;
}
/* Editor toasts ("Not saved yet…") are about the editor beneath the marketing
   overlays — don't let them float over the landing/explore pages. */
body:has(#homePage:not([hidden])) #toasts,
body:has(#explorePage:not([hidden])) #toasts { display: none; }
/* Bottom furniture, per mode:
   editor  — the canvas dock  (bottom 14 + 5*2 padding + 36 button = 60)
   present — the control bar  (bottom 16 + 8*2 padding + ~34 button = 66)
   neither — nothing to clear, so the toast keeps its own resting height. */
body { --toast-lift: 60px; }
body.presenting { --toast-lift: 72px; }
body.embed-mode, body.watching, body.recording,
body:has(#homePage:not([hidden])), body:has(#explorePage:not([hidden])) { --toast-lift: 8px; }
@media (max-width: 760px) {
  /* 44px touch buttons + 10px padding, and the dock rides the safe-area. */
  body { --toast-lift: calc(64px + env(safe-area-inset-bottom, 0px)); }
}
/* Likewise the editor chrome itself: the overlays cover it visually, but
   without this it stays keyboard-focusable and screen-reader-visible
   beneath the marketing pages (WCAG 2.4.3/2.4.7). visibility also removes
   it from the tab order and accessibility tree. */
body:has(#homePage:not([hidden])) :is(#topbar, #main),
body:has(#explorePage:not([hidden])) :is(#topbar, #main) { visibility: hidden; }
.toast {
  background: #262b3d;
  border: 1px solid var(--border);
  color: var(--text);
  padding: 9px 16px;
  border-radius: 10px;
  font-size: 12.5px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
  animation: toast-in .18s ease-out;
}
.toast.out { opacity: 0; transition: opacity .3s; }
@keyframes toast-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* scrollbars that match the chrome */
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(154, 161, 184, 0.28); border-radius: 8px; border: 3px solid transparent; background-clip: content-box; }
::-webkit-scrollbar-thumb:hover { background-color: rgba(154, 161, 184, 0.45); }
#canvasWrap::-webkit-scrollbar-thumb { background-color: rgba(20, 22, 31, 0.25); border: 3px solid transparent; background-clip: content-box; }

/* ============================================================
   Comment pane — shared annotation surface (every diagram type).
   #stage wraps #canvasWrap + the pane so the pane reserves real
   layout space (and never scrolls away with the diagram). Docks
   left / right (adjustable width) or bottom (adjustable height).
   ============================================================ */
#stage {
  flex: 1;
  min-width: 0;
  min-height: 0;
  position: relative;
  display: flex;
  flex-direction: row;
}
#stage.cp-bottom { flex-direction: column; }
#stage > #canvasWrap { flex: 1 1 auto; min-width: 0; min-height: 0; }

#commentPane {
  flex: none;
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--chrome);
  color: var(--text);
  overflow: hidden;
  z-index: 8;
}
#commentPane[hidden] { display: none; }
#stage.cp-left  #commentPane { order: -1; border-right: 1px solid var(--border); }
#stage.cp-right #commentPane { order: 1; border-left: 1px solid var(--border); }
#stage.cp-bottom #commentPane { order: 1; border-top: 1px solid var(--border); width: auto !important; }

#commentPaneResizer { position: absolute; z-index: 3; background: transparent; transition: background .12s; }
#stage.cp-right  #commentPaneResizer { left: 0; top: 0; bottom: 0; width: 7px; cursor: col-resize; }
#stage.cp-left   #commentPaneResizer { right: 0; top: 0; bottom: 0; width: 7px; cursor: col-resize; }
#stage.cp-bottom #commentPaneResizer { top: 0; left: 0; right: 0; height: 7px; cursor: row-resize; }
#commentPaneResizer:hover { background: var(--accent-soft); }

#commentPaneHead {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 9px 12px; border-bottom: 1px solid var(--border); flex: none;
}
.cp-title { font-size: 13px; font-weight: 600; white-space: nowrap; }
.cp-head-actions { display: flex; align-items: center; gap: 6px; }
#cpPosition { padding: 2px; }
#cpPosition .seg-btn { flex: none; padding: 3px 7px; font-size: 13px; }
#cpAddNote.sm { padding: 4px 9px; font-size: 12px; }
.cp-icon-btn { padding: 4px 7px; border-radius: 6px; color: var(--muted); }
.cp-icon-btn:hover { background: var(--chrome-3); color: var(--text); }

#commentPaneBody { flex: 1; overflow-y: auto; padding: 10px; display: flex; flex-direction: column; gap: 10px; }
#stage.cp-bottom #commentPaneBody { flex-direction: row; flex-wrap: wrap; align-items: flex-start; }

.cp-note {
  border: 1px solid var(--border); border-radius: 10px; background: var(--chrome-2);
  padding: 8px; display: flex; flex-direction: column; gap: 6px;
}
#stage.cp-bottom .cp-note { width: 240px; }
.cp-note.linking { box-shadow: 0 0 0 2px var(--accent-ring); border-color: var(--accent); }
.cp-note.active { border-color: var(--accent); }
.cp-note textarea {
  width: 100%; box-sizing: border-box; resize: vertical; min-height: 46px;
  border: none; background: transparent; color: var(--text); font: inherit; font-size: 12.5px; outline: none;
}
.cp-note-foot { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.cp-link-btn, .cp-del-btn {
  padding: 3px 8px; border-radius: 6px; font-size: 11.5px; color: var(--muted);
  border: 1px solid var(--border); background: var(--chrome);
}
.cp-link-btn:hover, .cp-del-btn:hover { color: var(--text); background: var(--chrome-3); }
.cp-link-btn.linked { color: var(--accent-ink); border-color: var(--accent-ring); }
.cp-linked-label { font-size: 11px; color: var(--muted); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cp-empty { color: var(--muted); font-size: 12px; padding: 14px; text-align: center; line-height: 1.5; }

/* Sized (width/height/viewBox) by JS each redraw to match #stage, so it needs
   only to be pinned at the stage origin — a percentage height here would
   collapse to 0 against the flex container and flatten the lines. */
#commentConnectors { position: absolute; top: 0; left: 0; pointer-events: none; overflow: visible; z-index: 7; }
#commentConnectors[hidden] { display: none; }
.cp-connector { stroke: var(--accent); stroke-width: 1.6; stroke-dasharray: 5 4; fill: none; opacity: .8; }
.cp-connector-dot { fill: var(--accent); }

/* While arming a shape link, the whole canvas reads as clickable. */
body.cp-linking #svgHost svg { cursor: crosshair; }

#stage:fullscreen { background-color: var(--canvas-bg); }

/* The comment pane is authoring chrome — keep it out of clean recordings. */
body.recording #commentPane, body.recording #commentConnectors { display: none !important; }

@media (max-width: 760px) { #commentPane { display: none; } }

/* ============================================================
   Threat model (STRIDE) — security-focused visuals for the
   threatmodel graph tool: threat badges, attack paths, the
   click-to-view popover and the inspector threat editor.
   ============================================================ */
.insp-subhead { font-size: 12px; font-weight: 700; color: var(--text); margin: 2px 0 8px; display: flex; align-items: center; gap: 6px; }
.tm-count-pill { background: var(--danger); color: #fff; font-size: 10px; font-weight: 700; border-radius: 9px; padding: 1px 7px; }

/* Attack-path "marching ants" — editor canvas only; exported SVG stays static. */
@keyframes tm-dash { to { stroke-dashoffset: -14; } }
#svgHost .tm-attack-path { animation: tm-dash .6s linear infinite; }
@media (prefers-reduced-motion: reduce) { #svgHost .tm-attack-path { animation: none; } }
#svgHost .tm-badge { cursor: pointer; }

/* Inspector: per-threat editor card */
.tm-threat-edit { border: 1px solid var(--border); border-left-width: 3px; border-radius: 8px; padding: 8px; margin-bottom: 8px; background: var(--chrome-2); display: flex; flex-direction: column; gap: 6px; }
.tm-te-row2 { display: flex; gap: 6px; }
.tm-te-row2 select { flex: 1; min-width: 0; }
.tm-threat-edit input, .tm-threat-edit textarea, .tm-threat-edit select {
  width: 100%; box-sizing: border-box; background: var(--chrome); color: var(--text);
  border: 1px solid var(--border); border-radius: 6px; padding: 5px 7px; font: inherit; font-size: 12px; resize: vertical;
}
.tm-threat-edit .tm-te-del { color: var(--danger); }
.tm-attack-check input { accent-color: var(--danger); }

/* Click-to-view threat popover (appended to <body>, fixed position) */
.tm-threat-pop {
  position: fixed; z-index: 3000; width: 328px; max-width: calc(100vw - 24px); max-height: 72vh; overflow: auto;
  background: var(--chrome-2); color: var(--text); border: 1px solid var(--border);
  border-radius: 12px; box-shadow: 0 18px 48px rgba(0, 0, 0, .42); font: 13px/1.45 var(--font);
}
[data-ui-theme="light"] .tm-threat-pop { box-shadow: 0 18px 48px rgba(20, 24, 40, .2); }
.tm-pop-head { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--border); position: sticky; top: 0; background: var(--chrome-2); }
.tm-pop-title { font-weight: 700; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tm-pop-count { font-size: 11px; color: var(--muted); white-space: nowrap; }
.tm-pop-x { border: none; background: none; color: var(--muted); font-size: 20px; line-height: 1; cursor: pointer; padding: 0 2px; }
.tm-pop-x:hover { color: var(--text); }
.tm-pop-body { padding: 10px 12px; display: flex; flex-direction: column; gap: 10px; }
.tm-threat-card { border: 1px solid var(--border); border-left-width: 4px; border-radius: 8px; padding: 8px 10px; background: var(--chrome); }
.tm-threat-top { display: flex; align-items: center; gap: 7px; margin-bottom: 4px; }
.tm-stride-chip { color: #fff; font-weight: 700; font-size: 11px; width: 20px; height: 20px; border-radius: 5px; display: inline-flex; align-items: center; justify-content: center; flex: none; }
.tm-threat-title { font-weight: 600; flex: 1; }
.tm-sev-chip { color: #fff; font-size: 10px; font-weight: 700; border-radius: 6px; padding: 2px 7px; text-transform: uppercase; letter-spacing: .03em; flex: none; }
.tm-threat-cat { font-size: 11px; color: var(--muted); margin-bottom: 6px; }
.tm-threat-row { display: grid; grid-template-columns: 74px 1fr; gap: 8px; font-size: 12px; margin-top: 4px; }
.tm-threat-row b { color: var(--muted); font-weight: 600; font-size: 10px; text-transform: uppercase; letter-spacing: .03em; padding-top: 1px; }

/* ============ Accounts (Phase 1) — header control + account modal ============ */
/* Header sign-in / account button reuses .lp-tbtn (home/explore) or .tb-btn
   (editor); these rules cover the signed-in avatar state. */
.acct-btn { display: inline-flex; align-items: center; gap: 7px; }
.acct-btn.is-in { padding-left: 5px; }
.acct-btn-av { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; flex: none;
  display: inline-grid; place-items: center; font-size: 11px; font-weight: 700;
  background: var(--accent); color: var(--on-accent); border: 1px solid var(--border); }
.acct-btn-av--i { text-transform: uppercase; }
.acct-btn-name { max-width: 90px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.acct-modal { width: min(440px, 94vw); }
.acct-lead { color: var(--muted); font-size: 14px; line-height: 1.6; margin: 2px 0 18px; }
.acct-lead b { color: var(--text); font-weight: 600; }
.acct-providers { display: flex; flex-direction: column; gap: 9px; }
.acct-provider { display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
  font: inherit; font-size: 14px; font-weight: 600; color: var(--text); cursor: pointer;
  background: var(--chrome-2); border: 1px solid var(--border-soft); border-radius: 11px;
  padding: 11px 14px; transition: border-color .12s, transform .12s, background .12s; }
.acct-provider:hover:not(:disabled) { border-color: var(--accent); transform: translateY(-1px); background: var(--chrome-3); }
/* Disabled until the 18+/Terms confirmation box is ticked — the age gate is
   the checkbox, and a clickable button beside an unticked box would lie. */
.acct-provider:disabled { opacity: .45; cursor: not-allowed; }
.acct-provider-mark { width: 26px; height: 26px; flex: none; display: grid; place-items: center;
  border-radius: 7px; background: var(--chrome); border: 1px solid var(--border-soft);
  font-weight: 800; font-size: 12px; color: var(--muted); }
.acct-fine { font-size: 11.5px; color: var(--faint); line-height: 1.5; margin-top: 16px; }

.acct-me { display: flex; align-items: center; gap: 13px; padding-bottom: 16px; border-bottom: 1px solid var(--border); }
.acct-avatar { width: 46px; height: 46px; border-radius: 50%; object-fit: cover; flex: none;
  display: grid; place-items: center; font-size: 18px; font-weight: 700;
  background: var(--accent); color: var(--on-accent); }
.acct-me-txt { min-width: 0; flex: 1; }
.acct-name { font-size: 15px; font-weight: 700; color: var(--text); }
.acct-email { font-size: 12.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct-tier { flex: none; font-size: 11px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
  padding: 4px 10px; border-radius: 999px; background: var(--chrome-3); color: var(--muted); border: 1px solid var(--border-soft); }
.acct-tier--basic { color: var(--d-indigo); border-color: color-mix(in srgb, var(--d-indigo) 40%, var(--border-soft)); background: color-mix(in srgb, var(--d-indigo) 12%, transparent); }
.acct-tier--pro { color: var(--accent-ink); border-color: var(--accent-ring); background: var(--accent-soft); }
.acct-perks { font-size: 13px; color: var(--muted); line-height: 1.6; margin: 16px 0 18px; }
.acct-actions { display: flex; gap: 9px; }
.acct-actions .btn-primary, .acct-actions .btn-ghost { padding: 9px 16px; font-size: 13px; }
/* Age notice above the provider fine print — quiet, but not buried. */
.acct-age { font-size: 12px; color: var(--muted); line-height: 1.55; margin-top: 14px; }
.acct-age b { color: var(--text); font-weight: 650; }
/* The 18+/Terms confirmation. A gate the user must operate, so it reads as a
   control (bordered row) rather than fine print; also reused for the
   re-acceptance notice shown to signed-in users after a material change. */
.acct-agegate { display: flex; align-items: flex-start; gap: 10px; margin: 2px 0 14px;
  padding: 11px 13px; border: 1px solid var(--border); border-radius: 11px;
  background: var(--panel-2); cursor: pointer; }
.acct-agegate input { margin-top: 2px; flex: none; accent-color: var(--accent); }
.acct-agegate span { font-size: 12.5px; line-height: 1.55; color: var(--text); }
.acct-agegate a { color: var(--accent-ink, var(--accent)); }
/* First-use 18+ acknowledgement bar (js/account-ui.js). A notice, not a wall:
   fixed to the foot, app usable behind it, gone once acknowledged. High z so
   no landing section covers it; safe-area padding for phones. */
#ageAckBar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 460;
  display: flex; align-items: center; justify-content: center; gap: 14px; flex-wrap: wrap;
  padding: 12px max(16px, env(safe-area-inset-left)) max(12px, env(safe-area-inset-bottom));
  background: var(--chrome); border-top: 1px solid var(--border);
  box-shadow: 0 -8px 24px rgba(0, 0, 0, .25); }
.age-ack-text { font-size: 13px; line-height: 1.5; color: var(--text); max-width: 640px; }
.age-ack-text b { font-weight: 700; }
.age-ack-text a { color: var(--accent-ink, var(--accent)); }
.age-ack-go { flex: none; font: inherit; font-size: 13px; font-weight: 650; cursor: pointer;
  color: var(--on-accent); background: var(--accent); border: 0; border-radius: 999px;
  padding: 9px 18px; }
.age-ack-go:hover { filter: brightness(1.08); }
/* Contextual disclaimer for high-risk ready-made diagrams (js/engagement.js).
   Quiet line pinned to the stage foot, above the bottom band; dark-chrome ink
   like all Present furniture. Kept out of the tap lanes' way (pointer-events
   only on the link). */
body.presenting #prRiskNote { position: absolute; left: 50%; transform: translateX(-50%);
  bottom: 4px; z-index: 40; max-width: min(92%, 720px); text-align: center;
  font-size: 11px; line-height: 1.4; color: rgba(233, 236, 248, .55);
  pointer-events: none; }
#prRiskNote { display: none; }
body.presenting #prRiskNote { display: block; }
#prRiskNote a { color: rgba(233, 236, 248, .75); pointer-events: auto; }

.acct-reaccept { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); }
.acct-reaccept-h { font-size: 13px; font-weight: 650; color: var(--text); margin-bottom: 6px; }
.acct-reaccept-b { font-size: 12.5px; color: var(--muted); line-height: 1.55; margin-bottom: 10px; }
/* Usage-analytics switch. A privacy control has to be as easy to find as the
   thing it controls, so it sits inline in the panel rather than behind a
   "preferences" disclosure. */
.acct-privacy { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); }
.acct-privacy-row { display: flex; align-items: flex-start; gap: 10px; cursor: pointer; }
.acct-privacy-row input { margin-top: 2px; flex: none; accent-color: var(--accent); }
.acct-privacy-row > span { display: flex; flex-direction: column; gap: 3px; }
.acct-privacy-h { display: block; font-size: 13px; font-weight: 600; color: var(--text); }
.acct-privacy-b { display: block; font-size: 12px; color: var(--muted); line-height: 1.55; }
.acct-privacy-b a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.acct-privacy-b a:hover { color: var(--accent-ink); }

/* Delete account. Deliberately understated until opened: a destructive action
   should be findable without being invitable. */
.acct-danger { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border); }
.acct-danger-link { font: inherit; font-size: 12.5px; font-weight: 550; cursor: pointer;
  color: var(--faint); background: none; border: 0; padding: 0;
  text-decoration: underline; text-underline-offset: 3px; transition: color .12s; }
.acct-danger-link:hover { color: var(--danger); }
.acct-danger-panel { display: flex; flex-direction: column; gap: 9px;
  border: 1px solid color-mix(in srgb, var(--danger) 34%, var(--border-soft));
  background: color-mix(in srgb, var(--danger) 7%, transparent);
  border-radius: var(--radius); padding: 13px 14px; }
.acct-danger-h { font-size: 13px; font-weight: 700; color: var(--danger); }
.acct-danger-b { font-size: 12.5px; color: var(--muted); line-height: 1.6; }
.acct-danger-b b { color: var(--text); font-weight: 620; }
.acct-danger-b code { font-family: var(--mono); font-size: 11.5px; color: var(--text);
  background: var(--chrome-2); border: 1px solid var(--border-soft);
  border-radius: 5px; padding: 1px 5px; }
.acct-danger-input { font: inherit; font-size: 13px; color: var(--text);
  background: var(--chrome-1); border: 1px solid var(--border-soft);
  border-radius: 9px; padding: 8px 11px; width: 100%; }
.acct-danger-input:focus-visible { outline: none; border-color: var(--danger); }
.acct-danger-actions { display: flex; gap: 9px; }
.acct-danger-actions .btn-danger, .acct-danger-actions .btn-ghost {
  padding: 8px 14px; font-size: 12.5px; }
.acct-danger-actions .btn-danger[disabled] { opacity: .45; cursor: not-allowed; }
/* Dev tier switcher (removed with the set_my_tier RPC before production). */
.acct-dev { margin-top: 18px; padding-top: 14px; border-top: 1px dashed var(--border); }
.acct-dev-k { display: block; font-family: var(--mono); font-size: 11px; letter-spacing: .04em;
  text-transform: uppercase; color: var(--faint); margin-bottom: 8px; }
.acct-dev-btns { display: flex; gap: 6px; }
.acct-dev-t { flex: 1; font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
  color: var(--muted); background: var(--chrome-2); border: 1px solid var(--border-soft);
  border-radius: 9px; padding: 7px 8px; transition: color .12s, border-color .12s, background .12s; }
.acct-dev-t:hover { color: var(--text); border-color: var(--accent); }
.acct-dev-t.on { color: var(--on-accent); background: var(--accent); border-color: var(--accent); }

/* ============ Explore left sidebar (types + collections tile) ============ */
.mkt-body { display: flex; align-items: flex-start; gap: 24px; margin-top: 18px; }
/* Sticky filter rail — stays alongside the grid as you scroll; its own
   scrollbar appears only when the rail outgrows the viewport. */
.mkt-side { flex: 0 0 212px; align-self: flex-start;
  display: flex; flex-direction: column; gap: 14px;
  position: sticky; top: 76px; max-height: calc(100vh - 96px); overflow-y: auto;
  scrollbar-width: thin; }
.mkt-main { flex: 1; min-width: 0; }

/* Type nav — vertical list (was the horizontal .mkt-tabs strip) */
.mkt-side-tabs { display: flex; flex-direction: column; gap: 2px; }
.mkt-side-tabs .mkt-tab { width: 100%; justify-content: flex-start; border-radius: 9px; font-weight: 550; flex: none; }

/* Collections — expandable tile */
.mkt-coll-panel { border: 1px solid var(--border-soft); border-radius: 12px; overflow: hidden; background: var(--chrome-2); }
.mkt-coll-toggle { display: flex; align-items: center; justify-content: space-between; width: 100%; font: inherit;
  font-size: 11.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--muted);
  cursor: pointer; background: none; border: none; padding: 12px 13px; }
.mkt-coll-toggle:hover { color: var(--text); }
.mkt-chev { transition: transform .18s ease; font-size: 11px; }
.mkt-coll-panel.is-collapsed .mkt-chev { transform: rotate(-90deg); }
.mkt-coll-list { display: flex; flex-direction: column; gap: 1px; padding: 0 6px 8px; max-height: 430px; overflow-y: auto; }
.mkt-coll-panel.is-collapsed .mkt-coll-list { display: none; }
.mkt-coll-empty { font-size: 12px; color: var(--faint); padding: 2px 8px 6px; }

/* Checkbox lists (Templates + Collections) — neutral rows, no colour fill; the
   only accent is the tick itself. Collapse with the panel toggle. */
.mkt-checklist { padding: 2px 6px 8px; max-height: 430px; overflow-y: auto; }
#exploreCatTabs.mkt-checklist { flex-direction: column; gap: 1px; }
.mkt-coll-panel.is-collapsed .mkt-side-tabs { display: none; }
.mkt-check-item { display: flex; align-items: center; gap: 9px; width: 100%; text-align: left; cursor: pointer;
  background: none; border: none; border-radius: 8px; padding: 7px 10px; color: var(--muted); font: inherit; font-size: 13px;
  transition: background .12s ease, color .12s ease; }
.mkt-check-item:hover { background: var(--chrome-3); color: var(--text); }
.mkt-check-item.active { color: var(--text); font-weight: 600; }
.mkt-check { position: relative; width: 16px; height: 16px; flex: none; border: 1.6px solid var(--border); border-radius: 5px;
  transition: background .12s ease, border-color .12s ease; }
.mkt-check-item:hover .mkt-check { border-color: var(--slate); }
.mkt-check-item.active .mkt-check { background: var(--accent); border-color: var(--accent); }
.mkt-check-item.active .mkt-check::after { content: ""; position: absolute; inset: 0; background: var(--on-accent);
  -webkit-mask: no-repeat center / 10px 10px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
  mask: no-repeat center / 10px 10px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E"); }
.mkt-check-lbl { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mkt-check-count { flex: none; font-family: var(--mono); font-size: 11px; font-weight: 700; color: var(--faint); }

/* Collection row (overrides the old grid-card .mkt-coll) */
.mkt-coll { display: flex; align-items: center; gap: 9px; width: 100%; text-align: left; cursor: pointer;
  background: none; border: none; border-radius: 8px; padding: 7px 8px; transition: background .12s; }
.mkt-coll:hover { background: var(--chrome-3); transform: none; box-shadow: none; border-color: transparent; }
.mkt-coll.is-active { background: var(--accent-soft); }
.mkt-coll-ico { width: 26px; height: 26px; flex: none; display: grid; place-items: center; font-size: 14px; border-radius: 7px;
  background: color-mix(in srgb, var(--coll) 18%, transparent); border: 1px solid color-mix(in srgb, var(--coll) 34%, transparent); }
.mkt-coll-name { flex: 1; min-width: 0; font-size: 13px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mkt-coll-count { flex: none; font-family: var(--mono); font-size: 11px; font-weight: 700; color: var(--faint); }

@media (max-width: 860px) {
  .mkt-body { flex-direction: column; }
  .mkt-side { flex: none; width: 100%; position: static; max-height: none; overflow: visible; flex-direction: row; flex-wrap: wrap; }
  .mkt-side-tabs { flex-direction: row; flex-wrap: wrap; flex: 1 1 100%; }
  .mkt-side-tabs .mkt-tab { width: auto; }
  .mkt-coll-panel { flex: 1 1 100%; }
  .mkt-coll-list { max-height: 260px; }
}

/* ---- Sidebar "Filters" tile (Industry, Role, quick toggles) ---- */
.mkt-coll-panel.is-collapsed .mkt-side-body { display: none; }
.mkt-side-body { display: flex; flex-direction: column; gap: 11px; padding: 4px 11px 13px; }
.mkt-field { display: flex; flex-direction: column; gap: 5px; }
.mkt-field-lab { font-size: 10.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--faint); }
.mkt-select--block { width: 100%; }
.mkt-side-toggles { display: flex; flex-wrap: wrap; gap: 6px; }
.mkt-side-toggles .mkt-toggle { padding: 6px 11px; font-size: 12px; }
.mkt-side-body .mkt-clear { align-self: flex-start; padding: 4px 2px; }
/* Top bar is now just Sort + count — keep it tidy. */
.mkt-toolbar { margin-top: 2px; }

/* Sidebar is a flex column with max-height; keep children at natural height
   (don't let flex shrink them, which clipped the panel bodies). */
.mkt-side > * { flex: 0 0 auto; }

/* ============================================================
   Present mode: Stagecraft II — pre-show settings, karaoke subtitles,
   live presentation recording, and the brand mark on every slide.
   (js/present-plus.js drives all of it.)
   ============================================================ */

/* ---- Intro card brand mark. Same lockup as the end slide. ---- */
.pr-intro-mark { width: 46px; height: 46px; margin: 0 auto 2px; color: #cdd5e4; }
.pr-intro-mark svg { width: 100%; height: 100%; display: block; overflow: visible; }

/* ---- Opening brand animation ------------------------------------------
   Deliberately playful and over-eager: the mark bounces in with squash and
   stretch, the accent dot arcs around and lands with a wobble, sparkles pop,
   and the wordmark springs up letter by letter with real overshoot. Every
   animation is gated on `.pr-brandin-run`, which present-plus re-applies each
   time the intro opens — CSS animations fire once per element, so without
   that a second show would display a finished logo and no motion. */
.pr-brandin {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
}
.pr-brandin .pr-intro-mark { width: 74px; height: 74px; margin: 0; }
.pr-brandin-glow {
  /* The purple bloom that used to sit behind the brand animation. Removed on
     request: the splash reads as a clean sheet now, and a violet radial behind
     the mark read as a smudge on the light theme's white card. The element
     stays in the DOM (the animation choreography references it) but paints
     nothing. */
  display: none;
}
/* Nothing moves until the run class lands. */
.pr-brandin-run .pr-brandin-glow { animation: prBiGlow 2.2s cubic-bezier(.22,.9,.28,1) forwards; }
.pr-brandin-run .pr-intro-mark { animation: prBiDrop 1.05s cubic-bezier(.28,1.5,.42,1) forwards; }
.pr-brandin .pr-intro-mark { opacity: 0; }

.pr-bi-stem { stroke-dasharray: 56; stroke-dashoffset: 56; }
.pr-bi-bowl { stroke-dasharray: 172; stroke-dashoffset: 172; }
.pr-brandin-run .pr-bi-stem { animation: prBiDraw .46s cubic-bezier(.65,0,.35,1) .34s forwards; }
.pr-brandin-run .pr-bi-bowl { animation: prBiDraw .7s cubic-bezier(.65,0,.35,1) .5s forwards; }
.pr-bi-node { opacity: 0; transform-box: fill-box; transform-origin: center; }
.pr-brandin-run .pr-bi-node { animation: prBiPop .5s cubic-bezier(.34,1.8,.5,1) forwards; }
.pr-brandin-run .pr-bi-node-a { animation-delay: .74s; }
.pr-brandin-run .pr-bi-node-b { animation-delay: .84s; }
/* The accent dot is the brand's one moving part — it gets the biggest arc. */
.pr-bi-orbit { opacity: 0; transform-box: fill-box; transform-origin: center; }
.pr-brandin-run .pr-bi-orbit { animation: prBiOrbit 1.1s cubic-bezier(.3,1.7,.45,1) .92s forwards; }

.pr-brandin-word {
  position: relative;
  display: flex;
  font-family: var(--sans);
  font-weight: 800;
  font-size: 31px;
  letter-spacing: -0.035em;
  color: #fff;
  line-height: 1.15;
  padding: 0 2px;
}
.pr-bi-l {
  display: inline-block;
  opacity: 0;
  transform: translateY(120%) scale(.5) rotate(-12deg);
  transform-origin: 50% 100%;
}
.pr-brandin-run .pr-bi-l { animation: prBiLetter .78s cubic-bezier(.28,1.7,.42,1) forwards; }
.pr-bi-l.um { color: var(--iris-bright); }
/* 46ms apart: enough to read as a run of letters, tight enough to stay one move. */
.pr-brandin-run .pr-bi-l:nth-child(1) { animation-delay: 1.12s; }
.pr-brandin-run .pr-bi-l:nth-child(2) { animation-delay: 1.166s; }
.pr-brandin-run .pr-bi-l:nth-child(3) { animation-delay: 1.212s; }
.pr-brandin-run .pr-bi-l:nth-child(4) { animation-delay: 1.258s; }
.pr-brandin-run .pr-bi-l:nth-child(5) { animation-delay: 1.304s; }
.pr-brandin-run .pr-bi-l:nth-child(6) { animation-delay: 1.35s; }
.pr-brandin-run .pr-bi-l:nth-child(7) { animation-delay: 1.396s; }
.pr-brandin-run .pr-bi-l:nth-child(8) { animation-delay: 1.442s; }
.pr-brandin-run .pr-bi-l:nth-child(9) { animation-delay: 1.488s; }
.pr-brandin-run .pr-bi-l:nth-child(10) { animation-delay: 1.534s; }
.pr-brandin-sheen {
  position: absolute;
  inset: 0 auto 0 -60%;
  width: 55%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.85), transparent);
  transform: skewX(-18deg);
  opacity: 0;
  pointer-events: none;
}
.pr-brandin-run .pr-brandin-sheen { animation: prBiSheen 1s cubic-bezier(.4,0,.2,1) 1.95s forwards; }
/* Sparkles: four dots that burst outward as the mark lands. */
.pr-bi-spark {
  position: absolute;
  top: 26px;
  left: 50%;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
  background: var(--iris-bright);
}
.pr-bi-spark:nth-of-type(2) { background: #ffd166; }
.pr-bi-spark:nth-of-type(3) { background: #46d4ff; }
.pr-bi-spark:nth-of-type(4) { background: #ff8fd0; }
.pr-brandin-run .pr-bi-spark { animation: prBiSpark .9s cubic-bezier(.2,.9,.3,1) forwards; }
.pr-brandin-run .pr-bi-spark:nth-of-type(1) { animation-delay: .74s; --sx: -66px; --sy: -30px; }
.pr-brandin-run .pr-bi-spark:nth-of-type(2) { animation-delay: .8s;  --sx: 62px;  --sy: -38px; }
.pr-brandin-run .pr-bi-spark:nth-of-type(3) { animation-delay: .86s; --sx: -48px; --sy: 30px; }
.pr-brandin-run .pr-bi-spark:nth-of-type(4) { animation-delay: .92s; --sx: 54px;  --sy: 26px; }

@keyframes prBiGlow {
  0% { opacity: 0; transform: translateX(-50%) scale(.5); }
  34% { opacity: 1; transform: translateX(-50%) scale(1.12); }
  100% { opacity: .55; transform: translateX(-50%) scale(1); }
}
/* Drop, squash on impact, rebound — the classic bounce, on the whole mark. */
@keyframes prBiDrop {
  0% { opacity: 0; transform: translateY(-90px) scale(.4) rotate(-16deg); }
  45% { opacity: 1; transform: translateY(6px) scale(1.18, .84) rotate(4deg); }
  62% { transform: translateY(-10px) scale(.94, 1.1) rotate(-2deg); }
  80% { transform: translateY(3px) scale(1.05, .96); }
  100% { opacity: 1; transform: none; }
}
@keyframes prBiDraw { to { stroke-dashoffset: 0; } }
@keyframes prBiPop {
  0% { opacity: 0; transform: scale(.15); }
  65% { opacity: 1; transform: scale(1.45); }
  100% { opacity: 1; transform: scale(1); }
}
@keyframes prBiOrbit {
  0% { opacity: 0; transform: translate(-40px, -30px) scale(.2) rotate(-90deg); }
  50% { opacity: 1; transform: translate(8px, 6px) scale(1.5) rotate(10deg); }
  72% { transform: translate(-3px, -2px) scale(.88); }
  100% { opacity: 1; transform: none; }
}
@keyframes prBiLetter {
  0% { opacity: 0; transform: translateY(120%) scale(.5) rotate(-12deg); }
  55% { opacity: 1; transform: translateY(-14%) scale(1.16) rotate(3deg); }
  78% { transform: translateY(4%) scale(.96) rotate(-1deg); }
  100% { opacity: 1; transform: none; }
}
@keyframes prBiSheen {
  0% { opacity: 0; left: -60%; }
  20% { opacity: 1; }
  100% { opacity: 0; left: 115%; }
}
@keyframes prBiSpark {
  0% { opacity: 0; transform: translate(-50%, 0) scale(.2); }
  30% { opacity: 1; transform: translate(calc(-50% + var(--sx) * .55), calc(var(--sy) * .55)) scale(1.3); }
  100% { opacity: 0; transform: translate(calc(-50% + var(--sx)), var(--sy)) scale(.3); }
}
/* ---- The same lockup on the presentation's title card ----------------
   The intro's performance in full — identical durations, delays and easing,
   so the start slide and the intro screen read as one animation. It runs
   ~2.95s, and app.js / present-plus hold the card for at least BRANDIN_MS
   (see maybeShowTitleCard / recShowStartSlide) so nothing is cut off. The
   shared .pr-bi-* classes do the drawing; these rules only re-scope the
   glow, the sparkle spread and the wordmark to the card's own layout. */
#prTitleCard .pr-bi-spark {
  position: absolute;
  top: 34%;
  left: 50%;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
  background: var(--iris-bright);
}
#prTitleCard .pr-bi-spark:nth-of-type(2) { background: #ffd166; }
#prTitleCard .pr-bi-spark:nth-of-type(3) { background: #46d4ff; }
#prTitleCard .pr-bi-spark:nth-of-type(4) { background: #ff8fd0; }
#prTitleCard #prTitleCardMark { opacity: 0; }
#prTitleCard #prTitleCardBrand .pr-bi-l {
  display: inline-block;
  opacity: 0;
  transform: translateY(120%) scale(.5) rotate(-12deg);
  transform-origin: 50% 100%;
}
/* Armed by app.js (Present) and present-plus (recording) each time the card
   is shown — CSS animations fire once per element, so the run class has to be
   removed and re-added or a second show would display a finished logo. */
#prTitleCard.pr-brandin-run #prTitleCardMark { animation: prBiDrop 1.05s cubic-bezier(.28,1.5,.42,1) forwards; }
#prTitleCard.pr-brandin-run .pr-bi-stem { animation: prBiDraw .46s cubic-bezier(.65,0,.35,1) .34s forwards; }
#prTitleCard.pr-brandin-run .pr-bi-bowl { animation: prBiDraw .7s cubic-bezier(.65,0,.35,1) .5s forwards; }
#prTitleCard.pr-brandin-run .pr-bi-node { animation: prBiPop .5s cubic-bezier(.34,1.8,.5,1) forwards; }
#prTitleCard.pr-brandin-run .pr-bi-node-a { animation-delay: .74s; }
#prTitleCard.pr-brandin-run .pr-bi-node-b { animation-delay: .84s; }
#prTitleCard.pr-brandin-run .pr-bi-orbit { animation: prBiOrbit 1.1s cubic-bezier(.3,1.7,.45,1) .92s forwards; }
#prTitleCard.pr-brandin-run .pr-bi-spark { animation: prBiSpark .9s cubic-bezier(.2,.9,.3,1) forwards; }
#prTitleCard.pr-brandin-run .pr-bi-spark:nth-of-type(1) { animation-delay: .74s; --sx: -84px; --sy: -34px; }
#prTitleCard.pr-brandin-run .pr-bi-spark:nth-of-type(2) { animation-delay: .8s;  --sx: 80px;  --sy: -44px; }
#prTitleCard.pr-brandin-run .pr-bi-spark:nth-of-type(3) { animation-delay: .86s; --sx: -62px; --sy: 34px; }
#prTitleCard.pr-brandin-run .pr-bi-spark:nth-of-type(4) { animation-delay: .92s; --sx: 70px;  --sy: 30px; }
#prTitleCard.pr-brandin-run #prTitleCardBrand .pr-bi-l { animation: prBiLetter .78s cubic-bezier(.28,1.7,.42,1) forwards; }
#prTitleCard.pr-brandin-run #prTitleCardBrand .pr-bi-l:nth-child(1) { animation-delay: 1.12s; }
#prTitleCard.pr-brandin-run #prTitleCardBrand .pr-bi-l:nth-child(2) { animation-delay: 1.166s; }
#prTitleCard.pr-brandin-run #prTitleCardBrand .pr-bi-l:nth-child(3) { animation-delay: 1.212s; }
#prTitleCard.pr-brandin-run #prTitleCardBrand .pr-bi-l:nth-child(4) { animation-delay: 1.258s; }
#prTitleCard.pr-brandin-run #prTitleCardBrand .pr-bi-l:nth-child(5) { animation-delay: 1.304s; }
#prTitleCard.pr-brandin-run #prTitleCardBrand .pr-bi-l:nth-child(6) { animation-delay: 1.35s; }
#prTitleCard.pr-brandin-run #prTitleCardBrand .pr-bi-l:nth-child(7) { animation-delay: 1.396s; }
#prTitleCard.pr-brandin-run #prTitleCardBrand .pr-bi-l:nth-child(8) { animation-delay: 1.442s; }
#prTitleCard.pr-brandin-run #prTitleCardBrand .pr-bi-l:nth-child(9) { animation-delay: 1.488s; }
#prTitleCard.pr-brandin-run #prTitleCardBrand .pr-bi-l:nth-child(10) { animation-delay: 1.534s; }
/* Glow behind the mark and the sheen across the wordmark — the two pieces the
   card used to go without. Positioned against the card, which fills the stage,
   so the glow sits over the mark rather than the card's centre. */
#prTitleCard .pr-brandin-glow { display: none; }
#prTitleCard.pr-brandin-run .pr-brandin-glow { animation: prBiGlow 2.2s cubic-bezier(.22,.9,.28,1) forwards; }
#prTitleCardBrand { position: relative; }
#prTitleCard.pr-brandin-run .pr-brandin-sheen { animation: prBiSheen 1s cubic-bezier(.4,0,.2,1) 1.95s forwards; }

/* Reduced motion: the finished composition, arrived at rather than performed. */
@media (prefers-reduced-motion: reduce) {
  .pr-brandin-run .pr-brandin-glow { animation: none; opacity: .55; transform: translateX(-50%) scale(1); }
  .pr-brandin .pr-intro-mark, .pr-brandin-run .pr-intro-mark { animation: none; opacity: 1; transform: none; }
  .pr-bi-stem, .pr-bi-bowl { stroke-dashoffset: 0; }
  .pr-brandin-run .pr-bi-stem, .pr-brandin-run .pr-bi-bowl { animation: none; }
  .pr-brandin-run .pr-bi-node, .pr-brandin-run .pr-bi-orbit,
  .pr-brandin-run .pr-bi-l { animation: none; opacity: 1; transform: none; }
  .pr-brandin-run .pr-brandin-sheen, .pr-brandin-run .pr-bi-spark { animation: none; opacity: 0; }
  #prTitleCard #prTitleCardMark,
  #prTitleCard.pr-brandin-run #prTitleCardMark { animation: none; opacity: 1; transform: none; }
  #prTitleCard.pr-brandin-run .pr-bi-stem, #prTitleCard.pr-brandin-run .pr-bi-bowl { animation: none; }
  #prTitleCard.pr-brandin-run .pr-bi-node, #prTitleCard.pr-brandin-run .pr-bi-orbit { animation: none; opacity: 1; transform: none; }
  #prTitleCard #prTitleCardBrand .pr-bi-l,
  #prTitleCard.pr-brandin-run #prTitleCardBrand .pr-bi-l { animation: none; opacity: 1; transform: none; }
  #prTitleCard.pr-brandin-run .pr-bi-spark { animation: none; opacity: 0; }
  /* The id-scoped glow/sheen rules above out-specify the generic resets. */
  #prTitleCard.pr-brandin-run .pr-brandin-glow { animation: none; opacity: .55; transform: translateX(-50%) scale(1); }
  #prTitleCard.pr-brandin-run .pr-brandin-sheen { animation: none; opacity: 0; }
}

/* ---- Settings opened from the intro: same popover, centered as a modal
   above the intro overlay (.pr-pop is z-9, #prIntro z-8, so it wins).
   prPopIn fills forwards with its own transform, so the centered variant
   needs its own keyframes or the fill would pin it to translateX only. ---- */
.pr-pop.pr-centered {
  bottom: auto; top: 50%;
  transform: translate(-50%, -50%);
  max-height: min(78vh, 640px);
  animation-name: prPopInCenter;
}
@keyframes prPopInCenter {
  from { opacity: 0; transform: translate(-50%, calc(-50% + 10px)); }
  to { opacity: 1; transform: translate(-50%, -50%); }
}
body.pr-idle .pr-pop.pr-centered { transform: translate(-50%, -50%); }

/* ---- Karaoke subtitles: every word is a span; the highlight sweeps
   word-by-word with the voice, like a progress bar made of words. Two layers
   of "liquid": the strip itself carries a soft accent wash whose right edge
   is --kfill (stamped by karaokeMark at the end of the word being spoken),
   and the current word fills with a gradient pill before settling into
   crisp white. background-image rides OVER the pill's base background:
   the shorthand in the base rule would wipe it, longhands here win. ---- */
#prSubtitle {
  background-image: linear-gradient(90deg,
    color-mix(in srgb, var(--iris) 30%, transparent),
    color-mix(in srgb, var(--iris-bright) 14%, transparent));
  background-repeat: no-repeat;
  background-size: calc(var(--kfill, 0) * 100%) 100%;
  transition: background-size .3s linear;
}
#prSubtitle .kw { color: rgba(255, 255, 255, 0.42); transition: color .18s ease; border-radius: 5px; }
#prSubtitle .kw.said { color: #ffffff; }
#prSubtitle .kw.now {
  color: #ffffff;
  background-image: linear-gradient(90deg, var(--iris), color-mix(in srgb, var(--iris-bright) 80%, #fff 20%));
  background-repeat: no-repeat;
  background-size: 0% 100%;
  animation: kwLiquid .34s ease-out forwards;
  box-shadow: 0 0 0 4px var(--iris), 0 2px 14px color-mix(in srgb, var(--iris) 55%, transparent);
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.35);
}
@keyframes kwLiquid { to { background-size: 100% 100%; } }
@media (prefers-reduced-motion: reduce) {
  #prSubtitle { transition: none; }
  #prSubtitle .kw.now, #prNarrationText.karaoke .kw.now { animation: none; background-size: 100% 100%; }
}

/* ---- Title card (recording start slide + watch mode): branded. ---- */
#prTitleCardMark { width: 54px; height: 54px; margin-bottom: 4px; opacity: .92; }
#prTitleCardMark svg { width: 100%; height: 100%; display: block; }
#prTitleCardBrand {
  margin-top: 14px; font-family: var(--sans); font-weight: 700; font-size: 15px;
  letter-spacing: -0.03em; opacity: .82;
}
#prTitleCardBrand .um { color: var(--iris-bright); }

/* ---- "Record presentation" option — the headline act of the popover. ---- */
.pr-rec-opt.pr-rec-live { border-color: color-mix(in srgb, #f0546a 45%, transparent); }
.pr-rec-opt.pr-rec-live b { display: flex; align-items: center; gap: 7px; }
.pr-rec-dot-inline {
  display: inline-block; width: 9px; height: 9px; border-radius: 50%;
  background: #f0546a; box-shadow: 0 0 8px rgba(240, 84, 106, 0.9);
  animation: prRecPulse 1.4s ease-in-out infinite;
}
@keyframes prRecPulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

/* ---- Recording HUD: 3-2-1 countdown (before capture starts), then a
   short-lived REC pill that fades itself out of the recording. ---- */
#prRecHud { position: absolute; inset: 0; z-index: 11; pointer-events: none; }
#prRecCount {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  font-family: var(--sans); font-weight: 800; font-size: clamp(72px, 14vw, 150px);
  color: #ffffff; text-shadow: 0 12px 60px rgba(0, 0, 0, .55);
}
#prRecCount.tick { animation: prCountTick .92s var(--ease-out) both; }
@keyframes prCountTick {
  from { opacity: 0; transform: translate(-50%, -50%) scale(1.35); }
  25% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  85% { opacity: 1; }
  to { opacity: 0; transform: translate(-50%, -50%) scale(.92); }
}
#prRecPill {
  position: absolute; top: 14px; left: 50%; transform: translateX(-50%);
  display: flex; align-items: center; gap: 8px;
  padding: 7px 14px; border-radius: 999px;
  background: rgba(13, 15, 24, 0.88); border: 1px solid rgba(255, 255, 255, .14);
  color: #f3f4fa; font-size: 12.5px; font-weight: 600;
  opacity: 0; transition: opacity .4s ease;
}
#prRecPill.show { opacity: 1; }
#prRecPill kbd {
  font-family: var(--mono); font-size: 11px; padding: 1px 5px; border-radius: 4px;
  background: rgba(255, 255, 255, .12); border: 1px solid rgba(255, 255, 255, .16);
}

/* ---- While recording (body.pr-rec): the tab IS the video. Editor chrome
   goes away entirely (the extra .watching class hides topbar/palette/side);
   the control bar, popovers and toasts must never appear in the file. The
   progress bar, captions, subtitles, presenter and brand mark stay — they
   are the presentation. ---- */
body.pr-rec #presentBar, body.pr-rec .pr-pop, body.pr-rec #toasts { display: none !important; }
body.pr-rec #prEnd .pr-end-ctas, body.pr-rec #prEnd .pr-end-share { display: none; }
body.pr-rec #prIntro { display: none; }
body.pr-rec #canvasWrap, body.pr-rec #canvasWrap * { cursor: default; }
/* The FILE is 16:9, whatever shape the window is: the stage letterboxes to
   the largest 16:9 box the window offers, and the tab capture is cropped to
   #stage — so the box IS the frame. Fixed-positioning frees it from the app
   column (the box must be measured against the window alone; fixed boxes are
   also immune to ancestor overflow) and the body paints the bars. Everything
   Present draws lives inside #stage by contract, so nothing is lost off the
   edges; fitPresent/layoutPresentChrome re-measure on the class toggle, the
   same machinery as a window resize. */
body.pr-rec #stage {
  position: fixed;
  inset: 0;
  margin: auto;
  width: min(100vw, calc(100vh * 16 / 9));
  height: min(100vh, calc(100vw * 9 / 16));
}
body.pr-rec { background: #000; }

/* ============================================================
   Landing v2 — "The page is the canvas."
   The home overlay is styled as one continuous Diagramium canvas: the
   editor's dot grid runs behind everything, sections are labeled frames
   (mono scene tags), and every feature is explained by a live diagram of
   itself. Hover language borrows the product's own selection UI. One
   signature moment per section; calm at rest.
   Motion primitives (used everywhere, nothing else):
     RISE  — opacity+translateY entrance (.home-anim / .sr, scroll-fx.js)
     INK   — SVG strokes draw in via stroke-dashoffset
     POP   — nodes scale .92→1 on --ease-spring
   Scoped to #homePage (plus the shared .lp-top header, also used by
   #explorePage and mirrored in the generated pages/*.html).
   ============================================================ */

/* ---- Shared header (home + explore) — 60px glass bar. On the home overlay
   the hairline + glass appear only after a little scroll (js/landing.js
   toggles .is-scrolled); on explore it is always on. ---- */
.lp-top {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 62;
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  background: var(--glass-bg);
  border-bottom: 1px solid var(--border);
}
#homePage .lp-top {
  background: transparent;
  border-bottom-color: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  transition: background var(--dur-3) var(--ease-out), border-color var(--dur-3) var(--ease-out);
}
#homePage .lp-top.is-scrolled {
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  background: var(--glass-bg);
  border-bottom-color: var(--border);
}
.lp-top-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 60px;
  max-width: none;
  margin: 0 auto;
  padding: 0 clamp(18px, 4vw, 64px);
}
.lp-brandline { display: flex; align-items: center; gap: 10px; text-decoration: none; color: inherit; }
.lp-brandline .lock-icon { width: 30px; height: 30px; color: var(--slate); display: inline-flex; }
.lp-brandline .lock-icon svg { width: 30px; height: 30px; display: block; }
.wordmark { font-weight: 700; font-size: 21px; letter-spacing: -0.03em; color: var(--wordmark); }
.wordmark .um { color: var(--accent); }
/* Brand name in running text: the "ium" suffix always carries the accent. */
.um { color: var(--accent); }
.dbrand { font-weight: bolder; }
.lp-toolbar { display: flex; align-items: center; gap: 8px; position: relative; min-width: 0; }
.pillnav { display: none; gap: 2px; margin-right: 6px; }
.lp-mobile-menu-btn { display: inline-flex; justify-content: center; min-width: 74px; }
@media (min-width: 1080px) {
  .pillnav { display: flex; }
  .lp-mobile-menu-btn { display: none !important; }
}
.pillnav a,
.pillnav-action {
  font-size: 14px; color: var(--muted); text-decoration: none;
  padding: 7px 12px; border-radius: 8px; font-weight: 480; cursor: pointer;
  transition: color var(--dur-1) var(--ease-out), background var(--dur-1) var(--ease-out);
}
.pillnav-action {
  display: none;
  width: 100%; border: 0; background: transparent; font-family: inherit;
  text-align: left;
}
.pillnav a:hover,
.pillnav-action:hover { color: var(--text); background: var(--accent-soft); }
@media (max-width: 1079px) {
  .pillnav {
    display: none;
    position: absolute;
    z-index: 80;
    top: calc(100% + 12px);
    right: 0;
    width: min(290px, calc(100vw - 24px));
    max-height: min(70vh, 520px);
    overflow: auto;
    padding: 8px;
    margin: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 3px;
    border: 1px solid var(--border);
    border-radius: 16px;
    background: color-mix(in srgb, var(--chrome) 96%, transparent);
    box-shadow: 0 20px 54px rgba(4,7,18,.24), 0 2px 10px rgba(4,7,18,.12);
    backdrop-filter: blur(18px) saturate(1.08);
  }
  .pillnav.is-open { display: flex; }
  .pillnav a,
  .pillnav-action {
    display: flex;
    align-items: center;
    min-height: 44px;
    padding: 10px 13px;
    border-radius: 11px;
    font-size: 14px;
  }
  .pillnav a[aria-current="page"] { color: var(--text); background: var(--panel-2); }
  .lp-toolbar > .accent-btn,
  .lp-toolbar > .theme-toggle-btn,
  #odOpenBtnTop { display: none !important; }
}
@media (max-width: 520px) {
  .lp-top-inner { padding-inline: 10px; gap: 8px; }
  .lp-toolbar { gap: 5px; }
  .lp-brandline { gap: 7px; }
  .lp-brandline .beta-badge { display: none; }
  .lp-mobile-menu-btn { min-width: 66px; padding-inline: 9px; }
  .lp-toolbar > .lp-tbtn-cta { padding-inline: 10px; }
  #btnExploreClearCache,
  #btnHomeClearCache { display: none !important; }
}
/* Below 430 every remaining control earns its keep at icon-or-tight size, so
   "Open editor" stays on screen — measured: at 390 the bar ran to 418px and
   the primary CTA was simply unreachable. Nothing is removed: the Menu button
   keeps its accessible name, only its printed word goes. */
@media (max-width: 430px) {
  .lp-toolbar { gap: 4px; }
  /* `.lp-toolbar .lp-tbtn` (0,2,0), not a bare `.lp-tbtn` (0,1,0): this block
     sits BEFORE the ≤600 one, every ≤430 viewport also matches ≤600, and at
     equal specificity the later rule wins — so the bare selector was dead and
     the measured 390px squeeze never actually applied. */
  .lp-toolbar .lp-tbtn { padding: 7px 9px; font-size: 12px; min-height: 40px; }
  .lp-mobile-menu-btn { min-width: 0; }
  .lp-mobile-menu-btn span:not([aria-hidden]) { display: none; }
  .lp-toolbar > .lp-tbtn-cta { padding-inline: 9px; }
}
@media (max-width: 360px) {
  .lp-brandline .wordmark { display: none; }
}
.lp-tbtn {
  font: inherit; font-size: 13px; font-weight: 560; color: var(--text); cursor: pointer;
  background: transparent; border: 1px solid var(--border); border-radius: 9px;
  padding: 8px 13px; display: inline-flex; align-items: center; gap: 7px;
  text-decoration: none;
  /* Chrome buttons never wrap their own label: a two-line "Sign in" reads as a
     broken control, and it was happening at every width up to desktop. */
  white-space: nowrap; flex: none;
  transition: border-color var(--dur-1) var(--ease-out), background var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.lp-tbtn:hover { border-color: var(--slate-2); background: var(--accent-soft); }
.lp-tbtn:focus-visible, .pillnav a:focus-visible, .pillnav-action:focus-visible,
.lp-mobile-menu-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.lp-tbtn .glyph { font-family: var(--mono); font-size: 13px; line-height: 1; display: inline-flex; }
.lp-tbtn-icon { padding: 8px 9px; }
/* The one primary action in the bar reads as filled, everything else stays
   quiet — the outline treatment made "Open editor" one chip among six. */
.lp-tbtn-cta {
  border-color: transparent; background: var(--accent); color: var(--on-accent); font-weight: 600;
  box-shadow: 0 1px 0 rgba(255, 255, 255, .16) inset, 0 6px 16px -8px var(--accent);
}
.lp-tbtn-cta:hover { border-color: transparent; background: var(--accent); filter: brightness(1.07); transform: translateY(-1px); }

/* ---- The canvas ground: dot grid + one soft iris wash. ---- */
#homePage::before {
  content: ""; position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background-image:
    radial-gradient(900px 520px at 72% 6%, color-mix(in srgb, var(--iris) 7%, transparent), transparent 70%),
    radial-gradient(circle, var(--dot) 1px, transparent 1.2px);
  background-size: auto, 24px 24px;
}
[data-ui-theme="light"] #homePage::before {
  background-image:
    radial-gradient(900px 520px at 72% 6%, color-mix(in srgb, var(--iris) 9%, transparent), transparent 70%),
    radial-gradient(circle, color-mix(in srgb, var(--slate) 26%, transparent) 1px, transparent 1.2px);
}
#homePage .home-inner {
  position: relative; z-index: 1;
  max-width: none; width: 100%;
  margin: 0;
  padding: 60px clamp(18px, 4vw, 64px) 0;
  text-align: left;
  display: flex; flex-direction: column; align-items: stretch; gap: 0;
}

/* ---- Scene tags: every section is a labeled frame on the canvas.
   Drawn as a NODE on an EDGE — a port dot, the label, then a hairline rail
   running out to the margin — so the scenes read as steps of one diagram
   rather than isolated captions. Decorative only (aria-hidden in markup). ---- */
.lp-scene-tag {
  display: flex; align-items: center; gap: 10px;
  font-family: var(--mono); font-size: 11px; letter-spacing: .04em;
  color: var(--faint); margin-bottom: 18px; user-select: none;
}
.lp-scene-tag::before {
  content: ""; width: 7px; height: 7px; flex: none; border-radius: 2px;
  border: 1.5px solid color-mix(in srgb, var(--accent) 70%, transparent);
  background: var(--accent-soft);
}
.lp-scene-tag::after {
  content: ""; flex: 1 1 auto; height: 1px;
  background: linear-gradient(90deg, var(--border), transparent);
}

/* ---- Section rhythm + masthead. ---- */
#homePage .lp-section { position: relative; width: 100%; padding: clamp(72px, 9vw, 122px) 0 0; }
.lp-sec-head { margin-bottom: 34px; }
.lp-kicker {
  font-family: var(--mono); font-size: 12px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--accent-ink); font-weight: 600;
}
#homePage .lp-section h2 {
  font-size: clamp(30px, 3.8vw, 46px); font-weight: 620; letter-spacing: -0.025em;
  line-height: 1.08; margin: 12px 0 0; text-wrap: balance; max-width: 22ch;
}
#homePage .lp-section .lp-sub {
  color: var(--muted); max-width: 58ch; margin-top: 14px;
  font-size: 16.5px; line-height: 1.6; font-weight: 430;
}

/* ============ Hero — Script & Stage ============ */
.lp-hero {
  display: grid; grid-template-columns: 1fr; gap: 48px; align-items: center;
  width: 100%; padding: clamp(56px, 9vh, 104px) 0 clamp(40px, 6vh, 72px);
}
@media (min-width: 980px) { .lp-hero { grid-template-columns: 0.92fr 1.08fr; gap: clamp(40px, 4.5vw, 72px); } }
.lp-eyebrow {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--mono); font-size: 12px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted); font-weight: 560;
}
.lp-eyebrow-dot { width: 8px; height: 8px; background: var(--accent); border-radius: 2px; flex: none; }
/* Sized so the second line ("explain themselves.") holds ONE line on a
   desktop column: at the old 76px cap the phrase broke in two and the
   headline spent three lines — half the first viewport — saying it. */
#homePage .home-hero {
  font-size: clamp(38px, 4.8vw, 64px); font-weight: 640; letter-spacing: -0.033em;
  line-height: 1.04; margin: 20px 0 0; color: var(--text); text-wrap: balance;
}
.lp-warm {
  /* Ends bright on purpose: the old run finished on base iris, which sat at
     low contrast against the near-black ground and read as the DIM half of
     the headline. Both stops stay in the brand ramp. */
  background: linear-gradient(92deg, var(--iris-bright), var(--d-violet) 55%, var(--iris-bright));
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
  background-size: 200% 100%;
  animation: lp-ink-fill 900ms var(--ease-out) both;
}
@keyframes lp-ink-fill { from { background-position: 120% 0; opacity: .25; } to { background-position: 0% 0; opacity: 1; } }
#homePage .lp-hero .home-hero-sub {
  color: var(--muted); font-size: 17.5px; line-height: 1.62; font-weight: 430;
  margin: 22px 0 0; max-width: 46ch; text-align: left;
}
#homePage .lp-hero .home-hero-sub strong { color: var(--text); font-weight: 600; }
.lp-cta { display: flex; gap: 12px; margin-top: 32px; flex-wrap: wrap; align-items: center; }
.lp-btn {
  font-family: var(--font); font-weight: 570; font-size: 15px; border-radius: 11px;
  padding: 13px 22px; cursor: pointer; border: 1px solid transparent;
  text-decoration: none; display: inline-flex; align-items: center; gap: 9px;
  transition: transform var(--dur-1) var(--ease-out), filter var(--dur-1) var(--ease-out),
    border-color var(--dur-1) var(--ease-out), box-shadow var(--dur-2) var(--ease-out),
    background var(--dur-1) var(--ease-out);
}
.lp-btn:active { transform: translateY(0) scale(.985); }
.lp-btn-primary {
  background: var(--accent); color: var(--on-accent);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .18) inset, 0 10px 26px -12px var(--accent);
}
.lp-btn-primary:hover {
  transform: translateY(-1px); filter: brightness(1.07);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .18) inset, 0 14px 32px -12px var(--accent);
}
.lp-btn-arrow { font-weight: 400; transition: transform var(--dur-2) var(--ease-out); }
.lp-btn:hover .lp-btn-arrow { transform: translateX(3px); }
.lp-btn-ghost { background: transparent; color: var(--text); border-color: var(--border); }
.lp-btn-ghost:hover { border-color: var(--slate-2); background: var(--accent-soft); }
.lp-btn-lg { font-size: 16px; padding: 15px 28px; }
.lp-hero-fine {
  margin-top: 22px; font-family: var(--mono); font-size: 12.5px; color: var(--faint);
  letter-spacing: .01em;
}

/* The stage: a canvas frame housing the live editor + diagram. */
.lp-stage {
  position: relative; display: flex; flex-direction: column;
  border: 1px solid color-mix(in srgb, var(--iris) 28%, var(--edge));
  border-radius: var(--r-4);
  /* Nearly solid panel + light blur: the 78%-translucent glass and the 80px
     iris glow made the demo read as decoration; a crisp sheet reads as the
     product. */
  background: color-mix(in srgb, var(--panel) 92%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  box-shadow: var(--shadow-lg), 0 1px 0 rgba(255, 255, 255, .05) inset;
  overflow: hidden;
}
.lp-stage-bar {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border-soft);
}
.lp-stage-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--accent); opacity: .8; flex: none; }
.lp-stage-file { font-family: var(--mono); font-size: 11.5px; color: var(--faint); flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lp-stage-live { font-family: var(--mono); font-size: 11px; color: var(--d-green); animation: lp-live-in var(--dur-3) var(--ease-out) both; }
[data-ui-theme="light"] .lp-stage-live { color: #0a7d5c; }
@keyframes lp-live-in { from { opacity: 0; } to { opacity: 1; } }
.lp-stage-present {
  font: inherit; font-size: 12.5px; font-weight: 620; color: var(--on-accent);
  background: var(--accent); border: none; border-radius: 999px; padding: 6px 14px;
  cursor: pointer; display: inline-flex; align-items: center; gap: 6px; flex: none;
  box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--accent) 60%, transparent);
  transition: transform var(--dur-1) var(--ease-spring), filter var(--dur-1) var(--ease-out);
}
.lp-stage-present:hover { filter: brightness(1.08); transform: scale(1.04); }
.lp-stage-present:active { transform: scale(.96); }
.lp-stage-present span { font-size: 10px; }
.lp-stage-panes { display: grid; grid-template-columns: 1fr; }
@media (min-width: 640px) { .lp-stage-panes { grid-template-columns: 236px 1fr; } }
@media (min-width: 1100px) { .lp-stage-panes { grid-template-columns: 290px 1fr; } }
/* Grid/flex children may not force the column wider than the viewport. */
.lp-stage-panes > *, .lp-hero > *, .lp-who > * { min-width: 0; }
.lp-stage-script { position: relative; border-bottom: 1px solid var(--border-soft); }
@media (min-width: 640px) { .lp-stage-script { border-bottom: none; border-right: 1px solid var(--border-soft); } }
.lp-stage-script textarea {
  display: block; width: 100%; height: 100%; min-height: 148px; resize: none;
  background: transparent; border: none; outline: none;
  font-family: var(--mono); font-size: 12.5px; line-height: 1.75; color: var(--ink-2);
  padding: 14px 16px; caret-color: var(--accent);
}
.lp-stage-script textarea:focus { color: var(--text); }
.lp-stage-script:focus-within { box-shadow: 0 0 0 2px var(--accent-ring) inset; border-radius: 4px; }
.lp-stage-script textarea::selection { background: var(--accent-soft); }
.lp-stage-ghost {
  position: absolute; left: 16px; bottom: 10px; pointer-events: none;
  font-family: var(--mono); font-size: 10.5px; color: var(--faint);
}
.lp-stage-canvas {
  position: relative; min-height: 230px; display: flex; align-items: center; justify-content: center;
  background-image: radial-gradient(circle, var(--dot) 1px, transparent 1.2px);
  background-size: 20px 20px;
  padding: 10px 12px;
}
.lp-stage-canvas svg { width: 100%; height: auto; max-height: 300px; display: block; }
.hero-actor-name { fill: var(--text); font-size: 13px; font-weight: 600; font-family: var(--font); }
.hero-lifeline { stroke: var(--faint); stroke-width: 1; stroke-dasharray: 4 4; opacity: .55; }
.hero-msg-text { fill: var(--ink-2); font-size: 11.5px; font-family: var(--mono); }
.hero-note rect { fill: var(--accent-soft); stroke: color-mix(in srgb, var(--accent) 45%, transparent); stroke-width: 1.2; }
.hero-note-text { fill: var(--ink-2); font-size: 11px; font-family: var(--mono); }
.hero-empty-hint { fill: var(--faint); font-size: 13px; font-family: var(--mono); }
/* INK entrance: each .hstep fades/rises in at its --hd delay when .hero-anim runs. */
.hero-anim .hstep { opacity: 0; transform: translateY(5px); animation: hero-step-in .42s var(--ease-out) var(--hd, 0s) forwards; }
@keyframes hero-step-in { to { opacity: 1; transform: none; } }
.hero-new { animation: hero-step-in .42s var(--ease-out) both; }
.hero-hidden { opacity: 0; }
.hero-revealed { opacity: 1; transition: opacity .3s var(--ease-out); }
.lp-stage-progress {
  height: 3px; overflow: hidden;
  /* Step ticks on the track: playback advances through STEPS, and the rail
     should say so — the same sequencing language Present uses. */
  background:
    repeating-linear-gradient(90deg, transparent 0 calc(12.5% - 3px), var(--chrome) calc(12.5% - 3px) 12.5%),
    var(--border-soft);
}
/* Light theme needs a deeper track, or the paper-coloured ticks vanish into
   it (#f5f6fa on #e8eaf4 measured ~1.05:1 — no visible ticks at all). */
[data-ui-theme="light"] .lp-stage-progress {
  background:
    repeating-linear-gradient(90deg, transparent 0 calc(12.5% - 3px), var(--chrome) calc(12.5% - 3px) 12.5%),
    color-mix(in srgb, var(--slate-2) 45%, var(--border-soft));
}
.lp-stage-progress span { display: block; height: 100%; width: 0; background: var(--accent); border-radius: 2px; }
.lp-stage-caption {
  padding: 9px 14px; font-family: var(--mono); font-size: 11px; color: var(--faint);
  border-top: 1px solid var(--border-soft);
}

/* ============ Proof strip — the editor's status bar, telling the truth. ============ */
.lp-proof {
  display: grid; grid-template-columns: repeat(2, 1fr);
  border: 1px solid var(--border); border-radius: var(--r-3);
  background: color-mix(in srgb, var(--panel) 62%, transparent);
  margin-top: clamp(40px, 6vh, 64px);
  overflow: hidden;
}
@media (min-width: 720px) { .lp-proof { grid-template-columns: repeat(5, auto); justify-content: space-between; } }
.lp-proof-cell {
  display: flex; flex-direction: column; gap: 4px;
  padding: 20px clamp(18px, 2.6vw, 34px);
  border-top: 1px solid var(--border-soft);
}
.lp-proof-cell:nth-child(-n+2) { border-top: none; }
@media (min-width: 720px) { .lp-proof-cell { border-top: none; } }
@media (max-width: 719.98px) { .lp-proof-cell-wide { grid-column: 1 / -1; } }
.lp-proof-num {
  font-size: 27px; font-weight: 640; letter-spacing: -0.02em; color: var(--text);
  font-variant-numeric: tabular-nums; line-height: 1.1;
}
.lp-proof-formats { font-size: 16px; font-family: var(--mono); font-weight: 560; letter-spacing: 0; padding-top: 7px; }
.lp-proof-label { font-family: var(--mono); font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--faint); }

/* ============ Templates — command bar + spreadsheet grid + selection hover. ============ */
.lp-discover-search {
  display: flex; align-items: center; gap: 10px; width: 100%;
  margin: 4px 0 18px; padding: 6px 6px 6px 18px; color: var(--muted);
  background: color-mix(in srgb, var(--panel) 72%, transparent);
  border: 1px solid var(--border); border-radius: var(--r-3);
  transition: border-color var(--dur-1) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
}
.lp-discover-search:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
  color: var(--text);
}
.lp-discover-search input {
  flex: 1; min-width: 0; background: none; border: none; outline: none;
  font-size: 15.5px; color: var(--text); padding: 11px 0;
}
.lp-discover-search input::placeholder { color: var(--faint); }
.lp-discover-search input::-webkit-search-cancel-button { display: none; }
.lp-search-kbd {
  font-family: var(--mono); font-size: 11.5px; color: var(--faint);
  border: 1px solid var(--border); border-radius: 6px; padding: 3px 8px; flex: none;
}
.lp-search-go {
  flex: none; font-size: 14px; font-weight: 600; color: var(--on-accent); cursor: pointer;
  background: var(--accent); border: none; border-radius: 10px; padding: 11px 22px;
  box-shadow: 0 1px 0 rgba(255, 255, 255, .18) inset;
  transition: filter var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.lp-search-go:hover { filter: brightness(1.07); transform: translateY(-1px); }
.lp-coll-row { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 26px; }
.lp-coll {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 13px; font-weight: 520; color: var(--muted); text-decoration: none;
  border: 1px solid var(--border); border-radius: 999px; padding: 7px 14px;
  transition: color var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out), background var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.lp-coll:hover { color: var(--text); border-color: var(--slate-2); background: var(--accent-soft); transform: translateY(-1px); }
.lp-coll span { font-size: 13px; }
/* Spreadsheet grid: tiles share hairlines via 1px gaps over the border color. */
.lp-discover-grid {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px;
  background: var(--border-soft);
  border: 1px solid var(--border-soft); border-radius: var(--r-4);
  overflow: hidden;
}
@media (min-width: 760px) { .lp-discover-grid { grid-template-columns: repeat(auto-fill, minmax(265px, 1fr)); } }
.lp-tpl-card {
  position: relative; display: flex; flex-direction: column; text-align: left; cursor: pointer;
  background: var(--chrome); text-decoration: none; color: inherit;
  transition: background var(--dur-2) var(--ease-out);
}
.lp-tpl-card:hover { background: var(--panel); }
.lp-tpl-thumb { position: relative; aspect-ratio: 16/10; overflow: hidden; }
.lp-tpl-thumb svg { width: 100%; height: 100%; display: block; padding: 12px 16px; transition: transform var(--dur-3) var(--ease-out); }
.lp-tpl-card:hover .lp-tpl-thumb svg { transform: scale(1.04); }
.lp-tpl-body { display: flex; flex-direction: column; gap: 3px; padding: 12px 15px 15px; border-top: 1px solid var(--border-soft); }
.lp-tpl-name { font-size: 14px; font-weight: 570; letter-spacing: -0.01em; color: var(--text); }
.lp-tpl-kind { font-family: var(--mono); font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--faint); }
/* The product's selection UI on hover: bounding box + 8 handles. */
.lp-tpl-card::before {
  content: ""; position: absolute; inset: 7px; z-index: 2; pointer-events: none;
  border: 1px solid color-mix(in srgb, var(--iris) 65%, transparent);
  border-radius: 4px; opacity: 0;
  transition: opacity var(--dur-1) var(--ease-out);
}
.lp-tpl-card::after {
  content: ""; position: absolute; inset: 4px; z-index: 2; pointer-events: none; opacity: 0;
  background-image:
    radial-gradient(circle, var(--iris) 0 2.6px, transparent 2.6px),
    radial-gradient(circle, var(--iris) 0 2.6px, transparent 2.6px),
    radial-gradient(circle, var(--iris) 0 2.6px, transparent 2.6px),
    radial-gradient(circle, var(--iris) 0 2.6px, transparent 2.6px),
    radial-gradient(circle, var(--iris) 0 2.6px, transparent 2.6px),
    radial-gradient(circle, var(--iris) 0 2.6px, transparent 2.6px),
    radial-gradient(circle, var(--iris) 0 2.6px, transparent 2.6px),
    radial-gradient(circle, var(--iris) 0 2.6px, transparent 2.6px);
  background-position: 0 0, 50% 0, 100% 0, 0 50%, 100% 50%, 0 100%, 50% 100%, 100% 100%;
  background-size: 7px 7px; background-repeat: no-repeat;
  transition: opacity var(--dur-1) var(--ease-out);
}
.lp-tpl-card:hover::before, .lp-tpl-card:hover::after,
.lp-tpl-card:focus-visible::before, .lp-tpl-card:focus-visible::after { opacity: 1; }
.lp-discover-more { margin-top: 26px; }

/* ============ The specimen — 25 diagram types. ============ */
#homePage .home-cards {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;
  width: 100%; margin-top: 34px;
}
@media (min-width: 640px) { #homePage .home-cards { grid-template-columns: repeat(auto-fill, minmax(205px, 1fr)); } }
#homePage .home-card {
  position: relative; display: flex; flex-direction: column;
  background: color-mix(in srgb, var(--panel) 72%, transparent);
  border: 1px solid var(--border); border-radius: var(--r-3);
  overflow: hidden; text-decoration: none; color: inherit;
  transition: border-color var(--dur-2) var(--ease-out), transform var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
}
#homePage .home-card:hover {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--iris) 45%, var(--border));
  box-shadow: var(--shadow-sm);
}
#homePage .hc-preview {
  display: flex; align-items: center; justify-content: center;
  padding: 16px 14px 12px;
  border-bottom: 1px solid var(--border-soft);
}
#homePage .hc-preview svg { width: 100%; height: auto; max-height: 84px; transition: transform var(--dur-3) var(--ease-out); }
#homePage .home-card:hover .hc-preview svg { transform: scale(1.05); }
/* Keyboard gets the same lift the pointer gets. */
#homePage .home-card:focus-visible {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--iris) 45%, var(--border));
  box-shadow: var(--shadow-sm);
}
.who-card:focus-visible {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--tint) 55%, var(--border));
}
.lp-tpl-card:focus-visible { background: var(--panel); }
#homePage .hc-name {
  padding: 10px 14px 5px; font-weight: 570; font-size: 13.5px; letter-spacing: -0.01em;
  text-align: left; line-height: 1.3; color: var(--text);
  display: flex; align-items: center; gap: 6px;
}
#homePage .hc-name::after {
  content: "→"; font-weight: 400; color: var(--accent-ink); opacity: 0;
  transform: translateX(-4px); transition: opacity var(--dur-2) var(--ease-out), transform var(--dur-2) var(--ease-out);
}
#homePage .home-card:hover .hc-name::after { opacity: 1; transform: none; }
#homePage .hc-continue {
  padding: 0 14px 10px; font-size: 11px; font-weight: 500; color: var(--accent-ink);
  min-height: 14px; font-family: var(--mono);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ============ For everyone — duotone photos that bloom on hover. ============ */
.lp-who { display: grid; grid-template-columns: 1fr; gap: 14px; }
@media (min-width: 780px) { .lp-who { grid-template-columns: repeat(3, 1fr); } }
/* Two-card variant (the kids card is gone — Diagramium is 18+): centred pair
   instead of a three-column grid with an empty seat. */
@media (min-width: 780px) { .lp-who--two { grid-template-columns: repeat(2, 1fr); max-width: 960px; margin-inline: auto; } }
.lp-who .card, .who-card {
  --tint: var(--accent);
  display: flex; flex-direction: column;
  background: color-mix(in srgb, var(--panel) 72%, transparent);
  border: 1px solid var(--border); border-radius: var(--r-4);
  overflow: hidden; text-decoration: none; color: inherit;
  transition: transform var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out);
}
.who--everyday { --tint: var(--d-amber); }
.who--pro { --tint: var(--d-indigo); }
.who-card:hover {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--tint) 55%, var(--border));
  box-shadow: 0 18px 40px -20px color-mix(in srgb, var(--tint) 55%, transparent);
}
.who-banner { position: relative; height: clamp(180px, 22vh, 250px); overflow: hidden; }
/* The banners are <picture> (WebP with a JPEG fallback, two widths — see
   scripts/build_photo_assets.py). <picture> is an inline box by default, so
   the img's `height: 100%` below would resolve against an auto-height inline
   parent and collapse; sizing the wrapper to fill the banner keeps the
   percentage meaningful. */
.who-banner picture { display: block; width: 100%; height: 100%; }
.who-banner img {
  display: block; width: 100%; height: 100%; object-fit: cover; object-position: center 28%;
  filter: grayscale(.85) contrast(1.02);
  transition: filter .5s var(--ease-out), transform .6s var(--ease-out);
}
.who-card:hover .who-banner img { filter: grayscale(0); transform: scale(1.03); }
.who-banner::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: var(--tint); z-index: 2;
}
.who-icon {
  position: absolute; z-index: 3; left: 16px; bottom: 13px; width: 42px; height: 42px;
  border-radius: 12px; font-size: 21px;
  background: color-mix(in srgb, var(--tint) 22%, var(--panel));
  border: 1.5px solid var(--tint);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 8px 18px -6px rgba(8, 10, 20, .5);
}
.who-body { padding: 20px 22px 22px; display: flex; flex-direction: column; }
.lp-who .k { font-family: var(--mono); font-size: 11px; letter-spacing: .09em; text-transform: uppercase; color: var(--tint); }
.lp-who h3 { font-size: 18px; font-weight: 620; margin: 8px 0 0; letter-spacing: -0.01em; }
.lp-who p { color: var(--muted); font-size: 14px; line-height: 1.55; margin-top: 8px; }
.who-cta {
  margin-top: 14px; font-family: var(--mono); font-size: 11.5px; color: var(--faint);
  transition: color var(--dur-1) var(--ease-out);
}
.who-card:hover .who-cta { color: var(--tint); }
/* Cast marquee: honest "made for people like" — one quiet moving line. */
.lp-cast-wrap { position: relative; margin-top: 26px; }
.lp-cast {
  position: relative; overflow: hidden;
  border-top: 1px solid var(--border-soft); border-bottom: 1px solid var(--border-soft);
  display: flex;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.lp-cast-line { display: flex; flex: none; align-items: center; gap: 0; padding: 13px 0; }
.lp-cast-item {
  flex: none; font-family: var(--mono); font-size: 12.5px; color: var(--muted);
  padding: 0 26px; position: relative; white-space: nowrap;
}
.lp-cast-item::after {
  content: ""; position: absolute; right: -3px; top: 50%; width: 5px; height: 5px;
  border-radius: 50%; background: var(--accent); opacity: .5; transform: translateY(-50%);
}
.lp-cast-animated .lp-cast-line { animation: lp-cast-scroll 56s linear infinite; }
.lp-cast:hover .lp-cast-line, .lp-cast.cast-paused .lp-cast-line,
.lp-cast.cast-offscreen .lp-cast-line { animation-play-state: paused; }
/* Without the JS clone (or under reduced motion) nothing moves — let the
   strip scroll instead of clipping. */
.lp-cast:not(.lp-cast-animated) { overflow-x: auto; }
.lp-cast-toggle {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 34px; height: 34px; border-radius: 50%; border: 1px solid var(--border);
  background: var(--glass-bg); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  color: var(--muted); font-size: 9px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: color var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out);
}
.lp-cast-toggle:hover { color: var(--text); border-color: var(--slate-2); }
@keyframes lp-cast-scroll { from { transform: translateX(0); } to { transform: translateX(-100%); } }

/* ============ Local-first — the quiet scene. ============ */
.lp-quiet { text-align: center; display: flex; flex-direction: column; align-items: center; }
.lp-quiet .lp-scene-tag { align-self: flex-start; }
#homePage .lp-quiet h2.lp-quiet-title {
  font-size: clamp(32px, 4.6vw, 54px); font-weight: 350; letter-spacing: -0.02em;
  line-height: 1.18; max-width: none; margin: 10px auto 0;
}
[data-ui-theme="light"] #homePage .lp-quiet h2.lp-quiet-title { font-weight: 400; }
.lp-quiet-dia { margin: 38px 0 0; width: min(480px, 100%); }
.lp-quiet-dia svg { width: 100%; height: auto; display: block; }
.lq-label { fill: var(--text); font-size: 12.5px; font-weight: 600; font-family: var(--font); }
.lq-edge { stroke: var(--d-green); stroke-width: 1.6; }
.lq-never-edge { stroke: var(--faint); stroke-width: 1.3; stroke-dasharray: 5 5; }
.lq-never-label { fill: var(--faint); font-size: 10.5px; font-family: var(--mono); letter-spacing: .08em; }
.sr .lq-step { opacity: 0; transform: translateY(5px); }
.sr-in .lq-step { opacity: 1; transform: none; transition: opacity .4s var(--ease-out) var(--hd, 0s), transform .4s var(--ease-out) var(--hd, 0s); }
/* The connectors DRAW rather than fade — the one motion this page borrows
   from the product itself. 60 covers the 54-unit edge; reduced-motion resets
   it below alongside the other reveals. */
.sr .lq-edge { stroke-dasharray: 60; stroke-dashoffset: 60; }
.sr-in .lq-edge { stroke-dashoffset: 0; transition: stroke-dashoffset .5s var(--ease-out) calc(var(--hd, 0s) + .12s); }
.sr .lq-cloud { opacity: 0; }
.sr-in .lq-cloud { opacity: 1; transition: opacity .5s var(--ease-out) 950ms; }
.sr .lq-stamp { opacity: 0; transform: scale(.4); transform-box: fill-box; transform-origin: center; }
.sr-in .lq-stamp { opacity: 1; transform: scale(1); transition: opacity .2s var(--ease-out) 1350ms, transform .25s var(--ease-spring) 1350ms; }
.lp-quiet-rows {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 0;
  margin-top: 36px; border-top: 1px solid var(--border-soft);
  width: min(980px, 100%);
}
.lp-quiet-row {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: 14.5px; font-weight: 520; color: var(--ink-2);
  padding: 16px clamp(12px, 2.6vw, 28px);
}
.lp-quiet-row svg { color: var(--d-green); flex: none; }
.lp-quiet-note { margin-top: 22px; font-size: 13.5px; color: var(--faint); }
.lp-quiet-note .link-btn { color: var(--muted); }
.lp-quiet-note .link-btn:hover { color: var(--accent-ink); }

/* ============ Pricing — one priced object. ============ */
.pricing-grid {
  display: grid; grid-template-columns: 1fr; gap: 1px;
  background: var(--border-soft);
  border: 1px solid var(--border); border-radius: var(--r-4);
  overflow: hidden; margin-top: 34px;
}
@media (min-width: 640px) { .pricing-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1020px) { .pricing-grid { grid-template-columns: repeat(4, 1fr); } }
/* Two-tier variant: keep the pair at a readable width instead of stranding
   two empty columns of the 4-up grid. */
.pricing-grid--two { max-width: 900px; }
@media (min-width: 1020px) { .pricing-grid--two { grid-template-columns: repeat(2, 1fr); } }
.price-card {
  position: relative; display: flex; flex-direction: column;
  background: var(--chrome); padding: 26px 24px 24px;
  transition: background var(--dur-2) var(--ease-out);
}
.price-card:hover { background: var(--panel); }
/* Tier illustration — same translucent-fill + fixed-hex-stroke idiom as the
   .hc-preview minis, so it reads in both themes. Decorative only. */
.price-art { margin: -4px 0 12px; }
.price-art svg { display: block; width: 100%; height: auto; max-height: 88px; }
.pricing-modal .price-art { margin: 0 0 8px; }
.pricing-modal .price-art svg { max-height: 60px; }
.price-card--pro {
  background: color-mix(in srgb, var(--iris) 5%, var(--chrome));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--iris) 55%, transparent) inset;
}
.price-card--pro:hover { background: color-mix(in srgb, var(--iris) 8%, var(--chrome)); }
.price-badge {
  position: absolute; top: 14px; right: 14px;
  font-family: var(--mono); font-size: 10px; font-weight: 600; letter-spacing: .09em;
  text-transform: uppercase; color: var(--accent-ink);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: 999px; padding: 4px 10px;
  background: var(--accent-soft);
}
.price-name { font-family: var(--mono); font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); font-weight: 600; }
.price-amt {
  font-size: 38px; font-weight: 640; letter-spacing: -0.02em; margin-top: 12px;
  font-variant-numeric: tabular-nums; color: var(--text);
}
.price-amt span { font-size: 13.5px; font-weight: 480; color: var(--faint); letter-spacing: 0; margin-left: 2px; }
.price-tag { font-size: 13.5px; color: var(--muted); margin-top: 5px; }
.price-feats { list-style: none; margin: 18px 0 22px; padding: 0; display: flex; flex-direction: column; flex: 1; }
.price-feats li {
  position: relative; font-size: 13.5px; color: var(--ink-2); line-height: 1.45;
  padding: 8px 0 8px 24px; border-top: 1px solid var(--border-soft);
}
.price-feats li::before { content: "✓"; position: absolute; left: 2px; top: 8px; color: var(--d-green); font-size: 12px; font-weight: 700; }
.price-feats li.on { color: var(--muted); }
.price-feats li.on::before { color: var(--faint); }

/* NOT included on this tier. A price list is a COMPARISON, so a limit has to
   be visible: a feature merely left off the list reads as an oversight, and
   the visitor discovers the limit later in the product instead. ✕ + --faint
   (a real token — an invented one fails silently to its fallback and would
   look right in dark and wrong in light). Never a strikethrough: struck text
   means a changed price, not an absent feature. Specificity beats the base
   ::before rule, so the ✓ is genuinely replaced. */
.price-feats li.off { color: var(--faint); }
.price-feats li.off::before { content: "\2715"; color: var(--faint); font-weight: 600; }
.price-cta {
  font: inherit; font-size: 14px; font-weight: 600; cursor: pointer;
  border: 1px solid var(--border); border-radius: 10px; padding: 11px 16px;
  background: transparent; color: var(--text);
  transition: border-color var(--dur-1) var(--ease-out), background var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out), filter var(--dur-1) var(--ease-out);
}
.price-cta:hover { border-color: var(--accent); background: var(--accent-soft); transform: translateY(-1px); }
.price-cta:disabled { opacity: .55; cursor: default; transform: none; }
.price-cta--pro {
  background: var(--accent); color: var(--on-accent); border-color: transparent;
  box-shadow: 0 1px 0 rgba(255, 255, 255, .18) inset, 0 10px 24px -12px var(--accent);
}
.price-cta--pro:hover { background: var(--accent); filter: brightness(1.07); }
.price-card.is-current { box-shadow: 0 0 0 1.5px var(--accent) inset; }
.price-note { margin-top: 18px; font-size: 12.5px; color: var(--faint); }
/* The pricing modal reuses .pricing-grid — keep it compact there. */
.pricing-modal { width: min(940px, calc(100vw - 32px)); } /* sized to the two-tier grid cap */
.pricing-modal .pricing-grid { margin-top: 0; }
.pricing-modal-sub { color: var(--muted); font-size: 13.5px; line-height: 1.55; margin: 0 0 18px; max-width: 72ch; }

/* ============ FAQ — native details, hairline rows. ============ */
.lp-faq { max-width: none; display: flex; flex-direction: column; }
.lp-faq-item { border-top: 1px solid var(--border-soft); }
.lp-faq-item:last-child { border-bottom: 1px solid var(--border-soft); }
.lp-faq-item summary {
  list-style: none; cursor: pointer; display: flex; align-items: center; justify-content: space-between; gap: 18px;
  font-size: 16.5px; font-weight: 560; letter-spacing: -0.01em; color: var(--text);
  padding: 19px 2px;
  transition: color var(--dur-1) var(--ease-out);
}
.lp-faq-item summary::-webkit-details-marker { display: none; }
.lp-faq-item summary:hover { color: var(--accent-ink); }
.lp-faq-plus { position: relative; width: 14px; height: 14px; flex: none; }
.lp-faq-plus::before, .lp-faq-plus::after {
  content: ""; position: absolute; background: var(--faint); border-radius: 1px;
  transition: transform var(--dur-2) var(--ease-out), background var(--dur-2) var(--ease-out);
}
.lp-faq-plus::before { left: 6px; top: 0; width: 2px; height: 14px; }
.lp-faq-plus::after { left: 0; top: 6px; width: 14px; height: 2px; }
.lp-faq-item[open] .lp-faq-plus { transform: rotate(45deg); }
.lp-faq-plus { transition: transform var(--dur-2) var(--ease-out); }
.lp-faq-a { overflow: hidden; }
.lp-faq-a p { font-size: 14.5px; line-height: 1.65; color: var(--muted); max-width: 62ch; padding: 0 2px 20px; }
@supports (interpolate-size: allow-keywords) {
  .lp-faq-item::details-content {
    block-size: 0; overflow-y: clip;
    transition: content-visibility var(--dur-3) allow-discrete, block-size var(--dur-3) var(--ease-out);
  }
  .lp-faq-item[open]::details-content { block-size: auto; }
}

/* ============ Finale. ============ */
.lp-finale { padding: clamp(88px, 12vw, 150px) 0 clamp(64px, 8vw, 110px); text-align: center; }
.lp-finale-title {
  font-size: clamp(34px, 4.8vw, 60px); font-weight: 640; letter-spacing: -0.03em;
  display: flex; align-items: center; justify-content: center; gap: clamp(10px, 1.6vw, 20px);
  flex-wrap: wrap;
}
.lp-finale-play {
  display: inline-flex; align-items: center; justify-content: center;
  width: clamp(52px, 6vw, 72px); height: clamp(52px, 6vw, 72px); border-radius: 50%;
  background: var(--accent); color: var(--on-accent); text-decoration: none;
  font-size: clamp(17px, 2vw, 24px); padding-left: 5px;
  box-shadow: 0 1px 0 rgba(255, 255, 255, .2) inset, 0 14px 40px -12px var(--accent);
  transition: transform var(--dur-2) var(--ease-spring), filter var(--dur-1) var(--ease-out);
  position: relative;
}
.lp-finale-play:hover { transform: scale(1.07); filter: brightness(1.07); }
.lp-finale-play::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  border: 1.5px solid var(--accent); opacity: 0; pointer-events: none;
}
.lp-finale-play:hover::after { animation: lp-play-ring .9s var(--ease-out); }
@keyframes lp-play-ring { from { opacity: .6; transform: scale(1); } to { opacity: 0; transform: scale(1.55); } }
.lp-cta-center { justify-content: center; margin-top: 38px; }

/* ============ Footer. ============ */
.lp-foot { position: relative; width: 100%; margin-top: clamp(40px, 6vw, 80px); padding: 0 0 34px; }
/* While the fixed 18+ notice is up it covers the footer's base bar — pay the
   bar's height so the legal links stay reachable. The bar removes itself from
   the DOM on acknowledge, so :has() tracks the live state exactly. */
body:has(#ageAckBar) .lp-foot { padding-bottom: 130px; }
/* Comfortable touch targets for the link columns on coarse pointers. */
@media (max-width: 760px) {
  .lp-foot-col { gap: 4px; }
  .lp-foot-col a, .lp-foot-col .link-btn { padding-block: 8px; }
}
.lp-foot::before {
  content: ""; position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: 100vw; border-top: 1px solid var(--border); pointer-events: none;
}
.lp-foot-grid {
  display: grid; grid-template-columns: 1fr; gap: 34px;
  padding: 44px 0 38px;
}
@media (min-width: 760px) { .lp-foot-grid { grid-template-columns: 1.4fr 1fr 1fr 1fr 1fr; gap: 24px; } }
.lp-foot-brandcol { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; }
.lp-foot-mark { width: 34px; height: 34px; color: var(--slate); display: inline-flex; }
.lp-foot-mark svg { width: 34px; height: 34px; display: block; }
.lp-foot-tag { color: var(--muted); font-size: 14px; line-height: 1.5; max-width: 30ch; }
.lp-foot-col { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; }
.lp-foot-h { font-family: var(--mono); font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--faint); font-weight: 600; margin-bottom: 4px; }
.lp-foot-col a, .lp-foot-col .link-btn {
  font-size: 13.5px; color: var(--muted); text-decoration: none; cursor: pointer;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat; background-size: 0% 1px; background-position: 0 100%;
  padding-bottom: 2px;
  transition: color var(--dur-1) var(--ease-out), background-size var(--dur-2) var(--ease-out);
}
.lp-foot-col a:hover, .lp-foot-col .link-btn:hover { color: var(--text); background-size: 100% 1px; }
.lp-foot-col .link-btn { text-decoration: none; }
.lp-foot-base {
  display: flex; align-items: center; justify-content: space-between; gap: 14px 24px; flex-wrap: wrap;
  border-top: 1px solid var(--border-soft); padding-top: 22px;
}
.lp-foot-mono { font-family: var(--mono); font-size: 12px; color: var(--faint); }
/* The maker's credit is its OWN LINE, sitting under the Diagramium lockup.
   It used to be spliced into the middle of the base bar's mono sentence —
   "A product of [logo] DhuRee Labs Inc. · Local-first & private · 2026" — which
   read as a run-on and, at 475px + 382px of links against a 743px bar, wrapped
   into two ragged left-aligned rows while `justify-content: space-between`
   flung them to opposite edges at every width just above the wrap point. One
   short mono line plus one link cluster fits, so the base bar is stable again.
   `width: 100%` is what forces its own row inside the two ROW-flex footers
   (explore + search); in the landing footer's column-flex brand block it is a
   harmless full-width stretch. */
/* Social row — one markup on every footer (index's three + every generated
   page). Colours are INHERITED with opacity, deliberately token-free: the
   footers already set their own ink, and an invented token fails silently to
   its fallback in one theme (CLAUDE.md). */
.lp-foot-social { display: flex; align-items: center; gap: 14px; margin-top: 10px; width: 100%; }
.lp-foot-social a { display: inline-flex; align-items: center; gap: 7px; color: inherit; opacity: .72; font-size: 12.5px; text-decoration: none; }
.lp-foot-social a:hover { opacity: 1; text-decoration: none; }
.lp-foot-social svg { width: 15px; height: 15px; fill: currentColor; flex: none; }

.lp-foot-maker-row {
  width: 100%; display: flex; align-items: center; flex-wrap: wrap; gap: 0 5px;
  font-family: var(--mono); font-size: 12px; color: var(--faint);
}
/* The maker's credit, linked out to DhuRee Labs. The mark is INLINE SVG rather
   than a file for the same reason the Diagramium mark is: the app has to keep
   working offline and inside a single-file export, and a <img src> would be a
   broken box in both. Opens in a new tab — the visitor is mid-task in an
   editor, and navigating the whole page away from unsaved work is hostile. */
.lp-foot-maker { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); text-decoration: none; border-radius: 6px; padding: 2px 4px; margin: 0 -2px; transition: color .14s ease, background .14s ease; }
.lp-foot-maker:hover, .lp-foot-maker:focus-visible { color: var(--text); background: var(--panel-2); }
/* The mark's 64-unit box carries built-in padding (the ink spans roughly
   9..54), so it needs to sit a shade larger than the 12px text beside it to
   read as the same optical weight. Sizing BOTH axes explicitly matters: the
   source file ships width="64" height="64", and an SVG that escapes its CSS
   falls back to those — a 64px logo in a 12px footer row, which is what
   "broken, too big" looks like. */
.lp-foot-maker .dl-mark { width: 16px; height: 16px; display: block; flex: none; }
/* Ink follows the surrounding text, which is already theme-aware — this is
   what the two source files (light/dark) encode by hand. */
.lp-foot-maker .dl-ink { fill: currentColor; }
/* The accent is a real brand value, not a theme colour. Both source variants
   are kept: #f0562b on dark, #e04e1e on light. */
.lp-foot-maker .dl-accent { fill: #f0562b; }
:root[data-ui-theme="light"] .lp-foot-maker .dl-accent { fill: #e04e1e; }
.lp-foot-maker .ext { width: 10px; height: 10px; display: block; flex: none; opacity: .55; }
/* Wraps: this row is the ONE thing that pushed the page wider than a phone —
   382px of nowrap links overflowed a 390px viewport by exactly its padding. */
.lp-foot-links { font-family: var(--mono); font-size: 12px; color: var(--faint); display: inline-flex; flex-wrap: wrap; gap: 4px 8px; align-items: center; }
.lp-foot-links .link-btn { font-family: var(--mono); font-size: 12px; color: var(--faint); background: none; border: none; cursor: pointer; padding: 0; text-decoration: none; }
.lp-foot-links .link-btn:hover { color: var(--accent-ink); }
/* Explore page still uses the simple footer lockup. */
#explorePage .lp-foot { margin-top: 32px; padding: 34px 0 8px; display: flex; align-items: center; justify-content: space-between; gap: 18px 24px; flex-wrap: wrap; }
/* wrap:wrap is what lets .lp-foot-maker-row's width:100% drop to its own line
   under the mark + tagline rather than squeezing in beside them. */
#explorePage .lp-foot-brand { display: flex; align-items: center; gap: 11px; flex-wrap: wrap; }
#explorePage .lp-foot-meta { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }

/* ============ Landing reduced-motion + small screens. ============ */
@media (prefers-reduced-motion: reduce) {
  .home-anim { animation: none; }
  .lp-warm { animation: none; background-position: 0 0; }
  .hero-anim .hstep, .hero-new { animation: none; opacity: 1; transform: none; }
  .lp-cast-animated .lp-cast-line { animation: none; }
  .sr .lq-step, .sr .lq-cloud, .sr .lq-stamp { opacity: 1; transform: none; }
  .sr .lq-edge { stroke-dasharray: none; stroke-dashoffset: 0; }
  .lp-finale-play:hover::after { animation: none; }
  .lp-tpl-thumb svg, .hc-preview svg, .who-banner img { transition: none; }
  #homePage .home-card, .who-card, .lp-sample, .lp-btn, .lp-tbtn, .lp-coll { transition-duration: 0s; }
}
@media (max-width: 600px) {
  .lp-top-inner { padding: 0 14px; }
  /* 42px minimum hit height inside the 60px bar — comfortable thumb targets
     without growing the header. */
  .lp-tbtn { padding: 8px 10px; font-size: 12.5px; min-height: 42px; }
  .lp-brandline { gap: 8px; }
}
/* Last squeeze before 360: everything shaves a little so the primary CTA
   never leaves the screen. Sits AFTER the 600px block on purpose — same
   specificity, so source order is what decides. */
@media (max-width: 380px) {
  .lp-tbtn { font-size: 11.5px; padding: 7px 7px; gap: 5px; }
  .lp-toolbar { gap: 3px; }
  .lp-toolbar > .lp-tbtn-cta { padding-inline: 8px; }
  .lp-top-inner { padding-inline: 8px; }
}
@media (max-width: 430px) {
  .lp-brandline .wordmark { display: none; }
}
@media (max-width: 520px) {
  .lp-proof-cell { padding: 14px 16px; }
  .lp-proof-num { font-size: 21px; }
  .lp-proof-num.lp-proof-formats { font-size: 14px; }
  .lp-stage-script textarea { min-height: 116px; }
  .lp-search-go { display: none; }
  .lp-search-kbd { display: none; }
}
/* Coarse-pointer comfort: collection chips reach ~44px targets. */
@media (max-width: 760px) {
  .lp-coll { padding: 10px 15px; }
}

/* ---- Topbar document menu (New / File / Templates) --------------------
   Folds the document actions behind one trigger so the right-hand controls,
   Export especially, survive a narrow window. Panel sits above the canvas and
   well below modals (3000). */
.tb-menu { position: relative; display: inline-flex; }
.tb-menu-caret { font-size: 10px; opacity: .7; margin-left: 1px; }
.tb-menu-trigger[aria-expanded="true"] { background: var(--chrome-3); }
.tb-menu-panel {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 200;
  min-width: 172px; padding: 6px;
  background: var(--chrome-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: var(--shadow);
  display: flex; flex-direction: column; gap: 2px;
}
.tb-menu-panel[hidden] { display: none; }
.tb-menu-item {
  appearance: none; background: transparent; border: 0;
  color: var(--text); font: inherit; font-size: 13px; text-align: left;
  padding: 8px 10px; border-radius: 7px; cursor: pointer; white-space: nowrap;
  /* Icon + label on one baseline. The gap is what stops three words in a
     column reading as a list of nothing in particular. */
  display: flex; align-items: center; gap: 9px;
}
.tb-mi-ico { width: 16px; height: 16px; flex: none; color: var(--muted); }
.tb-menu-item:hover .tb-mi-ico { color: var(--accent); }
.tb-menu-item:hover { background: var(--chrome-3); }
.tb-menu-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* The title is the one elastic thing in a bar of fixed-width buttons, so it is
   what gives when the window narrows. Its WIDTHS now live with the island
   rules (`#topbar #docTitle`, which outranks these one-class media rules) —
   the old clamps here were dead and are gone rather than left to mislead.
   These blocks keep the other things that yield. */
@media (max-width: 980px) {
  #toolChip { display: none; }
}
@media (max-width: 820px) {
  .brand .wordmark { display: none; }
}

/* Below the width where the islands stop fitting, secondary buttons keep
   their icon and drop their word. (Zoom moved to the canvas dock and Help to
   the overflow menu, so neither needs hiding here any more.) */
@media (max-width: 1024px) {
  /* Between the phone squeeze and full desktop the islands are at their
     tightest: labels go, and the optional chrome (tool chip, autosave) with
     them, so the title never has to push a primary control off the bar. */
  #topbar .tb-btn > .tb-lbl { display: none; }
  #topbar #autoSaveWrap { display: none; }
  #topbar #docTitle { max-width: 190px; }
}

/* ---- Present frame guide (js/present-guide.js) ------------------------------
   A dashed box at the aspect Present and the animated exports fit into, drawn
   around the drawing so the empty screen is visible while authoring rather than
   discovered mid-presentation. Pure overlay: no pointer events, never printed,
   never exported, and hidden the moment the show starts. */
#pgFrame {
  position: absolute;
  pointer-events: none;
  z-index: 3;
  border: 1.5px dashed var(--accent-ring, rgba(139, 92, 246, 0.55));
  border-radius: 4px;
  box-shadow: 0 0 0 100vmax rgba(10, 12, 20, 0.035);   /* dims the wasted area */
}
#pgBadge {
  position: absolute;
  left: 0; bottom: 100%; margin-bottom: 6px;
  max-width: 100%;
  padding: 4px 9px;
  border-radius: 7px;
  font: 500 11.5px/1.45 var(--sans, -apple-system, sans-serif);
  white-space: normal;
  background: var(--chrome-3, #1a1d27);
  border: 1px solid var(--border, #2a2f3d);
  color: var(--muted, #8b93a7);
}
#pgBadge.good { color: #34d399; border-color: rgba(52, 211, 153, 0.45); }
#pgBadge.warn { color: #f59e0b; border-color: rgba(245, 158, 11, 0.45); }
#pgBadge.bad  { color: #f87171; border-color: rgba(248, 113, 113, 0.45); }
/* The guide is an authoring aid — the audience must never see it. */
body.presenting #pgFrame, body.recording #pgFrame { display: none !important; }
#btnPresentGuide[aria-pressed="true"] { color: var(--accent-ink, #a78bfa); }

/* ---- The Present button ---------------------------------------------------
   The one action the whole product is named for, so it stops looking like the
   twelve neutral toolbar buttons beside it and reads as the primary thing to
   press. A filled iris pill with a glass hexagon glyph: the same shape family
   as the brand mark, so it belongs to Diagramium rather than to a UI kit.

   It overrides .tb-btn deliberately — that class supplies the toolbar's quiet
   ghost treatment, which is exactly what this button must NOT have. */
.btn-present, #btnPresent.btn-present {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 14px 6px 10px;
  border-radius: 10px;
  color: #fff;
  font-weight: 650;
  letter-spacing: -0.01em;
  background: linear-gradient(150deg, var(--iris-bright) 0%, var(--iris) 48%, var(--iris-deep) 100%);
  /* Two shadows doing different jobs: a coloured glow that lifts the pill off
     the chrome, and an inset top highlight that gives the surface a curve
     instead of a flat fill. */
  box-shadow: 0 3px 12px -3px color-mix(in srgb, var(--iris) 70%, transparent),
              inset 0 1px 0 rgba(255, 255, 255, 0.28);
  transition: transform .14s cubic-bezier(.2,.8,.3,1), box-shadow .18s, filter .18s;
}
.btn-present:hover:not(:disabled), #btnPresent.btn-present:hover:not(:disabled) {
  background: linear-gradient(150deg, var(--iris-bright) 0%, var(--iris) 48%, var(--iris-deep) 100%);
  color: #fff;
  transform: translateY(-1px);
  filter: saturate(1.08) brightness(1.06);
  box-shadow: 0 6px 18px -4px color-mix(in srgb, var(--iris) 82%, transparent),
              inset 0 1px 0 rgba(255, 255, 255, 0.34);
}
.btn-present:active:not(:disabled), #btnPresent.btn-present:active:not(:disabled) {
  transform: translateY(0);
  box-shadow: 0 2px 8px -3px color-mix(in srgb, var(--iris) 70%, transparent),
              inset 0 2px 5px rgba(0, 0, 0, 0.22);
}
.btn-present:focus-visible, #btnPresent.btn-present:focus-visible {
  outline: 2px solid var(--iris-bright);
  outline-offset: 2px;
}
.btn-present:disabled, #btnPresent.btn-present:disabled { opacity: .45; box-shadow: none; transform: none; }

/* The glyph: a translucent hexagon with a solid play head, so it reads as one
   layered object rather than two stacked icons. */
.btn-present .bp-glyph { width: 17px; height: 17px; flex: none; overflow: visible; }
.btn-present .bp-hex   { fill: rgba(255, 255, 255, 0.34); }
.btn-present .bp-play  { fill: #fff; }
.btn-present .bp-label { line-height: 1.2; }
/* A slow breath on the hexagon — enough to catch the eye on an idle canvas,
   far too slow to nag. Stops entirely on hover so it never fights a click. */
@media (prefers-reduced-motion: no-preference) {
  .btn-present .bp-hex { animation: bpBreath 3.6s ease-in-out infinite; transform-origin: 12px 12px; }
  .btn-present:hover .bp-hex { animation: none; fill: rgba(255, 255, 255, 0.46); }
}
@keyframes bpBreath {
  0%, 100% { fill: rgba(255, 255, 255, 0.30); transform: scale(1); }
  50%      { fill: rgba(255, 255, 255, 0.46); transform: scale(1.06); }
}

/* The hero's copy of the button sits on a light stage bar rather than the dark
   toolbar, and carries more weight there — so it keeps the same identity at a
   slightly larger scale. `.lp-stage-present span { font-size: 10px }` used to
   shrink the old "▶" glyph; the label must not inherit that. */
.lp-stage-present.btn-present { font-size: 12.5px; padding: 7px 16px 7px 11px; border-radius: 11px; }
.lp-stage-present.btn-present .bp-glyph { width: 18px; height: 18px; }
.lp-stage-present.btn-present .bp-label { font-size: 12.5px; }

/* ---- "Present" written inline in prose -------------------------------------
   The copy refers to the feature by name a dozen times, and it was spelled with
   a bare "▶" — a glyph that renders differently on every platform and reads as
   a stray arrowhead rather than the product's headline action. This is the
   button's identity shrunk to fit a line of text: same gradient, same hexagon,
   no interaction, because it is a NOUN in a sentence and not something to
   click. Baseline-aligned so it never pushes the line height around. */
.bp-inline {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  vertical-align: baseline;
  padding: 1px 8px 2px 6px;
  margin: 0 1px;
  border-radius: 7px;
  background: linear-gradient(150deg, var(--iris-bright) 0%, var(--iris) 55%, var(--iris-deep) 100%);
  color: #fff;
  font-weight: 640;
  font-size: 0.92em;      /* relative: it has to sit in headlines and body text alike */
  line-height: 1.35;
  white-space: nowrap;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.26);
}
.bp-inline svg { width: 1em; height: 1em; flex: none; }
.bp-inline .bp-hex  { fill: rgba(255, 255, 255, 0.36); }
.bp-inline .bp-play { fill: #fff; }

/* ---- Step-0 sheet title (js/present-plus.js syncSheetTitle) ----------------
   The opening slide, drawn on the white tile itself. Everything here is inked
   for paper: near-black text, quiet bordered chips, and a PLAIN mark — the
   intro overlay's purple glow square belongs on dark chrome and reads as a
   smudge on white, so it is deliberately absent. The inner column is a fixed
   560px design that JS scales down to fit small sheets. */
#prSheetTitle { display: none; }
body.presenting #prSheetTitle:not([hidden]) {
  display: flex;
  align-items: center;
  justify-content: center;
  position: absolute;
  overflow: hidden;
  pointer-events: none;
  z-index: 4;                    /* over the sheet, under spotlight (6) and notes */
}
.pr-st-in {
  width: 560px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 10px;
  transform-origin: 50% 50%;
}
/* The tile hosts the SAME .pr-brandin animation as the splash — these are
   only the recolours white paper needs. The intro's mark ink (#cdd5e4) and
   wordmark white would both vanish on the sheet. */
.pr-st-brandin { margin-bottom: 2px; }
/* The step-zero sheet follows the Present palette too. `aurora` deliberately
   uses a dark sheet, so paper-only ink literals would make its opening title
   disappear before the diagram even begins. */
#prSheetTitle .pr-intro-mark { color: var(--pr-sheet-ink, #232838); }
#prSheetTitle .pr-brandin-word { color: var(--pr-sheet-ink, #171b26); font-size: 28px; }
#prSheetTitle .pr-bi-l.um { color: var(--iris, #6a5cff); }
/* The sheen is a white light-sweep — invisible noise on a white card. */
#prSheetTitle .pr-brandin-sheen { display: none; }
.pr-st-eyebrow {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 620;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--iris, #6a5cff);
}
.pr-st-title {
  margin: 2px 0 4px;
  font-family: var(--sans);
  font-weight: 750;
  font-size: 40px;
  line-height: 1.12;
  letter-spacing: -0.025em;
  color: var(--pr-sheet-ink, #10131c);
}
.pr-st-meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
}
.pr-st-meta span {
  padding: 5px 13px;
  border: 1px solid color-mix(in srgb, var(--pr-sheet-ink, #10131c) 16%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--pr-sheet-ink, #10131c) 7%, var(--pr-sheet, #fff));
  font-size: 12.5px;
  font-weight: 550;
  color: color-mix(in srgb, var(--pr-sheet-ink, #10131c) 74%, var(--pr-sheet, #fff));
  white-space: nowrap;
}

/* ---- Title card, recomposed as the premium opening slide -------------------
   The animations were always wired (mark draws, dot arcs, letters spring);
   what read as cheap was the composition — a 54px mark orphaned from a
   15px wordmark parked at the bottom. Now it is the FULL lockup, large, with
   the document's facts beneath it. The markup order stays untouched (the
   letter-delay chain is wired to it); flex `order` rebuilds the layout. */
#prTitleCardMark { width: 96px; height: 96px; margin-bottom: 0; order: 0; }
#prTitleCardBrand { order: 1; margin: 0 0 16px; font-size: 30px; opacity: 1; }
#prTitleCardHeader { order: 2; font-family: var(--mono); font-size: 12px; font-weight: 620; letter-spacing: 0.18em; text-transform: uppercase; }
#prTitleCardTitle { order: 3; }
/* The engagement strip rides directly under the title (the card lays out by
   flex order, so markup position alone would strand it at the top). */
#prTitleCard .eng-strip--title { order: 4; }
#prTitleCardFooter { order: 5; }

/* ---- Master mute (control bar + splash) ------------------------------------
   One switch for every Present sound. The speaker glyph swaps its waves for a
   cross via aria-pressed, so the state is visible at a glance and readable by
   assistive tech from the same attribute. */
#prMuteToggle .mu-off, #prIntroMute .mu-off { display: none; }
#prMuteToggle[aria-pressed="true"] .mu-off, #prIntroMute[aria-pressed="true"] .mu-off { display: block; }
#prMuteToggle[aria-pressed="true"] .mu-on, #prIntroMute[aria-pressed="true"] .mu-on { display: none; }
#prMuteToggle[aria-pressed="true"], #prIntroMute[aria-pressed="true"] { color: #f0546a; }
#prIntroMute { display: inline-flex; align-items: center; gap: 6px; }
#prIntroMute svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
#prIntroMute[aria-pressed="true"]:hover { color: #f0546a; }

/* Order hint in the video export options — states the fixed sequence so the
   controls read as one flow rather than five unrelated checkboxes. */
.exp-hint { margin: 6px 0 2px; font-size: 12px; line-height: 1.55; color: var(--muted); }

/* ================================================================
   Notes panel, premium layout (Present + the video export's twin)
   ----------------------------------------------------------------
   The column is a flex COLUMN with three zones: the header, a body
   that scrolls, and a bottom-anchored "Why this matters" card. The
   note used to size the panel; a long one then pushed the deep-dive
   cards off the bottom and, on the way, overlapped the control band.
   Now the note yields — it scrolls inside its own zone — and the
   card below it always keeps its place. A video has no scrollbar,
   so the export mirrors this by reserving the card's height FIRST
   and clamping the note to what is left (drawNotesPanel).
   ================================================================ */
body.presenting #prNarration {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
body.presenting #prNarrationText {
  flex: 1 1 auto;
  min-height: 0;                 /* without this a flex child refuses to shrink */
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--iris) 55%, transparent) transparent;
}
body.presenting #prNarrationText::-webkit-scrollbar { width: 7px; }
body.presenting #prNarrationText::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--iris) 45%, transparent);
  border-radius: 99px;
}
body.presenting #prNarrationText::-webkit-scrollbar-track { background: transparent; }
/* A scrolled note should look scrollable: the text fades out at the bottom
   edge rather than being guillotined mid-word. */
body.presenting #prNarrationText {
  -webkit-mask-image: linear-gradient(180deg, #000 calc(100% - 22px), transparent 100%);
  mask-image: linear-gradient(180deg, #000 calc(100% - 22px), transparent 100%);
}
body.presenting #prNarrationText:not(.pr-scrollable) {
  -webkit-mask-image: none; mask-image: none;
}
/* The deep-dive cards sit at the FOOT of the column, in reading order after
   the note, and never scroll away with it. */
/* The deep-dive cards sit at the FOOT of the column, in reading order after
   the note,
body.presenting #prNoteTech,
body.presenting #prNoteTakeaway { flex: 0 0 auto; margin-top: auto; }
body.presenting #prNoteTech,
body.presenting #prNoteTakeaway { margin-top: 8px; }
/* Premium treatment for "Why this matters": a tinted card with an accent
   rail, so it reads as commentary ABOUT the step rather than more of it. */
/* Premium treatment for "Why this matters": a tinted card with an accent
   rail{
  background: color-mix(in srgb, var(--iris) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--iris) 24%, transparent);
  border-left: 3px solid var(--iris);
  border-radius: 10px;
  padding: 2px 2px 2px 0;
}

/* Editor: the per-step "Why this matters" field, visually subordinate to the
   note it belongs to. */
.pnl-sub {
  font-size: 11px; font-weight: 700; letter-spacing: .04em;
  color: var(--iris-bright); margin: 9px 0 3px;
}
.pnl-why { min-height: 52px; opacity: .95; }

/* ---- "Why this matters": a bright insight tile, not a footnote ----------
   Amber on purpose. Everything else on the Present stage is iris/violet, so
   a warm card reads instantly as commentary ABOUT the step rather than more
   of the step — and it survives every note style, which is why the colours
   here are literals rather than currentColor. ---------------------------- */
/* ---- "Why this matters": a bright insight tile, not a footnote ----------
   Amber on purpose. Everything else on the Present stage is iris/violet, so
   a warm card reads instantly as commentary ABOUT the step rather than more
   of the step — and it survives every note style{
  background: linear-gradient(135deg,
    color-mix(in srgb, #f59e0b 22%, transparent),
    color-mix(in srgb, #fbbf24 9%, transparent));
  border: 1px solid color-mix(in srgb, #f59e0b 45%, transparent);
  border-left: 3px solid #f59e0b;
  border-radius: 12px;
  padding: 4px 4px 6px 2px;
  box-shadow: 0 6px 20px color-mix(in srgb, #f59e0b 16%, transparent),
              inset 0 1px 0 color-mix(in srgb, #fff 14%, transparent);
}

.pr-why-badge {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; flex: 0 0 24px; border-radius: 8px;
  background: linear-gradient(135deg, #f59e0b, #fbbf24);
  color: #1a1205;                       /* the glyph, dark on bright amber */
  box-shadow: 0 2px 8px color-mix(in srgb, #f59e0b 45%, transparent);
}
.pr-why-badge svg { width: 15px; height: 15px; }

/* Light note styles: the same tile, inked for paper. */
/* Light note styles: the same tile{ color: #4a3208; }

/* ---- Notes panel: the NOTE gets the room, not the trimmings -------------
   Measured on a 505px panel: the step image took ~250px and the why tile
   ~161px, leaving the note 39px to show 427px of text — a paragraph reduced
   to a scrollbar. The note is the thing being read aloud, so it wins ties.
   The image and the why tile are capped in panel-relative units; the note
   takes everything left over and only scrolls once it truly cannot fit. --- */
body.presenting #prNarrationImg {
  flex: 0 1 auto;
  max-height: 26%;
  object-fit: contain;
  min-height: 0;
}

body.presenting #prNarrationText { flex: 1 1 auto; min-height: 60px; }

/* ---- The focused shape: one animated outline ---------------------------
   The animated trace is the sole focus treatment. The former static rail,
   pulsing node shadow and DOM-lens border produced competing outlines. */
body.presenting #svgHost .pr-focus-glow {
  animation: none;
  filter: none;
}
/* The travelling segment. Brighter and heavier than it was: this ring is the
   only thing marking the narrated shape, and at 2.4px with a 4px shadow it
   read as a faint scratch rather than a lit trace — the same reason the
   export's ring had to be built up (drawFocusRing in app.js; keep the two
   in step, they are the live and recorded halves of one effect). */
body.presenting #svgHost .pr-focus-ring {
  /* One accent, thin and luminous — the Fluent-style progress stroke. Still
     SATURATED (the sheet is white paper; a pale stroke vanishes there), but
     electric indigo now and a single soft halo instead of the stacked
     violet glow. Keep in step with drawFocusRing in app.js — live and
     recorded halves of one effect. The lap duration is set INLINE by
     applyFocusGlow to the step's own estimated length, so the stroke
     travels the perimeter once per step rather than on a fixed clock. */
  stroke: var(--pr-focus, var(--iris, #4f46e5));
  stroke-width: 3;
  /* Without this the stroke is in USER units and a zoomed-out diagram draws
     the ring at half a pixel — present but invisible. */
  vector-effect: non-scaling-stroke;
  stroke-linecap: round;
  stroke-dasharray: 34 66;                 /* pathLength=100: a clear comet */
  opacity: 1;
  pointer-events: none;
  animation: prRingTrace 3.2s linear infinite;
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--pr-focus, var(--iris, #4f46e5)) 52%, transparent));
}
#stage[data-pr-sheet-tone="dark"] #svgHost .pr-focus-ring {
  stroke: color-mix(in srgb, var(--pr-focus, var(--iris-bright, #8b80ff)) 72%, #fff);
  stroke-width: 3.25;
  filter: drop-shadow(0 0 7px color-mix(in srgb, var(--pr-focus, var(--iris-bright, #8b80ff)) 60%, transparent));
}

/* Transient laptop diagram camera. A slim vertical rail is visible only while
   zoom is actively used; it changes the SVG viewBox, never this DOM layer or
   the guide/Why/title chrome around it. */
#prDesktopZoom {
  position: absolute; top: 50%; z-index: 44; transform: translateY(-50%) translateX(-7px);
  display: grid; grid-template-rows: 36px 58px 36px; place-items: center;
  width: 42px; padding: 4px 0; color: var(--pr-stage-ink, #eef2ff);
  border: 1px solid color-mix(in srgb, var(--pr-stage-ink, #eef2ff) 15%, transparent);
  border-radius: 22px; background: color-mix(in srgb, var(--pr-panel, #101a37) 88%, transparent);
  box-shadow: 0 12px 34px rgba(3,7,18,.28); backdrop-filter: blur(14px);
  opacity: 0; pointer-events: none; transition: opacity .16s ease, transform .16s ease;
}
#prDesktopZoom[hidden] { display: none; }
#prDesktopZoom.show { opacity: 1; transform: translateY(-50%); pointer-events: auto; }
#prDesktopZoom::before {
  content: ''; position: absolute; top: 36px; bottom: 36px; left: 50%; width: 1px;
  background: color-mix(in srgb, var(--pr-stage-ink, #eef2ff) 24%, transparent);
}
#prDesktopZoom button {
  position: relative; z-index: 1; display: grid; place-items: center; width: 32px; height: 32px;
  padding: 0; border: 0; border-radius: 50%; color: inherit; background: var(--pr-panel, #101a37);
  font: 650 21px/1 var(--sans); cursor: pointer;
}
#prDesktopZoom button:hover { color: #fff; background: color-mix(in srgb, var(--iris) 48%, var(--pr-panel, #101a37)); }
#prDesktopZoom button:disabled { opacity: .36; }
#prDesktopZoom #prDesktopZoomReset { width: 38px; height: 26px; border-radius: 999px; font-size: 9px; letter-spacing: -.02em; }
@media (max-width: 900px), (pointer: coarse) { #prDesktopZoom { display: none !important; } }
@keyframes prRingTrace {
  from { stroke-dashoffset: 100; }
  to   { stroke-dashoffset: 0; }
}
@media (prefers-reduced-motion: reduce) {
  body.presenting #svgHost .pr-focus-ring {
    animation: none; stroke-dasharray: none; opacity: .9;
  }
}

/* ---- Present frame: the target size, written on the frame ---------------
   The dashed box already showed the SHAPE to aim for; these show the size.
   Width along the top edge, height down the right — a marquee readout, so
   the author never has to leave the drawing to find out what to aim for. -- */
.pg-dim {
  position: absolute;
  font: 600 10.5px/1 var(--sans, system-ui), system-ui;
  letter-spacing: .04em;
  color: var(--iris-bright, #8b80ff);
  background: color-mix(in srgb, var(--bg, #0f1117) 82%, transparent);
  border: 1px solid color-mix(in srgb, var(--iris, #6a5cff) 40%, transparent);
  border-radius: 5px;
  padding: 3px 6px;
  pointer-events: none;
  white-space: nowrap;
}
.pg-dim-w { top: -11px; left: 50%; transform: translateX(-50%); }
.pg-dim-h { right: -13px; top: 50%; transform: translateY(-50%) rotate(90deg); transform-origin: center; }
.pg-dim[hidden] { display: none; }

/* ---- Panel layout, final word ------------------------------------------
   `#prNarration[data-note-style] #prNarrationText` sets flex:0 1 auto and
   out-specifies a `body.presenting #id` rule (two IDs + an attribute beats
   one ID + one class). That is why the note refused to grow and every spare
   pixel pooled BELOW the why card instead of inside the note. Matching that
   selector is the fix; raising specificity beats fighting with !important.

   The why card has since moved out of this column entirely (it floats over
   the diagram), so the note now grows all the way to the panel floor and the
   bottom padding is the panel's own — symmetric with the top by default
   rather than by arithmetic. ------------------------------------------- */
body.presenting #prNarration:not([data-note-style]) #prNarrationText,
body.presenting #prNarration[data-note-style="corporate"] #prNarrationText {
  flex: 1 1 auto;
  min-height: 72px;
}

/* ---- The step picture ---------------------------------------------------
   Two wrong answers were tried before this one. `object-fit: contain` in a
   fixed box letterboxed a square into a 16:10 slot — most of the "image" was
   empty panel showing as bars. `cover` in the same box filled it edge to edge
   by slicing a third off the top and bottom of every picture, which is worse:
   it silently deletes content.

   The box now has NO aspect of its own. `height: auto` with a max-height lets
   the element take the picture's own proportions, so nothing is cropped and
   nothing is padded — a square renders square, a wide one renders wide, and
   the only limit is how much of the column a picture may claim.
   ---------------------------------------------------------------------- */
body.presenting #prNarration[data-note-style] #prNarrationImg,
body.presenting #prNarrationImg {
  flex: 0 0 auto;
  display: block;
  width: auto;
  max-width: 100%;
  height: auto;
  max-height: 34%;
  margin-inline: auto;                  /* a fitted box is often narrower */
  object-fit: contain;                  /* no-op at the natural aspect; a guard */
  border-radius: 12px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .28);
}

/* One sentence should not cost a quarter of the panel. Tightening the tile
   is the cheapest way to make the NOTE bigger, which is the thing being read
   aloud. Three lines is enough for a "why" — anything longer is a note. */
/* One sentence should not cost a quarter of the panel. Tightening the tile
   is the cheapest way to make the NOTE bigger{ max-height: 26%; padding: 2px 3px 4px 1px; }

/* ---- Why card, with some snap ------------------------------------------
   Amber glass rather than a flat tint: a diagonal wash, a bright top edge
   that reads as light catching a raised surface, a stronger left rail, and
   a badge that glows a little. It should look like something worth reading,
   not a disclaimer at the bottom of the panel. ------------------------- */
/* ---- Why card, with some snap ------------------------------------------
   Amber glass rather than a flat tint: a diagonal wash, a bright top edge
   that reads as light catching a raised surface, a stronger left rail, and
   a badge that glows a little. It should look like something worth reading{ position: relative; overflow: hidden; }
/* A soft glare across the top-left corner — the "glass" cue. */

.pr-why-badge {
  box-shadow: 0 2px 10px color-mix(in srgb, #f59e0b 60%, transparent),
              inset 0 1px 0 color-mix(in srgb, #fff 45%, transparent);
  animation: prWhyPulse 3.4s ease-in-out infinite;
}
@keyframes prWhyPulse {
  0%, 100% { box-shadow: 0 2px 10px color-mix(in srgb, #f59e0b 45%, transparent),
                         inset 0 1px 0 color-mix(in srgb, #fff 45%, transparent); }
  50%      { box-shadow: 0 2px 16px color-mix(in srgb, #f59e0b 85%, transparent),
                         inset 0 1px 0 color-mix(in srgb, #fff 55%, transparent); }
}
@media (prefers-reduced-motion: reduce) { .pr-why-badge { animation: none; } }
/* Light note styles keep the glass, in ink that works on cream. */
/* Light note styles keep the glass{ color: #92620a; }

/* ---- No stepper, no lane ------------------------------------------------
   The 34px top lane exists only to hold the numbered dots. With the stepper
   off, keeping the lane would move the wasted space rather than reclaim it,
   so it collapses to a gutter and the sheet grows into it. The header still
   carries "Step N of M", which is what the dots were mostly saying. ------ */
body.presenting.pr-no-stepper #canvasWrap { --pr-lane-top: 10px; }

/* ============================================================
   PRESENT GEOMETRY — one grid, measured, symmetric
   ------------------------------------------------------------
   The show is:  gutter | sheet | gutter | notes | gutter
   with the sheet and the notes panel sharing the SAME top and
   bottom lane, so their tiles line up as a pair.

   What was wrong:
   • #zoomLayer carries 34px of padding, so the sheet started 34px
     below and right of the lane the notes panel was pinned to —
     the two tiles could never align, and 34px of stage was lost
     on every edge.
   • The notes panel was pinned `right: 26px` while the canvas
     reserved a FLAT 280px for it, so the two numbers disagreed:
     the sheet ran under the panel on one side and left ~37px of
     dead stage on the other.
   The reserve is now MEASURED from the panel itself (see
   reserveNotesColumn), so the two can no longer drift apart.
   ============================================================ */
body.presenting #canvasWrap {
  --pr-gutter: 18px;
  padding-left: var(--pr-gutter);
  /* padding-right is written by reserveNotesColumn() — it is the panel's real
     footprint plus one gutter, not a guess. */
  overflow: hidden;               /* the sheet always fits, so a scrollbar is
                                     only ever a stray 11px of lost width */
}
body.presenting #zoomLayer { padding: 0; }
body.presenting #prNarration.pos-right { right: var(--pr-gutter) !important; }
body.presenting #prNarration.pos-left { left: var(--pr-gutter) !important; }

/* ================================================================
   Why card: the note's OWN styling, plus one mark of importance
   ----------------------------------------------------------------
   Every note style (sleek, glass, aurora, paper, corporate, kids,
   classroom, tech) now paints the why card too — the selectors above
   list both elements, so a new style cannot dress one and forget the
   other. What used to be a hardcoded amber card is gone: a panel
   where one tile ignores the chosen theme reads as an accident.

   The card is no longer the signal. The BADGE is: a lit amber
   lightbulb sitting on whatever surface the theme provides, which
   says "this one matters" without overruling the design.
   ================================================================ */
/* ================================================================
   Why card: the note's OWN styling, plus one mark of importance
   ----------------------------------------------------------------
   Every note style (sleek, glass, aurora, paper, corporate, kids,
   classroom, tech) now paints the why card too — the selectors above
   list both elements, so a new style cannot dress one and forget the
   other. What used to be a hardcoded amber card is gone: a panel
   where one tile ignores the chosen theme reads as an accident.

   The card is no longer the signal. The BADGE is: a lit amber
   lightbulb sitting on whatever surface the theme provides{
  background: revert;
  border: revert;
  box-shadow: revert;
  padding: 10px 12px 11px;
  border-radius: inherit;
  position: relative;
  overflow: hidden;
}
   /* the old amber glare */
/* One consistent tell across every theme: a warm rail down the leading edge,
   thin enough to read as emphasis rather than as a different component. */
/* the old amber glare */
/* One consistent tell across every theme: a warm rail down the leading edge{
  border-left: 3px solid #f59e0b !important;
  box-shadow: inset 0 0 0 999px rgba(245, 158, 11, .05);
}

/* Light themes need the label darker to stay legible on cream/white. */

/* ---- Arrival: picture, note, then why — a beat apart ------------------
   One block sliding sideways reads as a panel moving. Three elements
   arriving in reading order reads as a slide being built, which is the
   premium feel: the eye is led rather than shoved. ------------------- */
/* ---- Arrival: picture, note, then why — a beat apart ------------------
   One block sliding sideways reads as a panel moving. Three elements
   arriving in reading order reads as a slide being built{
  animation: prNoteInR .6s cubic-bezier(.22, 1, .36, 1) .16s both;
}

#prNarration.pr-note-in #prNoteStep,
#prNarration.pr-note-in #prNoteTitle {
  animation: prNoteInR .5s cubic-bezier(.22, 1, .36, 1) both;
}
#prNarration.pos-left.pr-note-in #prNoteStep,
#prNarration.pos-left.pr-note-in #prNoteTitle { animation-name: prNoteInL; }
/* The stagger: title first, picture, then the words, then the why card. */
#prNarration.pr-note-in #prNoteTitle { animation-delay: .04s; }
#prNarration.pr-note-in #prNarrationText { animation-delay: .10s; }
/* The badge lights up as its card lands, so the "important" mark is the last
   thing to arrive rather than something that was always sitting there. */
#prNarration.pr-note-in .pr-why-badge {
  animation: prWhyPop .5s cubic-bezier(.34, 1.56, .64, 1) .3s both,
             prWhyPulse 3.4s ease-in-out 1s infinite;
}
@keyframes prWhyPop {
  from { opacity: 0; transform: scale(.5) rotate(-12deg); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  #prNarration.pr-note-in #prNoteStep,
  #prNarration.pr-note-in #prNoteTitle,
  #prNarration.pr-note-in .pr-why-badge { animation: prNoteFade .25s ease both; }
}

/* ---- Title-slide picture -------------------------------------------------
   One image for the whole diagram, on the opening card. Capped by VIEWPORT
   height rather than pixels so it cannot push the title off a short screen,
   and given the same rounded, shadowed treatment as the step pictures so the
   two read as the same product. ------------------------------------------ */
#prTitleCardImg {
  display: block;
  /* Same rule as the step picture: no aspect of its own, so the cover keeps
     its own proportions instead of losing its top and bottom to a 16:10 slot.

     The HEIGHT is set, not capped. `max-height` cannot make a picture bigger,
     and generated art is 448px on its long edge — so the old
     `max-height: 34vh` drew a 448px cover at 306px on a 900px screen and the
     40% of the card it was given went unused. A percentage resolves against
     the card (which is `inset: 0`, so it has a definite height) rather than
     against the viewport, which is the stage's box and not the window's.

     It stays the elastic block, same rule as the export's stack: on a card too
     short to hold picture + title + brand it is the picture that gives way. */
  width: auto;
  max-width: 86%;
  height: min(56%, 60vh);
  min-height: 0;
  flex-shrink: 1;
  object-fit: contain;
  border-radius: 16px;
  margin: 0 auto 18px;
  box-shadow: 0 22px 60px -22px rgba(0, 0, 0, .7), 0 0 0 1px rgba(255, 255, 255, .08);
  animation: prTitleImgIn .7s cubic-bezier(.22, 1, .36, 1) .12s both;
}
#prTitleCardImg[hidden] { display: none; }
/* ---- The cover BESIDE the words -----------------------------------------
   Stacked, the picture competes with the title and the brand for the card's
   HEIGHT, so `max-height: 34vh` above draws a 448px cover at ~306px on a
   900px-tall screen while more than half the card's width sits empty next to
   it. Standing them side by side spends that width instead and roughly
   doubles the picture.

   A grid, not a row of flex items, because the card's children are a flat
   list (mark, header, img, title, footer, brand) with no wrapper to group the
   text half — column 2 collects them in source order while the picture holds
   column 1 across every row.

   `pr-cover-side` is set by maybeShowTitleCard, and only on the same two
   conditions the export's card uses (a picture no wider than about 4:3, a
   landscape stage) so the show and the recording open on the same shape.

   The HEIGHT is set rather than capped on purpose: an <img> at width/height
   auto draws at its natural 448px and a `max-height` can only ever make that
   smaller. Expressing the bound in vw as well as vh is what keeps a square
   from ever needing max-width — at 40vw its width equals its height, so the
   clamp that would distort a forced-height image can never engage. */
/* Stacked, the two groups just sit in the card's own column and the gap
   between them matches the gap inside them, so the order and spacing are
   exactly what the flat list produced: mark, header, picture, title, footer,
   brand. */
#prTitleCardLead, #prTitleCardTail {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
#prTitleCard.pr-cover-side {
  display: grid;
  /* Rows spelled out. `grid-row: 1 / -1` looks like the obvious way to make
     the picture span the text, and it silently does NOT work here: -1 resolves
     against the EXPLICIT grid, and with no explicit rows that collapses to a
     single row — the picture then shares row 1 with the first text block,
     stretches it to the picture's height, and shoves everything else off the
     bottom of the card. Two named rows, one per group, and the span is
     unambiguous. */
  /* 1fr rows, not auto. The picture's height is a PERCENTAGE, and a percentage
     on a grid item resolves against its grid area — which for auto rows is
     sized by the content it is trying to be measured against, so it collapses
     to something arbitrary (it came out at 328px, smaller than the stacked
     card it was meant to beat). Two equal fractions of a card that has a
     definite height make the area definite, and the percentage means what it
     says. It also puts the row boundary on the card's centre line, which is
     what `align-self` below hangs the two text groups from. */
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: 1fr 1fr;
  align-items: center;
  align-content: center;
  justify-items: center;
  column-gap: clamp(20px, 3.5vw, 56px);
  row-gap: 10px;
}
#prTitleCard.pr-cover-side > .pr-brandin-glow { grid-area: 1 / 1; }
#prTitleCard.pr-cover-side #prTitleCardLead { grid-column: 2; grid-row: 1; align-self: end; }
#prTitleCard.pr-cover-side #prTitleCardTail { grid-column: 2; grid-row: 2; align-self: start; }
#prTitleCard.pr-cover-side #prTitleCardImg {
  grid-column: 1;
  grid-row: 1 / 3;
  height: 86%;
  width: auto;
  max-width: none;
  max-height: none;
  margin: 0;
}
@keyframes prTitleImgIn {
  from { opacity: 0; transform: scale(.94) translateY(14px); filter: blur(8px); }
  60%  { opacity: 1; filter: blur(0); }
  to   { opacity: 1; transform: none; filter: none; }
}
@media (prefers-reduced-motion: reduce) {
  #prTitleCardImg { animation: prNoteFade .25s ease both; }
}

/* ==========================================================================
   "Why this matters" — the step's insight, floated OVER the diagram
   --------------------------------------------------------------------------
   It used to sit at the foot of the notes column, where it competed with the
   note for the same few pixels and was read last if at all. Over the drawing
   it lands on the thing it is about, and the note column gets its full height
   back.

   Geometry: absolutely positioned inside #canvasWrap. An absolute child is
   laid out against the PADDING box, and while presenting the wrap's padding
   is exactly the reserve for the notes column and the subtitle strip
   (reserveNotesColumn). positionWhyNote reads that padding and writes the
   bubble's left/top itself, in the wrap's offset space — an absolute child
   resolves against the PADDING box, which is neither the sheet nor the box
   this bubble wants, so declaring insets in CSS here would only look right
   by accident.

   Theming: the tile carries its OWN data-note-style, mirrored from the panel
   by renderPresent. The rule in CLAUDE.md still holds — the why card wears
   the note style — but it can no longer be orphaned by a selector that only
   reaches inside #prNarration.
   ========================================================================== */
#prNoteWhy { display: none; }
body.presenting #prNoteWhy:not([hidden]) {
  display: flex;
  position: absolute;
  /* left/top are WRITTEN BY positionWhyNote — the bubble opens off the side
     of the step it is talking about, so its box is measured, never declared
     here. */
  z-index: 6;
  gap: 8px;
  align-items: flex-start;
  /* Shrink-wrapped and narrow: it sits BESIDE the shape now, so it has to
     leave the drawing room to breathe. The ch cap keeps the measure
     readable, the vw cap stops a long insight from crowding the diagram. */
  width: max-content;
  max-width: min(32ch, 24vw);
  padding: 9px 12px;
  border-radius: 14px;
  pointer-events: none;                 /* never steals a click from the sheet */
  /* An aside beside the shape, not a caption under it — smaller than the
     panel note on purpose. */
  font-size: calc(clamp(12px, .62vw + 4px, 18px) * var(--pr-text-k, 1));
  line-height: 1.36;
  /* Four short lines is a spoken remark; past that it is a paragraph and
     belongs in the note. */
  max-height: 34%;
  overflow: hidden;
  /* Frosted, not opaque. This tile sits ON the drawing, and a solid slab
     simply deletes whatever is under it — the diagram it is explaining. A
     light translucent surface with dark ink keeps the shapes behind it
     legible as shapes while the words stay perfectly readable, which is what
     the blur is for: it separates the two layers without hiding either.
     --pr-why-bg/--pr-why-edge feed the tail, so it stays part of the same
     piece of glass. */
  --pr-why-bg: rgba(255, 255, 255, .86);
  --pr-why-edge: rgba(15, 23, 42, .10);
  background: var(--pr-why-bg);
  color: #16202e;
  border: 1px solid var(--pr-why-edge);
  box-shadow: 0 4px 14px rgba(15, 23, 42, .10);
  -webkit-backdrop-filter: blur(9px) saturate(1.25);
  backdrop-filter: blur(9px) saturate(1.25);
}
/* Entrance: fade + a slight lift, then ONE soft highlight sweep across the
   text block. The sweep lives on .pr-why-main::after because the card's own
   pseudo-elements belong to the anchor line (see the tail comment above). */
@keyframes prWhyIn { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }
#prNoteWhy .pr-why-main { position: relative; overflow: hidden; }
body.presenting #prNoteWhy.pr-why-in .pr-why-main::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(105deg, transparent 30%, rgba(255, 255, 255, .5) 50%, transparent 70%);
  transform: translateX(-120%);
  animation: prWhySweep .9s ease .35s 1 both;
  pointer-events: none;
}
@keyframes prWhySweep { to { transform: translateX(120%); } }
@media (prefers-reduced-motion: reduce) {
  body.presenting #prNoteWhy.pr-why-in { animation: none; }
  body.presenting #prNoteWhy.pr-why-in .pr-why-main::after { content: none; }
}
/* ---- The tail ----------------------------------------------------------
   What makes it a speech bubble rather than a floating card: it points at
   the shape being explained. positionWhyNote picks WHICH EDGE the tail sits
   on (data-tail) and how far along that edge (--pr-why-tail), so the point
   lands on the shape's centre even after the bubble has been clamped into
   the sheet. Two stacked triangles — the outer one is the bubble's 1px
   outline, the inner its fill — so the tail is outlined like the rest of it.
   data-tail="none" is the honest case: nothing lit, so nothing to point at.

   ::before and ::after belong to the TAIL alone. There used to be an amber
   rail on the left edge drawn with ::before, which (a) silently overwrote the
   tail's outline triangle from a later rule and (b) sat exactly across the
   join whenever the tail was on that edge, so the point read as a detached
   arrowhead. Importance is the badge and the amber label now — do not give
   this element a third pseudo-element. */
body.presenting #prNoteWhy:not([hidden])::before,
body.presenting #prNoteWhy:not([hidden])::after {
  content: '';
  position: absolute;
}
/* The ANCHOR: a hairline from the card's facing edge toward the shape,
   ending in a small dot — an annotation's leader line, not a cartoon tail.
   ::before is the line, ::after the dot; both read --pr-why-tail for where
   along the edge the shape's centre actually is. */
body.presenting #prNoteWhy[data-tail="left"]:not([hidden])::before,
body.presenting #prNoteWhy[data-tail="right"]:not([hidden])::before {
  top: var(--pr-why-tail, 50%);
  width: 16px; height: 1px;
  background: rgba(15, 23, 42, .32);
}
body.presenting #prNoteWhy[data-tail="left"]:not([hidden])::before { right: 100%; }
body.presenting #prNoteWhy[data-tail="right"]:not([hidden])::before { left: 100%; }
body.presenting #prNoteWhy[data-tail="left"]:not([hidden])::after,
body.presenting #prNoteWhy[data-tail="right"]:not([hidden])::after {
  top: var(--pr-why-tail, 50%);
  width: 4px; height: 4px; border-radius: 50%;
  transform: translateY(-1.5px);
  background: rgba(15, 23, 42, .40);
}
body.presenting #prNoteWhy[data-tail="left"]:not([hidden])::after { right: calc(100% + 14px); }
body.presenting #prNoteWhy[data-tail="right"]:not([hidden])::after { left: calc(100% + 14px); }
body.presenting #prNoteWhy[data-tail="top"]:not([hidden])::before,
body.presenting #prNoteWhy[data-tail="bottom"]:not([hidden])::before {
  left: var(--pr-why-tail, 50%);
  width: 1px; height: 16px;
  background: rgba(15, 23, 42, .32);
}
body.presenting #prNoteWhy[data-tail="top"]:not([hidden])::before { bottom: 100%; }
body.presenting #prNoteWhy[data-tail="bottom"]:not([hidden])::before { top: 100%; }
body.presenting #prNoteWhy[data-tail="top"]:not([hidden])::after,
body.presenting #prNoteWhy[data-tail="bottom"]:not([hidden])::after {
  left: var(--pr-why-tail, 50%);
  width: 4px; height: 4px; border-radius: 50%;
  transform: translateX(-1.5px);
  background: rgba(15, 23, 42, .40);
}
body.presenting #prNoteWhy[data-tail="top"]:not([hidden])::after { bottom: calc(100% + 14px); }
body.presenting #prNoteWhy[data-tail="bottom"]:not([hidden])::after { top: calc(100% + 14px); }
/* No shape lit, no one to point at — a tail into empty space reads as a
   mistake, so the bubble simply becomes a card. Written at the SAME
   specificity as the base ::before/::after rule (body + class + id +
   attribute), because `#prNoteWhy[data-tail]::before` alone loses to it and
   the `content: none` is then silently ignored — invisible today only
   because the tail's borders happen to be 0 in that state. */
body.presenting #prNoteWhy[data-tail="none"]:not([hidden])::before,
body.presenting #prNoteWhy[data-tail="none"]:not([hidden])::after { content: none; }
#prNoteWhy .pr-why-badge {
  flex: none;
  display: grid;
  place-items: center;
  width: 1.5em; height: 1.5em;
  border-radius: .5em;
  /* Restrained amber: a tinted chip with an outlined icon, not a glowing
     gradient pill — the insight is the words, the icon just files it. */
  background: rgba(245, 158, 11, .12);
  color: #b45309;
  border: 1px solid rgba(180, 83, 9, .25);
  box-shadow: none;
}
#prNoteWhy .pr-why-badge svg { width: 1em; height: 1em; }
#prNoteWhy .pr-why-main { min-width: 0; }
#prNoteWhy .pr-why-label {
  font-size: .64em;
  font-weight: 700;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: #b45309;
  margin-bottom: .3em;
}
#prNoteWhyText { font-weight: 500; }

/* ---- The tile wears the note style ------------------------------------- */
#prNoteWhy[data-note-style="sleek"] {
  --pr-why-bg: rgba(255, 255, 255, .78);
  --pr-why-edge: rgba(90, 104, 200, .22);
  background: var(--pr-why-bg);
  border: 1px solid var(--pr-why-edge);
  color: #16202e;
}
#prNoteWhy[data-note-style="glass"] {
  --pr-why-bg: rgba(255, 255, 255, .70);
  --pr-why-edge: rgba(15, 23, 42, .12);
  background: var(--pr-why-bg);
  border: 1px solid var(--pr-why-edge);
  color: #16202e;
}
#prNoteWhy[data-note-style="aurora"] {
  --pr-why-bg: rgba(248, 246, 255, .78);
  --pr-why-edge: rgba(124, 96, 220, .24);
  background: var(--pr-why-bg);
  border: 1px solid var(--pr-why-edge);
  color: #1d1633;
}
#prNoteWhy[data-note-style="tech"] {
  --pr-why-bg: rgba(240, 253, 252, .80);
  --pr-why-edge: rgba(13, 148, 136, .26);
  background: var(--pr-why-bg);
  border: 1px solid var(--pr-why-edge);
  color: #0f2c2a;
}
/* Light themes: dark ink, and the amber label darkened to stay legible. */
#prNoteWhy[data-note-style="paper"],
#prNoteWhy[data-note-style="kids"],
#prNoteWhy[data-note-style="classroom"],
#prNoteWhy[data-note-style="corporate"] {
  --pr-why-bg: rgba(255, 255, 255, .82);
  --pr-why-edge: rgba(30, 58, 138, .20);
  background: var(--pr-why-bg);
  border: 1px solid var(--pr-why-edge);
  color: #14213d;
  border-radius: 8px;
}
#prNoteWhy .pr-why-label { color: #a16207; }
#prNoteWhy[data-note-style="paper"] {
  --pr-why-bg: rgba(253, 251, 246, .84);
  --pr-why-edge: rgba(120, 100, 70, .20);
  background: var(--pr-why-bg);
  border: 1px solid var(--pr-why-edge);
  color: #2a2318;
}
#prNoteWhy[data-note-style="kids"] {
  --pr-why-bg: rgba(255, 250, 252, .84);
  --pr-why-edge: rgba(244, 114, 182, .32);
  background: var(--pr-why-bg);
  border: 1px solid var(--pr-why-edge);
  color: #4a2540;
  border-radius: 22px;
}
#prNoteWhy[data-note-style="classroom"] {
  --pr-why-bg: rgba(247, 252, 249, .82);
  --pr-why-edge: rgba(20, 70, 50, .20);
  background: var(--pr-why-bg);
  border: 1px solid var(--pr-why-edge);
  color: #12312a;
}
#prNoteWhy[data-note-style="corporate"] {
  background: rgba(255, 255, 255, .96);
  border: 1px solid rgba(30, 58, 138, .18);
  border-radius: 8px;
}

/* ---- Arrival: it opens OUT of the shape ------------------------------
   The direction follows the tail, so the bubble always grows away from the
   thing it is pointing at rather than drifting in from somewhere unrelated. */
body.presenting #prNoteWhy.pr-why-in:not([hidden]) {
  /* One calm entrance for every direction: fade + a 7px lift, then the
     single highlight sweep (on .pr-why-main::after). The old directional
     grow read as the card inflating out of the shape — more motion than an
     annotation deserves. */
  animation: prWhyIn .32s cubic-bezier(.22, 1, .36, 1) .12s both;
}
body.presenting #prNoteWhy[data-tail="right"].pr-why-in:not([hidden]),
body.presenting #prNoteWhy[data-tail="top"].pr-why-in:not([hidden]),
body.presenting #prNoteWhy[data-tail="bottom"].pr-why-in:not([hidden]),
body.presenting #prNoteWhy[data-tail="none"].pr-why-in:not([hidden]) {
  animation-name: prWhyIn;
}
@keyframes prWhyOpenR {
  from { opacity: 0; transform: translateX(-10px) scale(.94); }
  to   { opacity: 1; transform: none; }
}
@keyframes prWhyOpenL {
  from { opacity: 0; transform: translateX(10px) scale(.94); }
  to   { opacity: 1; transform: none; }
}
@keyframes prWhyOpenD {
  from { opacity: 0; transform: translateY(-10px) scale(.94); }
  to   { opacity: 1; transform: none; }
}
@keyframes prWhyOpenU {
  from { opacity: 0; transform: translateY(10px) scale(.94); }
  to   { opacity: 1; transform: none; }
}
/* The badge lights up as the tile lands — same beat as before the move. */
body.presenting #prNoteWhy.pr-why-in .pr-why-badge {
  animation: prWhyPop .44s cubic-bezier(.34, 1.56, .64, 1) .3s both;
}
@media (prefers-reduced-motion: reduce) {
  body.presenting #prNoteWhy.pr-why-in:not([hidden]),
  body.presenting #prNoteWhy.pr-why-in .pr-why-badge {
    animation: prNoteFade .25s ease both;
    transform: none;
  }
}

/* Nowhere to sit: on a stage this narrow the sheet is a sliver and the tile
   would be a column of single words over the drawing. positionWhyNote makes
   the call, because it is the one place that has measured the real box.
   Later in the file than the display:flex rule on purpose — same specificity,
   so source order is what decides. */
body.presenting #prNoteWhy.pr-why-off:not([hidden]) { display: none; }

/* The bottom band is ONE thing to a viewer. With "Bottom subtitles" off the
   karaoke strip AND the step-name caption both stay away — the caption is a
   separate feature with its own toggle, but leaving it alone down there put a
   chip in exactly the place the user had just cleared, which reads as the
   subtitle they turned off. present-plus.js syncSubsBand owns this class. */
body.pr-no-subs #prCaption,
body.presenting.pr-no-subs #prCaption { display: none; }

/* ==========================================================================
   Narrated samples — the landing row, and the Present-only lock
   --------------------------------------------------------------------------
   Seven fully-produced tours open straight into Present from `?present=<id>`
   (js/sample-present.js). They are published at the `pro` tier, so the point
   of the lock is that a visitor can watch the whole show without being able
   to take it: no editor, no canvas, no way back except home.
   ========================================================================== */

.lp-sample-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 1rem;
}

.lp-sample {
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--border-soft);
  border-radius: 16px;
  background: var(--chrome);
  text-decoration: none;
  color: inherit;
  transition: transform .22s cubic-bezier(.22, 1, .36, 1), box-shadow .22s ease,
    border-color .18s ease;
}

.lp-sample:hover,
.lp-sample:focus-visible {
  transform: translateY(-3px);
  border-color: var(--iris-bright, #8b80ff);
  box-shadow: 0 18px 40px -18px rgba(0, 0, 0, .55);
}

/* The card art is the diagram's own title image. It only becomes visible once
   the image has actually decoded (`has-img`), so a slow catalogue shows the
   emoji rather than an empty frame. */
.lp-sample-art {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 10;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(124, 108, 248, .16), transparent 70%),
    var(--panel-2);
  overflow: hidden;
}

.lp-sample-art img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity .3s ease, transform .35s cubic-bezier(.22, 1, .36, 1);
}

.lp-sample-art.has-img img { opacity: 1; }
.lp-sample-art.has-img .lp-sample-emoji { opacity: 0; }
.lp-sample:hover .lp-sample-art.has-img img { transform: scale(1.04); }

.lp-sample-emoji {
  font-size: 2.4rem;
  line-height: 1;
  transition: opacity .25s ease;
}

/* The play badge is the whole affordance: these cards do not open an editor,
   they start a show. */
.lp-sample-play {
  position: absolute;
  top: calc(16 / 10 * 100% / 2);
  left: 50%;
  transform: translate(-50%, -50%) scale(.92);
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  background: rgba(12, 14, 22, .62);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  border: 1px solid rgba(255, 255, 255, .22);
  color: #fff;
  opacity: 0;
  transition: opacity .2s ease, transform .25s cubic-bezier(.22, 1, .36, 1);
  pointer-events: none;
}

.lp-sample-play svg { width: 1.35rem; height: 1.35rem; margin-left: 2px; }

.lp-sample:hover .lp-sample-play,
.lp-sample:focus-visible .lp-sample-play {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}

.lp-sample-body {
  display: flex;
  flex-direction: column;
  gap: .3rem;
  padding: .85rem .95rem 1rem;
  border-top: 1px solid var(--border-soft);
}

.lp-sample-name {
  font-weight: 620;
  font-size: 1rem;
  letter-spacing: -0.01em;
  color: var(--text);
  /* Two-line ceiling on both text blocks: rows of cards keep one height, so
     the grid never staggers on a long title. */
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}

.lp-sample-blurb {
  font-size: .85rem;
  line-height: 1.45;
  color: var(--muted);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}

/* ---- The lock ----------------------------------------------------------
   While a sample is playing there is no editor to reach: the canvas chrome,
   the toolbars and every save/export path are gone. sample-present.js sends
   both exits (button and Escape) home, so this is belt AND braces rather
   than styling standing in for behaviour. */
/* `.present-link` is any ?present=<id> arrival; `.sample-locked` is the subset
   that must also stay out of the editor. They were the same class, so an
   UNLOCKED share link presented inside the editor chrome — palette and
   inspector still laid out, the stage squeezed to a strip beside them, which
   is not what someone who followed a shared link came to see.
   Locking governs the EXIT (js/sample-present.js); this governs the LAYOUT,
   and every ?present= arrival wants the full-screen show. */
body.sample-locked #topbar,   body.present-link #topbar,
body.sample-locked #sidebar,  body.present-link #sidebar,
body.sample-locked #inspector, body.present-link #inspector,
body.sample-locked #hintBar,  body.present-link #hintBar,
body.sample-locked #btnExport, body.present-link #btnExport,
body.sample-locked #fileMenuBtn, body.present-link #fileMenuBtn,
body.sample-locked #prNarrationEditBtn, body.present-link #prNarrationEditBtn,
body.sample-locked #prScriptGen, body.present-link #prScriptGen { display: none !important; }
/* The two flex siblings of #stage. `body.presenting` only DIMS these
   (opacity .45, line ~2113) because presenting from inside the editor keeps
   its furniture; a shared link has no editor to return to mid-show, and
   leaving them laid out squeezed the stage to a 36px strip between a 212px
   palette and a 292px inspector. Measured, not guessed. */
body.sample-locked #palette, body.present-link #palette,
body.sample-locked #side,    body.present-link #side { display: none !important; }

/* Nothing on the canvas is interactive under the lock — a stray drag must not
   modify a diagram the visitor is not allowed to keep. */
body.sample-locked:not(.presenting) #canvasWrap { pointer-events: none; }

/* ============================================================
   /embed/<id> — Present Mode inside somebody else's page.
   ------------------------------------------------------------
   THE LOOK IS NOT REDEFINED HERE. Everything below SUBTRACTS: the site
   chrome, the editor, the account controls, the ways out. The show itself —
   the stage bezel, the notes panel, the focus ring, the title and closing
   slides, the audience skins — is the same CSS a show on diagramium.com
   runs, which is the whole point of embedding the real player instead of
   shipping a second one.

   `body.embed-mode` is the layout lock and it is NOT `sample-locked`: that
   class force-disables Loop (js/present-plus.js's finale skips looping for a
   locked sample so its return-home can fire), which would make `loop=1`
   silently do nothing. The three option classes — embed-no-controls,
   embed-brand-minimal, embed-notes-* — are set from the URL by js/embed.js.

   Everything is `display: none`, never removal: js/app.js has 67 top-level
   `$('#id').addEventListener(...)` calls that throw on a missing element, so
   an embed has to be the full document with the furniture switched off.
   ============================================================ */

/* `notes=hidden` was set on the body by js/embed.js and implemented NOWHERE —
   the class existed, the option was documented, and the panel stayed on
   screen. embed.js calls setNotesVisible(false) in applyPreEnter, but
   enterPresent runs afterwards and shows the panel again, so the JS toggle
   lost a race it could not win. Measured on a 900x520 frame: the panel came
   back at 854px wide and canvasWrap kept an 86px bottom reserve, so a
   publisher who asked for no notes still paid for them.

   Fixed in CSS for the same reason `body.pr-no-subs` hides #prCaption rather
   than chasing every code path that sets .hidden: the class is the single
   authority, and no later JS can undo it. Hiding the panel also zeroes the
   reserve for free — reserveNotesColumn measures the panel's REAL rect, and
   a display:none panel measures 0, so the padding it pays becomes 0 without
   this rule having to know the arithmetic. */
body.embed-mode.embed-notes-hidden #prNarration,
body.embed-mode.embed-notes-hidden #prWhyDock,
body.embed-mode.embed-notes-hidden #prNoteWhy { display: none !important; }

/* ---- the site: gone -------------------------------------------------- */
body.embed-mode #topbar,
body.embed-mode #palette,
body.embed-mode #side,
body.embed-mode #hintBar,
body.embed-mode #emptyState,
body.embed-mode #miLauncher,
body.embed-mode #commentPane,
body.embed-mode #commentConnectors,
body.embed-mode #homePage,
body.embed-mode #explorePage,
body.embed-mode #searchPage,
body.embed-mode #rolesPage,
body.embed-mode #toasts,
body.embed-mode .modal-backdrop,
body.embed-mode #prBrand,
body.embed-mode #pgFrame,
/* canvas-kit appends its chrome to document.body, OUTSIDE #stage, and the
   rules that normally hide it are keyed on body.presenting / body.watching.
   That is fine for a show — and wrong for the two states where the embed
   never reaches Present: the loading veil and the unavailable panel. The
   rulers were visibly framing the "This presentation is unavailable" card
   before this line existed. */
body.embed-mode #ckToolbar,
body.embed-mode #ckMenu,
body.embed-mode #ckLayers,
body.embed-mode #ckMinimap,
body.embed-mode #ckRulerH,
body.embed-mode #ckRulerV,
body.embed-mode #ckGuides,
body.embed-mode #ckOrderBar,
body.embed-mode #ckPalette,
body.embed-mode #sideExpand { display: none !important; }

/* ---- the editor, the account, the exits: gone ------------------------
   Behaviour is locked in JS too (js/embed.js no-ops exitPresent and swallows
   every accelerator), so this is the second of two locks rather than the
   only one. #prExit / #prEndEdit / #prEndExplore are the three doors that
   would drop a viewer onto a bare editor canvas holding a diagram they are
   only allowed to watch. */
/* Zen is publisher-owned in an embed. js/embed.js mounts the fixed,
   sheet-native presentation with setEmbedPresentZen; setPresentZen still
   refuses viewer toggles, so this retires the button rather than leaving a
   dead control beside it. */
body.embed-mode #prZenToggle,
body.embed-mode #prExit,
body.embed-mode #prEndEdit,
body.embed-mode #prEndExplore,
body.embed-mode #prEndQa,
body.embed-mode #prQa,
body.embed-mode #prNarrationEditBtn,
body.embed-mode #prScriptGen,
body.embed-mode #btnExport,
body.embed-mode .acct-btn,
body.embed-mode #prMore,
body.embed-mode #prPin,
body.embed-mode #prToolLaser,
body.embed-mode #prToolPen { display: none !important; }

/* Engagement is not collected in an embed (js/embed.js pins
   Privacy.allowed() false), so nothing may offer to record one either — and
   the share controls would fail silently anyway: clipboard-write and
   web-share are not delegated to a cross-origin frame. */
body.embed-mode #prLikeBtn,
body.embed-mode .eng-strip,
body.embed-mode #prEndShare,
body.embed-mode #prSharePop,
body.embed-mode #prRecordPop { display: none !important; }

/* The frame IS the viewport. No page scroll, and nothing interactive on the
   canvas until the show is up — a stray drag must not edit a diagram the
   viewer is not allowed to keep. */
body.embed-mode { overflow: hidden; }
body.embed-mode:not(.presenting) #canvasWrap { pointer-events: none; }

/* ---- controls=0 ------------------------------------------------------
   Hiding #presentBar is not enough on its own. present-plus.js's
   syncBottomBand falls back to BAND_FALLBACK (66px) whenever the bar has no
   height, and writes that to --pr-band as an INLINE custom property on
   #canvasWrap and #stage — which feeds --pr-lane-bottom, the caption offset
   and the notes panel's bottom. So a chrome-less embed would reserve a
   control bar's worth of empty stage. `!important` is what beats the inline
   value; it is the only way to win against an element style. */
body.embed-mode.embed-no-controls #presentBar,
body.embed-mode.embed-no-controls #prSegBar,
body.embed-mode.embed-no-controls #prStepper,
body.embed-mode.embed-no-controls #prProgress { display: none !important; }
body.embed-mode.embed-no-controls #canvasWrap,
body.embed-mode.embed-no-controls #stage { --pr-band: 10px !important; }

/* ---- branding=minimal -----------------------------------------------
   Quieter, never anonymous: the nameplate keeps the mark so a viewer can
   still tell what they are looking at, and the wordmark, the title-slide
   lockup and the closing sign-off step back.

   NOTE THE ONE THAT IS NOT HERE. `#prSheetTitle` is the step-0 opening
   SLIDE — eyebrow, the document's own H2 title and the meta line — of which
   `.pr-st-brandin` is only one child. Hiding the container took the
   diagram's own opening slide off screen, which is CONTENT, not branding:
   `branding=minimal` was silently deleting the title of the thing being
   presented. Only the brand lockup inside it goes. */
body.embed-mode.embed-brand-minimal .pr-tv-word,
body.embed-mode.embed-brand-minimal #prTitleCardBrand,
body.embed-mode.embed-brand-minimal #prTitleCardMark,
body.embed-mode.embed-brand-minimal #prSheetTitle .pr-st-brandin,
body.embed-mode.embed-brand-minimal .pr-end-mark,
body.embed-mode.embed-brand-minimal .pr-end-brand { display: none !important; }

/* THE THIN HEADER — the sleek treatment, applied to the nameplate.
   `branding=minimal` removed the wordmark and stopped there, so the plate
   itself stayed 45px tall with a purple gradient, a bottom border and a 22px
   mark: the single heaviest object in a 520px-tall figure, and the first
   thing a reader saw above the diagram they came for.

   Same decision as the sleek export podium — small, no filled ground, one
   brand-coloured accent left doing the identifying — but reached through
   `--pr-plate` rather than by setting a height. That variable is the top
   lane's own measurement (styles.css:677 pads the stage by it), so lowering
   it thins the plate AND hands the reclaimed pixels to the diagram in one
   move; overriding `height` alone would have shrunk the bar and left the
   gap. Measured on a 900x520 frame: 45px plate, canvas starting at y=60.

   The variable is set on #stage, NOT on body: `body.presenting #stage`
   re-declares `--pr-plate: 45px` for its own subtree, so a body-level value
   is shadowed for every descendant and the plate stays 45px while the token
   still reads 26px on body — what a custom property always does when it is
   set on the wrong ancestor, and it fails silently. Two classes plus the id
   out-specify `body.presenting #stage` without relying on source order. */
body.embed-mode.embed-brand-minimal #stage { --pr-plate: 26px; }
body.embed-mode.embed-brand-minimal #prTvHead {
  background: none;
  border-bottom: 0;
  border-radius: 0;
  padding: 0 10px;
  gap: 7px;
}
body.embed-mode.embed-brand-minimal #prTvHead svg { width: 14px; height: 14px; }
body.embed-mode.embed-brand-minimal #prTvTitle { font-size: 12px; }
body.embed-mode.embed-brand-minimal #prStepTime,
body.embed-mode.embed-brand-minimal #prTvStep { font-size: 11px; }
/* The nav pill supplies its own chrome, which reads as a second bar once the
   plate behind it is gone. Keep the buttons, drop the slab. */
body.embed-mode.embed-brand-minimal #prTvNav {
  background: none;
  height: 22px;
  padding: 0;
}
body.embed-mode.embed-brand-minimal #prTvNav button svg { width: 13px; height: 13px; }

/* ---- the unavailable state ------------------------------------------
   Built by js/embed.js and appended to #stage, following the rule that every
   Present overlay lives inside the stage so it survives full screen. Dark in
   both themes, like the rest of the Present chrome and like #watchBoot, so a
   failed embed still reads as a player rather than as a broken page. */
#embedNotice {
  position: absolute; inset: 0; z-index: 30;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 14px; padding: 28px; text-align: center;
  background: radial-gradient(60rem 40rem at 50% -10%, #171a2c, #0b0d14 70%);
  color: #e9ecff;
}
#embedNotice .embed-notice-mark { width: 52px; height: 52px; color: rgba(233, 236, 255, .82); }
#embedNotice .embed-notice-mark svg { width: 100%; height: 100%; }
#embedNotice .embed-notice-title { font-size: 17px; font-weight: 650; letter-spacing: .01em; }
#embedNotice .embed-notice-body {
  font-size: 13.5px; line-height: 1.55; max-width: 42ch;
  color: rgba(233, 236, 255, .68);
}

/* A sample that is not published yet: shown, named, and clearly not clickable
   — the row must read the same before and after the publish lands. */
.lp-sample.is-pending { cursor: default; opacity: .72; }
.lp-sample.is-pending:hover { transform: none; border-color: var(--border-soft); box-shadow: none; }
.lp-sample.is-pending .lp-sample-play { display: none; }
.lp-sample-soon {
  position: absolute;
  left: 50%;
  bottom: .7rem;
  transform: translateX(-50%);
  padding: .18rem .6rem;
  border-radius: 999px;
  font-size: .7rem;
  font-weight: 620;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: #e9e6ff;
  background: rgba(12, 14, 22, .72);
  border: 1px solid rgba(255, 255, 255, .16);
}
/* The badge now lives inside the art box, so it centres on the picture. */
.lp-sample-play { top: 50%; }
/* The body inherits the card's own --chrome; do NOT give it a background of its
   own. The trap here is INVENTED TOKENS: --panel-bg/--text-strong/--text-muted
   do not exist in this stylesheet, so `var(--panel-bg, #171a24)` silently
   serves the hardcoded fallback in BOTH themes — it looks right in dark and
   only in dark. Worse, --canvas-bg is real but it is the DRAWING SURFACE
   (#d9dcea), light in both themes, so using it for a card put light text on a
   light slab at 1.19 contrast. The real tokens are --chrome/--panel/--panel-2,
   --text/--muted/--faint, --border-soft. */

/* Hover matches the template cards: the surface lifts to --panel. */
.lp-sample:not(.is-pending):hover { background: var(--panel); }

/* ---- Explore: active-filter chips --------------------------------------
   Uses the real tokens (--chrome/--panel/--text/--muted/--border-soft); see
   the note by .lp-sample-body about invented ones. */
.mkt-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 0 12px;
}

.mkt-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px 4px 10px;
  border: 1px solid var(--border-soft);
  border-radius: 999px;
  background: var(--panel-2);
  color: var(--text);
  font-size: 12.5px;
  font-weight: 520;
  line-height: 1.3;
  cursor: pointer;
  transition: background var(--dur-2, .18s) var(--ease-out, ease),
    border-color var(--dur-2, .18s) var(--ease-out, ease);
}

.mkt-chip:hover,
.mkt-chip:focus-visible { background: var(--panel); border-color: var(--border); }

.mkt-chip-x {
  font-size: 10px;
  color: var(--faint);
  transition: color var(--dur-2, .18s) var(--ease-out, ease);
}

.mkt-chip:hover .mkt-chip-x { color: var(--text); }

/* ---- Beta badge --------------------------------------------------------
   Sits beside the wordmark everywhere the wordmark appears. Deliberately
   quiet: it is an honesty signal, not a decoration, and it should read as a
   note on the product rather than a feature of it. Uses the real tokens (see
   the note by .lp-sample-body about invented ones). */
.beta-badge {
  align-self: center;
  margin-left: .45rem;
  padding: .1rem .38rem;
  border: 1px solid var(--border-soft);
  border-radius: 5px;
  background: var(--panel-2);
  color: var(--muted);
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  line-height: 1.5;
  white-space: nowrap;
  /* The brand is a flex row; without this the badge stretches to its height. */
  flex: none;
}

/* ---- Sample card states ------------------------------------------------
   Three, and they must look different at a glance: available, sold, not ready.
   All use the real tokens (see the note by .lp-sample-body). */

/* Sold. The card stays fully legible — a locked sample only sells if you can
   see what you are missing — so this dims nothing and only adds the label. */
.lp-sample.is-locked .lp-sample-play { display: none; }
.lp-sample-lock,
.lp-sample-free {
  position: absolute;
  left: 50%;
  bottom: .7rem;
  transform: translateX(-50%);
  padding: .18rem .6rem;
  border-radius: 999px;
  font-size: .7rem;
  font-weight: 620;
  letter-spacing: .04em;
  text-transform: uppercase;
  white-space: nowrap;
  border: 1px solid transparent;
}
.lp-sample-lock {
  color: #f5f3ff;
  background: rgba(124, 108, 248, .92);
  border-color: rgba(255, 255, 255, .18);
}
.lp-sample.is-locked:hover .lp-sample-lock { background: rgba(124, 108, 248, 1); }

/* Free to watch: quieter than the paid chip on purpose — it is a reassurance,
   not a call to action. The play button is the call to action. */
.lp-sample-free {
  color: #d7ffe9;
  background: rgba(6, 42, 28, .72);
  border-color: rgba(120, 255, 200, .28);
}

/* ---- /search — one box, ranked tiles -----------------------------------
   Real tokens only (--chrome/--panel/--panel-2, --text/--muted/--faint,
   --border/--border-soft) — see the note by .lp-sample-body about invented
   ones. The hero floats at ~30vh while the box is empty and docks to the top
   the moment results exist, which is what makes it read as a search page
   rather than a form. */
#searchPage {
  /* Fixed overlay like #homePage/#explorePage — these views sit ON TOP of the
     editor, which is always mounted underneath. In normal flow the editor's
     toolbar showed above the search box, which read as the editor bleeding
     into a page that is supposed to have no chrome at all. */
  position: fixed;
  inset: 0;
  z-index: 60;
  min-height: 100vh;
  background: var(--chrome);
  color: var(--text);
  overflow-y: auto;
  overflow-x: hidden;
}

.srch-wrap {
  max-width: 860px;
  margin: 0 auto;
  padding: 30vh 20px 60px;
  display: flex;
  flex-direction: column;
  align-items: center;
  transition: padding-top .28s cubic-bezier(.22, 1, .36, 1);
}

/* With results on screen the page becomes a gallery: the wrap lets go of its
   860px reading measure and the GRID takes the whole viewport. The controls
   keep their own caps below (.srch-box 620, .srch-facets 980) so the search
   cluster stays a coherent centred column inside the wide page. */
#searchPage.has-results .srch-wrap {
  padding-top: 34px;
  max-width: none;
  padding-left: clamp(20px, 4vw, 64px);
  padding-right: clamp(20px, 4vw, 64px);
}

/* Quiet accent atmosphere behind the whole page — two soft washes of the
   signal colour falling from the top corners. color-mix against transparent
   keeps it token-driven: it follows the visitor's accent AND both themes.
   #searchPage is a fixed overlay with z-index 60 (see its base rule), which
   already makes it a stacking context — so z-index -1 here paints above the
   page's own background and under every child, and NOTHING may re-position
   the page into normal flow (that is the editor-toolbar-bleeds-through bug
   its base rule documents). */
#searchPage::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(52rem 30rem at 16% -6%, color-mix(in srgb, var(--iris) 13%, transparent), transparent 62%),
    radial-gradient(60rem 34rem at 86% -10%, color-mix(in srgb, var(--iris-bright) 9%, transparent), transparent 60%);
}

.srch-brand {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  text-decoration: none;
  color: inherit;
  margin-bottom: 26px;
}
.srch-brand .brand-mark svg { width: 34px; height: 34px; display: block; }
.srch-brand .wordmark { font-size: 24px; font-weight: 750; letter-spacing: -0.03em; }

.srch-box {
  position: relative;
  width: 100%;
  max-width: 620px;
}
.srch-glass {
  position: absolute;
  left: 18px;
  top: 50%;
  transform: translateY(-50%);
  width: 18px;
  height: 18px;
  color: var(--faint);
  pointer-events: none;
}
#srchInput {
  width: 100%;
  padding: 15px 46px 15px 48px;
  font-size: 16px;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--border-soft);
  border-radius: 999px;
  outline: none;
  box-shadow: 0 8px 28px rgba(0, 0, 0, .12);
  transition: border-color var(--dur-2, .18s) ease, box-shadow var(--dur-2, .18s) ease;
}
#srchInput:focus { border-color: var(--accent); box-shadow: 0 8px 32px rgba(0, 0, 0, .18); }
#srchInput::-webkit-search-cancel-button { display: none; }

.srch-clear {
  position: absolute;
  right: 12px;
  top: 50%;
  transform: translateY(-50%);
  width: 28px;
  height: 28px;
  border-radius: 50%;
  font-size: 16px;
  line-height: 1;
  color: var(--muted);
  display: grid;
  place-items: center;
}
.srch-clear:hover { background: var(--panel-2); color: var(--text); }

.srch-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
  margin-top: 18px;
}
.srch-chips button {
  padding: 6px 13px;
  border: 1px solid var(--border-soft);
  border-radius: 999px;
  background: var(--panel);
  color: var(--muted);
  font-size: 12.5px;
  transition: color var(--dur-2, .18s) ease, border-color var(--dur-2, .18s) ease;
}
.srch-chips button:hover { color: var(--text); border-color: var(--border); }

/* ---- quick-pick facets (Subject / Industry) ----
   Deliberately quieter and smaller than .srch-chips: those are example
   SEARCHES and want to be tried, these are FILTERS and want to be scanned.
   They stay on screen once results appear — they are the control that took
   you there and the only way to take it off again. */
.srch-facets {
  width: 100%;
  max-width: 980px;   /* the control cluster stays a centred column on the wide page */
  margin-top: 14px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.srch-facet {
  /* No caption column any more. The four rows used to spend 84px and a bold
     word each on SUBJECT / INDUSTRY / TYPE / COMPLETENESS; the values say what
     they are ("Kids & Family", "Flowchart", "Narrated"), so the words were
     paying rent for nothing. Each row still exists as its own line, which is
     what keeps the four groups visually separate without naming them. */
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 1px 0;
  text-align: center;
}
/* Kept in the DOM for screen readers — the grouping is meaningful even though
   the sighted layout no longer needs the word. */
.srch-facet-lab {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap; border: 0;
}
.srch-facet-chips {
  display: contents;
}
/* The divider between values. A real element, not a ::after on the button —
   a pseudo-element sits inside the button's box, so it would highlight on
   hover and be clickable, reading as part of the chip rather than between
   two of them. */
.srch-sep {
  color: var(--border);
  font-size: 11px;
  padding: 0 1px;
  user-select: none;
}
/* A chip never breaks mid-label — "Manufacturing & Supply Chain" wrapping onto
   two lines inside one pill reads as two filters. */
.srch-facet-chips button { white-space: nowrap; }
.srch-facet-chips button {
  padding: 2px 7px;
  border: 1px solid transparent;
  border-radius: 7px;
  background: transparent;
  /* --muted, not --faint: at 11.5px this is normal-size text and needs 4.5:1.
     --faint clears it on the dark page and lands at 3.2 on the light one,
     which is the theme nobody screenshots. */
  color: var(--muted);
  font-size: 11.5px;
  line-height: 1.5;
  transition: color var(--dur-2, .18s) ease, background var(--dur-2, .18s) ease,
              border-color var(--dur-2, .18s) ease;
}
.srch-facet-chips button:hover {
  color: var(--text);
  background: var(--panel);
  border-color: var(--border-soft);
}
/* --accent-ink is the accent shifted for use AS TEXT (lighter on the dark
   page, darker on the light one); --accent itself is a fill colour and reads
   at 3.6:1 against light paper. --accent-soft/-ring are the matching tints. */
.srch-facet-chips button.is-on {
  color: var(--accent-ink);
  background: var(--accent-soft);
  border-color: var(--accent-ring);
  font-weight: 600;
}
.srch-facet-chips button.is-on:hover { color: var(--accent-ink); }
.srch-facet-chips button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.srch-status {
  margin: 0;
  font-size: 13px;
  color: var(--muted);
}
.srch-status a { color: var(--accent); }

.srch-result-tools {
  width: 100%; margin-top: 22px; padding: 11px 13px;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  border: 1px solid var(--border-soft); border-radius: 12px;
  background: color-mix(in srgb, var(--panel) 88%, transparent);
  box-shadow: var(--shadow-sm);
}
.srch-result-tools[hidden] { display: none; }
.srch-sort { display: inline-flex; align-items: center; gap: 8px; color: var(--faint); font: 650 11px/1 var(--sans); white-space: nowrap; }
.srch-sort select {
  min-height: 34px; padding: 0 30px 0 10px; color: var(--text); background: var(--panel-2);
  border: 1px solid var(--border-soft); border-radius: 8px; font: 600 12px/1 var(--sans);
}

#searchPage.has-results .srch-facets {
  margin-top: 18px; gap: 7px; padding: 13px 15px;
  border: 1px solid var(--border-soft); border-radius: 14px;
  background: color-mix(in srgb, var(--panel) 80%, transparent);
}
#searchPage.has-results .srch-facet {
  display: grid; grid-template-columns: 108px minmax(0, 1fr); gap: 10px;
  justify-items: start; align-items: start; text-align: left;
}
#searchPage.has-results .srch-facet-lab {
  position: static; width: auto; height: auto; padding: 5px 0 0; margin: 0;
  overflow: visible; clip: auto; clip-path: none; white-space: normal;
  color: var(--faint); font: 700 10px/1.3 var(--mono); letter-spacing: .08em; text-transform: uppercase;
}
#searchPage.has-results .srch-facet-chips { display: flex; flex-wrap: wrap; align-items: center; }

.srch-grid {
  width: 100%;
  margin-top: 18px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 16px;
}

.srch-pagination {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; width: 100%; margin: 26px 0 2px;
}
.srch-pagination[hidden] { display: none; }
.srch-pagination button {
  min-width: 38px; height: 38px; padding: 0 11px; border: 1px solid var(--border-soft);
  border-radius: 9px; color: var(--muted); background: var(--panel); font: 650 12px/1 var(--sans); cursor: pointer;
}
.srch-pagination button:hover:not(:disabled) { color: var(--text); border-color: var(--accent); }
.srch-pagination button[aria-current="page"] { color: var(--on-accent); background: var(--accent); border-color: var(--accent); }
.srch-pagination button:disabled { opacity: .35; cursor: default; }

.srch-tile {
  display: flex;
  flex-direction: column;
  text-align: left;
  border: 1px solid var(--border-soft);
  border-radius: 16px;
  /* A faint top-light on the card surface — reads as depth, not colour. */
  background: linear-gradient(180deg, color-mix(in srgb, var(--panel-2) 55%, var(--panel)), var(--panel) 52%);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  padding: 0 0 14px;
  transition: transform .2s cubic-bezier(.22, 1, .36, 1), border-color .15s ease, box-shadow .2s ease;
}
.srch-tile:hover, .srch-tile:focus-visible {
  transform: translateY(-4px);
  border-color: color-mix(in srgb, var(--accent) 55%, var(--border-soft));
  box-shadow: 0 0 0 3px var(--accent-ring), var(--shadow-lg);
}
.srch-thumb svg { transition: transform .25s cubic-bezier(.22, 1, .36, 1); }
.srch-tile:hover .srch-thumb svg { transform: scale(1.045); }

.srch-thumb {
  position: relative;
  aspect-ratio: 15 / 9;
  display: grid;
  place-items: center;
  background: var(--panel-2);
  border-bottom: 1px solid var(--border-soft);
  margin-bottom: 10px;
  overflow: hidden;
}
/* One-touch Edit / Present, mirroring Explore's tile overlay. */
.srch-hover {
  position: absolute; inset: 0; z-index: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 7px;
  background: color-mix(in srgb, var(--paper) 46%, transparent); opacity: 0; transition: opacity .16s ease;
}
.srch-tile:hover .srch-hover, .srch-tile:focus-within .srch-hover { opacity: 1; }
.srch-hover button {
  font: inherit; font-size: 12.5px; font-weight: 650; cursor: pointer; padding: 7px 14px; border-radius: 9px; min-width: 106px; text-align: center;
  transform: translateY(6px); transition: transform .16s ease, filter .12s;
}
.srch-tile:hover .srch-hover button, .srch-tile:focus-within .srch-hover button { transform: translateY(0); }
.srch-edit { color: var(--on-accent); background: var(--accent); border: 1px solid var(--accent); }
.srch-edit:hover { filter: brightness(1.05); }
.srch-present { color: #241a06; background: var(--marigold); border: 1px solid var(--marigold-deep); }
.srch-present:hover { filter: brightness(1.05); }
.srch-thumb svg { width: 100%; height: 100%; padding: 8px 12px; }
.srch-thumb-mode {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--faint);
}

.srch-name {
  padding: 0 14px;
  font-size: 14.5px;
  font-weight: 620;
  letter-spacing: -0.01em;
  line-height: 1.35;
  color: var(--text);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.srch-meta {
  padding: 4px 14px 0;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--faint);
}

@media (max-width: 560px) {
  .srch-wrap { padding-top: 18vh; }
  .srch-status { align-self: center; }
  .srch-result-tools { align-items: flex-start; flex-direction: column; gap: 9px; }
  .srch-sort { width: 100%; justify-content: space-between; }
  #searchPage.has-results .srch-facet { grid-template-columns: 1fr; gap: 2px; }
  #searchPage.has-results .srch-facet-lab { padding-left: 6px; }
}

/* ---- /search: chrome + footer ------------------------------------------ */

/* Footer pinned to the bottom on short pages: the page is a flex column and
   the search column takes the slack. */
#searchPage { display: flex; flex-direction: column; }
#searchPage .srch-wrap {
  flex: 1 0 auto;
  position: relative;
  z-index: 1;
  /* The page is a flex column, and a flex child with `margin: 0 auto` gives up
     cross-axis stretch — the wrap shrink-wrapped to its widest chip and the
     result grid collapsed to one narrow column. Ask for the width back;
     max-width still caps it and the auto margins still centre it. */
  width: 100%;
  /* No min-height: the empty page must fit ONE viewport with no scrollbar.
     The wrap flexes to (viewport − footer), so the slim base bar sits at the
     bottom edge of the first screen — this was written the other way when the
     footer was the five-column grid, which deserved to be below the fold; the
     base bar does not. Results grow the wrap and the page scrolls then. */
}
#searchPage .lp-foot {
  position: relative;
  z-index: 1;
  margin-top: 0;
  /* The landing footer sits inside .home-inner, which insets it by this same
     clamp; here the footer is a bare flex child, so without it the base bar
     ran edge to edge — text hard against the viewport on both sides. */
  padding: 0 clamp(18px, 4vw, 64px) 22px;
}

.srch-home {
  position: fixed;   /* #searchPage is the fixed box; this pins to its viewport */
  top: 14px;
  left: 14px;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  color: var(--text);
  text-decoration: none;
  transition: background var(--dur-2, .18s) ease, transform var(--dur-2, .18s) ease;
}
.srch-home svg { width: 30px; height: 30px; display: block; }
.srch-home:hover { background: var(--panel-2); transform: scale(1.06); }

/* Sits beside the mark, same fixed top-left cluster — the search page's only
   other chrome. Pilled rather than iconic because "Explore" has no glyph
   everyone reads the same way, and the word is the affordance. */
.srch-explore {
  position: fixed;
  top: 14px;
  left: 66px;                    /* clears .srch-home (14 + 44 + 8 gutter) */
  z-index: 2;
  display: inline-flex;
  align-items: center;
  height: 44px;
  padding: 0 16px;
  border-radius: 12px;
  border: 1px solid var(--border-soft);
  background: var(--chrome-2);
  color: var(--text);
  font-size: 13.5px;
  font-weight: 600;
  text-decoration: none;
  transition: background var(--dur-2, .18s) ease, border-color var(--dur-2, .18s) ease;
}
.srch-explore:hover { background: var(--panel-2); border-color: var(--accent); }

/* `hidden` must always win: these carry their own display values, and an
   author display beats the UA's [hidden] rule in some engines. */
.srch-chips[hidden], .srch-grid[hidden] { display: none; }

/* The Present lockup on a light page. Present's chrome is dark in both themes
   and .pr-brandin-word hardcodes white for it — invisible here. Re-ink under
   the page's own scope; the animation itself is untouched. */
.srch-logo { margin-bottom: 26px; }
/* Bigger here than in Present's splash. The base 31px is tuned for a full-bleed
   dark stage where the wordmark is the only thing on screen; on this page it
   sits under a 74px mark with a search box beneath it, and at 31px it read as
   a caption for the logo rather than the name of the product.
   SCOPED to .srch-logo on purpose — .pr-brandin-word is shared with the Present
   intro, and resizing it there would move an animation that is timed to it. */
.srch-logo .pr-brandin-word { color: var(--text); font-size: 41px; }
/* The mark inherits .pr-intro-mark's #cdd5e4 — near-white ink for Present's
   dark chrome, a ghost on this page. Same re-ink as the word. */
.srch-logo .pr-intro-mark { color: var(--text); }
.srch-logo .pr-brandin-sheen {
  /* A white sweep cannot read over a light page — tint it with the accent,
     which carries in both themes. */
  background: linear-gradient(100deg, transparent,
    color-mix(in srgb, var(--accent) 55%, transparent), transparent);
}

/* The account chip, pinned opposite the home mark. */
.srch-acct {
  position: fixed;
  top: 14px;
  right: 14px;
  z-index: 2;
}
.srch-theme {
  position: fixed; top: 14px; right: 104px; z-index: 2; height: 44px;
  border: 1px solid var(--border-soft); background: var(--chrome-2); color: var(--text);
}
.srch-theme .glyph { font-size: 17px; line-height: 1; }
@media (max-width: 620px) {
  .srch-theme { right: 72px; width: 44px; padding: 0; }
  .srch-theme .tb-lbl { display: none; }
  #searchPage.has-results .srch-facets {
    max-height: 232px; overflow: auto; overscroll-behavior: contain;
    scrollbar-width: thin;
  }
}

/* ============================================================
   ACCENT PALETTE — the whole site's signal colour, in one place
   ------------------------------------------------------------
   111 accent usages across this stylesheet read six tokens, so re-pointing
   them repaints everything — buttons, links, focus rings, the Present
   spotlight ring, the brand dot, chart series — with no component touched.

   Chosen with <html data-accent="…"> (persisted by js/accent.js). No
   attribute = iris, the default defined at :root.

   EACH PALETTE NEEDS TWO RAMPS, and they are not the same colour lightened.
     --iris        the signal, and ALSO link/label text on a LIGHT ground —
                   so the light ramp must clear 4.5:1 on white. That is why
                   every light value here is a deep shade, never the vivid one.
     --iris-ink    the accent used as text on --paper. On the dark ground it
                   must be LIGHT; on the light ground, DARK. It is the token
                   that inverts, and the one most easily got wrong.
     --iris-bright hovers, glows, the brand dot
     --iris-deep   pressed states, gradient far end
     --iris-soft   tinted fills — near-black on dark, near-white on light
   ============================================================ */

/* ---- the picker ---- */
.accent-btn { position: relative; }
.accent-dot {
  display: inline-block; width: 13px; height: 13px; border-radius: 50%;
  background: var(--iris); box-shadow: 0 0 0 1px var(--border-soft) inset;
  vertical-align: -2px;
}
.accent-pop {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 200;
  display: grid; grid-template-columns: repeat(6, 30px); gap: 8px;
  padding: 12px; border: 1px solid var(--border);
  border-radius: 12px; background: var(--panel);
  box-shadow: 0 14px 40px rgba(0,0,0,.35);
}
.accent-pop[hidden] { display: none; }
.accent-sw {
  width: 30px; height: 30px; border-radius: 8px; padding: 0;
  border: 2px solid transparent; cursor: pointer;
  transition: transform .14s ease, border-color .14s ease;
}
.accent-sw:hover { transform: scale(1.12); }
.accent-sw[aria-pressed="true"] { border-color: var(--text); }

/* ---- "these are samples" caveat ----------------------------------------
   Stated where the diagrams are actually browsed (Explore, /search) rather
   than buried in the legal pages: a caveat nobody reads at the moment it
   matters is decoration. Quiet by design — informative, not alarming. */
.sample-note {
  margin: 0 0 14px;
  padding: 9px 12px;
  border: 1px solid var(--border-soft);
  border-left: 3px solid var(--iris);
  border-radius: 8px;
  background: var(--panel-2);
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.55;
  /* 68ch is the readable measure for a PARAGRAPH; this is a two-line notice,
     and the narrower measure only bought it two extra lines of height. */
  max-width: 88ch;
}
.sample-note strong { color: var(--text); font-weight: 570; }
.sample-note[hidden] { display: none; }
#searchPage .sample-note { margin-top: 18px; align-self: flex-start; }

/* The word "Note" as a small lead-in tag, so the block reads as a notice at a
   glance rather than as another paragraph. Inline, not floated: it has to sit
   on the first line of the sentence it introduces and wrap with it. */
.sample-note-tag {
  display: inline-block;
  margin-right: 7px;
  padding: 1px 7px;
  border-radius: 5px;
  background: var(--accent-soft);
  color: var(--accent-ink);
  font-size: 10.5px;
  font-weight: 640;
  letter-spacing: .05em;
  text-transform: uppercase;
  vertical-align: 1px;
}
/* The link that reaches the page which actually governs. It is the note's one
   action, so it gets a nudge of space rather than running into the sentence. */
.sample-note .link-btn {
  margin-left: 4px;
  color: var(--accent-ink);
  font-size: inherit;
  white-space: nowrap;
}

/* Home: the whole .lp-discover section is centred, so the note is too — and it
   sits under the grid as the section's closing line rather than above it. */
.sample-note--home {
  margin: 22px auto 0;
  text-align: left;
}

/* Visually hidden, still announced and still crawled. Used by the editor's
   route heading (#editorHeading): the toolbar has nowhere to put a visible
   title, but a page with no h1 is a page a crawler cannot categorise.
   clip-path + 1px box is the technique that survives every engine; display:none
   and visibility:hidden would remove it from the accessibility tree too. */
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap; border: 0;
}


/* ============ Editor premium chrome (2026-08) ============ */
/* Side panel collapse: width to zero, canvas takes the room (flex does the
   rest). The fixed re-opener survives the overflow clip. */
#side.collapsed { width: 0 !important; border-left: none; overflow: hidden; }
#sideCollapse {
  margin-left: auto; width: 26px; height: 26px; border-radius: 7px;
  color: var(--muted); font-size: 13px; flex: none;
}
#sideCollapse:hover { background: rgba(128, 128, 128, .15); color: var(--text); }
#sideExpand {
  position: fixed; right: 8px; top: 50%; transform: translateY(-50%);
  z-index: 30; width: 26px; height: 52px; border-radius: 8px 0 0 8px;
  background: var(--panel, rgba(128,128,128,.12)); border: 1px solid var(--border);
  border-right: none; color: var(--muted); font-size: 13px;
}
#sideExpand:hover { color: var(--text); }
body.presenting #sideExpand, body.recording #sideExpand, body.watching #sideExpand { display: none; }

/* Hand tool button active state */
#btnHand.active { background: var(--iris, #6d7cff); color: #fff; }

/* The hint bar earns a few seconds, then leaves. Pointer-events were always
   none; now the residency is temporary too. */
#hintBar.hint-fade { animation: hintFade 7s ease forwards; }
@keyframes hintFade { 0%, 78% { opacity: 1; } 100% { opacity: 0; visibility: hidden; } }
@media (prefers-reduced-motion: reduce) { #hintBar.hint-fade { animation: none; opacity: 0; visibility: hidden; } }


/* Present-order mode banner */
#ckOrderBar {
  position: fixed; z-index: 45; transform: translateX(-50%);
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px; border-radius: var(--r-2, 10px);
  background: var(--glass-bg, rgba(20, 22, 32, .9));
  -webkit-backdrop-filter: var(--glass-blur, blur(10px)); backdrop-filter: var(--glass-blur, blur(10px));
  border: 1px solid var(--border); box-shadow: var(--shadow-sm, 0 8px 24px rgba(0,0,0,.3));
  font-size: 12.5px; color: var(--text);
}
#ckOrderBar[hidden] { display: none; }
#ckOrderBar .ck-order-msg b { color: var(--iris-bright, #8b95ff); }

/* ==========================================================================
   Adaptive Step Guide layout (body.pr-notes-bottom, app.js resolvedNotesLayout)
   --------------------------------------------------------------------------
   Wide documents — timelines, roadmaps, horizontal processes — waste a tall
   side column: the panel moves to the BOTTOM as a compact horizontal band
   (illustration · step text · why insight, side by side) and the diagram
   takes the full width above it. The reserve mechanism is unchanged:
   reserveNotesColumn measures the panel's REAL rect and hands it to the
   layout as canvasWrap padding — bottom mode simply pays with paddingBottom
   instead of paddingRight. Tall or compact documents keep the side panel.
   ======================================================================== */
body.pr-notes-bottom #prNarration {
  left: var(--pr-gutter, 18px) !important;
  right: var(--pr-gutter, 18px) !important;
  top: auto !important;
  /* The same control-bar lane the side panel respects — a band pinned to the
     screen edge slid UNDER the floating dock and its last line of text
     vanished behind the buttons. */
  bottom: var(--pr-lane-bottom, calc(var(--pr-band, 66px) + 12px)) !important;
  width: auto !important;
  min-width: 0 !important;
  height: clamp(180px, 24vh, 240px);
  max-height: 28vh;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) minmax(0, 30%);
  /* FOUR rows: head / step chip / title / note. The chip and the title used
     to share row 2 with the title nudged down by margin-top — margins of
     grid items in the same cell don't separate them, so "STEP 6 OF 16"
     rendered THROUGH the title's first line. Rows, not margins. */
  grid-template-rows: auto auto auto 1fr;
  column-gap: 18px;
  align-items: start;
}
body.pr-notes-bottom #prNarrationHead { grid-column: 1 / -1; grid-row: 1; }
body.pr-notes-bottom #prNarrationImg {
  grid-column: 1; grid-row: 2 / 5;
  height: 100%; width: auto; max-width: 30vw;
  align-self: stretch; object-fit: contain;
}
body.pr-notes-bottom #prNoteStep { grid-column: 2; grid-row: 2; }
body.pr-notes-bottom #prNoteTitle { grid-column: 2; grid-row: 3; margin-top: 2px; }
body.pr-notes-bottom #prNarrationText { grid-column: 2; grid-row: 4; overflow-y: auto; max-height: 100%; }
body.pr-notes-bottom #prNoteIcon, body.pr-notes-bottom #prNoteTakeaway { grid-column: 2; grid-row: 4; align-self: end; }
body.pr-notes-bottom #prPresenterStage { display: none; }
/* "Why this matters" lives at the FOOT of the Step Guide — its ONE home in
   both layouts (the floating over-the-diagram card is retired for the live
   show). A quiet premium card: soft amber-tinted surface, hairline border,
   small outlined chip, no glow. */
#prWhyDock { display: none; }
body.presenting #prNarration:not([data-note-style]) #prWhyDock:not([hidden]),
body.presenting #prNarration[data-note-style="corporate"] #prWhyDock:not([hidden]) {
  display: block;
  margin-top: auto;              /* the flex column pushes it to the foot */
  padding: 11px 13px;
  border-radius: 12px;
  background: rgba(245, 158, 11, .06);
  border: 1px solid rgba(180, 83, 9, .16);
  font-size: 13.5px;
  line-height: 1.45;
}
#prWhyDock .pr-whyd-head {
  display: flex; align-items: center; gap: 7px;
  margin-bottom: 6px;
}
#prWhyDock .pr-whyd-chip {
  flex: none; display: grid; place-items: center;
  width: 20px; height: 20px; border-radius: 6px;
  background: rgba(245, 158, 11, .12);
  border: 1px solid rgba(180, 83, 9, .22);
  color: #b45309;
}
#prWhyDock .pr-whyd-chip svg { width: 12px; height: 12px; }
#prWhyDock .pr-why-label {
  font-size: 10px; font-weight: 700; letter-spacing: .11em;
  text-transform: uppercase; color: #b45309;
}
#prWhyDock.pr-whyd-in { animation: prWhyIn .3s cubic-bezier(.22, 1, .36, 1) both; }
@media (prefers-reduced-motion: reduce) { #prWhyDock.pr-whyd-in { animation: none; } }
/* Bottom band: the same card becomes the band's third column. */
body.pr-notes-bottom #prWhyDock:not([hidden]) {
  grid-column: 3; grid-row: 2 / 5;
  align-self: stretch;
  margin-top: 0;
  overflow-y: auto;
}
body.pr-notes-bottom #prNoteWhy { display: none !important; }

/* Next / Last stand down while the title card owns the stage. */
body.pr-title-hold #prNext, body.pr-title-hold #prLast {
  opacity: .38; pointer-events: none;
}
/* Collapsed bottom band: a slim reopen rail across the bottom. */
body.pr-notes-bottom.pr-guide-collapsed #prNarration {
  height: 40px; min-height: 40px; width: auto !important;
  grid-template-rows: auto; overflow: hidden; cursor: pointer;
}
body.pr-notes-bottom.pr-guide-collapsed .pr-nh-label { writing-mode: horizontal-tb; }
body.pr-notes-bottom.pr-guide-collapsed #prNarrationHead { flex-direction: row; }
@media (max-width: 900px) {
  body.pr-notes-bottom #prNarration { grid-template-columns: auto minmax(0, 1fr); }
  body.pr-notes-bottom #prWhyDock:not([hidden]) { display: none; }
}

/* ==========================================================================
   Present on tablets and phones
   --------------------------------------------------------------------------
   resolvedNotesLayout already answers "bottom band" for viewports ≤ 1024px;
   these rules make the chrome earn the smaller box: tighter gutters, a
   shorter band, a dock with real tap targets that sheds its specialist
   tools, and a slimmer progress strip. Rotation re-lays through the
   debounced resize hook in app.js.
   ======================================================================== */
@media (max-width: 1024px) {
  body.presenting #canvasWrap { --pr-gutter: 10px; }
  body.pr-notes-bottom #prNarration {
    height: clamp(150px, 26vh, 210px);
    column-gap: 12px;
  }
  body.presenting #prSegBar { max-width: 86vw; top: max(6px, env(safe-area-inset-top)); }
  /* 44px CSS: Apple HIG floor; 48dp Android is met by the 4px gap. */
  #presentBar .pr-btn { min-width: 44px; min-height: 44px; }
}
@media (max-width: 700px) {
  /* PHONES render the show LIGHTWEIGHT: the diagram is the content, so the
     chrome slims to a whisper — 6px gutters, no cover art on the title card
     (the words carry it), a compact nameplate without the doc title or the
     running clock, and the Step Guide starts COLLAPSED (present-plus's
     guideEffectiveCollapsed) so the sheet takes almost the whole screen.
     One tap on the rail brings the notes back. */
  body.presenting #canvasWrap { --pr-gutter: 6px; }
  body.presenting #prTitleCardImg { display: none !important; }
  body.presenting #prTvTitle, body.presenting #prTvHead #prStepTime { display: none; }
  .pr-tv-word { font-size: 13px; }
  body.presenting #prTvHead svg { width: 18px; height: 18px; }
  /* The specialist tools stand down on a phone (their hiding now lives in
     the bar-diet breakpoints above); the dock keeps transport, sound,
     voice, settings, zen and exit — the controls a viewer actually reaches
     for mid-show. */
  body.presenting #prSegBar { gap: 2px; padding: 3px 5px; }
  #prSegBar .pr-seg { min-width: 6px; }
  body.pr-notes-bottom #prNarration {
    height: clamp(132px, 30vh, 190px);
    grid-template-columns: minmax(0, 1fr);
  }
  /* body.presenting joins the selector to out-specify the panel's own
     `:not([hidden])` display rule — same trap CLAUDE.md documents for the
     notes text. */
  body.presenting.pr-notes-bottom #prNarrationImg { display: none !important; }
  #presentBar { flex-wrap: wrap; max-width: 96vw; }
}

/* Phones are touch-first players. The whole bottom control console (and any
   popover opened from it) leaves the viewport; left/right stage halves remain
   the transport, while the fixed title/counter keeps orientation. This also
   applies inside blog embeds because the iframe evaluates its own width. */
@media (max-width: 760px), (max-height: 560px) and (max-width: 1024px) {
  body.presenting #presentBar,
  body.presenting .pr-pop,
  body.presenting #prShortcuts { display: none !important; }
  body.presenting #canvasWrap,
  body.presenting #stage { --pr-band: 10px !important; }
  body.presenting #prSubtitle { bottom: max(12px, env(safe-area-inset-bottom)); }
}

/* ---- Premium supporting pane: compact, content-led ---------------------
   The diagram remains the primary pane. The guide is a genuinely supporting
   surface: one slim measured gutter, a quiet outer shell, then image → note
   → insight as one reading group. Previously the 18px canvas gap + 22px
   panel padding + 16–18px note-card padding made a 25% panel feel much wider
   than it was and stranded the insight at the far foot of a mostly empty
   column. This keeps every authored piece visible without spending pixels on
   nested frames. */
body.presenting #canvasWrap { --pr-gutter: 10px; }
body.presenting #prNarration {
  padding: clamp(12px, 1.25vw, 16px);
  border-radius: 12px;
  border-color: rgba(255, 255, 255, .12) !important;
  background:
    linear-gradient(155deg, rgba(255, 255, 255, .055), transparent 38%),
    rgba(13, 16, 28, .96) !important;
  box-shadow: -14px 18px 42px -30px rgba(0, 0, 0, .82) !important;
}
body.presenting #prNarrationHead {
  margin: 0 2px 8px;
  font-size: 11px;
}
body.presenting #prNarrationImg {
  width: 100%;
  max-width: none;
  max-height: min(28%, 190px);
  margin: 0 0 10px;
  object-fit: contain;
  background: rgba(255, 255, 255, .035);
  border-radius: 10px;
  box-shadow: none;
}
body.presenting #prNarration[data-note-style] #prNarrationText,
body.presenting #prNarrationText {
  flex: 0 1 auto;
  min-height: 0;
  max-height: 43%;
  margin: 0;
  padding: 12px 14px;
  border-radius: 10px;
}
body.presenting #prWhyDock:not([hidden]) {
  flex: 0 1 auto;
  margin-top: 10px;
  padding: 10px 12px;
  border-radius: 10px;
  box-shadow: none;
}
body.presenting #prWhyDock .pr-whyd-head { margin-bottom: 4px; }

/* ---- Cinema finish: premium present mode --------------------------------
   The stage uses one restrained night palette. The diagram remains paper and
   therefore the brightest, clearest object; controls and guidance sit one
   depth behind it. Motion is tied to entry/step changes, never a constant
   peripheral spectacle, so it adds anticipation without competing with the
   diagram or narration. Audience skins and Zen deliberately keep their own
   visual language. */
body.presenting:not(.pr-zen):not(.pr-aud-kids):not(.pr-aud-tween) #stage {
  background:
    radial-gradient(900px 520px at 12% -18%, rgba(111, 82, 255, .22), transparent 62%),
    radial-gradient(720px 440px at 104% 110%, rgba(41, 204, 189, .12), transparent 66%),
    linear-gradient(145deg, #090b15 0%, #111225 52%, #080a12 100%);
  background-size: 112% 112%, 118% 118%, 100% 100%;
  animation: pr-stage-aura 18s ease-in-out infinite alternate;
}
@keyframes pr-stage-aura {
  from { background-position: 0% 0%, 100% 100%, 0 0; }
  to { background-position: 8% 5%, 92% 94%, 0 0; }
}
body.presenting:not(.pr-zen) #prTvHead {
  background:
    linear-gradient(90deg, rgba(124, 108, 248, .22), rgba(23, 26, 47, .74) 43%, rgba(56, 205, 190, .10)),
    rgba(10, 12, 23, .82);
  border-color: rgba(164, 151, 255, .30);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .08) inset, 0 12px 30px -24px #000;
}
body.presenting #prTvTitle {
  font-size: clamp(12.5px, 1.05vw, 15px);
  font-weight: 580;
  color: rgba(245, 247, 255, .82);
}
body.presenting #prTvStep {
  font-size: 12px;
  color: rgba(255, 255, 255, .92);
}
/* The authored title is still the document's title, just presented with a
   deliberate light surface rather than the flat grey pill that read as an
   editor control. This applies only while watching; exported/editing SVGs
   retain their authored appearance. */
body.presenting:not(.pr-zen):not(.pr-aud-kids):not(.pr-aud-tween)
  #svgHost g[data-role="doc-title"] rect {
  fill: #f7f5ff !important;
  stroke: rgba(100, 78, 216, .38) !important;
  stroke-width: 1.25 !important;
}
body.presenting:not(.pr-zen) #svgHost g[data-role="doc-title"] text {
  font-weight: 800;
  letter-spacing: -.018em;
}
body.presenting #prNarration {
  color: #f2f4ff !important;
  background:
    linear-gradient(155deg, rgba(158, 145, 255, .13), transparent 34%),
    linear-gradient(180deg, rgba(20, 23, 42, .98), rgba(10, 12, 23, .98)) !important;
  border-color: rgba(190, 183, 255, .17) !important;
}
body.presenting #prNoteStep:not([hidden]) {
  color: #bcb2ff;
  font-size: 10px;
  letter-spacing: .16em;
  margin-bottom: 8px;
}
body.presenting #prNoteTitle:not([hidden]) {
  font-size: clamp(18px, 1.45vw, 22px);
  line-height: 1.18;
  letter-spacing: -.022em;
  margin-bottom: 9px;
}
body.presenting #prNarrationImg {
  background:
    radial-gradient(circle at 50% 0%, rgba(150, 137, 255, .16), transparent 65%),
    rgba(4, 6, 15, .62);
  border: 1px solid rgba(207, 201, 255, .14);
  box-shadow: 0 12px 28px -22px #000;
}
body.presenting #prNarration[data-note-style] #prNarrationText,
body.presenting #prNarrationText {
  font-size: calc(17px * var(--pr-text-k, 1));
  line-height: 1.58;
  color: #edf0ff;
  background: rgba(255, 255, 255, .055);
  border: 1px solid rgba(255, 255, 255, .08);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .045);
}
body.presenting #prWhyDock:not([hidden]) {
  background: linear-gradient(135deg, rgba(245, 158, 11, .17), rgba(245, 158, 11, .055));
  border-color: rgba(251, 191, 36, .28);
  color: #fff3d6;
}
body.presenting #prWhyDock.pr-whyd-in {
  animation: pr-why-arrive .42s cubic-bezier(.22, 1, .36, 1) both;
}
@keyframes pr-why-arrive {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}
/* One coordinated beat when the panel receives a new step: the visual moves
   first, then the reading surfaces settle. Existing entrance classes are
   preserved; this simply gives their final state more depth. */
body.presenting #prNarration.pr-note-in #prNarrationImg {
  animation: pr-note-media-arrive .52s cubic-bezier(.22, 1, .36, 1) both;
}
@keyframes pr-note-media-arrive {
  from { opacity: 0; transform: translateY(10px) scale(.985); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  body.presenting:not(.pr-zen):not(.pr-aud-kids):not(.pr-aud-tween) #stage { animation: none; }
  body.presenting #prWhyDock.pr-whyd-in,
  body.presenting #prNarration.pr-note-in #prNarrationImg { animation: none; }
}

/* ---- Visual Step Guide -------------------------------------------------
   The guide is not a second narration document. It is a compact visual
   companion to the diagram: identify the current step, show its image, then
   give the step number and the one insight worth carrying forward. Matching
   the media width for image, counter and insight makes this a composed card
   rather than a vertical list of unrelated panels. */
body.presenting #prNarration {
  --pr-guide-media-w: min(100%, 340px);
  align-items: stretch;
  padding: 8px;
  border-radius: 10px;
  color: var(--pr-sheet-ink) !important;
  background: var(--pr-sheet) !important;
  border-color: color-mix(in srgb, var(--pr-sheet-ink) 8%, transparent) !important;
  box-shadow: -8px 12px 28px -26px rgba(21, 24, 40, .32) !important;
}
body.presenting #prNarrationHead,
body.presenting #prNoteIcon,
body.presenting #prNoteTakeaway,
body.presenting #prNoteTech { display: none !important; }
body.presenting #prNoteTitle:not([hidden]) {
  order: 1;
  display: block;
  margin: 2px 2px 12px;
  color: var(--pr-sheet-ink);
  font: 760 clamp(18px, 1.5vw, 23px)/1.15 var(--sans);
  letter-spacing: -.028em;
}
body.presenting #prNarrationImg:not([hidden]) {
  order: 2;
  align-self: center;
  width: var(--pr-guide-media-w);
  max-width: var(--pr-guide-media-w);
  max-height: min(34%, 230px);
  margin: 0 0 8px;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 12px;
  box-shadow: 0 14px 28px -24px rgba(25, 31, 49, .48);
}
body.presenting #prNoteStep:not([hidden]) {
  order: 3;
  align-self: center;
  box-sizing: border-box;
  width: var(--pr-guide-media-w);
  margin: 0;
  padding: 7px 10px 8px;
  border-top: 1px solid rgba(94, 76, 192, .20);
  color: #5a48ba;
  font: 750 10px/1.1 var(--sans);
  letter-spacing: .13em;
  text-align: center;
  text-transform: uppercase;
}
/* The authored note is the explanation, not optional decoration. Keep it in
   the same measured column as the image and counter, with a quiet reading
   surface rather than the old nested card and its wasted border space. */
body.presenting #prNarration #prNarrationText {
  order: 4;
  display: block !important;
  align-self: center;
  box-sizing: border-box;
  width: var(--pr-guide-media-w);
  max-height: min(32%, 240px);
  margin: 2px 0 0;
  padding: 0 2px;
  overflow-y: auto;
  color: var(--pr-sheet-ink) !important;
  background: transparent !important;
  border: 0 !important;
  border-radius: 0;
  box-shadow: none !important;
  font-size: calc(15px * var(--pr-text-k, 1));
  line-height: 1.48;
}
/* The step title earns a gentle header of its own, so the guide reads as one
   composed panel: current step → image → explanation → takeaway. */
body.presenting #prNarration #prNoteTitle:not([hidden]) {
  align-self: stretch;
  box-sizing: border-box;
  width: auto;
  margin: -8px -8px 12px;
  padding: 11px 14px;
  color: var(--pr-sheet-ink);
  background: color-mix(in srgb, var(--iris) 5%, var(--pr-sheet));
  border: 0;
  border-bottom: 1px solid color-mix(in srgb, var(--iris) 12%, transparent);
  border-radius: 9px 9px 0 0;
  box-shadow: none;
}
body.presenting #prWhyDock:not([hidden]) {
  order: 5;
  align-self: center;
  box-sizing: border-box;
  width: var(--pr-guide-media-w);
  margin: 12px 0 0;
  padding: 11px 12px 12px;
  color: #49330c;
  background: linear-gradient(135deg, #fff7df, #fff0c8);
  border: 1px solid rgba(211, 150, 31, .32);
  border-left: 3px solid #d89822;
  border-radius: 0 11px 11px 0;
  box-shadow: 0 10px 22px -20px rgba(119, 75, 8, .52);
}
/* Out-specifies the legacy "push to the foot" rule: the insight belongs
   directly below its note, never stranded at the bottom of an empty panel. */
body.presenting #prNarration #prWhyDock:not([hidden]) { margin-top: 10px; }
body.presenting #prWhyDock .pr-whyd-head { margin-bottom: 5px; }
body.presenting #prWhyDock .pr-why-label { color: #9a6511; }
body.presenting #prWhyDock .pr-whyd-chip {
  width: 19px;
  height: 19px;
  color: #7e4d04;
  background: rgba(216, 152, 34, .16);
  border-color: rgba(190, 122, 16, .25);
}
/* The normal title bar uses the actual sheet palette. Its border and the
   violet step control provide the hierarchy; no dark band sits above a light
   diagram anymore. */
body.presenting:not(.pr-zen) #prTvHead {
  color: var(--pr-sheet-ink);
  background: var(--pr-sheet) !important;
  border-color: color-mix(in srgb, var(--iris) 32%, transparent);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .72) inset, 0 8px 22px -22px rgba(21, 24, 40, .6);
}
body.presenting:not(.pr-zen) .pr-tv-word { color: var(--pr-sheet-ink); }
body.presenting:not(.pr-zen) #prTvTitle { color: color-mix(in srgb, var(--pr-sheet-ink) 67%, transparent); }
body.presenting:not(.pr-zen) #prTvTitle:not(:empty)::before { color: color-mix(in srgb, var(--pr-sheet-ink) 30%, transparent); }
body.presenting:not(.pr-zen) #prStepTime { color: color-mix(in srgb, var(--pr-sheet-ink) 62%, transparent); }
body.presenting:not(.pr-zen) #prTvNav {
  background: color-mix(in srgb, var(--pr-sheet) 84%, var(--iris));
  border-color: color-mix(in srgb, var(--pr-sheet-ink) 13%, transparent);
}
body.presenting:not(.pr-zen) #prTvStep,
body.presenting:not(.pr-zen) #prTvNav button { color: var(--pr-sheet-ink); }

/* ---- Engagement strip on the Present nameplate (.eng-strip--present) ----
   Views · watch time · like, riding the dark TV chrome during a show — same
   element the tiles use, so counts and the like state stay one system. */
#prTvHead .eng-strip--present {
  margin: 0 10px 0 auto;
  padding: 0;
  border-top: 0;
  font-size: 11.5px;
  color: rgba(233, 236, 255, .62);
  gap: 9px;
}
#prTvHead .eng-strip--present .eng-like,
#prTvHead .eng-strip--present .eng-share {
  color: rgba(233, 236, 255, .62);
  margin-left: 0;
}
#prTvHead .eng-strip--present .eng-like:hover,
#prTvHead .eng-strip--present .eng-share:hover {
  color: #fff;
  background: rgba(233, 236, 255, .1);
}
#prTvHead .eng-strip--present .eng-like.is-on { color: var(--iris-bright, #8b80ff); }
@media (max-width: 700px) {
  /* Phones keep the strip but shed the share button — the end slide carries
     sharing, and the slim nameplate has exactly this much room. */
  #prTvHead .eng-strip--present .eng-share { display: none; }
  #prTvHead .eng-strip--present { font-size: 11px; gap: 7px; }
}


/* ---- Engagement on the TITLE SLIDE (.eng-strip--title) ------------------
   Views · watch time · like · share, centered right under the heading — the
   same live element the tiles carry, dressed for the card's light ground.
   The dock's own thumb (#prLikeBtn) keeps liking one tap away for the whole
   run on every device. */
#prTitleCard .eng-strip--title {
  margin: 14px 0 2px;
  padding: 6px 14px;
  border-top: 0;
  border-radius: 999px;
  background: color-mix(in srgb, var(--pr-sheet-ink, currentColor) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--pr-sheet-ink, currentColor) 14%, transparent);
  justify-content: center;
  align-self: center;
  width: max-content;
  font-size: 14.5px;
  font-weight: 600;
  color: color-mix(in srgb, var(--pr-sheet-ink, currentColor) 76%, transparent);
  gap: 16px;
}
#prTitleCard .eng-strip--title svg { width: 17px; height: 17px; }
#prTitleCard .eng-strip--title .eng-like,
#prTitleCard .eng-strip--title .eng-share {
  margin-left: 0;
  color: color-mix(in srgb, var(--pr-sheet-ink, currentColor) 68%, transparent);
  pointer-events: auto;
}
#prTitleCard .eng-strip--title .eng-like:hover,
#prTitleCard .eng-strip--title .eng-share:hover {
  color: var(--pr-sheet-ink, currentColor);
  background: color-mix(in srgb, var(--pr-sheet-ink, currentColor) 11%, transparent);
}
#prTitleCard .eng-strip--title .eng-like.is-on { color: var(--iris-bright, var(--iris-deep, #5546e6)); }

#prLikeBtn .pr-like-count { font-size: 10.5px; font-weight: 700; margin-left: 2px; }
#prLikeBtn.is-on { color: var(--iris-bright, #8b80ff); }
#prLikeBtn.is-on svg { fill: color-mix(in srgb, var(--iris-bright, #8b80ff) 25%, transparent); }

/* ============================================================
   Diagrams by role  (/roles)  — js/roles.js + js/role-profiles.js
   ------------------------------------------------------------
   Tokens only, and only ones that EXIST: --chrome / --panel / --panel-2,
   --text / --muted / --faint, --border / --border-soft, --accent, --iris-*.
   An unknown custom property fails silently to its fallback and looks perfect
   in dark while staying wrong in light, which is the trap CLAUDE.md documents.
   Both themes are exercised here; the light one is the one nobody screenshots.
   ============================================================ */
#rolesPage {
  position: fixed; inset: 0; z-index: 60; overflow-y: auto; overscroll-behavior: contain;
  background: var(--chrome); color: var(--text);
}
/* One bar. Free-floating fixed controls let the sticky rail scroll underneath
   them, which read as chips sliding behind the account button. */
.roles-topbar {
  position: sticky; top: 0; z-index: 4; display: flex; align-items: center; gap: 14px;
  height: 56px; padding: 0 20px;
  background: var(--chrome); border-bottom: 1px solid var(--border-soft);
}
.roles-home {
  flex: 0 0 auto; width: 30px; height: 30px; display: grid; place-items: center;
  color: var(--text); opacity: .82; transition: opacity .18s ease;
}
.roles-home:hover { opacity: 1; }
.roles-home svg { width: 100%; height: 100%; }
.roles-explore {
  font-size: 13px; font-weight: 550; color: var(--muted);
  text-decoration: none; letter-spacing: -0.01em;
}
.roles-explore:hover { color: var(--text); }
.roles-acct { margin-left: auto; }

/* ---- hero ---- */
.roles-hero { max-width: 780px; margin: 0 auto; padding: 54px 24px 26px; text-align: center; }
.roles-eyebrow {
  display: inline-block; font-family: var(--mono, ui-monospace, monospace);
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--accent); margin-bottom: 14px;
}
.roles-title {
  margin: 0 0 12px; font-size: clamp(28px, 4.6vw, 44px); line-height: 1.08;
  letter-spacing: -0.028em; font-weight: 620; color: var(--text);
}
.roles-sublede { margin: 0 auto; max-width: 620px; font-size: 15.5px; line-height: 1.62; color: var(--muted); }

/* ---- role picker: a searchable combobox ----
   A dropdown rather than a scrolling chip rail — the role list is meant to
   grow, and a horizontal scroller hides everything past the fourth item. */
.roles-pickwrap {
  position: sticky; top: 56px; z-index: 3;
  display: flex; align-items: center; justify-content: center; gap: 12px;
  padding: 14px 24px 14px;
  background: color-mix(in srgb, var(--chrome) 94%, transparent);
  backdrop-filter: blur(10px); border-bottom: 1px solid var(--border-soft);
}
.roles-pick { position: relative; width: min(460px, 100%); }
.roles-pick-emoji {
  position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
  font-size: 17px; line-height: 1; pointer-events: none;
}
.roles-pick-input {
  width: 100%; box-sizing: border-box; padding: 11px 38px 11px 40px;
  border-radius: 11px; border: 1px solid var(--border);
  background: var(--panel); color: var(--text);
  font: inherit; font-size: 14.5px; font-weight: 540; letter-spacing: -0.012em;
  transition: border-color .16s ease, background .16s ease;
}
.roles-pick-input::placeholder { color: var(--faint); font-weight: 450; }
.roles-pick-input:focus { outline: none; border-color: var(--accent); background: var(--panel-2); }
.roles-pick.open .roles-pick-input { border-color: var(--accent); border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.roles-pick-caret {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  width: 24px; height: 24px; display: grid; place-items: center;
  background: none; border: 0; cursor: pointer; color: var(--faint); padding: 0;
}
.roles-pick-caret svg { width: 18px; height: 18px; transition: transform .18s ease; }
.roles-pick.open .roles-pick-caret svg { transform: rotate(180deg); }

.roles-pick-list {
  position: absolute; left: 0; right: 0; top: calc(100% - 1px); z-index: 5;
  margin: 0; padding: 5px; list-style: none; max-height: 330px; overflow-y: auto;
  background: var(--panel); border: 1px solid var(--accent); border-top: 0;
  border-radius: 0 0 11px 11px; box-shadow: 0 18px 40px rgba(0,0,0,.28);
}
.roles-pick-opt {
  display: flex; align-items: flex-start; gap: 10px; padding: 9px 10px;
  border-radius: 8px; cursor: pointer;
}
.roles-pick-opt.active, .roles-pick-opt:hover { background: var(--panel-2); }
.roles-pick-opt.current { box-shadow: inset 2px 0 0 var(--accent); }
.roles-pick-oe { font-size: 16px; line-height: 1.3; flex: 0 0 auto; }
.roles-pick-ot { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.roles-pick-on { font-size: 13.5px; font-weight: 560; color: var(--text); }
.roles-pick-od { font-size: 12px; color: var(--muted); line-height: 1.4; }
.roles-pick-empty { padding: 14px 10px; font-size: 13px; color: var(--faint); text-align: center; }
.roles-pick-count { font-size: 12px; color: var(--faint); white-space: nowrap; }

/* ---- body ---- */
.roles-body { max-width: 1180px; margin: 0 auto; padding: 34px 24px 96px; }

/* identity */
.roles-identity { display: flex; align-items: flex-start; gap: 18px; margin-bottom: 20px; }
.roles-id-mark {
  flex: 0 0 auto; width: 58px; height: 58px; display: grid; place-items: center;
  font-size: 30px; line-height: 1; border-radius: 16px;
  background: var(--panel-2); border: 1px solid var(--border-soft);
}
.roles-id-name { margin: 2px 0 4px; font-size: 27px; font-weight: 620; letter-spacing: -0.024em; color: var(--text); }
.roles-id-tag { margin: 0; font-size: 15px; color: var(--muted); line-height: 1.5; }
.roles-also { margin-top: 7px; font-size: 12.5px; color: var(--faint); }

.roles-measured {
  position: relative; margin: 0 0 22px; padding: 16px 18px 16px 20px;
  border-left: 3px solid var(--accent); border-radius: 0 12px 12px 0;
  background: var(--panel);
}
.roles-measured-label {
  display: block; font-family: var(--mono, ui-monospace, monospace); font-size: 10px;
  letter-spacing: .13em; text-transform: uppercase; color: var(--accent); margin-bottom: 5px;
}
.roles-measured p { margin: 0; font-size: 15.5px; line-height: 1.6; color: var(--text); }

.roles-inds { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; margin-bottom: 40px; }
.roles-sub { font-size: 12px; color: var(--faint); margin-right: 3px; }
.roles-ind {
  font-size: 12px; padding: 4px 10px; border-radius: 999px;
  background: var(--panel-2); border: 1px solid var(--border-soft); color: var(--muted);
}

/* sections */
.roles-sec { margin-bottom: 46px; }
.roles-h2 { margin: 0 0 5px; font-size: 19px; font-weight: 600; letter-spacing: -0.02em; color: var(--text); }
.roles-lede { margin: 0 0 18px; font-size: 13.5px; color: var(--muted); }

/* ceremonies */
.roles-cers { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.roles-cer {
  padding: 15px 17px; border-radius: 12px;
  background: var(--panel); border: 1px solid var(--border-soft);
}
.roles-cer-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 5px; }
.roles-cer-name { font-size: 14.5px; font-weight: 570; color: var(--text); }
.roles-cadence {
  font-family: var(--mono, ui-monospace, monospace); font-size: 10.5px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--accent);
  padding: 3px 8px; border-radius: 999px; background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.roles-cer-purpose { margin: 0 0 7px; font-size: 13.5px; line-height: 1.55; color: var(--muted); }
.roles-cer-brings { margin: 0; font-size: 13px; line-height: 1.5; color: var(--text); opacity: .92; }
.roles-cer-brings span { color: var(--accent); margin-right: 4px; }

/* phases */
.roles-phases {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(178px, 1fr)); gap: 10px;
}
.roles-phase {
  position: relative; display: flex; flex-direction: column; gap: 5px;
  padding: 15px 16px; border-radius: 12px;
  background: var(--panel); border: 1px solid var(--border-soft);
}
.roles-phase-n {
  width: 22px; height: 22px; display: grid; place-items: center; border-radius: 999px;
  font-size: 11px; font-weight: 640; color: var(--on-signal, #fff); background: var(--accent);
}
.roles-phase-name { font-size: 14px; font-weight: 570; color: var(--text); }
.roles-phase-what { font-size: 12.5px; line-height: 1.5; color: var(--muted); }
.roles-phase-draws { font-size: 12px; line-height: 1.5; color: var(--faint); border-top: 1px solid var(--border-soft); padding-top: 7px; margin-top: 2px; }

/* the diagram cards */
.roles-needs { display: grid; grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); gap: 12px; }
.roles-need {
  display: flex; flex-direction: column; gap: 7px; text-align: left; cursor: pointer;
  padding: 16px 17px 14px; border-radius: 13px;
  background: var(--panel); border: 1px solid var(--border-soft); color: inherit;
  font: inherit; transition: border-color .16s ease, background .16s ease, transform .16s ease;
}
.roles-need:hover { border-color: var(--accent); background: var(--panel-2); transform: translateY(-2px); }
.roles-need-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.roles-mode {
  font-family: var(--mono, ui-monospace, monospace); font-size: 10px; letter-spacing: .09em;
  text-transform: uppercase; color: var(--accent);
}
.roles-freq { font-size: 11px; color: var(--faint); }
.roles-need-title { margin: 0; font-size: 14.5px; font-weight: 575; line-height: 1.35; letter-spacing: -0.012em; color: var(--text); }
.roles-need-why { margin: 0; font-size: 12.8px; line-height: 1.52; color: var(--muted); flex: 1 1 auto; }
.roles-need-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  margin-top: 5px; padding-top: 10px; border-top: 1px solid var(--border-soft);
}
.roles-cta { font-size: 12.5px; font-weight: 550; color: var(--muted); }
.roles-need:hover .roles-cta { color: var(--text); }
.roles-cta.ready { color: var(--accent); }
.roles-badge {
  font-size: 10px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
  padding: 3px 7px; border-radius: 999px;
  color: var(--accent); background: color-mix(in srgb, var(--accent) 13%, transparent);
}

@media (max-width: 640px) {
  .roles-hero { padding: 40px 18px 20px; }
  .roles-body { padding: 26px 16px 72px; }
  .roles-pickwrap { padding: 12px 16px; }
  .roles-pick-count { display: none; }
  .roles-identity { gap: 13px; }
  .roles-id-mark { width: 48px; height: 48px; font-size: 25px; }
  .roles-id-name { font-size: 22px; }
  .roles-needs { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
  .roles-need, .roles-home, .roles-pick-input, .roles-pick-caret svg { transition: none; }
  .roles-need:hover { transform: none; }
}
/* The by-role entry sits in the subject row but is a different KIND of browse
   — job, not topic — so it carries the accent rather than blending in. */
.lp-coll-roles { border-color: color-mix(in srgb, var(--accent) 45%, transparent); color: var(--text); }
.lp-coll-roles:hover { border-color: var(--accent); }
/* The sample sketch on a role card. A real template shows its own icon_svg; an
   unbuilt one shows the SHAPE of its diagram type, which is what answers
   "what am I about to get?" before the click. */
.roles-thumb {
  display: block; margin: -4px -5px 9px; border-radius: 9px; overflow: hidden;
  background: var(--panel-2); border: 1px solid var(--border-soft);
  aspect-ratio: 150 / 90;
}
.roles-thumb-svg { width: 100%; height: 100%; display: block; padding: 9px 11px; box-sizing: border-box; }
/* Sketches are line art in the muted ink, with one accent element for focus. */
.roles-thumb-sketch { color: var(--muted); opacity: .85; }
.roles-thumb-sketch .a { stroke: var(--accent); }
.roles-need:hover .roles-thumb-sketch { opacity: 1; color: var(--text); }
/* A real template's artwork carries its own colours — do not tint it. */
.roles-thumb.is-real { background: var(--chrome); }
.roles-thumb.is-real .roles-thumb-svg { padding: 6px 8px; }
@media (prefers-reduced-motion: reduce) { .roles-thumb-sketch { transition: none; } }
/* Role matches on /search — a shortcut above the diagram grid, not a competing
   result set, so it is capped at three and styled as rows rather than tiles. */
.srch-roles { display: grid; gap: 6px; margin: 0 auto 22px; max-width: 720px; width: 100%; }
.srch-roles-head {
  margin: 0 0 2px; font-family: var(--mono, ui-monospace, monospace); font-size: 10.5px;
  letter-spacing: .12em; text-transform: uppercase; color: var(--faint);
}
.srch-role {
  display: flex; align-items: center; gap: 11px; padding: 11px 14px;
  border-radius: 11px; text-decoration: none; color: inherit;
  background: var(--panel); border: 1px solid var(--border-soft);
  transition: border-color .16s ease, background .16s ease;
}
.srch-role:hover { border-color: var(--accent); background: var(--panel-2); }
.srch-role-emoji { font-size: 19px; line-height: 1; flex: 0 0 auto; }
.srch-role-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1 1 auto; }
.srch-role-name { font-size: 14px; font-weight: 570; color: var(--text); }
.srch-role-tag { font-size: 12.3px; color: var(--muted); line-height: 1.4; }
.srch-role-n {
  flex: 0 0 auto; font-size: 11.5px; font-weight: 560; color: var(--accent);
  padding: 3px 9px; border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
@media (max-width: 640px) { .srch-role-n { display: none; } }

/* ============================================================
   Open a diagram — the recall path (js/open-diagram.js)
   Tokens copied from .lp-tpl-card, the reference card: --chrome / --panel /
   --panel-2, --text / --muted / --faint, --border / --border-soft, --accent.
   Never invent a custom property here — an unknown one fails SILENTLY to its
   fallback and looks correct in exactly one theme (CLAUDE.md).
   ============================================================ */
/* Lives inside .lp-hero-copy, so it takes the column's width rather than
   centring itself in the page. */
.od-resume {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 14px; margin: 18px 0 0; padding: 14px 16px;
  border: 1px solid var(--border); border-radius: 14px; background: var(--chrome);
}
.od-resume-copy { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1 1 320px; }
.od-resume-head { font-size: 14.5px; font-weight: 560; letter-spacing: -0.01em; color: var(--text); }
.od-resume-sub { font-size: 13px; line-height: 1.6; color: var(--muted); font-weight: 400; }
.od-resume-sub b { color: var(--text); font-weight: 560; }
/* SOFT accent treatment, deliberately a step below .lp-btn-primary: two
   filled accent buttons in the same viewport read as two primary actions,
   and "Start creating" is the one this page leads with. --accent-ink on
   --accent-soft is the pairing built for exactly this (see .lp-tbtn:hover),
   and it survives every accent palette — the old accent-filled version
   hardcoded #fff, which fails on the amber/lime/sky accents. */
.od-resume-btn {
  flex: 0 0 auto; font: inherit; font-size: 14px; font-weight: 570; cursor: pointer;
  color: var(--accent-ink); background: var(--accent-soft);
  border: 1px solid var(--accent-ring);
  border-radius: 10px; padding: 10px 18px;
  transition: filter var(--dur-2) var(--ease-out), background var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out);
}
.od-resume-btn:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 24%, transparent); }
.od-resume-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
@media (max-width: 640px) { .od-resume { margin-inline: 16px; } .od-resume-btn { width: 100%; } }

#odDialog { position: fixed; inset: 0; z-index: 9000; display: flex;
  align-items: center; justify-content: center; padding: 24px; }
#odDialog[hidden] { display: none; }
#odScrim { position: absolute; inset: 0; background: rgba(6, 8, 14, .62); backdrop-filter: blur(2px); }
.od-panel {
  position: relative; width: min(560px, 100%); max-height: min(78vh, 680px);
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--chrome); border: 1px solid var(--border);
  border-radius: 16px; box-shadow: 0 24px 60px rgba(0, 0, 0, .45);
}
.od-head { position: relative; padding: 20px 22px 14px; border-bottom: 1px solid var(--border-soft); }
.od-head h2 { margin: 0 0 5px; font-size: 18px; font-weight: 660; letter-spacing: -0.015em; color: var(--text); }
.od-head p { margin: 0; font-size: 13.5px; line-height: 1.55; color: var(--muted); padding-right: 30px; }
.od-close { position: absolute; top: 14px; right: 14px; width: 30px; height: 30px;
  font: inherit; font-size: 21px; line-height: 1; cursor: pointer; color: var(--faint);
  background: transparent; border: 1px solid transparent; border-radius: 8px; }
.od-close:hover { color: var(--text); border-color: var(--border); }
.od-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.od-list { list-style: none; margin: 0; padding: 8px; overflow-y: auto; flex: 1; }
.od-row { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; font: inherit; text-align: left; cursor: pointer;
  padding: 11px 13px; border: 1px solid transparent; border-radius: 10px;
  background: transparent; color: inherit;
  transition: background var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out); }
.od-row:hover { background: var(--panel); border-color: var(--border-soft); }
.od-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.od-row-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.od-row-title { font-size: 14.5px; font-weight: 570; letter-spacing: -0.01em; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.od-row-tool { font-family: var(--mono); font-size: 10.5px; letter-spacing: .07em;
  text-transform: uppercase; color: var(--faint); }
.od-row-go { flex: 0 0 auto; font-size: 12.5px; font-weight: 600; color: var(--accent); opacity: 0; }
.od-row:hover .od-row-go, .od-row:focus-visible .od-row-go { opacity: 1; }

.od-empty { margin: 0; padding: 26px 22px; font-size: 13.5px; line-height: 1.6; color: var(--muted); }
.od-foot { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
  gap: 10px; padding: 13px 18px; border-top: 1px solid var(--border-soft); background: var(--panel-2); }
.od-foot-note { font-size: 12px; color: var(--faint); }
.od-foot-btn { font: inherit; font-size: 13px; font-weight: 570; cursor: pointer;
  color: var(--text); background: transparent; border: 1px solid var(--border);
  border-radius: 9px; padding: 7px 13px; }
.od-foot-btn:hover { border-color: var(--accent); }
.od-foot-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
body.od-open { overflow: hidden; }

/* The top-bar entry — always reachable, not only after scrolling the hero. */
.od-top-btn[hidden] { display: none; }
.od-last {
  display: inline-block; margin-left: 8px; vertical-align: 1px;
  font-family: var(--mono); font-size: 9.5px; font-weight: 600;
  letter-spacing: .07em; text-transform: uppercase;
  color: var(--accent); border: 1px solid var(--accent);
  border-radius: 999px; padding: 1px 7px;
}

/* ========================================================================
   Zen-first Present quick rail + visual themes
   ------------------------------------------------------------------------
   The default show is deliberately content-first. The rail keeps only the
   actions someone reaches for while speaking: step, focus, guide/narration,
   palette, more and leave. Specialist ink tools, audience skins and timing
   remain available in More without taking over the screen.
   ======================================================================== */
body.presenting #stage {
  background: var(--pr-stage, #0b0d14);
}
body.presenting #presentBar {
  gap: 6px;
  padding: 6px 8px;
  background: color-mix(in srgb, var(--pr-panel, #121625) 92%, transparent);
  border-color: color-mix(in srgb, var(--pr-stage-ink, #e9ecff) 14%, transparent);
  box-shadow: 0 14px 34px -18px color-mix(in srgb, var(--pr-stage, #0b0d14) 82%, #000);
}
body.presenting #prRestart,
body.presenting #prFirst,
body.presenting #prLast,
body.presenting #prSpeedCycle,
body.presenting .prc-status,
body.presenting #prLikeBtn,
body.presenting #prMuteToggle,
body.presenting #prCaptionToggle,
body.presenting #prSmartToggle,
body.presenting #prAudienceCycle,
body.presenting #prToolLaser,
body.presenting #prToolPen,
body.presenting #prToolSpot,
body.presenting #prPin,
body.presenting #prZenToggle { display: none; }
body.presenting #prFocusToggle.on,
body.presenting #prFocusToggle[aria-pressed="true"] {
  color: var(--pr-stage-ink, #e9ecff);
  background: color-mix(in srgb, var(--pr-focus, var(--iris)) 32%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--pr-focus, var(--iris)) 50%, transparent) inset,
              0 0 14px -7px var(--pr-focus, var(--iris));
}
body.presenting #prThemeToggle {
  color: color-mix(in srgb, var(--iris-bright) 82%, var(--pr-stage-ink, #e9ecff));
}
body.presenting #prVoiceToggle[aria-pressed="true"],
body.presenting #prNarrationToggle.on {
  color: var(--pr-stage-ink, #e9ecff);
}

/* The quick palette sits in the same visual language as the compact rail,
   but remains a real radio group for keyboard and assistive-tech users. */
body.presenting .pr-pop {
  color: var(--pr-stage-ink, #e9ecff);
  background: color-mix(in srgb, var(--pr-panel, #121625) 94%, transparent);
  border-color: color-mix(in srgb, var(--pr-stage-ink, #e9ecff) 14%, transparent);
}
body.presenting .pr-pop-h,
body.presenting .pr-pop .pr-check,
body.presenting .pr-pop .pr-slider-row { color: color-mix(in srgb, var(--pr-stage-ink, #e9ecff) 84%, transparent); }
body.presenting .pr-pop .pr-mini,
body.presenting .pr-pop select,
body.presenting .pr-pop .pr-voice-row select {
  color: var(--pr-stage-ink, #e9ecff);
  background: color-mix(in srgb, var(--pr-stage-ink, #e9ecff) 7%, transparent);
  border-color: color-mix(in srgb, var(--pr-stage-ink, #e9ecff) 15%, transparent);
}
.pr-theme-pop { width: min(312px, calc(100% - 28px)); }
.pr-visual-theme-grid { display: grid; gap: 7px; }
.pr-visual-theme {
  display: flex; align-items: center; gap: 11px; width: 100%; padding: 9px;
  color: inherit; text-align: left; font: inherit; cursor: pointer;
  background: color-mix(in srgb, var(--pr-stage-ink, #e9ecff) 5%, transparent);
  border: 1px solid color-mix(in srgb, var(--pr-stage-ink, #e9ecff) 11%, transparent);
  border-radius: 11px;
  transition: transform .16s var(--ease-out), background .16s var(--ease-out), border-color .16s var(--ease-out);
}
.pr-visual-theme:hover { transform: translateY(-1px); background: color-mix(in srgb, var(--pr-stage-ink, #e9ecff) 9%, transparent); }
.pr-visual-theme.on {
  border-color: color-mix(in srgb, var(--iris-bright) 70%, transparent);
  background: color-mix(in srgb, var(--iris) 20%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--iris) 28%, transparent) inset;
}
.pr-visual-theme > span:last-child { display: grid; gap: 2px; min-width: 0; }
.pr-visual-theme b { font-size: 12.5px; font-weight: 680; }
.pr-visual-theme small { color: color-mix(in srgb, var(--pr-stage-ink, #e9ecff) 62%, transparent); font-size: 11px; line-height: 1.3; }
.pr-vt-swatch {
  width: 34px; height: 30px; flex: none; border-radius: 8px;
  border: 1px solid rgba(255,255,255,.20); box-shadow: inset 0 1px rgba(255,255,255,.28);
}
.pr-vt-current { background: linear-gradient(135deg, #eef2ff 0 47%, #6366f1 48% 68%, #1f2937 69%); }
.pr-vt-aurora { background: linear-gradient(135deg, #0e1330 0 45%, #8b5cf6 46% 65%, #22d3ee 66%); }
.pr-vt-gallery { background: linear-gradient(135deg, #fbf8f1 0 45%, #b8912f 46% 66%, #2b2517 67%); }
.pr-theme-note { margin: 10px 1px 0; color: color-mix(in srgb, var(--pr-stage-ink, #e9ecff) 60%, transparent); font-size: 11px; line-height: 1.45; }

/* Zen remains one continuous canvas — a stage theme colours the small control
   surfaces, while the diagram, title, splash and end card retain the chosen
   sheet as their shared background. The focus lens intentionally uses the
   same sheet-aware treatment above; Zen must not reintroduce a dark curtain. */
body.pr-zen.presenting #stage { background: var(--pr-sheet, #fff); }

@media (max-width: 760px), (max-height: 560px) and (max-width: 1024px) {
  /* Keep only the actual on-stage controls on a phone: guide/notes is still
     one tap away, while voice, palette and full-screen live in More. */
  body.presenting #prNarrationToggle,
  body.presenting #prFocusToggle { display: inline-flex !important; }
  body.presenting #prVoiceToggle,
  body.presenting #prThemeToggle,
  body.presenting #prFull { display: none !important; }
}

/* ========================================================================
   Zen guide — one calm reading composition
   ------------------------------------------------------------------------
   The guide's footprint is deliberately still measured by app.js: that is
   what keeps the SVG and panel from ever overlapping.  These are only the
   contents of that measured space.  Earlier iterations used a flex column
   and several competing note-style cards, which made a short note look like
   a card stranded in a tall empty drawer.  The final layer makes the title,
   picture, step tag, authored narration and insight explicit grid regions.
   Empty image/Why regions are published by updatePresentNarration(), so a
   sparse step gives its room back to the content that actually exists.
   ======================================================================== */
body.presenting:not(.pr-guide-collapsed) #prNarration {
  /* One shared measure keeps image, count, narration and insight visually
     locked together; only the title deliberately reaches the panel edge. */
  /* The image, narrative and insight share one actual reading measure.  The
     old 356px cap made artwork look like a thumbnail in a roomy guide while
     the explanation continued much wider beside it. */
  --pr-guide-media-w: calc(100% - 28px);
  display: grid !important;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto auto auto auto auto;
  grid-template-areas:
    "title"
    "media"
    "step"
    "note"
    "why";
  align-content: start;
  align-items: start;
  box-sizing: border-box;
  gap: 0;
  padding: 0 !important;
  overflow: hidden;
  color: var(--pr-sheet-ink) !important;
  background: var(--pr-sheet) !important;
  border: 0 !important;
  border-radius: 0;
  box-shadow: none !important;
}
/* Keep the structural visibility contracts ahead of this visual override.
   The guide is deliberately absent in a no-notes Zen show, a hidden embed or
   during recording even though its normal composition uses `display:grid`. */
body.presenting #prNarration[hidden],
body.pr-zen.presenting:not(.pr-zen-notes) #prNarration,
body.embed-mode.embed-notes-hidden.presenting #prNarration,
body.recording #prNarration { display: none !important; }
/* These are editor/presenter affordances, not part of the live reading grid.
   Keeping them explicitly out of grid auto-placement is important: a mounted
   presenter rig or a stale editor list must never manufacture an implicit row
   between an authored title and its image.  Collapsed mode restores only its
   small guide head below. */
body.presenting:not(.pr-guide-collapsed) #prNarrationHead,
body.presenting:not(.pr-guide-collapsed) #prPresenterStage,
body.presenting:not(.pr-guide-collapsed) #prNoteIcon,
body.presenting:not(.pr-guide-collapsed) #prNoteTakeaway,
body.presenting:not(.pr-guide-collapsed) #prNoteTech,
body.presenting:not(.pr-guide-collapsed) #prNarrationEdit,
body.presenting:not(.pr-guide-collapsed) #prNarrationEditBtns,
body.presenting:not(.pr-guide-collapsed) #prNarrationList { display: none !important; }
/* A side guide is part of the sheet rather than a raised slab.  The one
   hairline is enough to separate reading context from the diagram. */
body.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prNarration.pos-right {
  border-left: 1px solid color-mix(in srgb, var(--pr-sheet-ink) 11%, transparent) !important;
}
body.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prNarration.pos-left {
  border-right: 1px solid color-mix(in srgb, var(--pr-sheet-ink) 11%, transparent) !important;
}
body.presenting:not(.pr-guide-collapsed) #prNarration #prNoteTitle:not([hidden]) {
  grid-area: title;
  display: block;
  align-self: stretch;
  box-sizing: border-box;
  width: auto;
  margin: 0 !important;
  padding: clamp(11px, 1.45vw, 15px) clamp(14px, 1.7vw, 20px);
  color: var(--pr-sheet-ink);
  background: color-mix(in srgb, var(--iris) 6%, var(--pr-sheet));
  border: 0;
  border-bottom: 1px solid color-mix(in srgb, var(--iris) 15%, transparent);
  border-radius: 0;
  box-shadow: none;
  font: 740 clamp(17px, 1.45vw, 22px)/1.18 var(--sans);
  letter-spacing: -.024em;
}
body.presenting:not(.pr-guide-collapsed) #prNarrationImg:not([hidden]) {
  grid-area: media;
  display: block;
  justify-self: center;
  align-self: center;
  box-sizing: border-box;
  width: var(--pr-guide-media-w);
  max-width: var(--pr-guide-media-w);
  height: auto;
  max-height: min(31vh, 280px);
  margin: 12px 14px 7px !important;
  padding: 0;
  object-fit: contain;
  background: color-mix(in srgb, var(--iris) 4%, var(--pr-sheet));
  border: 1px solid color-mix(in srgb, var(--pr-sheet-ink) 9%, transparent);
  border-radius: 10px;
  box-shadow: 0 12px 26px -24px color-mix(in srgb, var(--pr-sheet-ink) 64%, transparent);
}
/* This needs the data-note-style selector too: an earlier legacy rule with
   two IDs otherwise restores `width:auto` and turns a full reading image into
   a tiny intrinsic thumbnail.  Side-guide artwork deliberately shares the
   narration measure, giving the panel one calm, aligned column. */
body.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prNarration[data-note-style] #prNarrationImg:not([hidden]),
body.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prNarrationImg:not([hidden]) {
  width: var(--pr-guide-media-w) !important;
  max-width: var(--pr-guide-media-w) !important;
  height: auto !important;
  max-height: min(39vh, 320px) !important;
  margin: 12px 14px 7px !important;
}
/* The small count stays under the picture and uses its same reading measure,
   so it never looks like a detached toolbar control. */
body.presenting:not(.pr-guide-collapsed) #prNarration #prNoteStep:not([hidden]) {
  grid-area: step;
  justify-self: center;
  box-sizing: border-box;
  width: var(--pr-guide-media-w);
  margin: 0 !important;
  padding: 7px 3px 8px;
  color: color-mix(in srgb, var(--iris) 76%, var(--pr-sheet-ink));
  background: transparent;
  border-top: 1px solid color-mix(in srgb, var(--iris) 18%, transparent);
  border-bottom: 0;
  border-radius: 0;
  font: 730 10px/1.1 var(--sans);
  letter-spacing: .13em;
  text-align: left;
  text-transform: uppercase;
}
body.presenting:not(.pr-guide-collapsed) #prNarration #prNarrationText {
  grid-area: note;
  display: block !important;
  justify-self: center;
  align-self: start;
  box-sizing: border-box;
  width: var(--pr-guide-media-w);
  min-height: 0;
  max-height: min(23vh, 208px) !important;
  margin: 0 !important;
  padding: 2px 2px 0 !important;
  overflow-y: auto;
  color: var(--pr-sheet-ink) !important;
  background: transparent !important;
  border: 0 !important;
  border-radius: 0;
  box-shadow: none !important;
  font-size: calc(15px * var(--pr-text-k, 1));
  line-height: 1.5;
}
/* The insight is a real supporting tile, not a second brand colour.  It
   inherits the selected Present palette through the sheet/iris tokens and
   remains visible in every note-style preset. */
body.presenting:not(.pr-guide-collapsed) #prWhyDock:not([hidden]) {
  grid-area: why;
  display: block !important;
  justify-self: center;
  align-self: start;
  box-sizing: border-box;
  width: var(--pr-guide-media-w);
  max-height: min(18vh, 150px);
  margin: 11px 0 14px !important;
  padding: 10px 12px 11px !important;
  overflow-y: auto;
  color: var(--pr-sheet-ink);
  background: color-mix(in srgb, var(--iris) 8%, var(--pr-sheet)) !important;
  border: 1px solid color-mix(in srgb, var(--iris) 22%, transparent) !important;
  border-left: 3px solid color-mix(in srgb, var(--iris) 80%, var(--pr-sheet-ink)) !important;
  border-radius: 0 10px 10px 0;
  box-shadow: none !important;
  font-size: calc(12.5px * var(--pr-text-k, 1));
  line-height: 1.42;
}
body.presenting:not(.pr-guide-collapsed) #prWhyDock .pr-whyd-head {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0 0 5px;
}
body.presenting:not(.pr-guide-collapsed) #prWhyDock .pr-whyd-chip {
  display: grid;
  place-items: center;
  width: 19px;
  height: 19px;
  color: color-mix(in srgb, var(--iris) 80%, var(--pr-sheet-ink));
  background: color-mix(in srgb, var(--iris) 13%, var(--pr-sheet));
  border: 1px solid color-mix(in srgb, var(--iris) 25%, transparent);
  border-radius: 6px;
}
body.presenting:not(.pr-guide-collapsed) #prWhyDock .pr-why-label {
  color: color-mix(in srgb, var(--iris) 77%, var(--pr-sheet-ink));
  font: 720 9.5px/1.1 var(--sans);
  letter-spacing: .115em;
  text-transform: uppercase;
}
body.presenting:not(.pr-guide-collapsed) #prWhyDockText { color: var(--pr-sheet-ink); }

/* A bottom guide becomes a single compact card: title across the top, image
   on the left, explanation in the centre and the insight at the right.  The
   step tag and its note intentionally share one vertical column. */
body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration {
  grid-template-columns: minmax(136px, .76fr) minmax(0, 1.46fr) minmax(196px, .88fr);
  grid-template-rows: auto auto minmax(0, 1fr);
  grid-template-areas:
    "title title title"
    "media step why"
    "media note why";
  align-content: stretch;
  align-items: stretch;
  height: clamp(160px, 24vh, 220px);
  max-height: 28vh;
  border: 1px solid color-mix(in srgb, var(--pr-sheet-ink) 10%, transparent) !important;
  border-radius: 12px;
  box-shadow: 0 12px 28px -28px color-mix(in srgb, var(--pr-sheet-ink) 58%, transparent) !important;
}
body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration #prNoteTitle:not([hidden]) {
  padding: 9px 14px 8px;
  font-size: clamp(15.5px, 1.65vw, 20px);
}
body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarrationImg:not([hidden]) {
  grid-area: media;
  justify-self: stretch;
  align-self: stretch;
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0 !important;
  padding: 10px 11px 11px;
  border: 0;
  border-right: 1px solid color-mix(in srgb, var(--pr-sheet-ink) 8%, transparent);
  border-radius: 0;
  box-shadow: none;
}
body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration #prNoteStep:not([hidden]) {
  justify-self: stretch;
  align-self: end;
  width: auto;
  margin: 0 14px !important;
  padding: 9px 0 4px;
  text-align: left;
}
body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration #prNarrationText {
  justify-self: stretch;
  align-self: stretch;
  width: auto;
  max-height: none !important;
  margin: 0 14px 11px !important;
  padding: 3px 0 0 !important;
  font-size: calc(14px * var(--pr-text-k, 1));
  line-height: 1.42;
}
body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prWhyDock:not([hidden]) {
  justify-self: stretch;
  align-self: stretch;
  width: auto;
  max-height: none;
  margin: 8px 12px 10px 0 !important;
  padding: 9px 10px 10px !important;
  font-size: calc(11.5px * var(--pr-text-k, 1));
  line-height: 1.36;
}
/* No empty media or insight columns: grid selection follows actual authored
   fields rather than whether a particular template happened to include art. */
body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration[data-pr-guide-image="0"][data-pr-guide-why="1"] {
  grid-template-columns: minmax(0, 1.46fr) minmax(196px, .88fr);
  grid-template-areas:
    "title title"
    "step why"
    "note why";
}
body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration[data-pr-guide-image="1"][data-pr-guide-why="0"] {
  grid-template-columns: minmax(136px, .76fr) minmax(0, 1.46fr);
  grid-template-areas:
    "title title"
    "media step"
    "media note";
}
body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration[data-pr-guide-image="0"][data-pr-guide-why="0"] {
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "title"
    "step"
    "note";
}

/* At compact desktop/tablet widths all authored pieces remain available: the
   image/insight columns tighten before narration type shrinks. */
@media (max-width: 900px) {
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration {
    grid-template-columns: minmax(118px, .69fr) minmax(0, 1.33fr) minmax(164px, .82fr);
    height: clamp(148px, 25vh, 204px);
  }
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration[data-pr-guide-image="0"][data-pr-guide-why="1"] {
    grid-template-columns: minmax(0, 1.33fr) minmax(164px, .82fr);
  }
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration[data-pr-guide-image="1"][data-pr-guide-why="0"] {
    grid-template-columns: minmax(118px, .69fr) minmax(0, 1.33fr);
  }
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prWhyDock:not([hidden]) {
    display: block !important;
  }
}
/* Phones intentionally surrender the optional picture (the existing phone
   rule does that) but retain both words and Why in two clean reading cells. */
@media (max-width: 700px) {
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarrationImg:not([hidden]) {
    display: none !important;
  }
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration,
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration[data-pr-guide-image="0"][data-pr-guide-why="1"],
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration[data-pr-guide-image="1"][data-pr-guide-why="0"] {
    grid-template-columns: minmax(0, 1.16fr) minmax(124px, .84fr);
    grid-template-rows: auto auto minmax(0, 1fr);
    grid-template-areas:
      "title title"
      "step why"
      "note why";
    height: clamp(132px, 30vh, 190px);
  }
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration[data-pr-guide-why="0"] {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "title"
      "step"
      "note";
  }
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration #prNoteTitle:not([hidden]) {
    padding: 7px 10px;
    font-size: clamp(13.5px, 4.3vw, 16px);
  }
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration #prNoteStep:not([hidden]) {
    margin: 0 9px !important;
    padding: 6px 0 3px;
    font-size: 8.8px;
  }
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration #prNarrationText {
    margin: 0 9px 7px !important;
    padding-top: 2px !important;
    font-size: calc(13px * var(--pr-text-k, 1));
    line-height: 1.35;
  }
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prWhyDock:not([hidden]) {
    margin: 5px 7px 7px 0 !important;
    padding: 7px 8px 8px !important;
    font-size: calc(10.7px * var(--pr-text-k, 1));
    line-height: 1.28;
  }
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prWhyDock .pr-whyd-head { margin-bottom: 3px; }
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prWhyDock .pr-whyd-chip {
    width: 16px;
    height: 16px;
  }
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prWhyDock .pr-why-label {
    font-size: 8px;
    letter-spacing: .08em;
  }
}
/* A rotated phone is wide only on paper: the browser chrome and transport
   leave very little height. Keep the guide as a compact bottom transcript,
   give artwork back to the focused diagram, and allow the copy to scroll. */
@media (max-height: 560px) and (max-width: 1024px) {
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarrationImg:not([hidden]) {
    display: none !important;
  }
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration,
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration[data-pr-guide-image="0"][data-pr-guide-why="1"],
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration[data-pr-guide-image="1"][data-pr-guide-why="0"] {
    grid-template-columns: minmax(0, 1.25fr) minmax(150px, .75fr);
    grid-template-rows: auto minmax(0, 1fr);
    grid-template-areas: "title title" "note why";
    height: min(124px, 35vh);
    max-height: 124px;
  }
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration[data-pr-guide-why="0"] {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "title" "note";
  }
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration #prNoteStep:not([hidden]) {
    display: none !important;
  }
  body.presenting.pr-notes-bottom:not(.pr-guide-collapsed) #prNarration #prNarrationText {
    overflow-y: auto;
    margin: 4px 10px 7px !important;
    font-size: calc(12.5px * var(--pr-text-k, 1));
  }
}
/* Collapsed mode is deliberately a real rail, not an empty, grid-sized box.
   It remains a one-tap return to the guide without stealing stage space. */
body.presenting.pr-guide-collapsed #prNarration {
  display: flex !important;
  align-items: center;
  justify-content: center;
  padding: 0 !important;
  color: var(--pr-sheet-ink) !important;
  background: color-mix(in srgb, var(--iris) 5%, var(--pr-sheet)) !important;
  border-color: color-mix(in srgb, var(--pr-sheet-ink) 11%, transparent) !important;
  box-shadow: none !important;
}
body.presenting.pr-guide-collapsed #prNarrationHead {
  display: flex !important;
  margin: 0 !important;
  color: color-mix(in srgb, var(--iris) 72%, var(--pr-sheet-ink));
  opacity: 1;
}
/* Zen uses the exact same sheet-native surfaces — no dark panel comes back
   when a presenter chooses to keep their notes visible. */
body.pr-zen.presenting:not(.pr-guide-collapsed) #prNarration {
  background: var(--pr-sheet) !important;
  box-shadow: none !important;
}

/* Zen side guide: this is the reading view, so its authored narration owns
   the available height rather than stopping at an arbitrary short-card cap.
   The image and title establish the step; the note then grows into the calm
   sheet space that remains, with its own scroll only when the writing truly
   exceeds the audience's view. */
body.pr-zen.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prNarration {
  grid-template-rows: auto auto auto minmax(0, 1fr) auto;
  align-content: stretch;
}
body.pr-zen.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prNarrationImg:not([hidden]) {
  max-height: min(29vh, 260px);
}
/* The legacy data-note-style image rule has two IDs, so repeat the aligned
   measure at this Zen-specific layer.  Zen keeps a slightly shorter media
   cap to protect the reading area below it. */
body.pr-zen.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prNarration[data-note-style] #prNarrationImg:not([hidden]),
body.pr-zen.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prNarrationImg:not([hidden]) {
  width: var(--pr-guide-media-w) !important;
  max-width: var(--pr-guide-media-w) !important;
  max-height: min(29vh, 260px) !important;
}
body.pr-zen.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prNarration #prNarrationText {
  align-self: stretch;
  max-height: none !important;
  padding: 5px 2px 2px !important;
  font-size: calc(17px * var(--pr-text-k, 1));
  line-height: 1.58;
}
/* Why is an insight marker in Zen, not another boxed panel.  Its small
   illuminated spine, haloed glyph and open-sided copy make it feel like a
   thought that has emerged from the note, while the sheet stays continuous. */
body.pr-zen.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prWhyDock:not([hidden]) {
  display: grid !important;
  grid-template-columns: 28px minmax(0, 1fr);
  grid-template-rows: auto auto;
  column-gap: 9px;
  row-gap: 2px;
  position: relative;
  align-self: end;
  max-height: min(22vh, 180px);
  margin: 10px 0 8px !important;
  padding: 10px 4px 10px 0 !important;
  overflow-y: auto;
  background: linear-gradient(90deg, color-mix(in srgb, var(--iris) 9%, var(--pr-sheet)), transparent 78%) !important;
  border: 0 !important;
  border-left: 0 !important;
  border-radius: 0;
  box-shadow: none !important;
}
body.pr-zen.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prWhyDock::before {
  content: '';
  position: absolute;
  top: 10px;
  bottom: 10px;
  left: 13px;
  width: 1px;
  background: linear-gradient(180deg, transparent, color-mix(in srgb, var(--iris-bright) 76%, var(--iris)) 18%, color-mix(in srgb, var(--iris) 43%, transparent) 78%, transparent);
  pointer-events: none;
}
body.pr-zen.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prWhyDock .pr-whyd-head {
  display: contents;
  margin: 0;
}
body.pr-zen.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prWhyDock .pr-whyd-chip {
  grid-column: 1;
  grid-row: 1 / span 2;
  align-self: start;
  z-index: 1;
  width: 26px;
  height: 26px;
  color: color-mix(in srgb, var(--iris-bright) 76%, var(--pr-sheet-ink));
  background: color-mix(in srgb, var(--iris) 16%, var(--pr-sheet));
  border: 1px solid color-mix(in srgb, var(--iris) 38%, transparent);
  border-radius: 50%;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--iris) 7%, transparent),
              0 7px 16px -12px color-mix(in srgb, var(--iris) 88%, transparent);
}
body.pr-zen.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prWhyDock .pr-whyd-chip svg {
  width: 13px;
  height: 13px;
}
body.pr-zen.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prWhyDock .pr-why-label {
  grid-column: 2;
  grid-row: 1;
  align-self: end;
  color: color-mix(in srgb, var(--iris) 82%, var(--pr-sheet-ink));
  font-size: 9px;
  letter-spacing: .14em;
}
body.pr-zen.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prWhyDockText {
  grid-column: 2;
  grid-row: 2;
  padding-bottom: 1px;
  color: var(--pr-sheet-ink);
  font-size: calc(13px * var(--pr-text-k, 1));
  line-height: 1.43;
}
/* With no step art, the auto-sized media row collapses and the note already
   owns the flexible track above Why.  Spend that genuinely free rail on a
   calmer, larger reading measure; the insight then lands flush with the rail
   rather than leaving a stranded band of blank sheet below it. */
body.pr-zen.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed)
  #prNarration[data-pr-guide-image="0"] #prWhyDock:not([hidden]) {
  margin-bottom: 0 !important;
}
@media (min-width: 900px) and (min-height: 680px) {
  body.pr-zen.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed)
    #prNarration[data-pr-guide-image="0"] #prNarrationText {
    padding-top: 11px !important;
    font-size: calc(18px * var(--pr-text-k, 1));
    line-height: 1.62;
  }
}

/* Mobile runner chrome ---------------------------------------------------
   The phone is a touch player, not a squeezed desktop control console. The
   transport bar is already removed at this breakpoint; these two 44px title
   actions keep sound and the few stage choices reachable without giving the
   diagram another bottom toolbar. */
.pr-mobile-head-btn,
.pr-mobile-menu { display: none; }
@media (max-width: 760px), (max-height: 560px) and (max-width: 1024px) {
  body.pr-zen-title #prZenTitle {
    min-height: 48px;
    height: 48px;
    padding: 2px 6px;
  }
  body.pr-zen-title.presenting #canvasWrap {
    padding-top: 48px !important;
    touch-action: none;
    overscroll-behavior: contain;
    cursor: grab;
  }
  body.pr-zen-title.presenting #canvasWrap:active { cursor: grabbing; }
  body.pr-zen-notes.pr-zen-title:not(.pr-notes-bottom) #prNarration { top: 48px !important; }
  #prZenMark {
    left: max(8px, env(safe-area-inset-left));
    width: 28px;
    height: 28px;
  }
  .pr-mobile-head-btn {
    position: absolute;
    z-index: 2;
    display: inline-grid;
    place-items: center;
    width: 44px;
    height: 44px;
    padding: 0;
    color: color-mix(in srgb, var(--pr-sheet-ink, #1b1b22) 82%, var(--iris));
    background: transparent;
    border: 0;
    border-radius: 12px;
    pointer-events: auto;
  }
  .pr-mobile-head-btn:active,
  .pr-mobile-head-btn[aria-expanded="true"] {
    background: color-mix(in srgb, var(--iris) 13%, transparent);
  }
  .pr-mobile-head-btn:focus-visible { outline: 2px solid var(--iris); outline-offset: -3px; }
  .pr-mobile-head-btn svg { width: 20px; height: 20px; }
  #prMobileMenuToggle { left: max(44px, calc(env(safe-area-inset-left) + 44px)); }
  #prMobileMute { right: max(6px, env(safe-area-inset-right)); }
  #prMobileMute .mu-off { display: none; }
  #prMobileMute[aria-pressed="true"] .mu-on { display: none; }
  #prMobileMute[aria-pressed="true"] .mu-off { display: inline; }
  #prZenStep {
    right: max(52px, calc(env(safe-area-inset-right) + 52px));
    min-width: 44px;
    padding: 4px 6px;
    font-size: 10px;
  }
  #prZenTitleText {
    max-width: calc(100% - 226px);
    font-size: clamp(16px, 5vw, 20px);
  }
  /* The compact camera already interpolates the SVG viewBox on every frame.
     A second half-second CSS glide made the focus lens chase that moving
     target and visibly lag beside the card on iOS. Snap geometry to the live
     DOM shape. On phones the exact renderer-attached trace is the focus cue;
     suppress the redundant broad lens so it can never chase a moving camera
     frame beside the card. */
  body.presenting #prSpotlight { display: none !important; transition: none; }
  #prMobileMenu.pr-mobile-menu:not([hidden]) {
    position: absolute;
    z-index: 10;
    top: 46px;
    left: max(8px, env(safe-area-inset-left));
    display: grid;
    width: min(330px, calc(100vw - 16px - env(safe-area-inset-left) - env(safe-area-inset-right)));
    box-sizing: border-box;
    gap: 10px;
    padding: 10px;
    color: var(--pr-stage-ink, #eef2ff);
    background: color-mix(in srgb, var(--pr-panel, #101a37) 96%, transparent);
    border: 1px solid color-mix(in srgb, var(--pr-stage-ink, #eef2ff) 16%, transparent);
    border-radius: 14px;
    box-shadow: 0 18px 48px rgba(3, 7, 18, .34);
    -webkit-backdrop-filter: blur(18px) saturate(1.08);
    backdrop-filter: blur(18px) saturate(1.08);
    pointer-events: auto;
  }
  .pr-mobile-menu-row {
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 12px;
    color: inherit;
    font: 650 14px/1 var(--sans);
    background: color-mix(in srgb, var(--pr-stage-ink, #eef2ff) 6%, transparent);
    border: 1px solid color-mix(in srgb, var(--pr-stage-ink, #eef2ff) 12%, transparent);
    border-radius: 11px;
  }
  .pr-mobile-menu-row b { color: var(--iris-bright); font-size: 12px; }
  .pr-mobile-menu-label {
    padding: 1px 2px 0;
    color: color-mix(in srgb, var(--pr-stage-ink, #eef2ff) 64%, transparent);
    font: 700 10px/1 var(--sans);
    letter-spacing: .11em;
    text-transform: uppercase;
  }
  .pr-mobile-zoom-row {
    display: grid;
    grid-template-columns: 54px minmax(0, 1fr) 54px;
    gap: 7px;
  }
  .pr-mobile-zoom-row button {
    min-height: 44px;
    display: grid;
    place-items: center;
    padding: 0;
    color: inherit;
    font: 700 23px/1 var(--sans);
    background: color-mix(in srgb, var(--pr-stage-ink, #eef2ff) 6%, transparent);
    border: 1px solid color-mix(in srgb, var(--pr-stage-ink, #eef2ff) 13%, transparent);
    border-radius: 11px;
  }
  .pr-mobile-zoom-row button:active { background: color-mix(in srgb, var(--iris) 20%, transparent); }
  .pr-mobile-zoom-row button:disabled { opacity: .35; }
  #prMobileZoomReset { display: flex; flex-direction: column; gap: 2px; font-size: 13px; }
  #prMobileZoomReset small {
    color: color-mix(in srgb, var(--pr-stage-ink, #eef2ff) 58%, transparent);
    font: 650 9px/1 var(--sans);
    letter-spacing: .08em;
    text-transform: uppercase;
  }
  .pr-mobile-theme-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
  .pr-mobile-theme-row button {
    min-height: 44px;
    padding: 7px 5px;
    color: inherit;
    font: 650 12px/1 var(--sans);
    background: color-mix(in srgb, var(--pr-stage-ink, #eef2ff) 5%, transparent);
    border: 1px solid color-mix(in srgb, var(--pr-stage-ink, #eef2ff) 12%, transparent);
    border-radius: 10px;
  }
  .pr-mobile-theme-row button.on {
    color: #fff;
    background: color-mix(in srgb, var(--iris) 48%, transparent);
    border-color: color-mix(in srgb, var(--iris-bright) 70%, transparent);
  }
}

/* ============================================================
   LIQUID WORKSPACE — full-bleed canvas, floating surfaces (2026-08)
   ------------------------------------------------------------
   The editor's three fixed columns became one full-viewport canvas with
   floating islands over it: two top control islands (inside #topbar, which
   keeps its id so every presenting/watching/embed hide rule still applies),
   a floating component library (#palette), a floating inspector (#side) and
   a bottom canvas dock (#edDock). Geometry contract:

   • The CANVAS never resizes when chrome collapses — #canvasWrap always
     fills #main, so zoom, scroll and diagram coordinates are stable through
     every panel toggle. Collapsing a panel only REVEALS canvas.
   • #zoomLayer's padding places the sheet in the free region between the
     floating panels. It is CONSTANT per breakpoint (never tracks collapse
     state) for the same stability reason. app.js measures it (svgHost
     offsets) rather than assuming 34px.
   • --shell-* vars describe the chrome footprint for anything that must
     stay clear of it (canvas dock centring, rulers, canvas-kit toolbar).
     They DO track collapse state — chrome may glide, content may not.
   ============================================================ */

body {
  --shell-gap: 12px;
  --shell-top: 62px;              /* islands: 10px margin + ~44px tall + 8px  */
  --lib-w: 268px;
  --insp-w: 300px;
  /* ---- Liquid-glass surface recipe (scoped here, NOT in the TOKENS block —
     build_pages.py copies that block into generated pages). One recipe for
     every MAJOR floating container: islands, panels, dock, popovers. Inputs
     stay off the glass — they use the ordinary chrome tokens. Three-level
     hierarchy: workspace canvas (lowest) < glass chrome < opaque page. ---- */
  --lw-surface: rgba(13, 16, 28, 0.80);              /* deep navy, never pure black */
  --lw-surface-border: rgba(148, 163, 210, 0.17);
  --lw-blur: blur(22px) saturate(130%);
  --lw-shadow: 0 16px 45px rgba(2, 4, 14, 0.42), 0 2px 8px rgba(2, 4, 14, 0.28);
  --lw-canvas: #ccd2e5;                               /* workspace ground, cool grey-blue */
  --lw-canvas-dot: rgba(24, 30, 52, 0.14);
  --lw-page-shadow: 0 1px 2px rgba(15, 18, 32, 0.16),
                    0 10px 26px rgba(15, 18, 32, 0.15),
                    0 34px 70px -18px rgba(15, 18, 32, 0.30);
  /* LITERAL px on purpose: canvas-kit.js parseFloat()s these off body's
     computed style, and a custom property keeps its calc() unresolved there. */
  --shell-left: 292px;            /* library footprint: 268 + 12 * 2 */
  --shell-right: 324px;           /* inspector footprint: 300 + 12 * 2 */
  --canvas-left: 326px;           /* sheet inset: 292 + 34 — static per breakpoint */
}
[data-ui-theme="light"] body {
  --lw-surface: rgba(248, 250, 255, 0.84);
  --lw-surface-border: rgba(255, 255, 255, 0.72);
  --lw-shadow: 0 16px 45px rgba(31, 45, 78, 0.10), 0 2px 8px rgba(31, 45, 78, 0.06);
  --lw-canvas: #ccd2e6;
  --lw-canvas-dot: rgba(31, 45, 78, 0.15);
  --lw-page-shadow: 0 1px 2px rgba(31, 45, 78, 0.10),
                    0 10px 26px rgba(31, 45, 78, 0.10),
                    0 34px 70px -18px rgba(31, 45, 78, 0.22);
}
body.lib-collapsed { --shell-left: 72px; }                       /* rail: 48 + 24 */
body:has(#side.collapsed) { --shell-right: 12px; }
body:has(#side.code-mode:not(.collapsed)) { --shell-right: 454px; }

/* ---- top islands ---- */
#topbar {
  position: absolute; top: 0; left: 0; right: 0; height: auto;
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--shell-gap); padding: 10px var(--shell-gap) 0;
  background: none; border-bottom: none; box-shadow: none;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  pointer-events: none; z-index: 42;
}
.tb-island {
  pointer-events: auto; min-height: 44px; min-width: 0;
  display: flex; align-items: center; gap: 5px;
  padding: 4px 6px; border-radius: 16px;
  background: var(--lw-surface);
  -webkit-backdrop-filter: var(--lw-blur); backdrop-filter: var(--lw-blur);
  border: 1px solid var(--lw-surface-border);
  box-shadow: var(--lw-shadow);
}
#topbar .brand { padding: 2px 6px 2px 2px; }
#topbar .brand-mark, #topbar .brand-mark svg { width: 30px; height: 30px; }
#topbar .beta-badge { display: none; }        /* the island is dense; Beta still shows on home */
/* Save status is a small pill, clearly separate from the Save COMMAND.
   The state colours are re-picked for the translucent island: the old
   #d97706 / #16a34a were tuned for the opaque topbar and measured ~3:1 once
   the glass composited over the light canvas. These clear 4.5:1 there. */
#topbar .save-state { margin-right: 0; font-size: 11px; font-weight: 650; padding: 3px 8px; gap: 5px; }
#topbar .save-state .ss-dot { width: 6px; height: 6px; }
#topbar .save-state.is-dirty { color: #fbbf24; border-color: color-mix(in srgb, #fbbf24 45%, var(--edge)); }
#topbar .save-state.is-saved { color: #4ade80; border-color: color-mix(in srgb, #4ade80 45%, var(--edge)); }
[data-ui-theme="light"] #topbar .save-state.is-dirty { color: #92400e; border-color: color-mix(in srgb, #92400e 40%, var(--edge)); }
[data-ui-theme="light"] #topbar .save-state.is-saved { color: #15803d; border-color: color-mix(in srgb, #15803d 40%, var(--edge)); }
/* The title is the island's one elastic block: it grows into free room up
   to a cap, truncates with an ellipsis (full text in the tooltip), and can
   never push the primary controls off screen. */
#topbar #docTitle {
  flex: 1 1 auto; width: auto; min-width: 80px; max-width: 340px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Present stays the one filled action; Export steps down to a quiet secondary. */
#topbar #btnExport {
  background: none; color: var(--text); font-weight: 600;
  border: 1px solid var(--border); box-shadow: none;
  padding: 7px 13px; border-radius: 10px;
  transition: background var(--dur-1), border-color var(--dur-1);
}
#topbar #btnExport:hover { background: var(--chrome-3); border-color: var(--accent); filter: none; transform: none; }

/* ---- full-bleed stage ---- */
#main { position: relative; }
#stage { min-width: 0; }
/* Level 1 — the workspace ground: a cool grey-blue a step deeper than the
   old near-panel pale, so the glass chrome and the opaque page both read
   against it. Presenting rules still win (they are more specific). */
body:not(.presenting) #canvasWrap {
  background: var(--lw-canvas);
  background-image: radial-gradient(var(--lw-canvas-dot) 1px, transparent 1px);
  background-size: 22px 22px;
}
/* Level 3 — the page: opaque, and the one surface with a real drop shadow. */
body:not(.presenting) #svgHost svg { box-shadow: var(--lw-page-shadow); }
/* The sheet lives CENTRED in the free region between the visible panels.
   The padding IS the usable-area definition (fitWorkspace in app.js measures
   it), and it now TRACKS panel state — collapse a panel and the region
   grows; the padding transition makes the sheet glide there rather than
   jump. Presenting/zen rules keep winning (body.presenting #zoomLayer
   { padding: 0 } is more specific).
   inline-flex, NOT flex: the layer must keep shrink-wrapping past 100% when
   the zoomed sheet overflows (a block-level flex box clamps to the scroller
   and the far-side padding falls out of the scroll range). margin:auto on
   the host centres while there is room and resolves to 0 in overflow, which
   is exactly the old anchored behaviour. */
#zoomLayer {
  padding: calc(var(--shell-top) + 26px) calc(var(--shell-right) + 22px) 112px calc(var(--shell-left) + 22px);
}
/* The padding glide is EDITOR-ONLY, and the opt-out must be explicit.
   A transition is chosen from the AFTER-change style: with the property
   declared unconditionally, adding body.presenting animated the editor
   padding down to 0 over 200ms — and enterPresent synchronously measures
   #zoomLayer's computed padding (stageBox), which at t=0 still reports the
   editor's ~660px, so the whole show fitted to a stage a third of its real
   size. Declaring it only while NOT presenting means entering the show has
   no transition to run and the padding is 0 the moment it is read. */
body:not(.presenting) #zoomLayer { transition: padding var(--dur-2) var(--ease-out); }
body.presenting #zoomLayer, body.watching #zoomLayer, body.recording #zoomLayer { transition: none !important; }
body:not(.presenting) #zoomLayer { display: inline-flex; }
body:not(.presenting) #svgHost { margin: auto; flex: none; }
#emptyState {
  padding: calc(var(--shell-top) + 26px) calc(var(--shell-right) + 22px) 112px calc(var(--shell-left) + 22px);
}
body:not(.presenting) #emptyState { transition: padding var(--dur-2) var(--ease-out); }
/* Stacked above the toast lane so a hint and a message never overlap. */
#hintBar { bottom: calc(var(--toast-lift, 40px) + 62px); }

/* ---- floating component library (left) ---- */
#palette {
  position: absolute; z-index: 40;
  top: var(--shell-top); left: var(--shell-gap);
  bottom: auto; max-height: calc(100% - var(--shell-top) - 14px);
  width: var(--lib-w);
  border-right: none; border: 1px solid var(--lw-surface-border); border-radius: 16px;
  background: var(--lw-surface);
  -webkit-backdrop-filter: var(--lw-blur); backdrop-filter: var(--lw-blur);
  box-shadow: var(--lw-shadow);
  padding: 10px;
  transition: width var(--dur-2) var(--ease-out);
}
.lib-head { display: flex; align-items: center; gap: 6px; }
.lib-search {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 7px;
  background: var(--chrome-2); border: 1px solid var(--border);
  border-radius: 9px; padding: 0 9px; height: 32px;
}
.lib-search svg { width: 14px; height: 14px; color: var(--muted); flex: none; }
.lib-search input {
  flex: 1; min-width: 0; background: none; border: none; outline: none;
  color: var(--text); font-size: 12.5px; height: 100%;
}
.lib-search input::placeholder { color: var(--muted); }
.lib-search:focus-within { border-color: var(--accent); }
#libCollapse {
  flex: none; width: 30px; height: 30px; border-radius: 8px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--muted);
}
#libCollapse svg { width: 15px; height: 15px; }
#libCollapse:hover { background: var(--chrome-3); color: var(--text); }
.lib-tabs {
  display: flex; gap: 2px; padding: 2px;
  background: var(--chrome-2); border: 1px solid var(--border-soft); border-radius: 9px;
}
.lib-tab {
  flex: 1; padding: 6px 4px; border-radius: 7px; font-size: 12px; font-weight: 600;
  color: var(--muted); text-align: center;
  transition: background var(--dur-1), color var(--dur-1);
}
.lib-tab:hover { color: var(--text); }
.lib-tab.active { background: var(--chrome-3); color: var(--text); box-shadow: inset 0 0 0 1px var(--border); }
.lib-empty { font-size: 12px; color: var(--muted); padding: 10px 2px; }
/* Dense list presentation: the two-column cards became one-column rows —
   icon, name, and a grip that appears on hover as the drag affordance.
   Scoped to the palette so kanban/swimlane reuse of .pal-grid elsewhere is
   untouched. */
#palGroups .pal-grid { grid-template-columns: 1fr; gap: 1px; }
#palGroups .pal-item {
  flex-direction: row; align-items: center; justify-content: flex-start;
  text-align: left; gap: 10px; min-height: 40px; padding: 6px 8px;
  background: none; border: none; border-radius: 9px; position: relative;
  transition: background var(--dur-1), color var(--dur-1);
}
#palGroups .pal-item:hover { background: var(--accent-soft); transform: none; }
#palGroups .pal-item:active { background: var(--accent-soft); }
#palGroups .pal-glyph {
  flex: none; width: 24px; display: inline-flex; align-items: center; justify-content: center;
  font-size: 16px; color: var(--muted);
}
#palGroups .pal-glyph svg { width: 20px; height: 20px; }
#palGroups .pal-item:hover .pal-glyph { color: var(--accent-ink); }
#palGroups .pal-name { font-size: 12.5px; color: var(--text); flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* drag grip — visible on hover/focus only */
#palGroups .pal-item::after {
  content: ""; flex: none; width: 7px; height: 14px; opacity: 0; margin-left: 4px;
  background-image: radial-gradient(currentColor 1px, transparent 1.2px);
  background-size: 4px 5px; color: var(--muted);
  transition: opacity var(--dur-1);
}
#palGroups .pal-item:hover::after, #palGroups .pal-item:focus-visible::after { opacity: .8; }
/* Any remaining emoji glyphs read as one quiet family; hover previews color. */
.pal-glyph { filter: grayscale(1) opacity(.85); transition: filter var(--dur-1), color var(--dur-1); }
.pal-item:hover .pal-glyph { filter: none; }
/* While a search is live every matching group shows its items, without
   writing details.open (which would fire toggle events app.js persists).
   The UA rule this overrides is `details:not([open]) > *:not(summary)`. */
body.lib-filtering #palGroups .pal-group:not([open]) > .pal-group-body { display: flex; }
body.lib-filtering #palGroups .pal-group > summary .pal-chevron { transform: rotate(90deg); }
/* group summaries: quieter headings, roomier list */
.pal-group > summary { padding: 9px 4px; }
.pal-group { border-bottom: none; padding-bottom: 2px; }

/* collapsed rail */
body.lib-collapsed #palette { width: 48px; padding: 6px; overflow: hidden; }
body.lib-collapsed #palette :is(.lib-head, .lib-tabs, #palGroups, .pal-footer) { display: none; }
body.lib-collapsed #libRailBtn { display: flex; }
#libRailBtn {
  flex: 1; width: 100%; border-radius: 10px;
  display: none; flex-direction: column; align-items: center; gap: 10px;
  padding-top: 12px; color: var(--muted);
}
#libRailBtn svg { width: 18px; height: 18px; flex: none; }
#libRailBtn span { writing-mode: vertical-rl; font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; }
#libRailBtn:hover { background: var(--chrome-2); color: var(--text); }

/* ---- floating inspector (right) ---- */
#side {
  position: absolute; z-index: 40;
  top: var(--shell-top); right: var(--shell-gap);
  bottom: auto; max-height: calc(100% - var(--shell-top) - 14px);
  border-left: none; border: 1px solid var(--lw-surface-border); border-radius: 16px;
  width: var(--insp-w);
  background: var(--lw-surface);
  -webkit-backdrop-filter: var(--lw-blur); backdrop-filter: var(--lw-blur);
  box-shadow: var(--lw-shadow);
  overflow: hidden;             /* the radius must clip the scrolling pane */
  transition: width var(--dur-2) var(--ease-out);
}
#side.collapsed { border: none; box-shadow: none; }
/* The text editor wants a tall column — content-height would collapse it to
   #codeWrap's 220px floor. Code mode pins the full working height. */
#side.code-mode:not(.collapsed) { height: calc(100% - var(--shell-top) - 14px); }
#tabs { padding: 8px 10px 0; }
.tab-note {
  padding: 7px 12px 9px; color: var(--muted); font-weight: 600; font-size: 12.5px;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  transition: color var(--dur-1);
}
.tab-note:hover { color: var(--text); }
.tab-note.on { color: var(--text); border-bottom-color: var(--accent); }
#sideExpand {
  top: auto; bottom: 76px; transform: none;
  right: var(--shell-gap); width: 34px; height: 40px;
  border-radius: 10px; border-right: 1px solid var(--lw-surface-border);
  background: var(--lw-surface);
  -webkit-backdrop-filter: var(--lw-blur); backdrop-filter: var(--lw-blur);
  box-shadow: var(--lw-shadow);
}
/* The floating notes editor keeps clear of the floating inspector. */
body:not(.presenting):has(#side:not(.collapsed)) #prNarration.show.pos-right {
  right: var(--shell-right) !important;
}

/* ---- bottom canvas dock ---- */
#edDock {
  position: absolute; z-index: 41; bottom: 14px;
  left: calc(var(--shell-left) + (100% - var(--shell-left) - var(--shell-right)) / 2);
  transform: translateX(-50%);
  display: flex; align-items: center; gap: 3px;
  padding: 5px; border-radius: 16px;
  background: var(--lw-surface);
  -webkit-backdrop-filter: var(--lw-blur); backdrop-filter: var(--lw-blur);
  border: 1px solid var(--lw-surface-border);
  box-shadow: var(--lw-shadow);
  transition: left var(--dur-2) var(--ease-out);
}
.dock-btn {
  min-width: 36px; height: 36px; padding: 0 8px; border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  color: var(--muted); font-size: 14px; line-height: 1;
  transition: background var(--dur-1), color var(--dur-1);
}
.dock-btn svg { width: 17px; height: 17px; }
.dock-btn:hover { background: var(--chrome-3); color: var(--text); }
.dock-btn.active, .dock-btn[aria-pressed="true"]:not(#dockGrid) { background: var(--accent-soft); color: var(--accent-ink); }
#dockGrid[aria-pressed="true"] { color: var(--text); }
#edDock .zoom-label { font-size: 12px; }
#btnHand.active { background: var(--accent); color: var(--on-accent); }
.dock-sep { width: 1px; height: 20px; background: var(--border); margin: 0 3px; flex: none; }
.dock-lbl { font-size: 12.5px; font-weight: 650; }
/* #miLauncher keeps its id (app.js binds it) but sheds the old round-FAB
   costume — inside the dock it is a labeled pill like its neighbours. */
#edDock #miLauncher {
  position: static; width: auto; height: 36px; border-radius: 10px;
  background: var(--accent-soft); color: var(--accent-ink);
  box-shadow: none;
}
#edDock #miLauncher:hover { transform: none; box-shadow: none; filter: brightness(1.12); }
#edDock #miLauncher svg { width: 16px; height: 16px; }
.dock-sheet-btn { display: none; }
body.no-canvas-grid #canvasWrap { background-image: none; }

/* The canvas-kit rulers adapt to the floating chrome. The horizontal ruler
   slides below the top islands (moving its TOP changes no x tick); the
   vertical ruler must NOT move (moving its top would shift every y tick), so
   the stretch that runs behind the islands is clipped away instead. Both are
   clipped out of the panel zones so they stop ghosting through the glass,
   and both lose the heavy fill — the workspace has no fixed dividers now. */
body:not(.embed-mode) #ckRulerH {
  top: var(--shell-top) !important;
  transition: top var(--dur-2) var(--ease-out), clip-path var(--dur-2) var(--ease-out);
  clip-path: inset(0 calc(var(--shell-right) - 6px) 0 calc(var(--shell-left) - 6px));
  background: transparent;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  border-bottom: none;
}
body:not(.embed-mode) #ckRulerV {
  left: calc(var(--shell-left) - 10px) !important;
  transition: left var(--dur-2) var(--ease-out), clip-path var(--dur-2) var(--ease-out);
  clip-path: inset(calc(var(--shell-top) + 22px) 0 66px 0);
  background: transparent;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  border-right: none;
}

/* Every mode that hides the old chrome hides the dock too. */
body.presenting #edDock, body.watching #edDock, body.recording #edDock,
body.embed-mode #edDock, body.sample-locked #edDock, body.present-link #edDock { display: none !important; }

/* A left-docked comment pane owns the left edge: the library glides away
   while the pane is open (same idea as watch mode, scoped by :has). */
body:has(#stage.cp-left #commentPane:not([hidden])) #palette { transform: translateX(calc(-100% - 20px)); opacity: 0; pointer-events: none; }
#palette { transition: width var(--dur-2) var(--ease-out), transform var(--dur-2) var(--ease-out), opacity var(--dur-2) var(--ease-out); }

/* ---- medium widths: one expanded panel at a time (js/editor-shell.js
   enforces the exchange; these are the sizes) ---- */
@media (max-width: 1150px) {
  body { --canvas-left: 98px; }   /* legacy var, still read by nothing critical */
}

/* ---- small screens: canvas stays full-bleed, panels become bottom sheets ---- */
@media (max-width: 760px) {
  body, body:has(#side.code-mode:not(.collapsed)), body:has(#side.collapsed), body.lib-collapsed {
    --shell-top: 58px; --canvas-left: 16px; --shell-left: 8px; --shell-right: 8px;
  }
  #zoomLayer { padding: calc(var(--shell-top) + 10px) 16px 96px 16px; }
  #emptyState { padding: calc(var(--shell-top) + 10px) 16px 96px 16px; }
  #topbar { padding: 8px 8px 0; }
  .tb-island { min-height: 42px; border-radius: 13px; }
  #topbar .save-state, #topbar #autoSaveWrap, #topbar #btnCommentPane { display: none; }
  #topbar #btnExport { padding: 7px 10px; }
  #topbar #docTitle { min-width: 56px; max-width: 120px; }

  /* sheets */
  #palette, #side {
    position: fixed; z-index: 60; left: 0; right: 0; bottom: 0; top: auto;
    width: auto !important; max-height: 68dvh;
    border-radius: 18px 18px 0 0; border-bottom: none;
    transform: translateY(105%);
    transition: transform var(--dur-2) var(--ease-out);
    box-shadow: var(--shadow-lg);
  }
  body.sheet-lib-open #palette { transform: none; }
  body.sheet-insp-open #side { transform: none; }
  /* Desktop collapse state has no meaning for a bottom sheet — reset every
     part of it, including the overflow that made the sheet unscrollable and
     the comment-pane glide that parked it off-screen. */
  body.lib-collapsed #palette { width: auto !important; padding: 10px; overflow-y: auto; }
  body:has(#stage.cp-left #commentPane:not([hidden])) #palette { transform: none; opacity: 1; pointer-events: auto; }
  body.lib-collapsed #palette :is(.lib-head, .lib-tabs, #palGroups, .pal-footer) { display: revert; }
  body.lib-collapsed #palette #palGroups { display: flex; }
  body.lib-collapsed #libRailBtn { display: none; }
  #side.collapsed { width: auto !important; border: 1px solid var(--border); overflow: hidden; }
  #libCollapse, #sideCollapse, #sideExpand { display: none !important; }
  .dock-sheet-btn { display: inline-flex; }
  .dock-sheet-btn[aria-expanded="true"] { background: var(--accent-soft); color: var(--accent-ink); }
  #dockMinimap, #dockSelect { display: none; }   /* touch: no minimap, selection is implicit */
  #edDock { left: 50%; bottom: max(10px, env(safe-area-inset-bottom)); max-width: calc(100vw - 16px); overflow-x: auto; }
  .dock-btn { min-width: 44px; height: 44px; }
  .tab, .tab-note { min-height: 44px; }
  .pal-item { min-height: 44px; }
  /* Touch floors for everything inside the bottom sheets and popovers. */
  .lib-tab { min-height: 44px; }
  .sw-trigger, .style-trigger { min-height: 44px; }
  .sw-pop .swatch, .sw-pop .swatch-custom, .sw-recent-chip { width: 30px; height: 30px; }
  #topbar .tb-btn, #topbar .btn-primary, #topbar .btn-present { min-height: 40px; }
}

/* ---- motion discipline ---- */
@media (prefers-reduced-motion: reduce) {
  #palette, #side, #edDock, .tb-island, #zoomLayer, #emptyState,
  body:not(.embed-mode) #ckRulerH, body:not(.embed-mode) #ckRulerV { transition: none; }
}

/* ---- compact Style controls + popovers (theme picker, quick decoration) ---- */
.style-trigger {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 8px 10px; border-radius: 9px;
  background: var(--chrome-2); border: 1px solid var(--border);
  color: var(--text); font-size: 12.5px; text-align: left;
  transition: border-color var(--dur-1), background var(--dur-1);
}
.style-trigger:hover { border-color: var(--accent); background: var(--chrome-3); }
.style-trigger[aria-expanded="true"] { border-color: var(--accent); }
.style-trigger-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.style-caret { font-size: 10px; color: var(--muted); flex: none; }
.dp-cur-swatch {
  width: 14px; height: 14px; flex: none; border-radius: 4px;
  border: 1px solid var(--accent); background: var(--accent-soft);
}
.style-pop {
  position: fixed; z-index: 210;
  overflow-y: auto; overscroll-behavior: contain;
  padding: 10px; border-radius: 14px;
  background: var(--lw-surface);
  -webkit-backdrop-filter: var(--lw-blur); backdrop-filter: var(--lw-blur);
  border: 1px solid var(--lw-surface-border);
  box-shadow: var(--lw-shadow);
  animation: ck-pop var(--dur-1) var(--ease-out);
}
/* Topbar dropdown panels share the recipe (major popovers only). */
#topbar .tb-menu-panel {
  background: var(--lw-surface);
  -webkit-backdrop-filter: var(--lw-blur); backdrop-filter: var(--lw-blur);
  border-color: var(--lw-surface-border);
  box-shadow: var(--lw-shadow);
  border-radius: 12px;
}
.style-pop[hidden] { display: none; }
.style-pop-h {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .08em;
  color: var(--muted); padding: 0 2px 8px;
}
/* Bigger theme previews inside the popover: the theme's own sheet with two
   node chips and its arrow ink — a miniature of the look, not a color dot. */
.style-pop-themes .theme-grid { grid-template-columns: 1fr 1fr 1fr; }
.style-pop-themes .theme-card { flex-direction: column; align-items: stretch; gap: 6px; padding: 6px; }
.theme-prev {
  display: flex; align-items: center; justify-content: center; gap: 3px;
  height: 40px; border-radius: 6px; border: 1px solid rgba(0, 0, 0, .12);
}
.theme-prev i { width: 13px; height: 10px; border-radius: 2.5px; border: 1px solid transparent; }
.theme-prev u { width: 9px; height: 1.5px; border-radius: 1px; }
.theme-card-name { font-size: 11px; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.theme-card.active .theme-card-name { font-weight: 700; }
#dpPop .dp-grid { grid-template-columns: repeat(auto-fill, minmax(92px, 1fr)); }
@media (prefers-reduced-motion: reduce) { .style-pop { animation: none; } }

/* The editor-mode notes column starts below the floating top islands and
   picks up the floating-surface look (present mode is untouched — these are
   body:not(.presenting) scoped). */
body:not(.presenting):not(.watching) #prNarration.show {
  top: var(--shell-top);
  border-radius: 14px 14px 0 0;
  box-shadow: var(--shadow-sm);
}

/* Touch widths: the authoring rulers (and their stage bands) are a desktop
   aid — on a phone they cut a tick strip through the middle of the canvas. */
@media (max-width: 760px) {
  #ckRulerH, #ckRulerV, #ckGuides { display: none !important; }
}

/* Phone squeeze: one row of islands, no horizontal overflow. Every hidden
   control keeps a living twin — Save lives in Menu → File, sign-in on the
   home header, Present keeps its icon, zoom keeps pinch + Fit. */
@media (max-width: 560px) {
  #topbar #btnSaveDoc, #topbar .acct-btn { display: none; }
  #topbar #btnPresent .bp-label { display: none; }
  #topbar #btnPresent { padding: 6px 9px; }
  #topbar .tb-island { gap: 3px; }
}
@media (max-width: 430px) {
  /* The title stays EDITABLE — a document must be nameable on a phone; it
     just gives up its spare width. */
  #topbar #docTitle { min-width: 44px; max-width: 90px; }
  #edDock { gap: 1px; padding: 4px; }
  #edDock #btnZoomOut, #edDock #btnZoomIn, #edDock .dock-lbl { display: none; }
  .dock-sep { margin: 0 1px; }
}

/* present-guide.js mounts its frame toggle beside #btnZoomFit, which now
   lives in the dock — dress the mounted .tb-btn as a dock button. */
#edDock #btnPresentGuide {
  min-width: 36px; height: 36px; padding: 0 8px; border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--muted); font-size: 15px;
}
#edDock #btnPresentGuide:hover { background: var(--chrome-3); color: var(--text); }
#edDock #btnPresentGuide[aria-pressed="true"] { background: var(--accent-soft); color: var(--accent-ink); }
@media (max-width: 760px) {
  #edDock #btnPresentGuide { min-width: 44px; height: 44px; }
}

/* ---- inspector typography & rhythm (8px system) ----
   Field labels drop the all-caps treatment — section headers keep a QUIET
   small-caps voice, so the two levels finally read as two levels. Scoped to
   the side panel so modal forms elsewhere keep their look. */
#side .field > label {
  text-transform: none; letter-spacing: 0;
  font-size: 12px; font-weight: 600; color: var(--muted);
}
#side .tabpane { padding: 12px 14px 16px; gap: 12px; }
#side .cs-grp { margin: 20px 0 2px; }
#side .cs-grp:first-child { margin-top: 2px; }
#side .cs-grp span { font-size: 11px; }
#side .field input[type="text"], #side .field input[type="number"],
#side .field select, #side .field textarea { padding: 6px 9px; }
#side .seg-btn { padding: 5px 4px; }
/* Advanced groups: whole sections fold behind a quiet disclosure. */
.cs-adv { border: 1px solid var(--border-soft); border-radius: 10px; }
.cs-adv > summary {
  list-style: none; cursor: pointer; user-select: none; -webkit-user-select: none;
  display: flex; align-items: center; gap: 8px;
  padding: 9px 11px; font-size: 12.5px; font-weight: 600; color: var(--muted);
  border-radius: 10px;
  transition: color var(--dur-1), background var(--dur-1);
}
.cs-adv > summary::-webkit-details-marker { display: none; }
.cs-adv > summary:hover { color: var(--text); background: var(--chrome-2); }
.cs-adv > summary .cs-adv-chev { margin-left: auto; transition: transform var(--dur-1); font-size: 10px; }
.cs-adv[open] > summary .cs-adv-chev { transform: rotate(90deg); }
.cs-adv-body { display: flex; flex-direction: column; gap: 12px; padding: 4px 11px 12px; }
@media (prefers-reduced-motion: reduce) { .cs-adv > summary .cs-adv-chev { transition: none; } }

/* Side-docked comment pane: clear the floating top islands so its own header
   row stays reachable (bottom dock keeps its own clearance). */
body:not(.presenting):not(.watching) #stage.cp-right #commentPane,
body:not(.presenting):not(.watching) #stage.cp-left  #commentPane {
  margin-top: var(--shell-top);
  border-radius: 14px 14px 0 0;
  border-top: 1px solid var(--border);
}

/* ---- compact colour control (swatchesHTML rows) ----
   The row shows a small trigger with the ACTIVE colour; the full swatch set
   lives in a floating popover reparented to <body> while open
   (js/editor-shell.js). The .swatch buttons inside keep their classes and
   bound listeners — this is presentation only. */
.sw-compact { position: relative; }
.sw-trigger {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 6px 10px; border-radius: 9px;
  background: var(--chrome-2); border: 1px solid var(--border);
  color: var(--text); font-size: 12px; text-align: left;
  transition: border-color var(--dur-1), background var(--dur-1);
}
.sw-trigger:hover { border-color: var(--accent); background: var(--chrome-3); }
.sw-trigger[aria-expanded="true"] { border-color: var(--accent); }
.sw-cur {
  flex: none; width: 16px; height: 16px; border-radius: 5px;
  border: 1px solid rgba(128, 128, 160, .35);
}
.sw-cur-none {
  background:
    linear-gradient(to top left, transparent 44%, var(--danger) 46%, var(--danger) 54%, transparent 56%),
    var(--chrome-3);
}
.sw-cur-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: var(--mono); font-size: 11px; color: var(--muted); }
.sw-pop {
  position: fixed; z-index: 214;
  padding: 10px; border-radius: 12px;
  background: var(--lw-surface);
  -webkit-backdrop-filter: var(--lw-blur); backdrop-filter: var(--lw-blur);
  border: 1px solid var(--lw-surface-border);
  box-shadow: var(--lw-shadow);
  animation: ck-pop var(--dur-1) var(--ease-out);
}
.sw-pop[hidden] { display: none; }
.sw-pop-row { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.sw-recent { margin-top: 9px; padding-top: 8px; border-top: 1px solid var(--border-soft);
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.sw-recent[hidden] { display: none; }
.sw-recent-h { flex-basis: 100%; font-size: 10.5px; font-weight: 600; color: var(--muted); }
.sw-recent-chip {
  width: 22px; height: 22px; border-radius: 6px; border: 2px solid transparent; padding: 0;
  cursor: pointer; transition: transform var(--dur-1);
}
.sw-recent-chip:hover { transform: scale(1.12); }
/* Per-step narration popup, opened from a selected shape's quick bar. Small,
   dismissible, and deliberately NOT a modal — you keep looking at the diagram
   while writing what it should say. */
.step-notes-pop {
  position: fixed; z-index: 216; width: 300px; max-width: calc(100vw - 16px);
  padding: 11px; border-radius: 12px;
  background: var(--lw-surface, var(--panel));
  -webkit-backdrop-filter: var(--lw-blur); backdrop-filter: var(--lw-blur);
  border: 1px solid var(--lw-surface-border, var(--border));
  box-shadow: var(--lw-shadow, 0 10px 30px rgba(0,0,0,.35));
}
.step-notes-pop .snp-h { font-size: 11px; font-weight: 650; color: var(--text);
  margin-bottom: 7px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.step-notes-pop .snp-text {
  width: 100%; box-sizing: border-box; min-height: 84px; resize: vertical;
  font: inherit; font-size: 12.5px; line-height: 1.4; padding: 7px 8px;
  border-radius: 8px; border: 1px solid var(--border);
  background: var(--panel-2); color: var(--text);
}
.step-notes-pop .snp-hint { font-size: 11.5px; color: var(--muted); line-height: 1.4; }
.step-notes-pop .snp-foot { display: flex; justify-content: space-between; align-items: center; margin-top: 8px; }
.step-notes-pop .snp-link { background: none; border: 0; padding: 0; cursor: pointer;
  font-size: 11.5px; color: var(--iris, #6a5cff); }
.step-notes-pop .snp-done { font-size: 11.5px; padding: 4px 12px; border-radius: 7px; cursor: pointer;
  border: 0; background: var(--iris, #6a5cff); color: #fff; }

/* Studio narration picker — attaches locally baked audio to the open
   document. Sits at the top of the Notes tab because it decides HOW every
   note below it is spoken. */

/* Notes pane — a tab beside Design, not a window over the canvas. Scrolls
   with the panel like every other pane. */
#notesPane { overflow-y: auto; }
.notes-pane-h { font-size: 11px; color: var(--muted); line-height: 1.4;
  padding: 2px 2px 10px; border-bottom: 1px solid var(--border-soft); margin-bottom: 10px; }

/* ---- target size guide (canvas-kit) ----
   A LIGHT DOTTED OUTLINE, nothing more. The previous on-canvas guide was
   removed for drawing a heavy cross through people's diagrams; this one has no
   fill and no centre lines, sits under every floating panel, and never takes a
   pointer event. It is chrome — a DOM element, never part of the diagram SVG,
   so it cannot reach an export. */
#ckFrame {
  position: fixed; z-index: 2; pointer-events: none;
  border: 1px dotted color-mix(in srgb, var(--iris, #6a5cff) 55%, transparent);
  border-radius: 3px;
}
/* INSIDE the frame: the clip-path that keeps the guide off the panels also
   removes anything drawn outside its own box, so a label hung above the top
   edge vanishes exactly when the frame meets the top of the canvas. */
/* Authoring chrome, never part of a show. The JS guard clears these on the
   next re-render, but entering Explain does not itself trigger one — so the
   rule that actually holds during a presentation is this one. */
body.presenting #ckFrame,
body.presenting #ckTargetMask { display: none !important; }

/* The focus mask: the device area reads as the working surface. Very faint —
   the earlier on-canvas guide was deleted for competing with the drawing. */
#ckTargetMask { position: fixed; inset: 0; z-index: 1; pointer-events: none; }
#ckTargetMask i { position: fixed; display: block; background: var(--canvas-bg, #0b0d14); opacity: .16; }
@media (prefers-color-scheme: light) { #ckTargetMask i { opacity: .10; } }

/* The live verdict — whether the diagram fits the device and whether its
   smallest label survives being looked at there. Inside the frame so the
   clip-path keeps it, and the only part that takes a pointer. */
#ckFrame .ck-frame-advice {
  position: absolute; left: 6px; bottom: 6px; max-width: calc(100% - 12px);
  display: flex; align-items: center; gap: 8px; pointer-events: auto;
  padding: 4px 6px 4px 8px; border-radius: 8px;
  background: var(--lw-surface, var(--panel)); border: 1px solid var(--border-soft);
  font-size: 10.5px; line-height: 1.3; color: var(--muted);
}
#ckFrame[data-tone="warn"] .ck-frame-advice { color: var(--text); border-color: #b4232a; }
#ckFrame[data-tone="warn"] .ck-adv-msg::before { content: '⚠ '; color: #b4232a; }
#ckFrame[data-tone="ok"] .ck-adv-msg::before { content: '✓ '; }
#ckFrame .ck-adv-msg { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#ckFrame .ck-adv-fit {
  flex: 0 0 auto; cursor: pointer; font-size: 10.5px; padding: 2px 8px; border-radius: 6px;
  border: 1px solid var(--border); background: var(--panel-2); color: var(--text);
}
#ckFrame .ck-adv-fit:hover { background: var(--panel); }

#ckFrame .ck-frame-tag {
  position: absolute; top: 4px; left: 6px;
  font-size: 9.5px; letter-spacing: .04em; white-space: nowrap;
  color: var(--muted); opacity: .85;
}
@supports not (color: color-mix(in srgb, red 50%, transparent)) {
  #ckFrame { border-color: rgba(106, 92, 255, .55); }
}

/* The palette-swap opt-out. Sits under the palette grid, unchecked, with the
   consequence spelled out — a control that discards work must say so. */
.theme-wipe { display: flex; gap: 8px; align-items: flex-start; margin: 10px 2px 2px;
  padding: 8px 9px; border-radius: 9px; background: var(--panel-2);
  border: 1px solid var(--border-soft); cursor: pointer; }
.theme-wipe input { margin-top: 2px; flex: 0 0 auto; }
.theme-wipe span { font-size: 11.5px; color: var(--text); line-height: 1.35; }
.theme-wipe small { display: block; margin-top: 2px; font-size: 10.5px; color: var(--muted); }

/* ---- the universal colour picker (js/color-picker.js) ----
   Injected into the same .sw-pop the compact control already owns, so the
   trigger, the reparenting and the outside/Escape close are unchanged. The pop
   grows a lot taller, hence the cap + scroll: a colour control must never run
   off the bottom of a short window. Tokens are the real ones from the design
   system — --chrome/--panel/--text/--muted/--border-soft — never invented
   names, which fail silently to their fallback. */
.sw-pop { max-height: min(70vh, 560px); overflow-y: auto; overscroll-behavior: contain; }
.sw-more { margin-top: 2px; }
.sw-sec { margin-top: 9px; padding-top: 8px; border-top: 1px solid var(--border-soft);
  display: flex; flex-wrap: wrap; gap: 5px; align-items: center; }
.sw-sec[hidden] { display: none; }
.sw-sec-h { flex: 0 0 100%; font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 3px; }
.sw-chip { width: 20px; height: 20px; border-radius: 6px; border: 1px solid var(--border);
  cursor: pointer; padding: 0; }
.sw-chip:hover, .sw-chip:focus-visible { outline: 2px solid var(--accent, #6d7cff); outline-offset: 1px; }
/* A derived colour is marked, not just coloured — the corner notch says the
   palette computed this one rather than an author choosing it. */
.sw-chip-derived { position: relative; }
.sw-chip-derived::after { content: ''; position: absolute; right: -1px; bottom: -1px;
  width: 7px; height: 7px; border-radius: 0 0 5px 0;
  background: linear-gradient(135deg, transparent 50%, var(--panel) 50%); }
.sw-custom { flex-direction: column; align-items: stretch; }
.sw-hex { width: 100%; box-sizing: border-box; font-family: var(--mono, ui-monospace, monospace);
  font-size: 12px; padding: 5px 7px; border-radius: 7px;
  border: 1px solid var(--border); background: var(--panel-2); color: var(--text); }
.sw-num-row { display: flex; gap: 5px; margin-top: 5px; }
.sw-num { flex: 1 1 0; display: flex; align-items: center; gap: 3px; font-size: 10.5px; color: var(--muted); }
.sw-num input { width: 100%; min-width: 0; box-sizing: border-box; font-size: 11.5px; padding: 3px 4px;
  border-radius: 6px; border: 1px solid var(--border); background: var(--panel-2); color: var(--text); }
.sw-actions { display: flex; gap: 5px; margin-top: 7px; }
.sw-act { flex: 1 1 auto; font-size: 11.5px; padding: 5px 8px; border-radius: 7px; cursor: pointer;
  border: 1px solid var(--border); background: var(--panel-2); color: var(--text); }
.sw-act:hover { background: var(--panel); }
.sw-act-primary { background: var(--accent, #6d7cff); border-color: transparent; color: #fff; }
.sw-preview { display: flex; align-items: center; gap: 6px; margin-top: 9px; }
.sw-prev-chip { width: 34px; height: 24px; border-radius: 6px; border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; }
.sw-prev-ink { padding: 0 3px; border-radius: 3px; }
.sw-prev-note { font-size: 11px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sw-contrast { margin-top: 6px; font-size: 11px; line-height: 1.35; }
.sw-contrast-ok { color: var(--muted); }
/* A warning, never a block — the spec is explicit that an intentional choice
   still applies. Carries a ⚠ glyph so the state is not colour alone. */
.sw-contrast-warn { color: #b4232a; }
.sw-state { margin-top: 6px; font-size: 10.5px; color: var(--muted); line-height: 1.35; }
.sw-state-custom { color: var(--text); }
@media (prefers-reduced-motion: reduce) { .sw-pop { animation: none; } .sw-recent-chip:hover { transform: none; } }

/* ============================================================
   INFINITE WORKSPACE (2026-08, pass 3)
   ------------------------------------------------------------
   The editor is a spatial world, not a document with a page in it.

   • The ARTBOARD is gone from the editor. Every renderer paints its page as
     one rect tagged [data-kind="bg"] (13 renderers, exactly one each) — we
     hide that rect on screen and let the world grid show through. Document
     CSS neither serializes into exported SVG nor applies to a rasterized
     copy, so SVG / PNG / GIF / video and Present keep painting the page
     exactly as before. Nothing about export geometry changes.
   • The GRID lives on #zoomLayer (the scrolling content box), so it travels
     with the world instead of sitting still behind it, and it tiles across
     the whole pan space with no seam or restart.
   • PAN SPACE is --pan on every side, so the world continues far past the
     drawing in all four directions. fitWorkspace subtracts it, so the
     usable-area maths from pass 2 is untouched.
   ============================================================ */

body {
  --pan: 1400px;                     /* world margin around the drawing */
  --grid-minor: 22px;                /* set from JS per zoom (setZoom) */
  --grid-dot: rgba(41, 48, 82, 0.30);
  --grid-major: rgba(41, 48, 82, 0.46);
  --canvas-world: #ced3e6;           /* cool neutral, slight blue-violet tint */
  --canvas-glow: rgba(255, 255, 255, 0.18);
}
[data-ui-theme="light"] body {
  --grid-dot: rgba(44, 51, 88, 0.26);
  --grid-major: rgba(44, 51, 88, 0.40);
  --canvas-world: #d6dae9;
  --canvas-glow: rgba(255, 255, 255, 0.30);
}

/* ---- the world ---- */
body:not(.presenting) #canvasWrap {
  background-color: var(--canvas-world);
  /* A soft radial lift under the middle of the view, so the working area
     reads as illuminated without a tile or a seam anywhere. Painted on the
     SCROLLER (not the content) so it stays put as a light source. */
  background-image: radial-gradient(120% 110% at 50% 42%, var(--canvas-glow) 0%, transparent 70%);
  background-repeat: no-repeat;
  background-size: 100% 100%;
}
/* The grid travels with the world. Two layers: minor dots and a major marker
   every 8 minor intervals, both one device pixel at any zoom. */
body:not(.presenting):not(.no-canvas-grid) #zoomLayer {
  background-image:
    radial-gradient(circle at center, var(--grid-major) var(--grid-major-r, 1.15px), transparent var(--grid-major-r, 1.15px)),
    radial-gradient(circle at center, var(--grid-dot) var(--grid-dot-r, 0.9px), transparent var(--grid-dot-r, 0.9px));
  background-size:
    calc(var(--grid-minor) * 8) calc(var(--grid-minor) * 8),
    var(--grid-minor) var(--grid-minor);
  background-position: var(--grid-origin-x, 0px) var(--grid-origin-y, 0px);
  background-attachment: local, local;
}
/* Zoomed far out the minor dots turn to mud — drop them and keep the majors. */
body.grid-coarse:not(.presenting):not(.no-canvas-grid) #zoomLayer {
  background-image: radial-gradient(circle at center, var(--grid-major) var(--grid-major-r, 1.15px), transparent var(--grid-major-r, 1.15px));
  background-size: calc(var(--grid-minor) * 8) calc(var(--grid-minor) * 8);
}
/* The two painting rules above now exclude .no-canvas-grid themselves — the
   old `body.no-canvas-grid #zoomLayer` override was (0,2,1) and lost to the
   coarse rule's (0,3,1), so the dock's grid toggle silently did nothing
   whenever the view was zoomed out far enough to be in coarse mode. */
body.no-canvas-grid #canvasWrap { background-image: none; }

/* ---- the drawing sits directly on the world ---- */
/* The page rect, hidden on screen only. Exports and Present still paint it. */
body:not(.presenting):not(.watching):not(.recording) #svgHost svg [data-kind="bg"] { display: none; }
/* …and with no page there is no page furniture: no card shadow, no radius. */
body:not(.presenting) #svgHost svg {
  box-shadow: none;
  border-radius: 0;
  overflow: visible;
}

/* ---- pan space in every direction ---- */
body:not(.presenting) #zoomLayer {
  padding:
    calc(var(--pan) + var(--shell-top) + 26px)
    calc(var(--pan) + var(--shell-right) + 22px)
    calc(var(--pan) + 112px)
    calc(var(--pan) + var(--shell-left) + 22px);
}
@media (max-width: 760px) {
  body:not(.presenting) #zoomLayer {
    padding: calc(var(--pan) + var(--shell-top) + 10px) calc(var(--pan) + 16px)
             calc(var(--pan) + 96px) calc(var(--pan) + 16px);
  }
}

/* ---- rulers must not draw a page edge ---- */
/* They fade toward the floating panels rather than stopping at a hard line. */
body:not(.embed-mode):not(.presenting) #ckRulerH {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 46px, #000 calc(100% - 46px), transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, #000 46px, #000 calc(100% - 46px), transparent 100%);
}
body:not(.embed-mode):not(.presenting) #ckRulerV {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 46px, #000 calc(100% - 46px), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 46px, #000 calc(100% - 46px), transparent 100%);
}

/* ---- export frame (opt-in, never a white page) ---- */
#expFrame {
  position: absolute; z-index: 3; pointer-events: none;
  border: 1px dashed color-mix(in srgb, var(--accent) 58%, transparent);
  border-radius: 2px;
}
#expFrame[hidden] { display: none; }
body.frame-focus #expFrame { box-shadow: 0 0 0 100vmax color-mix(in srgb, #0a0d18 38%, transparent); }
/* Bottom-RIGHT: the aspect guide's own verdict badge owns the top-left, and
   two labels stacked there overlapped into noise. */
#expFrameTag {
  position: absolute; right: 0; top: 100%; margin-top: 6px;
  padding: 3px 8px; border-radius: 6px; white-space: nowrap;
  font: 600 10.5px/1.4 var(--mono);
  color: var(--muted); background: var(--lw-surface);
  -webkit-backdrop-filter: var(--lw-blur); backdrop-filter: var(--lw-blur);
  border: 1px solid var(--lw-surface-border);
}
body.presenting #expFrame, body.watching #expFrame, body.recording #expFrame,
body.embed-mode #expFrame { display: none !important; }

@media (prefers-reduced-motion: reduce) {
  #zoomLayer, #canvasWrap { transition: none; }
}

/* ---- six-theme selector (pass 3) ----
   A theme is a whole visual system, so its preview shows the system: canvas,
   heading type, two node kinds at the theme's own radius, a primary and a
   secondary connector, and a note. Six large cards, not two dozen chips. */
.style-pop-themes { width: 340px; }
.style-pop-themes > .theme-grid { display: flex; flex-direction: column; gap: 8px; }
.theme-card6 {
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 7px; border-radius: 11px; text-align: left;
  background: var(--chrome-2); border: 1px solid var(--border-soft);
  transition: border-color var(--dur-1), background var(--dur-1);
}
.theme-card6:hover { border-color: var(--accent); background: var(--chrome-3); }
.theme-card6.active { border-color: var(--accent); background: var(--accent-soft); }
.theme-card6:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tc6-prev { flex: none; width: 116px; height: 65px; border-radius: 7px; overflow: hidden;
  border: 1px solid var(--border); display: block; }
.tc6-prev svg { display: block; width: 100%; height: 100%; }
.tc6-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.tc6-meta b { font-size: 13px; font-weight: 650; color: var(--text); }
.tc6-meta small { font-size: 11px; color: var(--muted); }
/* legacy: kept, clearly labelled, and never the default choice */
.theme-legacy {
  display: flex; flex-direction: column; gap: 5px; margin-top: 10px;
  padding: 9px 11px; border-radius: 10px;
  background: color-mix(in srgb, #f59e0b 10%, transparent);
  border: 1px solid color-mix(in srgb, #f59e0b 34%, var(--border));
}
.theme-legacy b { font-size: 11.5px; color: var(--text); }
.theme-legacy span { font-size: 11.5px; line-height: 1.45; color: var(--muted); }
.theme-legacy-try {
  align-self: flex-start; margin-top: 2px; padding: 5px 10px; border-radius: 7px;
  font-size: 11.5px; font-weight: 600; color: var(--accent-ink);
  background: var(--accent-soft); border: 1px solid var(--accent-ring);
}
.theme-legacy-all { margin-top: 10px; }
.theme-legacy-all > summary {
  list-style: none; cursor: pointer; font-size: 11.5px; font-weight: 600;
  color: var(--muted); padding: 7px 2px;
}
.theme-legacy-all > summary::-webkit-details-marker { display: none; }
.theme-legacy-all > summary:hover { color: var(--text); }
.theme-grid-legacy { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; padding-top: 4px; }

/* A dark theme's world needs a light glow lift, not a white one. */
body.world-dark { --canvas-glow: rgba(150, 170, 230, 0.10); }

/* ============================================================
   HOMEPAGE SHOWCASE — "See diagrams come alive"
   Stage ~70% + panel on desktop; stage-first and swipeable on touch.
   The stage box is reserved from the bundle's own aspect ratio, so a slide
   swap never shifts the page (CLS stays at zero).
   ============================================================ */
.lp-showcase { position: relative; }
.sc-reassure {
  display: inline-flex; align-items: center; gap: 7px;
  margin: 2px 0 22px; padding: 5px 12px 5px 10px; border-radius: 999px;
  font-size: 12.5px; font-weight: 550; color: var(--muted);
  background: var(--panel); border: 1px solid var(--edge);
}
.sc-reassure svg { width: 14px; height: 14px; flex: none; color: var(--accent-ink); }

.sc-grid { display: grid; grid-template-columns: minmax(0, 1fr) 310px; gap: 22px; align-items: start; }
.sc-stage-wrap { min-width: 0; }

.sc-stage {
  position: relative; width: 100%;
  aspect-ratio: var(--sc-ar, 16 / 9);
  /* HEIGHT-LED sizing. Left to fill the column, a 1.79 diagram on a 1380px
     stage stood ~770px tall and swallowed the section. The cap governs, and
     max-width is that cap times the diagram's own ratio, so the box shrinks
     proportionally and centres — no letterbox gutters, and the reserved
     aspect still prevents layout shift. */
  --sc-maxh: clamp(240px, 38vh, 430px);
  max-height: var(--sc-maxh);
  max-width: calc(var(--sc-maxh) * var(--sc-arn, 1.7778));
  margin-inline: auto;
  border-radius: 16px; overflow: hidden;
  background: var(--panel);
  border: 1px solid var(--edge);
  box-shadow: 0 18px 50px -22px rgba(2, 4, 14, .6);
}
.sc-stage:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.sc-layer { position: absolute; inset: 0; }
.sc-layer svg { display: block; width: 100%; height: 100%; }
.sc-front { opacity: 0; }
.sc-fail {
  position: absolute; inset: 0; display: none;
  align-items: center; justify-content: center;
  font-size: 13px; color: var(--muted);
}
.sc-stage.sc-failed .sc-fail { display: flex; }

.sc-progress { height: 3px; margin: 12px auto 12px; max-width: calc(var(--sc-maxh, 430px) * var(--sc-arn, 1.7778)); border-radius: 2px; background: var(--edge); overflow: hidden; }
.sc-progress i { display: block; height: 100%; width: 0; border-radius: 2px; background: var(--accent);
  transition: width 260ms cubic-bezier(.22,1,.36,1); }

.sc-nav { display: flex; flex-wrap: wrap; gap: 6px; }
.sc-chip {
  padding: 6px 12px; border-radius: 999px; font-size: 12.5px; font-weight: 600;
  color: var(--muted); background: var(--panel); border: 1px solid var(--edge);
  transition: color var(--dur-1), border-color var(--dur-1), background var(--dur-1);
}
.sc-chip:hover { color: var(--text); border-color: var(--slate-2); }
.sc-chip.on { color: var(--on-accent); background: var(--accent); border-color: var(--accent); }
.sc-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.sc-panel {
  background: var(--panel); border: 1px solid var(--edge); border-radius: 16px;
  padding: 18px 18px 16px; display: flex; flex-direction: column; gap: 9px;
}
.sc-kind { font: 600 10.5px/1 var(--mono); letter-spacing: .12em; text-transform: uppercase; color: var(--accent-ink); margin: 0; }
.sc-title { font: 650 19px/1.24 var(--sans); letter-spacing: -.015em; color: var(--text); margin: 0; }
.sc-summary { font-size: 13.5px; line-height: 1.55; color: var(--muted); margin: 0; }
.sc-meta { font: 500 11.5px/1.5 var(--mono); color: var(--faint); margin: 2px 0 4px; }
.sc-actions { display: flex; flex-direction: column; gap: 7px; }
.sc-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 38px; padding: 8px 14px; border-radius: 10px;
  font-size: 13px; font-weight: 600; text-decoration: none; text-align: center;
  border: 1px solid var(--edge); color: var(--text); background: none;
  transition: background var(--dur-1), border-color var(--dur-1);
}
.sc-btn-ghost:hover { background: var(--panel-2); border-color: var(--slate-2); }
.sc-btn-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.sc-btn-primary:hover { filter: brightness(1.07); }
.sc-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.sc-transport { display: flex; align-items: center; gap: 6px; margin-top: 4px; padding-top: 12px; border-top: 1px solid var(--edge); }
.sc-ico {
  width: 38px; height: 38px; border-radius: 10px; display: inline-flex;
  align-items: center; justify-content: center; color: var(--muted);
  border: 1px solid var(--edge); background: none;
  transition: color var(--dur-1), background var(--dur-1);
}
.sc-ico svg { width: 17px; height: 17px; fill: currentColor; }
.sc-ico:hover { color: var(--text); background: var(--panel-2); }
.sc-ico:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sc-pos { margin-left: auto; font: 600 12px/1 var(--mono); color: var(--faint); font-variant-numeric: tabular-nums; }
/* One icon at a time: pressed = playing, so show Pause. */
.sc-i-play { display: none; }
#scPlay[aria-pressed="false"] .sc-i-play { display: block; }
#scPlay[aria-pressed="false"] .sc-i-pause { display: none; }

.sc-cta { margin-top: 26px; padding-top: 20px; border-top: 1px solid var(--edge); text-align: center; }
.sc-cta-h { font: 650 17px/1.3 var(--sans); color: var(--text); margin: 0 0 12px; }
.sc-cta-row { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.sc-cta .sc-btn { min-width: 172px; }

/* slide swap */
.sc-swap .sc-stage { animation: sc-in 260ms cubic-bezier(.22,1,.36,1); }
@keyframes sc-in { from { opacity: .55; transform: translateY(4px); } to { opacity: 1; transform: none; } }

/* ---- tablet: stage above, panel below, selectors scroll ---- */
@media (max-width: 980px) {
  .sc-grid { grid-template-columns: 1fr; gap: 16px; }
  .sc-actions { flex-direction: row; flex-wrap: wrap; }
  .sc-actions .sc-btn { flex: 1 1 auto; }
  .sc-nav { flex-wrap: nowrap; overflow-x: auto; overscroll-behavior-x: contain;
    scrollbar-width: none; padding-bottom: 2px; }
  .sc-nav::-webkit-scrollbar { display: none; }
  .sc-chip { flex: none; }
}

/* ---- mobile ---- */
@media (max-width: 620px) {
  /* Phones get more of the viewport back: the panel below is the point. */
  .sc-stage { border-radius: 13px; --sc-maxh: clamp(180px, 30vh, 300px); }
  .sc-panel { padding: 15px; border-radius: 13px; }
  .sc-title { font-size: 17px; }
  .sc-chip { min-height: 40px; padding: 8px 13px; }
  .sc-ico { width: 44px; height: 44px; }
  .sc-btn { min-height: 44px; }
  .sc-cta .sc-btn { min-width: 0; flex: 1 1 100%; }
}

/* ---- reduced motion: completed diagrams, plain crossfades, no step run ---- */
@media (prefers-reduced-motion: reduce) {
  .sc-swap .sc-stage { animation: none; }
  .sc-progress i { transition: none; }
  .sc-front { transition: none !important; }
}

/* ============================================================
   THE HAND CHROME SKIN (body.pr-hand — doodle documents in Present).
   LAST IN THE FILE, NON-NEGOTIABLE: the Present polish sections above set
   type with `font:` SHORTHANDS (e.g. `font: 760 .../1.15 var(--sans)` on the
   panel title), which reset font-family and silently beat any earlier rule of
   equal specificity — that is exactly how the first version of this skin
   applied to the note body and not to the titles beside it. Position wins the
   ties; the !important on font-family alone wins against the shorthands that
   polish sections may yet grow. Everything else here is plain.
   ============================================================ */
body.presenting.pr-hand #prNarration[data-note-style] #prNarrationText,
body.presenting.pr-hand #prNoteTitle,
body.presenting.pr-hand #prNoteStep,
body.presenting.pr-hand #prNarrationHead,
body.presenting.pr-hand #prNoteTakeaway,
body.presenting.pr-hand #prCaption,
body.presenting.pr-hand #prSubtitle,
body.presenting.pr-hand #prWhyDock,
body.presenting.pr-hand #prWhyDock .pr-why-label,
body.presenting.pr-hand #prNoteWhy,
body.presenting.pr-hand #prTitleCardTitle,
body.presenting.pr-hand #prTitleCardHeader,
body.presenting.pr-hand #prTitleCardFooter,
body.presenting.pr-hand #prIntroTitle,
body.presenting.pr-hand #prIntroHeader,
body.presenting.pr-hand #prIntroMeta,
body.presenting.pr-hand #prTvTitle,
body.presenting.pr-hand #prZenTitleText,
body.presenting.pr-hand #prZenStep,
body.presenting.pr-hand #prTvStep,
body.presenting.pr-hand #prStepTime,
body.presenting.pr-hand .pr-tv-word,
body.presenting.pr-hand #prProgress,
body.presenting.pr-hand #prEnd h2 {
  font-family: 'Comic Sans MS', 'Comic Neue', 'Bradley Hand', 'Segoe Print',
    'Chalkboard SE', 'Marker Felt', cursive !important;
}
/* BRIGHT ink (operator): the handwriting reads best crisp and near-black on
   the white paper — the sheet-ink token is lifted for every panel consumer,
   and the two titles take full strength plus a touch more weight. */
body.presenting.pr-hand { --pr-sheet-ink: #1c212b; }
body.presenting.pr-hand #prNoteTitle { color: #1c212b; font-weight: 700; }
body.presenting.pr-hand #prTvTitle { color: rgba(28, 33, 43, 0.95); font-weight: 640; }
body.presenting.pr-hand #prZenTitleText { font-weight: 700 !important; }
body.presenting.pr-hand #prNarration[data-note-style] #prNarrationText { font-weight: 600; }
/* The brand marks wobble through the page-level filter (index.html
   #prHandChromeFx) and sit a touch off-axis. */
body.presenting.pr-hand #prTvHome svg,
body.presenting.pr-hand #prZenMark svg,
body.presenting.pr-hand #prZenTitle .pr-stage-mark svg,
body.presenting.pr-hand #prTitleCardMark svg,
body.presenting.pr-hand .pr-intro-mark svg {
  filter: url(#prHandChromeFx);
  transform: rotate(-2deg);
}
body.presenting.pr-hand .pr-tv-word { transform: rotate(-0.8deg); }
/* The FULL name in the doodle header (operator, Aug 24): the zen title bar
   carries only the D mark, so the wordmark is drawn on as the mark button's
   ::after — handwritten, ink-dark, tilted with the mark. Content in CSS is
   presentation-only text (the button's aria-label still says everything), and
   it keeps the skin self-contained: no markup change, no other theme sees it. */
body.presenting.pr-hand #prZenMark {
  width: auto !important;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  overflow: visible;
}
body.presenting.pr-hand #prZenMark::after {
  content: 'Diagramium';
  font-family: 'Comic Sans MS', 'Comic Neue', 'Bradley Hand', 'Segoe Print',
    'Chalkboard SE', 'Marker Felt', cursive;
  font-size: 15px;
  font-weight: 700;
  color: #1c212b;
  letter-spacing: -.01em;
  transform: rotate(-1.2deg);
  white-space: nowrap;
}
/* The why card is a HANDWRITTEN PAPER NOTE under the hand skin (operator,
   Aug 23 night — "pure handwritten note section"): pale sticky-note paper, a
   pencil-grey hand-drawn outline (the uneven four-corner radius), a soft
   drop like a note lying on the page, and nothing of the product panel left —
   no rules, no chrome borders, no tinted plate. !important against the
   Present polish sections for the same reason as the font block above. */
body.presenting.pr-hand #prWhyDock,
/* The zen redesign styles the dock through a (1,5,1)-specificity chain with
   its own !important background and borders — and zen surfaces are the
   DEFAULT dress of the redesigned Present, which is why the paper note kept
   losing while looking correct in isolation. Among !important declarations
   specificity decides, so the skin carries a zen-strength selector of its
   own. */
body.pr-hand.pr-zen.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prWhyDock:not([hidden]) {
  background: #fffbe6 !important;
  border: 2.2px solid #444a55 !important;
  border-top: 2.2px solid #444a55 !important;
  /* Opposite corner-pair to the ::after pass below — two strokes that agree
     nowhere is what reads as drawn twice by hand. */
  border-radius: 18px 240px 20px 235px / 235px 18px 245px 20px !important;
  box-shadow: 3px 4px 0 rgba(49, 54, 63, 0.10) !important;
  transform: rotate(-1.2deg);
  padding: 12px 15px !important;
  color: #31363f !important;
  position: relative;
}
/* THE SECOND PEN PASS. One uneven-radius border still reads as a CSS trick;
   what sells "drawn by hand" is the doubled outline — the second stroke a
   hand lays when the first didn't feel closed. Slightly inset, its OWN corner
   radii, a touch rotated, half strength. ::after, deliberately: #prNoteWhy's
   pseudo-elements are the tail anchor's (see the why-card notes), but the
   DOCK has no tail contract, and its ::after is free. */
body.presenting.pr-hand #prWhyDock::after,
body.pr-hand.pr-zen.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prWhyDock:not([hidden])::after {
  content: '';
  position: absolute;
  inset: -5px -4px -4px -6px;
  border: 2px solid rgba(68, 74, 85, 0.45);
  border-radius: 245px 20px 230px 16px / 16px 230px 22px 250px;
  transform: rotate(0.9deg);
  pointer-events: none;
}
body.presenting.pr-hand #prWhyDock .pr-why-label,
body.pr-hand.pr-zen.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prWhyDock:not([hidden]) .pr-why-label {
  color: #8a6d1a !important;
  border: 0 !important;
  letter-spacing: .06em;
}
/* The lightbulb chip drops its product gradient for a hand-drawn box. */
body.presenting.pr-hand #prWhyDock .pr-whyd-chip,
body.pr-hand.pr-zen.presenting:not(.pr-notes-bottom):not(.pr-guide-collapsed) #prWhyDock:not([hidden]) .pr-whyd-chip {
  background: transparent !important;
  border: 1.8px solid #444a55 !important;
  border-radius: 9px 2px 8px 3px / 3px 8px 2px 9px !important;
  color: #8a6d1a !important;
}
body.presenting.pr-hand #prNoteWhy .pr-why-main {
  background: #fffbe6 !important;
  border: 2px solid #7a7f89 !important;
  border-radius: 255px 15px 225px 15px / 15px 225px 15px 255px !important;
  box-shadow: 2px 3px 0 rgba(49, 54, 63, 0.12) !important;
  transform: rotate(-0.7deg);
  color: #31363f !important;
}
