/*
  Theme tokens: light + dark. Toggled via data-theme="light|dark" on
  <html> (see main.js), defaulting to the OS preference on first load.
  This is the player's visual identity -- the rendered video IS this UI,
  there's no separate "app chrome" to hide -- so these values are also
  what gets baked into the final capture.

  Both dark and light now match the "orange brackets" reference mockups
  (2026-08-18): the same #ff6b00 accent on a near-black surface (dark) or
  white/gray-scale surface (light), Inter + JetBrains Mono.
*/

/* Self-hosted (player/assets/fonts/) rather than a Google Fonts CDN
   <link>, deliberately: this stylesheet gets loaded by a headless
   Chromium instance during automated video capture (see
   capture/record.ts), where a live network fetch for fonts is one more
   thing that can flake or race against first paint -- a local file has
   neither failure mode. Both are the actual variable-font files Google
   Fonts serves (a single file covering the whole weight axis), so one
   @font-face per weight below all point at the same url() -- exactly
   what fonts.googleapis.com itself does for a modern woff2 client. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../../assets/fonts/Inter-Variable.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../../assets/fonts/Inter-Variable.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../../assets/fonts/Inter-Variable.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../../assets/fonts/Inter-Variable.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../../assets/fonts/JetBrainsMono-Variable.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../../assets/fonts/JetBrainsMono-Variable.woff2") format("woff2");
}

:root {
  --ob-orange-start: #ffa726;
  --ob-orange-end: #e64a19;
  --ob-font-sans: "Inter", -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
  --ob-font-mono: "JetBrains Mono", "SF Mono", "Cascadia Code", "Fira Code", Consolas, monospace;

  --ob-zoom-duration: 0.5s;

  /* Reference's borderRadius scale (DEFAULT/lg/xl) -- --ob-radius is the
     existing general-purpose token (kept as an alias of -sm so every
     current call site picks up the new, much sharper default without
     needing its own edit); -lg/-xl exist for new chrome that wants a
     bigger radius than the reference's own body content actually uses
     (everything in the reference -- code blocks, the filename badge,
     the Run All button -- is plain `rounded`, i.e. -sm). */
  --ob-radius-sm: 2px;
  --ob-radius-lg: 4px;
  --ob-radius-xl: 8px;
  --ob-radius: var(--ob-radius-sm);
}

/* ---- Dark ("orange brackets" reference) ---- */
:root[data-theme="dark"] {
  --ob-bg: #050505; /* editor-bg -- page background, and the active-cell code block's own bg */
  --ob-bg-elevated: #0d0d0d; /* shell-bg -- header/footer/idle-code-block bg, markdown-cell hover */
  --ob-bg-inset: #261812; /* surface-container-low -- output panel / inline-code bg */
  --ob-border: #1f1f1f; /* border-subtle */
  --ob-text: #f8ddd2; /* on-surface */
  --ob-text-muted: #e2bfb0; /* on-surface-variant */
  --ob-prompt: #ff6b00; /* primary-container -- active cell's "[n]:" prompt */
  --ob-selection: rgba(255, 107, 0, 0.25);
  --ob-badge-bg: rgba(31, 31, 31, 0.5); /* filename badge -- reference's bg-[#1F1F1F]/50 */

  /* New chrome-specific tokens (no equivalent in the old palette): the
     brand accent proper (primary-container) vs. its softer/lighter
     variant (primary) used for the active code block's own border. */
  --ob-accent: #ff6b00;
  --ob-accent-soft: #ffb693;

  --ob-syntax-keyword: #ff6b00;
  --ob-syntax-string: #a5d6ff;
  --ob-syntax-comment: #8b949e;
  --ob-syntax-function: #d2a8ff;
  --ob-syntax-number: #9ccaff; /* tertiary -- not specified by the reference; kept in the same blue family as string, distinct enough to tell apart */
  --ob-syntax-builtin: #ffb693; /* primary -- softer than keyword so a builtin reads distinctly from a keyword */
  --ob-syntax-operator: #ff6b00; /* same as keyword -- matches this file's own prior GitHub-dark convention (operator == keyword) */
  /* Plain identifiers and PascalCase type/class names (see
     lib/highlight.js's tok-variable/tok-type) both stayed the same
     on-surface body-text color before those spans existed -- kept equal
     to --ob-text here so this theme renders byte-identical to before. */
  --ob-syntax-variable: var(--ob-text);
  --ob-syntax-type: var(--ob-text);

  --ob-mistake-bg: #93000a; /* error-container */
  --ob-mistake-text: #ffdad6; /* on-error-container */

  /* The active-cell glow (notebook.css's .ob-cell--active .ob-code-cell)
     wants this theme's own accent at ~20% opacity -- a plain custom
     property can't have its alpha adjusted by the consumer, so it's
     pre-mixed here instead of computed from --ob-accent. */
  --ob-accent-glow: rgba(255, 107, 0, 0.2);

  color-scheme: dark;
}

/* ---- Light ("orange brackets" reference) ---- */
:root[data-theme="light"] {
  --ob-bg: #ffffff;
  --ob-bg-elevated: #f9fafb; /* gray-50 -- header/footer/idle-code-block bg, markdown-cell hover */
  --ob-bg-inset: #f3f4f6; /* gray-100 -- output panel / inline-code bg (not shown in the reference; kept one step deeper than -elevated, same relationship as dark's) */
  --ob-border: #e5e7eb; /* gray-200 */
  --ob-text: #111827; /* gray-900 -- headings */
  /* The reference actually uses three grays here (900/600/500: headings /
     paragraph+filename badge / code-prompt+icon-buttons) where dark's own
     reference collapsed to two (on-surface/on-surface-variant). Rather
     than add a third token this system doesn't otherwise have, every
     "muted" role uses gray-600 -- close enough to gray-500 that the
     small extra darkness on the code prompt / icon buttons isn't
     noticeable, and it keeps the same 2-token shape as dark. */
  --ob-text-muted: #4b5563; /* gray-600 */
  --ob-prompt: #ff6b00; /* active cell's "[n]:" prompt -- same accent as dark */
  --ob-selection: rgba(255, 107, 0, 0.2);
  --ob-badge-bg: #f3f4f6; /* gray-100, solid (not translucent like dark's) -- matches the reference exactly */

  --ob-accent: #ff6b00;
  /* Dark's reference gives the active code block's own border a lighter
     "soft" shade distinct from the bold left-accent-bar/prompt color;
     light's reference uses the SAME bold accent for both -- so here
     -soft just equals -accent, and .ob-cell--active .ob-code-cell's
     border-color (see notebook.css, shared by both themes) comes out
     correct with no extra CSS. */
  --ob-accent-soft: #ff6b00;

  --ob-syntax-keyword: #d73a49;
  --ob-syntax-string: #032f62;
  --ob-syntax-comment: #6a737d;
  --ob-syntax-function: #6f42c1;
  --ob-syntax-number: #0550ae; /* not specified by the reference; kept from this file's prior light palette -- a distinct blue in the same family as string */
  --ob-syntax-builtin: #953800; /* not specified by the reference; kept from this file's prior light palette */
  --ob-syntax-operator: #d73a49; /* same as keyword, matching dark's own convention */
  --ob-syntax-variable: var(--ob-text); /* see dark's own comment on these two -- byte-identical to before tok-variable/tok-type existed */
  --ob-syntax-type: var(--ob-text);

  --ob-mistake-bg: #ffebe9;
  --ob-mistake-text: #cf222e;

  --ob-accent-glow: rgba(255, 107, 0, 0.2); /* see dark's own comment */

  color-scheme: light;
}

/* ---- 8 VS Code-derived themes (2026-08-18) ----
   Same token set, same structure (chrome/fonts/spacing/radius untouched)
   as dark/light above -- only the tokens below change per theme. Colors
   are sourced from each theme's actual published source (a fetched
   theme JSON, or the real .vsix for the two Open VSX-only entries),
   not estimated from the theme's name -- see each block's own comment
   for exactly where its values came from. Any value a source doesn't
   define at all (a handful of the secondary tokens, e.g. bg-inset,
   badge-bg, mistake-bg/text) is called out inline as derived rather
   than sourced. */

/* Ayu Dark Bordered (teabyii.ayu) -- colors.editor.background/
   editor.foreground and every --ob-syntax-* value fetched directly from
   ayu-theme/vscode-ayu's ayu-dark.json. bg-inset/border/badge/mistake
   have no equivalent concept in the source and are derived from bg. */
:root[data-theme="ayu-dark-bordered"] {
  --ob-bg: #10141c;
  --ob-bg-elevated: #0d1017;
  --ob-bg-inset: #161b24;
  --ob-border: #1c212b;
  --ob-text: #bfbdb6;
  --ob-text-muted: #8a8681;
  --ob-prompt: #e6b450;
  --ob-selection: rgba(230, 180, 80, 0.2);
  --ob-badge-bg: rgba(230, 180, 80, 0.14);

  --ob-accent: #e6b450;
  --ob-accent-soft: #f0cc7a;
  --ob-accent-glow: rgba(230, 180, 80, 0.2);

  --ob-syntax-keyword: #ff8f40;
  --ob-syntax-string: #aad94c;
  --ob-syntax-comment: #5a6673;
  --ob-syntax-function: #ffb454;
  --ob-syntax-number: #d2a6ff; /* not in source -- ayu's well-known constant/number tone */
  --ob-syntax-builtin: #39bae6; /* not in source -- ayu's well-known support/tag blue */
  --ob-syntax-operator: #ff8f40;
  --ob-syntax-variable: #bfbdb6;
  --ob-syntax-type: #59c2ff; /* not in source -- ayu's well-known entity/type blue */

  --ob-mistake-bg: #3d1f1f;
  --ob-mistake-text: #ffd6d6;

  color-scheme: dark;
}

/* One Dark Pro Night Flat (zhuangtongfa.Material-theme) -- comment/
   keyword/function/string/variable colors fetched directly from
   Binaryify/OneDark-Pro's themes/OneDark-Pro-night-flat.json
   tokenColors. accent/number/builtin/type/bg-elevated/bg-inset aren't
   in that theme's own tokenColors -- these are the canonical One Dark
   palette values used consistently across every One Dark Pro variant
   (its base color scheme, not specific to "night flat"'s tweaks). */
:root[data-theme="one-dark-pro-night-flat"] {
  --ob-bg: #282c34;
  --ob-bg-elevated: #21252b;
  --ob-bg-inset: #2c313a;
  --ob-border: #3b414d;
  --ob-text: #abb2bf;
  --ob-text-muted: #7a828e;
  --ob-prompt: #528bff;
  --ob-selection: rgba(97, 175, 239, 0.2);
  --ob-badge-bg: rgba(97, 175, 239, 0.12);

  --ob-accent: #528bff;
  --ob-accent-soft: #7aa2ff;
  --ob-accent-glow: rgba(82, 139, 255, 0.2);

  --ob-syntax-keyword: #c678dd;
  --ob-syntax-string: #98c379;
  --ob-syntax-comment: #5c6370;
  --ob-syntax-function: #61afef;
  --ob-syntax-number: #d19a66;
  --ob-syntax-builtin: #56b6c2;
  --ob-syntax-operator: #c678dd;
  --ob-syntax-variable: #e06c75;
  --ob-syntax-type: #e5c07b;

  --ob-mistake-bg: #47191c;
  --ob-mistake-text: #ffd7d7;

  color-scheme: dark;
}

/* Beautiful Dracula Darker (NguyenHoangLam.beautiful-dracula) -- fetched
   directly from lamhoang1256/beautiful-dracula's themes/Dracula-darker.json
   (this fork's real background, #1E1F35, is deliberately darker than
   stock Dracula's #282a36 -- confirming the doc's own flag that forks
   vary here). Syntax colors match the official Dracula spec, which this
   fork's tokenColors also use unchanged. accent uses this fork's own
   activityBarBadge/activeBorder color rather than stock Dracula's
   purple, since it's what the extension itself visibly brands with. */
:root[data-theme="dracula-darker"] {
  --ob-bg: #1e1f35;
  --ob-bg-elevated: #191a21;
  --ob-bg-inset: #282a36;
  --ob-border: #44475a;
  --ob-text: #f8f8f2;
  --ob-text-muted: #b7b9d1;
  --ob-prompt: #ff6bcb;
  --ob-selection: rgba(255, 107, 203, 0.2);
  --ob-badge-bg: rgba(255, 107, 203, 0.12);

  --ob-accent: #ff6bcb;
  --ob-accent-soft: #bd93f9;
  --ob-accent-glow: rgba(255, 107, 203, 0.2);

  --ob-syntax-keyword: #ff79c6;
  --ob-syntax-string: #f1fa8c;
  --ob-syntax-comment: #6272a4;
  --ob-syntax-function: #50fa7b;
  --ob-syntax-number: #bd93f9; /* official Dracula spec -- numbers/constants are purple */
  --ob-syntax-builtin: #8be9fd;
  --ob-syntax-operator: #ff79c6;
  --ob-syntax-variable: #f8f8f2;
  --ob-syntax-type: #8be9fd; /* official Dracula spec -- class names are cyan */

  --ob-mistake-bg: #4d1616;
  --ob-mistake-text: #ffb3b3;

  color-scheme: dark;
}

/* Codex Gruvbox Dark (Xuanxi1111.codex-theme) -- every value below
   fetched directly from BXCQ/Codex-Theme-Collection's
   themes/codex-gruvbox-dark.json (the actual source the extension
   ships). This resolves the doc's own flagged ambiguity: keyword AND
   storage.type/storage.modifier all map to the same red (#fb4934) in
   this port -- Gruvbox's separate orange/yellow tones color entity.name.type
   (#fabd2f, used here for --ob-syntax-type) and variable.parameter
   (#fe8019) instead, not a second keyword class. */
:root[data-theme="gruvbox-dark"] {
  --ob-bg: #282828;
  --ob-bg-elevated: #1d2021;
  --ob-bg-inset: #3c3836;
  --ob-border: #3c3836;
  --ob-text: #ebdbb2;
  --ob-text-muted: #a89984;
  --ob-prompt: #83a598;
  --ob-selection: rgba(131, 165, 152, 0.2);
  --ob-badge-bg: rgba(131, 165, 152, 0.14);

  --ob-accent: #83a598;
  --ob-accent-soft: #a8c5ba;
  --ob-accent-glow: rgba(131, 165, 152, 0.2);

  --ob-syntax-keyword: #fb4934;
  --ob-syntax-string: #b8bb26;
  --ob-syntax-comment: #928374;
  --ob-syntax-function: #83a598;
  --ob-syntax-number: #d3869b;
  --ob-syntax-builtin: #8ec07c;
  --ob-syntax-operator: #fb4934;
  --ob-syntax-variable: #ebdbb2;
  --ob-syntax-type: #fabd2f;

  --ob-mistake-bg: #3d1917;
  --ob-mistake-text: #f5c6c2;

  color-scheme: dark;
}

/* GitHub Light (GitHub.github-vscode-theme) -- syntax/border/bg tokens
   match this project's original "light" theme (already sourced from the
   same github-vscode-theme palette). What's different here is the
   accent: "light" deliberately re-branded its accent to this app's own
   #ff6b00 orange, but a theme explicitly named "GitHub Light" should
   look like actual GitHub -- accent uses GitHub's real link/focus blue
   (#0969da) instead. */
:root[data-theme="github-light"] {
  --ob-bg: #ffffff;
  --ob-bg-elevated: #f9fafb;
  --ob-bg-inset: #f3f4f6;
  --ob-border: #e5e7eb;
  --ob-text: #24292e;
  --ob-text-muted: #57606a;
  --ob-prompt: #0969da;
  --ob-selection: rgba(9, 105, 218, 0.15);
  --ob-badge-bg: #f3f4f6;

  --ob-accent: #0969da;
  --ob-accent-soft: #218bff;
  --ob-accent-glow: rgba(9, 105, 218, 0.18);

  --ob-syntax-keyword: #d73a49;
  --ob-syntax-string: #032f62;
  --ob-syntax-comment: #6a737d;
  --ob-syntax-function: #6f42c1;
  --ob-syntax-number: #0550ae;
  --ob-syntax-builtin: #953800;
  --ob-syntax-operator: #d73a49;
  --ob-syntax-variable: #24292e;
  --ob-syntax-type: #6f42c1;

  --ob-mistake-bg: #ffebe9;
  --ob-mistake-text: #cf222e;

  color-scheme: light;
}

/* 2017 Light (Visual Studio C/C++) (ms-vscode.cpptools-themes) -- fetched
   directly from microsoft/vscode-cpptools's Themes/themes/cpptools_light_vs.json
   (its real path -- Themes/, not Extension/themes/ as the doc guessed).
   Faithful to a real quirk of the source: there's no generic
   "function call" scope at all in this theme (only preprocessor/operator
   functions get a color), so plain function calls render in the same
   black as the rest of the text here, same as they do in real VS. */
:root[data-theme="vs2017-light"] {
  --ob-bg: #ffffff;
  --ob-bg-elevated: #f3f3f3;
  --ob-bg-inset: #e5ebf1;
  --ob-border: #d3d3d3;
  --ob-text: #000000;
  --ob-text-muted: #393939;
  --ob-prompt: #0000ff;
  --ob-selection: rgba(0, 0, 255, 0.12);
  --ob-badge-bg: #f3f3f3;

  --ob-accent: #0000ff;
  --ob-accent-soft: #4d4dff;
  --ob-accent-glow: rgba(0, 0, 255, 0.15);

  --ob-syntax-keyword: #0000ff;
  --ob-syntax-string: #a31515;
  --ob-syntax-comment: #008000;
  --ob-syntax-function: #000000; /* no generic function scope in the source -- see comment above */
  --ob-syntax-number: #098658;
  --ob-syntax-builtin: #2b91af; /* no generic builtin scope either -- reuses the type color, the closest real analog */
  --ob-syntax-operator: #000000;
  --ob-syntax-variable: #000000;
  --ob-syntax-type: #2b91af;

  --ob-mistake-bg: #fdecea;
  --ob-mistake-text: #cd3131;

  color-scheme: light;
}

/* Islands Light (butov-dev.the-islands-theme) -- fetched directly from
   rbutov/vscode-the-islands-theme's src/islands-light.json (confirmed
   via the extension's own package.json contributes.themes entry).
   bg-elevated/bg-inset/badge/mistake have no equivalent in the source
   and are derived from editorGroup.border. */
:root[data-theme="islands-light"] {
  --ob-bg: #ffffff;
  --ob-bg-elevated: #f5f5f7;
  --ob-bg-inset: #edeef2;
  --ob-border: #e9eaee;
  --ob-text: #080808;
  --ob-text-muted: #57575c;
  --ob-prompt: #3871e1;
  --ob-selection: rgba(56, 113, 225, 0.15);
  --ob-badge-bg: #f5f5f7;

  --ob-accent: #3871e1;
  --ob-accent-soft: #6b93ea;
  --ob-accent-glow: rgba(56, 113, 225, 0.18);

  --ob-syntax-keyword: #0033b3;
  --ob-syntax-string: #067d17;
  --ob-syntax-comment: #8c8c8c;
  --ob-syntax-function: #00627a;
  --ob-syntax-number: #1750eb;
  --ob-syntax-builtin: #00627a;
  --ob-syntax-operator: #0033b3;
  --ob-syntax-variable: #080808;
  --ob-syntax-type: #0033b3; /* no separate class/type scope in source -- reuses keyword, which already covers storage.type */

  --ob-mistake-bg: #fdecea;
  --ob-mistake-text: #c62828;

  color-scheme: light;
}

/* Auralis Paper High Contrast (auralis-labs.auralis-theme-system) --
   fetched directly from the real published .vsix (auralis-labs.auralis-
   theme-system-0.9.2, downloaded from Open VSX and unzipped -- Open VSX
   only serves package.json/README/vsix over its file API, not arbitrary
   paths, so the theme JSON itself had to come out of the package).
   themes/paper-contrast-color-theme.json's own colors/tokenColors. */
:root[data-theme="auralis-high-contrast"] {
  --ob-bg: #fffcf8;
  --ob-bg-elevated: #f3ede5;
  --ob-bg-inset: #f5f0ea;
  --ob-border: #b9b0a3;
  --ob-text: #1f1610;
  --ob-text-muted: #4b4038;
  --ob-prompt: #d45b3d;
  --ob-selection: rgba(212, 91, 61, 0.18);
  --ob-badge-bg: #f0e8de;

  --ob-accent: #d45b3d;
  --ob-accent-soft: #ec8e76;
  --ob-accent-glow: rgba(212, 91, 61, 0.2);

  --ob-syntax-keyword: #ad215f;
  --ob-syntax-string: #1e7729;
  --ob-syntax-comment: #736046;
  --ob-syntax-function: #044cb6;
  --ob-syntax-number: #b25400;
  --ob-syntax-builtin: #044cb6;
  --ob-syntax-operator: #a43c2f;
  --ob-syntax-variable: #3a312a;
  --ob-syntax-type: #007982;

  --ob-mistake-bg: #fbe2e0;
  --ob-mistake-text: #cc272e;

  color-scheme: light;
}

/* Default before JS applies an explicit choice, so there's no flash. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --ob-bg: #050505;
    --ob-bg-elevated: #0d0d0d;
    --ob-bg-inset: #261812;
    --ob-border: #1f1f1f;
    --ob-text: #f8ddd2;
    --ob-text-muted: #e2bfb0;
    --ob-prompt: #ff6b00;
    --ob-badge-bg: rgba(31, 31, 31, 0.5);
    --ob-accent: #ff6b00;
    --ob-accent-soft: #ffb693;
    --ob-syntax-keyword: #ff6b00;
    --ob-syntax-string: #a5d6ff;
    --ob-syntax-comment: #8b949e;
    --ob-syntax-function: #d2a8ff;
    --ob-syntax-number: #9ccaff;
    --ob-syntax-builtin: #ffb693;
    --ob-syntax-operator: #ff6b00;
    --ob-syntax-variable: #f8ddd2;
    --ob-syntax-type: #f8ddd2;
    --ob-mistake-bg: #93000a;
    --ob-mistake-text: #ffdad6;
    --ob-accent-glow: rgba(255, 107, 0, 0.2);
    color-scheme: dark;
  }
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) {
    --ob-bg: #ffffff;
    --ob-bg-elevated: #f9fafb;
    --ob-bg-inset: #f3f4f6;
    --ob-border: #e5e7eb;
    --ob-text: #111827;
    --ob-text-muted: #4b5563;
    --ob-prompt: #ff6b00;
    --ob-badge-bg: #f3f4f6;
    --ob-accent: #ff6b00;
    --ob-accent-soft: #ff6b00;
    --ob-syntax-keyword: #d73a49;
    --ob-syntax-string: #032f62;
    --ob-syntax-comment: #6a737d;
    --ob-syntax-function: #6f42c1;
    --ob-syntax-number: #0550ae;
    --ob-syntax-builtin: #953800;
    --ob-syntax-operator: #d73a49;
    --ob-syntax-variable: #111827;
    --ob-syntax-type: #111827;
    --ob-mistake-bg: #ffebe9;
    --ob-mistake-text: #cf222e;
    --ob-accent-glow: rgba(255, 107, 0, 0.2);
    color-scheme: light;
  }
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  background: var(--ob-bg);
  color: var(--ob-text);
  font-family: var(--ob-font-sans);
}

::selection {
  background: var(--ob-selection);
}

/* Real OS pointer, hidden during capture only (?silent=1 -> main.js sets
   data-silent -- see main.js's initTheme/silent handling). Xvfb has no
   cursor pixmap by default (nothing loads one without a window manager
   present), so the real pointer was already invisible in any recording
   regardless of how it was driven -- this just makes that explicit
   instead of leaving a real-but-unrendered cursor sitting at whatever
   stale position Playwright last moved it to. #ob-fake-cursor (see
   notebook.css) is the actual in-frame pointer now: a real DOM element,
   animated by the deterministic engine/cursor.js the same way zoom/
   scroll are, so it's guaranteed to be part of what x11grab captures.
   Left visible outside capture (live preview) so a human testing in a
   real browser still has a working cursor. */
:root[data-silent="true"],
:root[data-silent="true"] * {
  cursor: none;
}
