:root{
    --toolbar-bg:#23201b; --toolbar-text:#fff;
    --paper:#f4efe6; --paper-2:#efe8db; --ink:#23201b; --ink-soft:#6b6357;
    --line:#d8cfbf; --line-2:#c8bda8;
    --accent:#e0613a; --accent-deep:#b8451f; --teal:#2f6f6a;
    --chrome:#fbf8f2; --chrome-edge:#e7ddcc;
    --node-bg:#ffffff; --node-ink:#23201b;
    /* The map cards' fill. Every theme's --node-bg is its "surface" colour
       and is reused by dialogs, panels and inputs; the cards read this
       derived variable instead, and the per-map "nodeBg" theme setting
       writes ONLY this one (applyThemeConfigVars) - so a translucent card
       colour, e.g. #ffffff80 over a look's texture, never bleeds into the
       Preferences card or the sign-in form. */
    --card-bg:var(--node-bg);
    --canvas-dot:#d8cfbf;
    --stage-glow:rgba(255,255,255,.5);
    --link:#b8451f;
    /* Text painted ON a fill, and the semantic text hues. A theme overrides
       whichever of these its own palette cannot carry at 3:1 - see
       test/color-contrast.test.mjs. The values here are the light theme's,
       and are what an imported custom theme inherits. */
    --accent-fg:#ffffff; --teal-fg:#ffffff;
    --accent-text:color-mix(in srgb, var(--accent) 70%, var(--ink)); --teal-text:var(--teal);
    --danger:#b23528; --warn:#8c5d00; --ok:#187432;
    /* Universal hover tint: overlays the ink colour, so it darkens light
       surfaces and lightens dark surfaces - always a visible delta. */
    --hover:color-mix(in srgb, var(--ink) 12%, transparent);
    --hover-strong:color-mix(in srgb, var(--ink) 20%, transparent);
    --shadow:0 2px 4px rgba(40,30,15,.06),0 8px 24px rgba(40,30,15,.10);
    --shadow-lg:0 10px 40px rgba(40,30,15,.18);
    --r:14px;
    --sans:"Bricolage Grotesque",system-ui,sans-serif;
    --serif:"Fraunces",Georgia,serif;
    /* Keep the brand rounded across the app's typography and colour themes. */
    --brand-font:"Quicksand","Nunito","Segoe UI",system-ui,sans-serif;
    --brand-ink:color-mix(in srgb, #398bed 65%, var(--ink));
    --brand-accent:color-mix(in srgb, #398bed 80%, var(--ink));
  }
  /* ===== Themes ===== */
  :root[data-theme="dark"]{
    --toolbar-bg:#f3f3f3; --toolbar-text:#1e1e1e;
    --paper:#1e1e1e; --paper-2:#252526; --ink:#d4d4d4; --ink-soft:#aaaaaa;
    --line:#3c3c3c; --line-2:#4a4a4a;
    --accent:#3794ff; --accent-deep:#1f6fdc; --teal:#4ec9b0;
    --chrome:#252526; --chrome-edge:#3c3c3c;
    --node-bg:#2d2d2d; --node-ink:#d4d4d4;
    --canvas-dot:#3a3a3a;
    --stage-glow:rgba(255,255,255,.04);
    --link:#79b8ff;
    --shadow:0 2px 4px rgba(0,0,0,.30),0 8px 24px rgba(0,0,0,.45);
    --shadow-lg:0 12px 40px rgba(0,0,0,.55);
    --teal-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 85%, var(--ink)); --danger:#e07f75; --warn:#d18c00; --ok:#24b14c;
  }
  :root[data-theme="light-owl"]{   /* Light Owl - github.com/sdras/night-owl-vscode-theme */
    --toolbar-bg:#403f53; --toolbar-text:#fbfbfb;
    --paper:#fbfbfb; --paper-2:#f0f0f0; --ink:#403f53; --ink-soft:#587280;
    --line:#ececec; --line-2:#d9d9d9;
    --accent:#2aa298; --accent-deep:#1f7d75; --teal:#0c969b;
    --chrome:#f0f0f0; --chrome-edge:#e0e0e0;
    --node-bg:#ffffff; --node-ink:#403f53;
    --canvas-dot:#d9d9d9;
    --stage-glow:rgba(255,255,255,.6);
    --link:#2073af;
    --shadow:0 1px 3px rgba(64,63,83,.10),0 8px 24px rgba(64,63,83,.08);
    --shadow-lg:0 12px 40px rgba(64,63,83,.16);
    --accent-text:color-mix(in srgb, var(--accent) 60%, var(--ink)); --teal-text:color-mix(in srgb, var(--teal) 70%, var(--ink)); --danger:#b83729; --warn:#906000; --ok:#197834;
  }
  :root[data-theme="night-owl"]{   /* Night Owl - github.com/sdras/night-owl-vscode-theme */
    --toolbar-bg:#d6deeb; --toolbar-text:#011627;
    --paper:#011627; --paper-2:#0b2942; --ink:#d6deeb; --ink-soft:#9bb0c0;
    --line:#1d3b53; --line-2:#2c5574;
    --accent:#7e57c2; --accent-deep:#6440a5; --teal:#7fdbca;
    --chrome:#011627; --chrome-edge:#1d3b53;
    --node-bg:#0b2942; --node-ink:#d6deeb;
    --canvas-dot:#1d3b53;
    --stage-glow:rgba(126,87,194,.06);
    --link:#85acff;
    --shadow:0 2px 4px rgba(0,0,0,.30),0 8px 24px rgba(0,0,0,.45);
    --shadow-lg:0 12px 40px rgba(0,0,0,.55);
    --teal-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 60%, var(--ink)); --danger:#df786e; --warn:#cb8800; --ok:#23ac4a;
  }
  :root[data-theme="dracula"]{
    --toolbar-bg:#f8f8f2; --toolbar-text:#282a36;
    --paper:#282a36; --paper-2:#21222c; --ink:#f8f8f2; --ink-soft:#c5c9d8;
    --line:#44475a; --line-2:#6272a4;
    --accent:#ff79c6; --accent-deep:#e857af; --teal:#8be9fd;
    --chrome:#21222c; --chrome-edge:#44475a;
    --node-bg:#44475a; --node-ink:#f8f8f2;
    --canvas-dot:#44475a;
    --stage-glow:rgba(189,147,249,.06);
    --link:#8be9fd;
    --shadow:0 2px 4px rgba(0,0,0,.30),0 8px 24px rgba(0,0,0,.45);
    --shadow-lg:0 12px 40px rgba(0,0,0,.55);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 65%, var(--ink)); --danger:#ecb0a9; --warn:#ffae09; --ok:#43d96e;
  }
  :root[data-theme="nord"]{
    --toolbar-bg:#eceff4; --toolbar-text:#2e3440;
    --paper:#2e3440; --paper-2:#3b4252; --ink:#eceff4; --ink-soft:#c6cbd4;
    --line:#434c5e; --line-2:#4c566a;
    --accent:#88c0d0; --accent-deep:#5e81ac; --teal:#a3be8c;
    --chrome:#3b4252; --chrome-edge:#434c5e;
    --node-bg:#434c5e; --node-ink:#eceff4;
    --canvas-dot:#434c5e;
    --stage-glow:rgba(143,188,187,.05);
    --link:#a9d2dd;
    --shadow:0 2px 4px rgba(0,0,0,.25),0 8px 24px rgba(0,0,0,.40);
    --shadow-lg:0 12px 40px rgba(0,0,0,.55);
    --accent-fg:#000000; --teal-fg:#000000; --teal-text:color-mix(in srgb, var(--teal) 70%, var(--ink)); --danger:#eeb8b3; --warn:#ffb92a; --ok:#5cde81;
  }
  :root[data-theme="catppuccin-dark"]{   /* Catppuccin Mocha - https://github.com/catppuccin/catppuccin */
    --toolbar-bg:#f5e0dc; --toolbar-text:#1e1e2e;
    --paper:#1e1e2e; --paper-2:#181825; --ink:#cdd6f4; --ink-soft:#a6adc8;
    --line:#313244; --line-2:#45475a;
    --accent:#cba6f7; --accent-deep:#a674e0; --teal:#94e2d5;
    --chrome:#181825; --chrome-edge:#313244;
    --node-bg:#313244; --node-ink:#cdd6f4;
    --canvas-dot:#313244;
    --stage-glow:rgba(203,166,247,.05);
    --link:#89b4fa;
    --shadow:0 2px 4px rgba(0,0,0,.30),0 8px 24px rgba(0,0,0,.45);
    --shadow-lg:0 12px 40px rgba(0,0,0,.55);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#e59289; --warn:#e49800; --ok:#27c153;
  }
  :root[data-theme="catppuccin-light"]{   /* Catppuccin Latte - https://github.com/catppuccin/catppuccin */
    --toolbar-bg:#4c4f69; --toolbar-text:#eff1f5;
    --paper:#eff1f5; --paper-2:#e6e9ef; --ink:#4c4f69; --ink-soft:#686b80;
    --line:#ccd0da; --line-2:#bcc0cc;
    --accent:#8839ef; --accent-deep:#6c2bc7; --teal:#179299;
    --chrome:#e6e9ef; --chrome-edge:#ccd0da;
    --node-bg:#ffffff; --node-ink:#4c4f69;
    --canvas-dot:#ccd0da;
    --stage-glow:rgba(136,57,239,.04);
    --link:#145ff5;
    --shadow:0 1px 3px rgba(76,79,105,.12),0 8px 24px rgba(76,79,105,.12);
    --shadow-lg:0 12px 40px rgba(76,79,105,.18);
    --accent-text:var(--accent); --teal-text:color-mix(in srgb, var(--teal) 55%, var(--ink)); --ok:#187533;
  }
  :root[data-theme="rose-pine-moon"]{   /* Rosé Pine Moon - https://rosepinetheme.com */
    --toolbar-bg:#e0def4; --toolbar-text:#232136;
    --paper:#232136; --paper-2:#2a273f; --ink:#e0def4; --ink-soft:#b2afc4;
    --line:#393552; --line-2:#44415a;
    --accent:#c4a7e7; --accent-deep:#a57dd5; --teal:#9ccfd8;
    --chrome:#2a273f; --chrome-edge:#393552;
    --node-bg:#393552; --node-ink:#e0def4;
    --canvas-dot:#2a283e;
    --stage-glow:rgba(196,167,231,.05);
    --link:#c4a7e7;
    --shadow:0 2px 4px rgba(0,0,0,.30),0 8px 24px rgba(0,0,0,.45);
    --shadow-lg:0 12px 40px rgba(0,0,0,.55);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#e79a92; --warn:#ec9e00; --ok:#29c857;
  }
  :root[data-theme="rose-pine-dawn"]{   /* Rosé Pine Dawn - https://rosepinetheme.com */
    --toolbar-bg:#575279; --toolbar-text:#faf4ed;
    --paper:#faf4ed; --paper-2:#f2e9e1; --ink:#575279; --ink-soft:#6f6b89;
    --line:#f2e9e1; --line-2:#dfdad9;
    --accent:#907aa9; --accent-deep:#7a6494; --teal:#56949f;
    --chrome:#faf4ed; --chrome-edge:#f2e9e1;
    --node-bg:#fffaf3; --node-ink:#575279;
    --canvas-dot:#dfdad9;
    --stage-glow:rgba(144,122,169,.04);
    --link:#7e649b;
    --shadow:0 1px 3px rgba(87,82,121,.12),0 8px 24px rgba(87,82,121,.12);
    --shadow-lg:0 12px 40px rgba(87,82,121,.18);
    --accent-text:color-mix(in srgb, var(--accent) 60%, var(--ink)); --teal-text:color-mix(in srgb, var(--teal) 50%, var(--ink)); --danger:#b43528; --warn:#8d5e00; --ok:#187533;
  }
  :root[data-theme="github-dark"]{
    --toolbar-bg:#f0f6fc; --toolbar-text:#0d1117;
    --paper:#0d1117; --paper-2:#161b22; --ink:#e6edf3; --ink-soft:#abb2b9;
    --line:#30363d; --line-2:#3d444d;
    --accent:#58a6ff; --accent-deep:#1f6feb; --teal:#3fb950;
    --chrome:#161b22; --chrome-edge:#30363d;
    --node-bg:#161b22; --node-ink:#e6edf3;
    --canvas-dot:#30363d;
    --stage-glow:rgba(88,166,255,.05);
    --link:#74b5ff;
    --shadow:0 2px 4px rgba(0,0,0,.30),0 8px 24px rgba(0,0,0,.45);
    --shadow-lg:0 12px 40px rgba(0,0,0,.55);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#df7c71; --warn:#ce8a00; --ok:#24af4c;
  }
  :root[data-theme="github-light"]{
    --toolbar-bg:#24292f; --toolbar-text:#ffffff;
    --paper:#ffffff; --paper-2:#f6f8fa; --ink:#1f2328; --ink-soft:#656d76;
    --line:#d0d7de; --line-2:#afb8c1;
    --accent:#0969da; --accent-deep:#0a5cc0; --teal:#1a7f37;
    --chrome:#f6f8fa; --chrome-edge:#d0d7de;
    --node-bg:#ffffff; --node-ink:#1f2328;
    --canvas-dot:#d8dee4;
    --stage-glow:rgba(9,105,218,.04);
    --link:#0968d9;
    --shadow:0 1px 3px rgba(31,35,40,.12),0 8px 24px rgba(31,35,40,.12);
    --shadow-lg:0 12px 40px rgba(31,35,40,.18);
    --accent-text:var(--accent); --danger:#be392b; --warn:#956400; --ok:#197c36;
  }
  :root[data-theme="slate-steel"]{   /* Slate & Steel - enterprise cloud monitoring */
    --toolbar-bg:#f8fafc; --toolbar-text:#0f172a;
    --paper:#0f172a; --paper-2:#1e293b; --ink:#cbd5e1; --ink-soft:#a0abbb;
    --line:#334155; --line-2:#3e4c63;
    --accent:#38bdf8; --accent-deep:#0284c7; --teal:#2dd4bf;
    --chrome:#1e293b; --chrome-edge:#334155;
    --node-bg:#1e293b; --node-ink:#cbd5e1;
    --canvas-dot:#334155;
    --stage-glow:rgba(56,189,248,.05);
    --link:#38bdf8;
    --shadow:0 2px 4px rgba(0,0,0,.30),0 8px 24px rgba(0,0,0,.45);
    --shadow-lg:0 12px 40px rgba(0,0,0,.55);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#e08177; --warn:#d28d00; --ok:#24b24d;
  }
:root[data-theme="vscode-onedark"]{   /* VSCode One Dark Pro */
    --toolbar-bg:#abb2bf; --toolbar-text:#282c34;
    --paper:#282c34; --paper-2:#21252b; --ink:#abb2bf; --ink-soft:#9aa1ac;
    --line:#3e4451; --line-2:#4a5261;
    --accent:#98c379; --accent-deep:#50a14f; --teal:#61afef;
    --chrome:#21252b; --chrome-edge:#3e4451;
    --node-bg:#2c323d; --node-ink:#abb2bf;
    --canvas-dot:#3e4451;
    --stage-glow:rgba(152,195,121,.05);
    --link:#61afef;
    --shadow:0 2px 4px rgba(0,0,0,.30),0 8px 24px rgba(0,0,0,.45);
    --shadow-lg:0 12px 40px rgba(0,0,0,.55);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#e2887e; --warn:#da9200; --ok:#26b850;
  }
:root[data-theme="monokai-pro"]{   /* VSCode Monokai Pro */
    --toolbar-bg:#fcfcfa; --toolbar-text:#2d2a2e;
    --paper:#2d2a2e; --paper-2:#19181a; --ink:#fcfcfa; --ink-soft:#b6b6b6;
    --line:#403e41; --line-2:#504d52;
    --accent:#a9dc76; --accent-deep:#6ca030; --teal:#ffd866;
    --chrome:#19181a; --chrome-edge:#403e41;
    --node-bg:#363337; --node-ink:#fcfcfa;
    --canvas-dot:#403e41;
    --stage-glow:rgba(169,220,118,.05);
    --link:#ff98b1;
    --shadow:0 2px 4px rgba(0,0,0,.35),0 8px 24px rgba(0,0,0,.50);
    --shadow-lg:0 12px 40px rgba(0,0,0,.60);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#e8a098; --warn:#f2a200; --ok:#2acc58;
  }
:root[data-theme="amazon-aws"]{   /* Amazon AWS Console */
    --toolbar-bg:#f9fafb; --toolbar-text:#111827;
    --paper:#161e2e; --paper-2:#1f2937; --ink:#d1d5db; --ink-soft:#a5abb4;
    --line:#374151; --line-2:#455160;
    --accent:#ff9900; --accent-deep:#ec7211; --teal:#93c5fd;
    --chrome:#1f2937; --chrome-edge:#374151;
    --node-bg:#232f3e; --node-ink:#d1d5db;
    --canvas-dot:#374151;
    --stage-glow:rgba(255,153,0,.05);
    --link:#ff9900;
    --shadow:0 2px 4px rgba(0,0,0,.30),0 8px 24px rgba(0,0,0,.45);
    --shadow-lg:0 12px 40px rgba(0,0,0,.55);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#e2867c; --warn:#d89000; --ok:#25b74f;
  }
:root[data-theme="synthwave"]{   /* Synthwave '84 Sunset */
    --toolbar-bg:#e2d9f3; --toolbar-text:#1a102f;
    --paper:#1a102f; --paper-2:#281b45; --ink:#e2d9f3; --ink-soft:#b6a6c9;
    --line:#483267; --line-2:#5a4080;
    --accent:#00f5d4; --accent-deep:#00c4ac; --teal:#ff71ce;
    --chrome:#281b45; --chrome-edge:#483267;
    --node-bg:#281b45; --node-ink:#e2d9f3;
    --canvas-dot:#483267;
    --stage-glow:rgba(255,113,206,.06);
    --link:#ff81d3;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#e07e73; --warn:#d18b00; --ok:#24b04c;
  }
:root[data-theme="matrix-green"]{   /* Matrix Green terminal */
    --toolbar-bg:#d7ffe0; --toolbar-text:#04140b;
    --paper:#030705; --paper-2:#07140b; --ink:#9ef7b2; --ink-soft:#6ec081;
    --line:#123a1f; --line-2:#1b4b2b;
    --accent:#39ff88; --accent-deep:#1fbf5f; --teal:#a9ffc0;
    --chrome:#07140b; --chrome-edge:#123a1f;
    --node-bg:#0b2414; --node-ink:#9ef7b2;
    --canvas-dot:#123a1f;
    --stage-glow:rgba(57,255,136,.06);
    --link:#39ff88;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#e18278; --warn:#d48e00; --ok:#25b34d;
  }
:root[data-theme="solarized"]{   /* Solarized Classic */
    --toolbar-bg:#073642; --toolbar-text:#fdf6e3;
    --paper:#fdf6e3; --paper-2:#eee8d5; --ink:#60757d; --ink-soft:#667474;
    --line:#eee8d5; --line-2:#d8d2be;
    --accent:#268bd2; --accent-deep:#206ea4; --teal:#2aa198;
    --chrome:#eee8d5; --chrome-edge:#d8d2be;
    --node-bg:#ffffff; --node-ink:#657b83;
    --canvas-dot:#d8d2be;
    --stage-glow:rgba(38,139,210,.05);
    --link:#2176b3;
    --shadow:0 1px 3px rgba(7,54,66,.12),0 8px 24px rgba(7,54,66,.10);
    --shadow-lg:0 12px 40px rgba(7,54,66,.18);
    --accent-text:#2076b3; --teal-text:#217e77;
  }
:root[data-theme="scholar-parchment"]{   /* Scholar Parchment */
    --toolbar-bg:#053327; --toolbar-text:#faf8f3;
    --paper:#faf8f3; --paper-2:#f1ece1; --ink:#2c2c2a; --ink-soft:#5a5a56;
    --line:#e2ddd3; --line-2:#d4cdbe;
    --accent:#0a4d3c; --accent-deep:#063a2e; --teal:#c5a059;
    --chrome:#f1ece1; --chrome-edge:#e2ddd3;
    --node-bg:#ffffff; --node-ink:#2c2c2a;
    --canvas-dot:#e2ddd3;
    --stage-glow:rgba(197,160,89,.06);
    --link:#0a4d3c;
    --shadow:0 1px 3px rgba(44,44,42,.10),0 8px 24px rgba(44,44,42,.08);
    --shadow-lg:0 12px 40px rgba(44,44,42,.16);
    --teal-fg:#000000; --accent-text:var(--accent); --teal-text:color-mix(in srgb, var(--teal) 50%, var(--ink)); --danger:#b53629; --warn:#8f5f00; --ok:#187633;
  }
:root[data-theme="paper-ink"]{   /* Paper & Ink editorial */
    --toolbar-bg:#111111; --toolbar-text:#fcfbf7;
    --paper:#fcfbf7; --paper-2:#f1efe9; --ink:#343434; --ink-soft:#6d6962;
    --line:#ddd9d0; --line-2:#cec9bd;
    --accent:#27364b; --accent-deep:#1b2636; --teal:#5c6b7a;
    --chrome:#f1efe9; --chrome-edge:#ddd9d0;
    --node-bg:#ffffff; --node-ink:#343434;
    --canvas-dot:#ddd9d0;
    --stage-glow:rgba(39,54,75,.05);
    --link:#27364b;
    --shadow:0 1px 3px rgba(52,52,52,.10),0 8px 24px rgba(52,52,52,.08);
    --shadow-lg:0 12px 40px rgba(52,52,52,.16);
    --accent-text:var(--accent); --danger:#b73629; --warn:#906000; --ok:#197834;
  }
:root[data-theme="mint-graphite"]{   /* Mint Graphite */
    --toolbar-bg:#18251f; --toolbar-text:#f7fbf9;
    --paper:#f7fbf9; --paper-2:#e6f3ee; --ink:#34423c; --ink-soft:#63726b;
    --line:#d8e8e1; --line-2:#c6dbd1;
    --accent:#0f9f75; --accent-deep:#0b7e5c; --teal:#34d399;
    --chrome:#e6f3ee; --chrome-edge:#d8e8e1;
    --node-bg:#ffffff; --node-ink:#34423c;
    --canvas-dot:#d8e8e1;
    --stage-glow:rgba(15,159,117,.05);
    --link:#0c7e5c;
    --shadow:0 1px 3px rgba(52,66,60,.10),0 8px 24px rgba(52,66,60,.08);
    --shadow-lg:0 12px 40px rgba(52,66,60,.16);
    --teal-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 65%, var(--ink)); --teal-text:#1c815c; --danger:#b73629; --warn:#906000; --ok:#197935;
  }
:root[data-theme="carbon-amber"]{   /* Carbon & Amber CAD */
    --toolbar-bg:#ffd066; --toolbar-text:#121212;
    --paper:#121212; --paper-2:#1a1400; --ink:#d4af37; --ink-soft:#c89900;
    --line:#332600; --line-2:#4a3a00;
    --accent:#ffb000; --accent-deep:#c98f00; --teal:#ffd066;
    --chrome:#1a1400; --chrome-edge:#332600;
    --node-bg:#1f1800; --node-ink:#d4af37;
    --canvas-dot:#332600;
    --stage-glow:rgba(255,176,0,.06);
    --link:#ffb000;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#dc7064; --warn:#c38200; --ok:#22a547;
  }
:root[data-theme="cyber-quantum"]{   /* Cyber-Quantum */
    --toolbar-bg:#e2f8ff; --toolbar-text:#0a101d;
    --paper:#0a101d; --paper-2:#10192c; --ink:#e2f8ff; --ink-soft:#a0b7c3;
    --line:#1b3b4d; --line-2:#28526b;
    --accent:#00f0ff; --accent-deep:#00b8c4; --teal:#ff0055;
    --chrome:#10192c; --chrome-edge:#1b3b4d;
    --node-bg:#10192c; --node-ink:#e2f8ff;
    --canvas-dot:#1b3b4d;
    --stage-glow:rgba(0,240,255,.07);
    --link:#00f0ff;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --accent-fg:#000000; --accent-text:var(--accent); --teal-text:color-mix(in srgb, var(--teal) 55%, var(--ink)); --danger:#e07d73; --warn:#d08b00; --ok:#24b04c;
  }
:root[data-theme="blueprint"]{   /* Architectural Blueprint */
    --toolbar-bg:#e0f2fe; --toolbar-text:#005596;
    --paper:#005596; --paper-2:#004478; --ink:#e0f2fe; --ink-soft:#9cdefd;
    --line:#4080b0; --line-2:#5797c2;
    --accent:#ffffff; --accent-deep:#c9e3f5; --accent-fg:#005596; --teal:#7dd3fc;
    --chrome:#004478; --chrome-edge:#4080b0;
    --node-bg:#004478; --node-ink:#e0f2fe;
    --canvas-dot:#4080b0;
    --stage-glow:rgba(255,255,255,.08);
    --link:#9cdefd;
    --shadow:0 2px 4px rgba(0,0,0,.35),0 8px 24px rgba(0,0,0,.50);
    --shadow-lg:0 12px 40px rgba(0,0,0,.60);
    --teal-fg:#000000; --accent-text:var(--accent); --teal-text:color-mix(in srgb, var(--teal) 65%, var(--ink)); --danger:#f3ccc8; --warn:#ffce6a; --ok:#8fe8a8;
  }
:root[data-theme="obsidian"]{   /* Obsidian Terminal */
    --toolbar-bg:#ffffff; --toolbar-text:#000000;
    --paper:#000000; --paper-2:#18181b; --ink:#a1a1aa; --ink-soft:#9b9ba4;
    --line:#27272a; --line-2:#333338;
    --accent:#ffffff; --accent-deep:#d4d4d8; --accent-fg:#000000; --teal:#e4e4e7;
    --chrome:#18181b; --chrome-edge:#27272a;
    --node-bg:#18181b; --node-ink:#a1a1aa;
    --canvas-dot:#27272a;
    --stage-glow:rgba(255,255,255,.05);
    --link:#ffffff;
    --shadow:0 2px 4px rgba(0,0,0,.50),0 8px 24px rgba(0,0,0,.65);
    --shadow-lg:0 12px 40px rgba(0,0,0,.75);
    --teal-fg:#000000; --accent-text:var(--accent); --danger:#dd7266; --warn:#c58400; --ok:#22a748;
  }
:root[data-theme="swiss-crimson"]{   /* Swiss Crimson */
    --toolbar-bg:#000000; --toolbar-text:#f4f4f4;
    --paper:#f4f4f4; --paper-2:#e6e6e6; --ink:#111111; --ink-soft:#616161;
    --line:#d1d1d1; --line-2:#c0c0c0;
    --accent:#d90429; --accent-deep:#a60320; --teal:#2f2f2f;
    --chrome:#e6e6e6; --chrome-edge:#d1d1d1;
    --node-bg:#ffffff; --node-ink:#111111;
    --canvas-dot:#d1d1d1;
    --stage-glow:rgba(217,4,41,.05);
    --link:#c50425;
    --shadow:0 1px 3px rgba(17,17,17,.10),0 8px 24px rgba(17,17,17,.08);
    --shadow-lg:0 12px 40px rgba(17,17,17,.16);
    --accent-text:color-mix(in srgb, var(--accent) 90%, var(--ink)); --danger:#b03427; --warn:#865900; --ok:#177131;
  }
:root[data-theme="nordic-sage"]{   /* Nordic Pine & Sage */
    --toolbar-bg:#1b2621; --toolbar-text:#f3f5f3;
    --paper:#f3f5f3; --paper-2:#e4eae6; --ink:#2d3a34; --ink-soft:#5f6c66;
    --line:#d5ddd8; --line-2:#c3cdc7;
    --accent:#3b604d; --accent-deep:#2c4a3a; --teal:#52b788;
    --chrome:#e4eae6; --chrome-edge:#d5ddd8;
    --node-bg:#ffffff; --node-ink:#2d3a34;
    --canvas-dot:#d5ddd8;
    --stage-glow:rgba(59,96,77,.05);
    --link:#3b604d;
    --shadow:0 1px 3px rgba(45,58,52,.10),0 8px 24px rgba(45,58,52,.08);
    --shadow-lg:0 12px 40px rgba(45,58,52,.16);
    --teal-fg:#000000; --accent-text:var(--accent); --teal-text:color-mix(in srgb, var(--teal) 45%, var(--ink)); --danger:#b13528;
  }
:root[data-theme="deep-ocean"]{   /* Deep Ocean Phosphor */
    --toolbar-bg:#e6fffa; --toolbar-text:#06181d;
    --paper:#06181d; --paper-2:#0d282e; --ink:#a3e5d9; --ink-soft:#6db8ac;
    --line:#113836; --line-2:#1a4a45;
    --accent:#10b981; --accent-deep:#0b8e61; --teal:#5eead4;
    --chrome:#0d282e; --chrome-edge:#113836;
    --node-bg:#0d282e; --node-ink:#a3e5d9;
    --canvas-dot:#113836;
    --stage-glow:rgba(16,185,129,.06);
    --link:#11c287;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#df786e; --warn:#cb8800; --ok:#23ac4a;
  }
:root[data-theme="google-material"]{   /* Google Material You */
    --toolbar-bg:#e3e2e6; --toolbar-text:#1f1f1f;
    --paper:#1f1f1f; --paper-2:#2d2d2d; --ink:#e3e2e6; --ink-soft:#acafad;
    --line:#444746; --line-2:#56595a;
    --accent:#a8c7fa; --accent-deep:#7fa8e8; --teal:#81d4fa;
    --chrome:#2d2d2d; --chrome-edge:#444746;
    --node-bg:#2d2d2d; --node-ink:#e3e2e6;
    --canvas-dot:#444746;
    --stage-glow:rgba(168,199,250,.05);
    --link:#a8c7fa;
    --shadow:0 2px 4px rgba(0,0,0,.35),0 8px 24px rgba(0,0,0,.50);
    --shadow-lg:0 12px 40px rgba(0,0,0,.60);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#e48f86; --warn:#e19700; --ok:#27be52;
  }
:root[data-theme="github-modern"]{   /* GitHub Modern (dark dimmed) */
    --toolbar-bg:#cdd9e5; --toolbar-text:#22272e;
    --paper:#22272e; --paper-2:#2d333b; --ink:#adbac7; --ink-soft:#99a3ac;
    --line:#444c56; --line-2:#545d68;
    --accent:#539bf5; --accent-deep:#316dca; --teal:#3fb950;
    --chrome:#2d333b; --chrome-edge:#444c56;
    --node-bg:#2d333b; --node-ink:#adbac7;
    --canvas-dot:#444c56;
    --stage-glow:rgba(83,155,245,.05);
    --link:#62a4f6;
    --shadow:0 2px 4px rgba(0,0,0,.30),0 8px 24px rgba(0,0,0,.45);
    --shadow-lg:0 12px 40px rgba(0,0,0,.55);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 85%, var(--ink)); --danger:#e18379; --warn:#d58f00; --ok:#25b44e;
  }
:root[data-theme="sakura-drift"]{   /* Eos Sakura Drift */
    --toolbar-bg:#2c1e23; --toolbar-text:#fff9fa;
    --paper:#fff9fa; --paper-2:#ffebf0; --ink:#4a3e43; --ink-soft:#816871;
    --line:#f7d6df; --line-2:#efc3ce;
    --accent:#ff758f; --accent-deep:#e35472; --teal:#8d86c9;
    --chrome:#ffebf0; --chrome-edge:#f7d6df;
    --node-bg:#ffffff; --node-ink:#4a3e43;
    --canvas-dot:#f7d6df;
    --stage-glow:rgba(255,117,143,.05);
    --link:#df002a;
    --shadow:0 1px 3px rgba(74,62,67,.10),0 8px 24px rgba(74,62,67,.08);
    --shadow-lg:0 12px 40px rgba(74,62,67,.16);
    --accent-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 50%, var(--ink)); --teal-text:color-mix(in srgb, var(--teal) 65%, var(--ink)); --danger:#b9372a; --warn:#906100; --ok:#197834;
  }
:root[data-theme="aurora-frost"]{   /* Nordic Aurora Frost */
    --toolbar-bg:#e0fbfc; --toolbar-text:#0b132b;
    --paper:#0b132b; --paper-2:#1c2541; --ink:#c0d6df; --ink-soft:#94acc3;
    --line:#1c2541; --line-2:#2a3a5e;
    --accent:#70e000; --accent-deep:#54a800; --teal:#64dfdf;
    --chrome:#1c2541; --chrome-edge:#1c2541;
    --node-bg:#1c2541; --node-ink:#c0d6df;
    --canvas-dot:#1c2541;
    --stage-glow:rgba(112,224,0,.06);
    --link:#70e000;
    --shadow:0 2px 4px rgba(0,0,0,.35),0 8px 24px rgba(0,0,0,.50);
    --shadow-lg:0 12px 40px rgba(0,0,0,.60);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#e07f75; --warn:#d18c00; --ok:#24b14c;
  }
:root[data-theme="espresso"]{   /* Velvet Espresso & Caramel */
    --toolbar-bg:#faedcd; --toolbar-text:#1a1412;
    --paper:#1a1412; --paper-2:#281e19; --ink:#e3d5ca; --ink-soft:#b5a99e;
    --line:#382b24; --line-2:#473731;
    --accent:#d4a373; --accent-deep:#b08350; --teal:#bc6c25;
    --chrome:#281e19; --chrome-edge:#382b24;
    --node-bg:#281e19; --node-ink:#e3d5ca;
    --canvas-dot:#382b24;
    --stage-glow:rgba(212,163,115,.06);
    --link:#d4a373;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --accent-fg:#000000; --accent-text:var(--accent); --teal-text:color-mix(in srgb, var(--teal) 70%, var(--ink)); --danger:#de786d; --warn:#ca8700; --ok:#23ab4a;
  }
:root[data-theme="arctic-glass"]{   /* Arctic Glass */
    --toolbar-bg:#102a43; --toolbar-text:#f4faff;
    --paper:#f4faff; --paper-2:#e9f5fb; --ink:#274154; --ink-soft:#5d7283;
    --line:#d6eaf4; --line-2:#c4deeb;
    --accent:#149bd7; --accent-deep:#0f7cac; --teal:#38bdf8;
    --chrome:#e9f5fb; --chrome-edge:#d6eaf4;
    --node-bg:#ffffff; --node-ink:#274154;
    --canvas-dot:#d6eaf4;
    --stage-glow:rgba(20,155,215,.05);
    --link:#0f77a6;
    --shadow:0 1px 3px rgba(39,65,84,.10),0 8px 24px rgba(39,65,84,.08);
    --shadow-lg:0 12px 40px rgba(39,65,84,.16);
    --teal-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 65%, var(--ink)); --teal-text:color-mix(in srgb, var(--teal) 45%, var(--ink)); --danger:#ba372a; --warn:#916100; --ok:#197a35;
  }
:root[data-theme="copper-lab"]{   /* Copper Laboratory */
    --toolbar-bg:#fff8f2; --toolbar-text:#1b1715;
    --paper:#1b1715; --paper-2:#29201c; --ink:#e4d7ce; --ink-soft:#b7a9a1;
    --line:#463128; --line-2:#573d32;
    --accent:#d97745; --accent-deep:#b45b30; --teal:#e89a70;
    --chrome:#29201c; --chrome-edge:#463128;
    --node-bg:#29201c; --node-ink:#e4d7ce;
    --canvas-dot:#463128;
    --stage-glow:rgba(217,119,69,.06);
    --link:#e39b76;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --teal-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 90%, var(--ink)); --danger:#de786d; --warn:#cb8800; --ok:#23ac4a;
  }
:root[data-theme="forest-night"]{   /* Forest Night */
    --toolbar-bg:#f4fff8; --toolbar-text:#101b17;
    --paper:#101b17; --paper-2:#172720; --ink:#d5e5dc; --ink-soft:#a1b2a9;
    --line:#294138; --line-2:#345448;
    --accent:#63c174; --accent-deep:#4a9e5d; --teal:#76d98a;
    --chrome:#172720; --chrome-edge:#294138;
    --node-bg:#172720; --node-ink:#d5e5dc;
    --canvas-dot:#294138;
    --stage-glow:rgba(99,193,116,.06);
    --link:#64c175;
    --shadow:0 2px 4px rgba(0,0,0,.35),0 8px 24px rgba(0,0,0,.50);
    --shadow-lg:0 12px 40px rgba(0,0,0,.60);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#e07d72; --warn:#cf8a00; --ok:#24af4c;
  }
:root[data-theme="cobalt"]{   /* Cobalt Studio */
    --toolbar-bg:#d7e3ff; --toolbar-text:#0b1530;
    --paper:#0b1530; --paper-2:#121f42; --ink:#d7e3ff; --ink-soft:#a2afc7;
    --line:#263b6b; --line-2:#304a87;
    --accent:#4f8cff; --accent-deep:#3a6dd6; --teal:#78a9ff;
    --chrome:#121f42; --chrome-edge:#263b6b;
    --node-bg:#121f42; --node-ink:#d7e3ff;
    --canvas-dot:#263b6b;
    --stage-glow:rgba(79,140,255,.06);
    --link:#83aeff;
    --shadow:0 2px 4px rgba(0,0,0,.35),0 8px 24px rgba(0,0,0,.50);
    --shadow-lg:0 12px 40px rgba(0,0,0,.60);
    --teal-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 90%, var(--ink)); --danger:#df7a6f; --warn:#cc8900; --ok:#23ad4b;
  }
:root[data-theme="lavender-mist"]{   /* Lavender Mist */
    --toolbar-bg:#211a32; --toolbar-text:#faf8ff;
    --paper:#faf8ff; --paper-2:#f0ebfa; --ink:#403a52; --ink-soft:#706983;
    --line:#e4ddf2; --line-2:#d6cee8;
    --accent:#7956c8; --accent-deep:#6041a6; --teal:#b69cff;
    --chrome:#f0ebfa; --chrome-edge:#e4ddf2;
    --node-bg:#ffffff; --node-ink:#403a52;
    --canvas-dot:#e4ddf2;
    --stage-glow:rgba(121,86,200,.05);
    --link:#7956c8;
    --shadow:0 1px 3px rgba(64,58,82,.10),0 8px 24px rgba(64,58,82,.08);
    --shadow-lg:0 12px 40px rgba(64,58,82,.16);
    --teal-fg:#000000; --accent-text:var(--accent); --teal-text:#7747ff; --danger:#b63629; --warn:#8f5f00; --ok:#187734;
  }
:root[data-theme="ruby-night"]{   /* Ruby Night */
    --toolbar-bg:#ffd4dc; --toolbar-text:#151314;
    --paper:#151314; --paper-2:#24191c; --ink:#e8dddf; --ink-soft:#b7abad;
    --line:#43242b; --line-2:#57313a;
    --accent:#f43f5e; --accent-deep:#c62b47; --teal:#ff7187;
    --chrome:#24191c; --chrome-edge:#43242b;
    --node-bg:#24191c; --node-ink:#e8dddf;
    --canvas-dot:#43242b;
    --stage-glow:rgba(244,63,94,.07);
    --link:#f88fa1;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --teal-fg:#000000; --danger:#df796e; --warn:#cc8800; --ok:#23ad4b;
  }
:root[data-theme="sandstone"]{   /* Sandstone */
    --toolbar-bg:#2d2118; --toolbar-text:#fbf6ee;
    --paper:#fbf6ee; --paper-2:#f3e7d4; --ink:#51463a; --ink-soft:#78695a;
    --line:#e8d8c2; --line-2:#dcc9ae;
    --accent:#b45309; --accent-deep:#924308; --teal:#e99a55;
    --chrome:#f3e7d4; --chrome-edge:#e8d8c2;
    --node-bg:#ffffff; --node-ink:#51463a;
    --canvas-dot:#e8d8c2;
    --stage-glow:rgba(180,83,9,.06);
    --link:#ae5009;
    --shadow:0 1px 3px rgba(81,70,58,.10),0 8px 24px rgba(81,70,58,.08);
    --shadow-lg:0 12px 40px rgba(81,70,58,.16);
    --teal-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 90%, var(--ink)); --teal-text:#a55815;
  }
:root[data-theme="electric-indigo"]{   /* Electric Indigo */
    --toolbar-bg:#e9e2ff; --toolbar-text:#0e0a22;
    --paper:#0e0a22; --paper-2:#191238; --ink:#dcd7ff; --ink-soft:#ada9c8;
    --line:#33255b; --line-2:#42336f;
    --accent:#8b5cf6; --accent-deep:#6f3ed4; --teal:#a78bfa;
    --chrome:#191238; --chrome-edge:#33255b;
    --node-bg:#191238; --node-ink:#dcd7ff;
    --canvas-dot:#33255b;
    --stage-glow:rgba(139,92,246,.07);
    --link:#b89dfa;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --teal-fg:#000000; --danger:#de786d; --warn:#cb8800; --ok:#23ac4a;
  }
:root[data-theme="teal-paper"]{   /* Teal Paper */
    --toolbar-bg:#12322f; --toolbar-text:#f7fcfb;
    --paper:#f7fcfb; --paper-2:#e6f2f0; --ink:#304744; --ink-soft:#607270;
    --line:#d4e7e3; --line-2:#c2dcd6;
    --accent:#0f766e; --accent-deep:#0b5a54; --teal:#2dd4bf;
    --chrome:#e6f2f0; --chrome-edge:#d4e7e3;
    --node-bg:#ffffff; --node-ink:#304744;
    --canvas-dot:#d4e7e3;
    --stage-glow:rgba(15,118,110,.05);
    --link:#0f766e;
    --shadow:0 1px 3px rgba(48,71,68,.10),0 8px 24px rgba(48,71,68,.08);
    --shadow-lg:0 12px 40px rgba(48,71,68,.16);
    --teal-fg:#000000; --accent-text:var(--accent); --teal-text:#197c70; --danger:#b73629; --warn:#906000; --ok:#197834;
  }
:root[data-theme="steel-orange"]{   /* Steel & Orange */
    --toolbar-bg:#d9dee3; --toolbar-text:#171a1d;
    --paper:#171a1d; --paper-2:#23282d; --ink:#d9dee3; --ink-soft:#a7aeb4;
    --line:#353b40; --line-2:#454c54;
    --accent:#f97316; --accent-deep:#d15c0b; --teal:#ff9a55;
    --chrome:#23282d; --chrome-edge:#353b40;
    --node-bg:#23282d; --node-ink:#d9dee3;
    --canvas-dot:#353b40;
    --stage-glow:rgba(249,115,22,.06);
    --link:#fa934b;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#df796e; --warn:#cc8800; --ok:#23ad4b;
  }
:root[data-theme="cosmic-rose"]{   /* Cosmic Rose */
    --toolbar-bg:#ffd6ec; --toolbar-text:#100c1b;
    --paper:#100c1b; --paper-2:#20142a; --ink:#e4d9ea; --ink-soft:#b5a8bb;
    --line:#38223f; --line-2:#482c52;
    --accent:#f472b6; --accent-deep:#d44e97; --teal:#f9a8d4;
    --chrome:#20142a; --chrome-edge:#38223f;
    --node-bg:#20142a; --node-ink:#e4d9ea;
    --canvas-dot:#38223f;
    --stage-glow:rgba(244,114,182,.07);
    --link:#f68ac2;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#df786e; --warn:#cb8800; --ok:#23ac4a;
  }
:root[data-theme="plasma-crimson"]{   /* Plasma Crimson */
    --toolbar-bg:#ffe8ed; --toolbar-text:#12080c;
    --paper:#12080c; --paper-2:#1f0e14; --ink:#f5d0d8; --ink-soft:#c7a3aa;
    --line:#4a1a28; --line-2:#5f2233;
    --accent:#ff2d55; --accent-deep:#d10f36; --teal:#ff5c7a;
    --chrome:#1f0e14; --chrome-edge:#4a1a28;
    --node-bg:#1f0e14; --node-ink:#f5d0d8;
    --canvas-dot:#4a1a28;
    --stage-glow:rgba(255,45,85,.08);
    --link:#ff879e;
    --shadow:0 2px 4px rgba(0,0,0,.45),0 8px 24px rgba(0,0,0,.60);
    --shadow-lg:0 12px 40px rgba(0,0,0,.70);
    --teal-fg:#000000; --danger:#de786d; --warn:#cb8800; --ok:#23ab4a;
  }
:root[data-theme="neon-noir"]{   /* Neon Noir */
    --toolbar-bg:#ffffff; --toolbar-text:#0a0a0f;
    --paper:#0a0a0f; --paper-2:#12121c; --ink:#c8c8d4; --ink-soft:#a5a5b6;
    --line:#1f1f2e; --line-2:#2a2a3e;
    --accent:#00ff9f; --accent-deep:#00cc7e; --teal:#a0ffd0;
    --chrome:#12121c; --chrome-edge:#1f1f2e;
    --node-bg:#12121c; --node-ink:#c8c8d4;
    --canvas-dot:#1f1f2e;
    --stage-glow:rgba(0,255,159,.07);
    --link:#00ff9f;
    --shadow:0 2px 4px rgba(0,0,0,.45),0 8px 24px rgba(0,0,0,.60);
    --shadow-lg:0 12px 40px rgba(0,0,0,.70);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#de756a; --warn:#c78500; --ok:#23a949;
  }
:root[data-theme="desert-oasis"]{   /* Desert Oasis */
    --toolbar-bg:#2c2116; --toolbar-text:#fdf6e9;
    --paper:#fdf6e9; --paper-2:#f5e8d0; --ink:#4a3c2a; --ink-soft:#776854;
    --line:#e8d5b5; --line-2:#dcc59e;
    --accent:#d97706; --accent-deep:#b45f05; --teal:#f59e0b;
    --chrome:#f5e8d0; --chrome-edge:#e8d5b5;
    --node-bg:#ffffff; --node-ink:#4a3c2a;
    --canvas-dot:#e8d5b5;
    --stage-glow:rgba(217,119,6,.06);
    --link:#a15805;
    --shadow:0 1px 3px rgba(74,60,42,.10),0 8px 24px rgba(74,60,42,.08);
    --shadow-lg:0 12px 40px rgba(74,60,42,.16);
    --teal-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 60%, var(--ink)); --teal-text:#9a6306; --danger:#b33528; --warn:#8d5e00; --ok:#187533;
  }
:root[data-theme="ice-crystal"]{   /* Ice Crystal */
    --toolbar-bg:#0c1e36; --toolbar-text:#f0f9ff;
    --paper:#f0f9ff; --paper-2:#e0f2fe; --ink:#1e3a5f; --ink-soft:#536f8d;
    --line:#bae6fd; --line-2:#a4d9f5;
    --accent:#0ea5e9; --accent-deep:#0b84bc; --teal:#38bdf8;
    --chrome:#e0f2fe; --chrome-edge:#bae6fd;
    --node-bg:#ffffff; --node-ink:#1e3a5f;
    --canvas-dot:#bae6fd;
    --stage-glow:rgba(14,165,233,.05);
    --link:#0a74a5;
    --shadow:0 1px 3px rgba(30,58,95,.10),0 8px 24px rgba(30,58,95,.08);
    --shadow-lg:0 12px 40px rgba(30,58,95,.16);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 55%, var(--ink)); --teal-text:color-mix(in srgb, var(--teal) 45%, var(--ink)); --danger:#b73629; --warn:#8f6000; --ok:#197834;
  }
:root[data-theme="ember-forge"]{   /* Ember Forge */
    --toolbar-bg:#fff5eb; --toolbar-text:#140e0a;
    --paper:#140e0a; --paper-2:#221610; --ink:#e8d5c4; --ink-soft:#bda897;
    --line:#3d2418; --line-2:#4e2d1e;
    --accent:#ef4444; --accent-deep:#c92828; --teal:#f87171;
    --chrome:#221610; --chrome-edge:#3d2418;
    --node-bg:#221610; --node-ink:#e8d5c4;
    --canvas-dot:#3d2418;
    --stage-glow:rgba(239,68,68,.08);
    --link:#f58f8f;
    --shadow:0 2px 4px rgba(0,0,0,.45),0 8px 24px rgba(0,0,0,.60);
    --shadow-lg:0 12px 40px rgba(0,0,0,.70);
    --teal-fg:#000000; --danger:#de786d; --warn:#cb8800; --ok:#23ab4a;
  }
:root[data-theme="violet-circuit"]{   /* Violet Circuit */
    --toolbar-bg:#f5f0ff; --toolbar-text:#0f0a1a;
    --paper:#0f0a1a; --paper-2:#1a1230; --ink:#d4c8f0; --ink-soft:#ada4c1;
    --line:#2e1f4a; --line-2:#3b2960;
    --accent:#a855f7; --accent-deep:#8b33de; --teal:#c084fc;
    --chrome:#1a1230; --chrome-edge:#2e1f4a;
    --node-bg:#1a1230; --node-ink:#d4c8f0;
    --canvas-dot:#2e1f4a;
    --stage-glow:rgba(168,85,247,.08);
    --link:#c791f9;
    --shadow:0 2px 4px rgba(0,0,0,.45),0 8px 24px rgba(0,0,0,.60);
    --shadow-lg:0 12px 40px rgba(0,0,0,.70);
    --teal-fg:#000000; --danger:#de756a; --warn:#c88600; --ok:#23a949;
  }
:root[data-theme="moss-stone"]{   /* Moss & Stone */
    --toolbar-bg:#1a2416; --toolbar-text:#f4f6f0;
    --paper:#f4f6f0; --paper-2:#e8eedc; --ink:#2e3a2a; --ink-soft:#616f59;
    --line:#d4dcc8; --line-2:#c2ccb4;
    --accent:#4d7c0f; --accent-deep:#3a5e0b; --teal:#84cc16;
    --chrome:#e8eedc; --chrome-edge:#d4dcc8;
    --node-bg:#ffffff; --node-ink:#2e3a2a;
    --canvas-dot:#d4dcc8;
    --stage-glow:rgba(77,124,15,.06);
    --link:#49760e;
    --shadow:0 1px 3px rgba(46,58,42,.10),0 8px 24px rgba(46,58,42,.08);
    --shadow-lg:0 12px 40px rgba(46,58,42,.16);
    --teal-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 95%, var(--ink)); --teal-text:#4e790d; --danger:#b43628; --warn:#8d5e00; --ok:#187633;
  }
:root[data-theme="twilight-amber"]{   /* Twilight Amber */
    --toolbar-bg:#fff8ec; --toolbar-text:#16120c;
    --paper:#16120c; --paper-2:#241e14; --ink:#e8dcc8; --ink-soft:#b7ac95;
    --line:#3a3020; --line-2:#4a3d2a;
    --accent:#fbbf24; --accent-deep:#d19f12; --teal:#fcd34d;
    --chrome:#241e14; --chrome-edge:#3a3020;
    --node-bg:#241e14; --node-ink:#e8dcc8;
    --canvas-dot:#3a3020;
    --stage-glow:rgba(251,191,36,.07);
    --link:#fbbf24;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#df7b70; --warn:#cd8900; --ok:#24ae4b;
  }
:root[data-theme="ocean-depth"]{   /* Ocean Depth */
    --toolbar-bg:#e8f4fa; --toolbar-text:#06141c;
    --paper:#06141c; --paper-2:#0c1e28; --ink:#b8d8e8; --ink-soft:#88afc2;
    --line:#0e2a38; --line-2:#163e52;
    --accent:#22d3ee; --accent-deep:#18aec7; --teal:#67e8f9;
    --chrome:#0c1e28; --chrome-edge:#0e2a38;
    --node-bg:#0c1e28; --node-ink:#b8d8e8;
    --canvas-dot:#0e2a38;
    --stage-glow:rgba(34,211,238,.07);
    --link:#22d3ee;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#de766b; --warn:#c98700; --ok:#23aa4a;
  }
:root[data-theme="charcoal-gold"]{   /* Charcoal Gold */
    --toolbar-bg:#f5f0e6; --toolbar-text:#121212;
    --paper:#121212; --paper-2:#1c1c18; --ink:#d4d0c8; --ink-soft:#aba99e;
    --line:#2e2e28; --line-2:#3c3c34;
    --accent:#eab308; --accent-deep:#c29304; --teal:#facc15;
    --chrome:#1c1c18; --chrome-edge:#2e2e28;
    --node-bg:#1c1c18; --node-ink:#d4d0c8;
    --canvas-dot:#2e2e28;
    --stage-glow:rgba(234,179,8,.07);
    --link:#eab308;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#de766b; --warn:#c98600; --ok:#23aa4a;
  }
:root[data-theme="soft-clay"]{   /* Soft Clay */
    --toolbar-bg:#2a1e18; --toolbar-text:#fbf6f2;
    --paper:#fbf6f2; --paper-2:#f5e8e0; --ink:#4a3a32; --ink-soft:#7b675c;
    --line:#e8d8cc; --line-2:#dcc8b8;
    --accent:#c2410c; --accent-deep:#9c340a; --teal:#fb923c;
    --chrome:#f5e8e0; --chrome-edge:#e8d8cc;
    --node-bg:#ffffff; --node-ink:#4a3a32;
    --canvas-dot:#e8d8cc;
    --stage-glow:rgba(194,65,12,.06);
    --link:#be400c;
    --shadow:0 1px 3px rgba(74,58,50,.10),0 8px 24px rgba(74,58,50,.08);
    --shadow-lg:0 12px 40px rgba(74,58,50,.16);
    --teal-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 95%, var(--ink)); --teal-text:#ae5004; --danger:#b43528; --warn:#8d5e00; --ok:#187533;
  }
:root[data-theme="azure-horizon"]{   /* Azure Horizon */
    --toolbar-bg:#e8f4ff; --toolbar-text:#0a1420;
    --paper:#0a1420; --paper-2:#121e30; --ink:#c0d8f0; --ink-soft:#97adc2;
    --line:#1a2e48; --line-2:#234063;
    --accent:#3b82f6; --accent-deep:#2e66d6; --teal:#60a5fa;
    --chrome:#121e30; --chrome-edge:#1a2e48;
    --node-bg:#121e30; --node-ink:#c0d8f0;
    --canvas-dot:#1a2e48;
    --stage-glow:rgba(59,130,246,.07);
    --link:#7babf9;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --teal-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 75%, var(--ink)); --danger:#de776c; --warn:#c98700; --ok:#23ab4a;
  }
:root[data-theme="jade-temple"]{   /* Jade Temple */
    --toolbar-bg:#e8fff4; --toolbar-text:#0a1612;
    --paper:#0a1612; --paper-2:#12241c; --ink:#c0e0d4; --ink-soft:#79b8a0;
    --line:#1a3a2e; --line-2:#234c3c;
    --accent:#10b981; --accent-deep:#0c9668; --teal:#34d399;
    --chrome:#12241c; --chrome-edge:#1a3a2e;
    --node-bg:#12241c; --node-ink:#c0e0d4;
    --canvas-dot:#1a3a2e;
    --stage-glow:rgba(16,185,129,.07);
    --link:#11c388;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#de786d; --warn:#cb8800; --ok:#23ab4a;
  }
:root[data-theme="magenta-pulse"]{   /* Magenta Pulse */
    --toolbar-bg:#fff0ff; --toolbar-text:#140a16;
    --paper:#140a16; --paper-2:#221022; --ink:#e8d0e8; --ink-soft:#c0a2c0;
    --line:#3a1a3a; --line-2:#4a234a;
    --accent:#ec4899; --accent-deep:#c82e7b; --teal:#f472b6;
    --chrome:#221022; --chrome-edge:#3a1a3a;
    --node-bg:#221022; --node-ink:#e8d0e8;
    --canvas-dot:#3a1a3a;
    --stage-glow:rgba(236,72,153,.08);
    --link:#f389bd;
    --shadow:0 2px 4px rgba(0,0,0,.45),0 8px 24px rgba(0,0,0,.60);
    --shadow-lg:0 12px 40px rgba(0,0,0,.70);
    --teal-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 75%, var(--ink)); --danger:#de776d; --warn:#ca8700; --ok:#23ab4a;
  }
:root[data-theme="graphite-terminal"]{   /* Graphite Terminal */
    --toolbar-bg:#e8e8e8; --toolbar-text:#1a1a1a;
    --paper:#1a1a1a; --paper-2:#242424; --ink:#b8b8b8; --ink-soft:#a2a2a2;
    --line:#333333; --line-2:#424242;
    --accent:#60a5fa; --accent-deep:#4585e0; --teal:#93c5fd;
    --chrome:#242424; --chrome-edge:#333333;
    --node-bg:#242424; --node-ink:#b8b8b8;
    --canvas-dot:#333333;
    --stage-glow:rgba(96,165,250,.06);
    --link:#60a5fa;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#dd7166; --warn:#c48300; --ok:#22a648;
  }
:root[data-theme="honeycomb"]{   /* Honeycomb */
    --toolbar-bg:#2a1e0a; --toolbar-text:#fffbeb;
    --paper:#fffbeb; --paper-2:#fef3c7; --ink:#4a3a1a; --ink-soft:#7d6e43;
    --line:#f5e6b0; --line-2:#ebd98e;
    --accent:#f59e0b; --accent-deep:#cc8108; --teal:#fbbf24;
    --chrome:#fef3c7; --chrome-edge:#f5e6b0;
    --node-bg:#ffffff; --node-ink:#4a3a1a;
    --canvas-dot:#f5e6b0;
    --stage-glow:rgba(245,158,11,.07);
    --link:#9a6406;
    --shadow:0 1px 3px rgba(74,58,26,.10),0 8px 24px rgba(74,58,26,.08);
    --shadow-lg:0 12px 40px rgba(74,58,26,.16);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 50%, var(--ink)); --teal-text:#996f03; --danger:#bb372a; --warn:#936200; --ok:#197a35;
  }
:root[data-theme="frosted-pine"]{   /* Frosted Pine */
    --toolbar-bg:#e8f8ec; --toolbar-text:#0c1610;
    --paper:#0c1610; --paper-2:#122018; --ink:#c0d8c8; --ink-soft:#8ab397;
    --line:#1a3a28; --line-2:#234c34;
    --accent:#34d399; --accent-deep:#27ac7d; --teal:#6ee7b7;
    --chrome:#122018; --chrome-edge:#1a3a28;
    --node-bg:#122018; --node-ink:#c0d8c8;
    --canvas-dot:#1a3a28;
    --stage-glow:rgba(52,211,153,.07);
    --link:#34d399;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#de766b; --warn:#c98700; --ok:#23aa4a;
  }
:root[data-theme="quantum-violet"]{   /* Quantum Violet */
    --toolbar-bg:#f0ecff; --toolbar-text:#0e0a18;
    --paper:#0e0a18; --paper-2:#18122a; --ink:#d0c8e8; --ink-soft:#aca3c0;
    --line:#2a1e48; --line-2:#38295e;
    --accent:#8b5cf6; --accent-deep:#6f3ee0; --teal:#a78bfa;
    --chrome:#18122a; --chrome-edge:#2a1e48;
    --node-bg:#18122a; --node-ink:#d0c8e8;
    --canvas-dot:#2a1e48;
    --stage-glow:rgba(139,92,246,.08);
    --link:#b597f9;
    --shadow:0 2px 4px rgba(0,0,0,.45),0 8px 24px rgba(0,0,0,.60);
    --shadow-lg:0 12px 40px rgba(0,0,0,.70);
    --teal-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 65%, var(--ink)); --danger:#de756a; --warn:#c88600; --ok:#23a949;
  }
:root[data-theme="copper-rust"]{   /* Copper Rust */
    --toolbar-bg:#fff5ec; --toolbar-text:#16120e;
    --paper:#16120e; --paper-2:#241a12; --ink:#e0d0c0; --ink-soft:#bba695;
    --line:#3a2a1e; --line-2:#4a3728;
    --accent:#ea580c; --accent-deep:#c04309; --teal:#fb923c;
    --chrome:#241a12; --chrome-edge:#3a2a1e;
    --node-bg:#241a12; --node-ink:#e0d0c0;
    --canvas-dot:#3a2a1e;
    --stage-glow:rgba(234,88,12,.08);
    --link:#f78f58;
    --shadow:0 2px 4px rgba(0,0,0,.40),0 8px 24px rgba(0,0,0,.55);
    --shadow-lg:0 12px 40px rgba(0,0,0,.65);
    --teal-fg:#000000; --accent-text:color-mix(in srgb, var(--accent) 75%, var(--ink)); --danger:#de776c; --warn:#c98700; --ok:#23aa4a;
  }
:root[data-theme="lunar-silver"]{   /* Lunar Silver */
    --toolbar-bg:#f0f4f8; --toolbar-text:#0e1014;
    --paper:#0e1014; --paper-2:#181c22; --ink:#c8d0d8; --ink-soft:#9faab5;
    --line:#2a3038; --line-2:#383f49;
    --accent:#94a3b8; --accent-deep:#74828f; --teal:#cbd5e1;
    --chrome:#181c22; --chrome-edge:#2a3038;
    --node-bg:#181c22; --node-ink:#c8d0d8;
    --canvas-dot:#2a3038;
    --stage-glow:rgba(148,163,184,.06);
    --link:#9caabd;
    --shadow:0 2px 4px rgba(0,0,0,.35),0 8px 24px rgba(0,0,0,.50);
    --shadow-lg:0 12px 40px rgba(0,0,0,.60);
    --accent-fg:#000000; --teal-fg:#000000; --accent-text:var(--accent); --danger:#de756a; --warn:#c88600; --ok:#23aa4a;
  }
*{box-sizing:border-box}
  html,body{margin:0;height:100%;overflow:hidden;font-family:var(--sans);color:var(--ink);background:var(--paper)}
  button{font-family:inherit;cursor:pointer}

  /* ===== App shell ===== */
  /* Three rows: a top header, the main content, and a status bar. Both bars
     span only the tracks to the right of the sidebar (they start beside it,
     not above/below it); .side spans all three rows and .stage is placed
     explicitly so nothing is left to auto-placement.
     The content track is minmax(0,1fr) - as every other layout's copy of this
     rule already was - because an fr track's automatic minimum is the
     min-content of its items, so one wide item in this track pushes the whole
     grid past the right edge. That is not hypothetical; see .statusbar. */
  .app{display:grid;grid-template-columns:auto minmax(0,1fr);grid-template-rows:auto auto minmax(0,1fr) auto;height:calc(100vh / var(--ui-zoom, 1));height:calc(100dvh / var(--ui-zoom, 1))}
  .topbar{grid-column:2 / -1;grid-row:1}
  .side{grid-column:1;grid-row:1 / 5}
  .stage{grid-column:2;grid-row:3}
  .statusbar{grid-column:2 / -1;grid-row:4}
  #tabStrip{grid-column:2/-1;grid-row:2;display:flex;align-items:flex-end;gap:6px;padding:4px 12px 0;overflow:hidden}
  #tabStrip.hidden{display:none}   /* collapsed row = auto 0, so the stage takes its place */
  #tabRow{display:flex;gap:4px;overflow-x:auto;scrollbar-width:none;flex:1;min-width:0;align-items:flex-end}
  #tabRow::-webkit-scrollbar{display:none}
  #tabRow .tab{display:flex;align-items:center;gap:6px;max-width:200px;min-width:0;padding:5px 8px 6px 10px;border:1px solid var(--line);border-bottom:none;border-radius:10px 10px 0 0;background:var(--paper-2);cursor:pointer;font-family:inherit;font-size:12px;color:var(--ink-soft);position:relative;top:1px;transition:background-color .2s ease,color .2s ease,border-color .2s ease}
  #tabRow .tab:hover{color:var(--ink)}
  #tabRow .tab.active{background:var(--paper);color:var(--ink);font-weight:600;border-color:var(--line-2)}
  #tabRow .tab-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  #tabRow .tab-close{width:16px;height:16px;flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;border-radius:4px;font-size:10px;line-height:1;color:var(--ink-soft)}
  #tabRow .tab-close:hover{background:var(--hover);color:var(--ink)}
  .tab-new{flex:0 0 auto;width:30px;height:26px;border:1px dashed var(--line-2);background:none;color:var(--ink-soft);border-radius:8px 8px 0 0;cursor:pointer;font-size:16px;line-height:1;font-family:inherit}
  .tab-new:hover{color:var(--accent-text);border-color:var(--accent)}
  /* Floating shells and single-column layouts have no top strip row -
     the tab strip stays hidden there unless tabbed workspace is enabled. */
body.ui-classic:not(.tabs-on) #tabStrip, body.ui-rail:not(.tabs-on) #tabStrip, body.ui-zen:not(.tabs-on) #tabStrip, body.ui-mirror:not(.tabs-on) #tabStrip, body.ui-deck:not(.tabs-on) #tabStrip, body.ui-dock:not(.tabs-on) #tabStrip, body.ui-split:not(.tabs-on) #tabStrip, body.ui-minimal:not(.tabs-on) #tabStrip, body.ui-outline:not(.tabs-on) #tabStrip{display:none}
  /* The closed markdown pane still has to be given a cell. ensureMdPane()
     builds #mdPane once and keeps it forever - it registers a window resize
     listener, so it is never torn down - while applyUiLayout() drops
     .md-ready on every switch. The three single-row shells below park the
     pane on row 1, which is right when row 1 is the only row and wrong the
     moment tabs-on adds a strip above the stage: a width:0 flex column of
     editor markup has an enormous max-content height, an `auto` track is
     sized to max-content before any 1fr track is fed, so row 1 swallowed the
     viewport and the stage collapsed to zero. Visiting the split editor once
     and returning was enough. These corrections must therefore NOT wait for
     .md-ready - the pane exists, and needs a cell, whether or not it is open.
     Beside the stage is that cell: at width 0 it paints nothing, and the
     stage's row is a 1fr track, which no content can inflate. This is what
     body.ui-split.tabs-on #mdPane already did. */
body.ui-rail.tabs-on .app{grid-template-rows:auto minmax(0,1fr)}
body.ui-rail.tabs-on #tabStrip{display:flex;grid-column:3/-1;grid-row:1}
body.ui-rail.tabs-on .side{grid-row:1/3}
body.ui-rail.tabs-on .topbar{grid-row:1/3}
body.ui-rail.tabs-on .stage{grid-column:3;grid-row:2}
body.ui-rail.tabs-on.md-ready .app{grid-template-rows:auto minmax(0,1fr)}
body.ui-rail.tabs-on.md-ready #tabStrip{grid-column:3/-1;grid-row:1}
body.ui-rail.tabs-on #mdPane{grid-column:3;grid-row:2}
body.ui-rail.tabs-on.md-ready .stage{grid-column:4;grid-row:2}
body.ui-classic.tabs-on .app{grid-template-rows:auto minmax(0,1fr)}
body.ui-classic.tabs-on #tabStrip{display:flex;grid-column:2;grid-row:1}
body.ui-classic.tabs-on .side{grid-row:1/3}
body.ui-classic.tabs-on .stage{grid-column:2;grid-row:2}
body.ui-classic.tabs-on.md-ready .app{grid-template-rows:auto minmax(0,1fr)}
body.ui-classic.tabs-on.md-ready #tabStrip{grid-column:2/-1;grid-row:1}
body.ui-classic.tabs-on #mdPane{grid-column:2;grid-row:2}
body.ui-classic.tabs-on.md-ready .stage{grid-column:3;grid-row:2}
  body.focus-mode #tabStrip{display:none}   /* focus is canvas-only */
  body.shared-view #tabStrip{display:none}  /* shared banner view is single-map */
  #tabRow .tab.active .tab-close{color:var(--ink-soft)}
  /* Markdown mode: split pane (editor | live map) */
  body.md-ready .app{grid-template-columns:auto auto minmax(0,1fr)}   /* md pane takes the middle track once it exists */
  #mdPane{width:0;overflow:hidden;position:relative;display:flex;flex-direction:column;min-width:0;min-height:0;background:var(--paper);border-right:1px solid var(--line);transition:width .22s cubic-bezier(.4,0,.2,1);grid-column:2;grid-row:3}
  body.md-ready .stage{grid-column:3;grid-row:3}
  body.md-mode #mdPane{width:var(--md-w,40vw)}
  body:not(.md-mode) #mdPane{border-right-width:0}   /* a closed pane must be 0 wide, not 1px of border - see #outlinePane */
  body.md-resizing #mdPane{transition:none}                            /* no easing while dragging the divider */
  @media (prefers-reduced-motion: reduce){ #mdPane{transition:none} }
  #mdPane .md-head{display:flex;align-items:center;gap:8px;padding:8px 12px;border-bottom:1px solid var(--line);font-size:12px;font-weight:600;color:var(--ink-soft)}
  #mdPane .md-pos{margin-left:auto;font-size:11px;font-weight:400;color:var(--ink-soft);font-variant-numeric:tabular-nums}
  /* markdown editor formatting toolbar */
  #mdPane .md-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:2px;padding:4px 8px;border-bottom:1px solid var(--line);background:var(--paper)}
  #mdPane .md-toolbar button{min-width:26px;height:26px;padding:0 6px;border:1px solid transparent;background:transparent;color:var(--ink-soft);cursor:pointer;border-radius:6px;font-size:12px;font-weight:600;line-height:1;display:inline-flex;align-items:center;justify-content:center}
  #mdPane .md-toolbar button:hover{background:var(--paper-2,rgba(127,127,127,.12));color:var(--ink);border-color:var(--line)}
  #mdPane .md-toolbar button s{text-decoration:line-through}
  #mdPane .md-toolbar .md-sep{width:1px;height:16px;background:var(--line);margin:0 4px}
  #mdPane.md-preview .md-toolbar{display:none}
  #mdPane .md-close{border:none;background:transparent;cursor:pointer;font-size:15px;line-height:1;color:var(--ink-soft);padding:2px 7px;border-radius:6px}
  #mdPane .md-close:hover{background:var(--paper-2,rgba(0,0,0,.05))}
  #mdPane .md-body{display:flex;flex:1;min-height:0;overflow:hidden}
  #mdPane .md-gutter{flex:0 0 auto;box-sizing:border-box;padding:12px 6px 12px 8px;text-align:right;overflow:hidden;user-select:none;background:var(--paper);border-right:1px solid var(--line);color:var(--ink-soft);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:20px}
  #mdPane .md-gutter .gl{height:20px;display:flex;align-items:center;justify-content:flex-end;gap:1px}
  #mdPane .md-gutter .gl.active{color:var(--accent-text);font-weight:700}
  #mdPane .md-gutter .gl-fold{flex:0 0 12px;width:12px;height:16px;display:inline-flex;align-items:center;justify-content:center;font-size:9px;line-height:1;border-radius:3px;cursor:pointer;color:var(--ink-soft);opacity:.7}
  #mdPane .md-gutter .gl-fold:empty{cursor:default}
  #mdPane .md-gutter .gl-fold:not(:empty):hover{background:color-mix(in srgb, var(--ink-soft) 20%, transparent);opacity:1}
  #mdPane .md-gutter .gl-num{min-width:1.6em}
  #mdPane .md-code{position:relative;flex:1;min-width:0;overflow:hidden}
  #mdPane .md-hl,#mdEditor{position:absolute;inset:0;margin:0;box-sizing:border-box;padding:12px 14px;border:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:20px;white-space:pre;tab-size:2}
  #mdPane .md-hl{overflow:hidden;pointer-events:none;color:var(--ink)}
  /* Scroll-sync targets a plain inner wrapper via transform, not the outer .md-hl/.md-gutter
     (which must stay put so their own overflow:hidden keeps clipping the visible window). */
  #mdPane .md-hl-inner,#mdPane .md-gutter-inner{will-change:transform}
  #mdPane .md-hl .hl-line{min-height:20px}
  /* active-line highlight: a real background on the actual row div - it's always exactly
     where the caret is, in or out of view, with no separate pixel math to keep in sync
     while clicking or scrolling (that used to drift; this can't). */
  #mdPane .md-hl .hl-line.active{background:var(--paper-2,rgba(127,127,127,.10))}
  #mdPane .md-fold-chip{display:inline-block;margin-left:6px;padding:0 7px;border-radius:8px;background:color-mix(in srgb, var(--ink-soft) 16%, transparent);color:var(--ink-soft);font-size:11px;font-style:normal}
  #mdPane .md-hl{z-index:1}
  #mdEditor{z-index:2}
  /* rendered preview */
  #mdPane .md-prev-btn{margin-left:2px;border:1px solid var(--line);background:transparent;color:var(--ink-soft);cursor:pointer;font-size:11px;font-weight:600;padding:2px 9px;border-radius:6px}
  #mdPane .md-prev-btn.on{background:var(--accent);color:var(--accent-fg, #fff);border-color:var(--accent)}
  #mdPane .md-wrap-btn{margin-left:2px;border:1px solid var(--line);background:transparent;color:var(--ink-soft);cursor:pointer;font-size:11px;font-weight:600;padding:2px 9px;border-radius:6px}
  #mdPane .md-wrap-btn.on{background:var(--accent);color:var(--accent-fg, #fff);border-color:var(--accent)}
  #mdPane.md-preview .md-wrap-btn{display:none}
  /* Word wrap: the textarea and highlight overlay share this rule so switching both to
     pre-wrap at once keeps them pixel-aligned (identical font/width/padding/text, so the
     browser wraps them the same way). The gutter's rows are normally a fixed 20px each
     (one line = one row); once wrapped, a line can span several visual rows, so
     mdSyncGutterRowHeights() overrides each .gl row's height in JS to match its
     corresponding .hl-line's actual (now possibly taller) rendered height. */
  #mdPane.md-wrap .md-hl,#mdPane.md-wrap #mdEditor{white-space:pre-wrap;overflow-wrap:anywhere}
  #mdPane .md-pdf-btn{display:none;border:1px solid var(--line);background:transparent;color:var(--ink-soft);cursor:pointer;font-size:11px;font-weight:600;padding:2px 9px;border-radius:6px}
  #mdPane .md-pdf-btn:hover{background:var(--paper-2,rgba(127,127,127,.12));color:var(--ink)}
  #mdPane.md-preview .md-pos{display:none}
  #mdPane.md-preview .md-pdf-btn{display:inline-flex;margin-left:auto}
  #mdPane .md-prev{position:absolute;inset:0;display:none;overflow:auto;padding:16px 22px;background:var(--paper);color:var(--ink);z-index:4;line-height:1.6;text-align:left}
  #mdPane.md-preview .md-prev{display:block}
  #mdPane.md-preview .md-hl,#mdPane.md-preview #mdEditor,#mdPane.md-preview .md-gutter{display:none}
  #mdPane .md-prev h1,#mdPane .md-prev h2,#mdPane .md-prev h3{margin:.6em 0 .3em;line-height:1.25}
  #mdPane .md-prev h1{font-size:1.6em;border-bottom:1px solid var(--line);padding-bottom:.2em}
  #mdPane .md-prev h2{font-size:1.3em;border-bottom:1px solid var(--line);padding-bottom:.2em}
  #mdPane .md-prev h3{font-size:1.1em}
  #mdPane .md-prev p,#mdPane .md-prev ul,#mdPane .md-prev ol,#mdPane .md-prev blockquote{margin:.5em 0}
  #mdPane .md-prev pre.mp-code{background:var(--paper-2,rgba(127,127,127,.10));padding:10px 12px;border-radius:8px;overflow:auto;font-size:12.5px}
  #mdPane .md-prev code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
  #mdPane .md-prev table{border-collapse:collapse;font-size:.95em}
  #mdPane .md-prev th,#mdPane .md-prev td{border:1px solid var(--line);padding:5px 9px}
  #mdPane .md-prev th{background:var(--paper-2,rgba(127,127,127,.12));font-weight:700;text-align:left}
  #mdPane .md-prev blockquote{border-left:3px solid var(--line);margin-left:0;padding:.2em 0 .2em 12px;color:var(--ink-soft)}
  #mdPane .md-prev img{max-width:100%}
  #mdPane .md-prev hr{border:0;border-top:1px solid var(--line);margin:1em 0}
  #mdPane .md-prev a{color:var(--link)}
  #mdPane .md-prev math{font-size:1.04em}
  #mdPane .md-prev math[display="block"]{display:block;margin:.6em auto;text-align:center}
  /* horizontal-rule divider node */
  .node.hr-node{min-width:54px;box-shadow:none}
  .node.hr-node .node-hr{display:block;width:54px;height:0;border-top:2px solid var(--ink-soft,#98a2b3);opacity:.6}
  #mdEditor{overflow:auto;resize:none;outline:none;background:transparent;color:transparent;caret-color:var(--ink)}
  #mdEditor::selection{background:color-mix(in srgb, var(--accent) 28%, transparent)}
  /* markdown syntax colors - driven by theme variables, so they follow the theme */
  #mdPane .hl-head{color:var(--accent-text);font-weight:700}
  #mdPane .hl-comment{color:var(--ink-soft);opacity:.6;font-style:italic}
  #mdPane .hl-code,#mdPane .hl-fence,#mdPane .hl-code-inline{color:var(--accent-text)}
  #mdPane .hl-strong{color:var(--ink);font-weight:700}
  #mdPane .hl-em{color:var(--ink);font-style:italic}
  #mdPane .hl-link,#mdPane .hl-img{color:var(--link)}
  #mdPane .hl-bullet,#mdPane .hl-task{color:var(--accent-text);font-weight:700}
  #mdPane .hl-quote{color:var(--ink-soft);font-style:italic}
  #mdPane .hl-punc{color:var(--ink-soft)}
  #mdPane .hl-hmark{color:var(--ink-soft);font-weight:700}
  #mdPane .hl-strike{color:var(--ink-soft);text-decoration:line-through}
  #mdPane .hl-hr{color:var(--ink-soft)}
  #mdPane .hl-tag{color:var(--ok)}
  #mdPane .hl-url{color:var(--link);text-decoration:underline}
  #mdPane .md-resize{position:absolute;top:0;right:0;width:7px;height:100%;cursor:col-resize;z-index:6}
  .tb#mdToggle.on{background:var(--accent);color:var(--accent-fg, #fff)}

  /* ===== Sidebar ===== */
  .side{width:268px;min-height:0;position:relative;background:var(--chrome);border-right:1px solid var(--chrome-edge);display:flex;flex-direction:column;transition:width .22s cubic-bezier(.4,0,.2,1),background-color .3s ease,border-color .3s ease}
  /* width is !important on every collapsed rule: the expand path writes an
     inline width, and whichever code collapses the sidebar next must win over
     it or the column stays open and blank. */
  .side.collapsed{width:36px !important;border:none}                 /* icon rail instead of fully gone */
  .side.resizing{transition:none}
  .side.collapsed .brand{display:none !important}  /* never bleed into the slim rail */
  .side.collapsed .side-tabs,
  .side.collapsed .side-pane,
  .side.collapsed .side-foot,
  .side.collapsed .new-map-row,   /* classic's wide "New mind map" row; left out, it overflowed a 0px sidebar into the tab strip */
  .side.collapsed .side-resize{display:none !important}
  /* Collapsed state = slim rail with the reopen button, so the sidebar can
     never get "lost". The same ☰ button sits in the brand header, beside
     the MindSpark title. */
  .side-rail{display:none;width:36px;flex:1 0 auto;justify-content:center;align-items:center}
  .side.collapsed .side-rail{display:flex}
  .side-rail .tb{background:none;border:none}
  /* Drag handle on the sidebar's right edge (desktop only) */
  .side-resize{position:absolute;top:0;right:-4px;width:8px;height:100%;cursor:col-resize;z-index:10;opacity:0;transition:opacity .15s}
  .side-resize:hover,.side-resize:active{opacity:.55;background:var(--accent)}
  .side-tabs{display:flex;gap:2px;padding:0 10px 10px}
  .side-tab{flex:1;padding:7px 6px;border:none;background:none;border-radius:9px;font-family:inherit;font-size:12.5px;font-weight:600;color:var(--ink-soft);cursor:pointer;display:flex;align-items:center;justify-content:center;gap:6px;white-space:nowrap}
  .side-tab:hover{background:var(--hover);color:var(--ink)}
  .side-tab.active{background:var(--paper-2);color:var(--ink)}
  .side-pane{flex:1;min-height:0;display:flex;flex-direction:column}
  /* Tab switching is class-based (never inline styles) so the collapsed-rail
     rules below always win: `.side-pane:not(.active)` is hidden, the active
     pane is shown, and `.side.collapsed` (declared later + !important) hides
     everything inside the slim rail. */
  .side-pane.active{display:flex}
  .side-pane:not(.active){display:none}
  /* Template list inside the sidebar Templates tab */
  .tpl-side-hdr{font-size:10.5px;text-transform:uppercase;letter-spacing:.1em;color:var(--accent-text);font-weight:700;padding:12px 8px 4px}
  .tpl-side-item{display:flex;align-items:center;gap:10px;width:100%;text-align:left;padding:8px 10px;border:none;background:none;border-radius:10px;cursor:pointer;font-family:inherit;color:var(--ink);margin:1px 0}
  .tpl-side-item:hover{background:var(--hover)}
  .tpl-side-ic{width:30px;height:30px;flex-shrink:0;display:flex;align-items:center;justify-content:center;color:#fff;border-radius:8px;font-size:14px}
  .tpl-side-meta{display:flex;flex-direction:column;line-height:1.3;min-width:0}
  .tpl-side-meta b{font-weight:600;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .tpl-side-meta i{font-style:normal;color:var(--ink-soft);font-size:11.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  @media (prefers-reduced-motion: reduce){ .side, #viewport{transition:none !important} }
  .brand{display:flex;align-items:center;gap:10px;padding:18px 18px 14px}
  .brand .mark{flex:0 0 auto; filter:drop-shadow(0 1px 3px rgba(67,158,241,.2))}
  .brand h1,.login-brand h1{
    font-family:var(--brand-font);font-weight:700;line-height:1.15;
    color:var(--brand-ink);letter-spacing:-.035em;white-space:nowrap;
  }
  .brand h1{font-size:22px;margin:0}
  .brand h1 span,.login-brand h1 span{color:var(--brand-accent)}
  .brand #newMap,
  .brand #toggleSide{width:32px;height:32px;flex:0 0 auto;font-size:16px}
  .brand #newMap{margin-left:auto}
  .brand #newMap:hover,
  .brand #toggleSide:hover{background:var(--hover)}
  .side-scroll{flex:1;min-height:0;overflow-y:auto;padding:6px 10px 12px}
  .side-label{font-size:11px;text-transform:uppercase;letter-spacing:.12em;color:var(--ink-soft);padding:10px 8px 6px;font-weight:700}
  .map-item{display:flex;align-items:center;gap:8px;padding:9px 10px;border-radius:10px;margin:2px 0;cursor:pointer;position:relative}
  .map-item:hover{background:var(--hover)}
  .map-item.active{background:var(--chrome);box-shadow:var(--shadow)}
  .map-item .dot{width:8px;height:8px;border-radius:50%;flex:0 0 auto}
  .map-item .nm{flex:1;font-size:14px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .map-item .row-menu{opacity:0;border:none;background:none;color:var(--ink-soft);padding:2px 5px;border-radius:6px;font-size:17px;line-height:1;flex:0 0 auto;cursor:pointer;transition:opacity .12s}
  .map-item:hover .row-menu, .map-item.active .row-menu{opacity:.65}
  .map-item .row-menu:hover{opacity:1;background:var(--line);color:var(--ink)}
  @media (pointer:coarse){ .map-item .row-menu{opacity:.6} }   /* always tappable on touch */
  .row-pop{position:fixed;background:var(--paper,#fff);border:1px solid var(--chrome-edge,#d0d7de);border-radius:10px;box-shadow:var(--shadow,0 8px 28px rgba(0,0,0,.16));padding:5px;min-width:152px;z-index:400;display:flex;flex-direction:column;gap:2px}
  .row-pop button{display:flex;align-items:center;gap:9px;width:100%;text-align:left;border:none;background:none;color:var(--ink);font-size:13.5px;padding:8px 10px;border-radius:7px;cursor:pointer;font-family:inherit}
  .row-pop button:hover{background:var(--hover)}
  .row-pop button.danger{color:var(--danger)}
  .row-pop button.danger:hover{background:rgba(184,69,31,.10)}
  .row-pop .rp-ic{width:16px;text-align:center;opacity:.85;font-size:14px}
  .side-foot{padding:12px 16px;padding-bottom:calc(12px + env(safe-area-inset-bottom, 0px));border-top:1px solid var(--chrome-edge);font-size:11.5px;color:var(--ink-soft);line-height:1.5}
  .side-foot b{color:var(--ink)}
  .qotd-sep{height:1px;background:var(--chrome-edge);margin:12px -16px 0;opacity:.9}
  .side-foot:has(#qotd[hidden]) .qotd-sep{display:none}
  .qotd{position:relative;margin-top:12px;padding:8px 10px 8px 10px;border-left:2px solid var(--accent);background:color-mix(in srgb, var(--accent) 7%, transparent);border-radius:0 8px 8px 0}
  .qotd[hidden]{display:none}
  .qotd-text{font-size:12px;line-height:1.45;color:var(--ink);font-style:italic;white-space:pre-wrap;word-break:break-word}
  .qotd-text::before{content:'\201C'} .qotd-text::after{content:'\201D'}
  .qotd-author{margin-top:4px;font-size:10.5px;color:var(--ink-soft);text-align:right;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .qotd-author::before{content:'- '}
  .qotd-word{font-size:14px;line-height:1.4;color:var(--ink);font-weight:600}
  .qotd-word .wotd-part{font-size:10.5px;color:var(--ink-soft);font-weight:400;opacity:.7}
  .qotd-worddef{font-size:12px;line-height:1.45;color:var(--ink-soft);margin-top:2px;white-space:pre-wrap;word-break:break-word}
  .qotd-refresh{position:absolute;top:4px;right:4px;width:20px;height:20px;display:grid;place-items:center;border:none;background:transparent;color:var(--ink-soft);border-radius:6px;cursor:pointer;font-size:12px;line-height:1;opacity:0;transition:opacity .15s, background-color .15s}
  .qotd:hover .qotd-refresh{opacity:1}
  .qotd-refresh:hover{background:var(--hover);color:var(--ink)}
  .side.collapsed .qotd{display:none}

  /* ===== Canvas region ===== */
  .stage{position:relative;overflow:hidden;background:
    radial-gradient(circle at 1px 1px,var(--canvas-dot) 1px,transparent 0);
    background-size:26px 26px;background-color:var(--paper)}
  .stage:before{content:"";position:absolute;inset:0;pointer-events:none;
    background:radial-gradient(ellipse at 50% 0%,var(--stage-glow),transparent 60%)}
  #viewport{position:absolute;inset:0;transform-origin:0 0}
  #edges{position:absolute;left:0;top:0;overflow:visible;pointer-events:none}

  /* ===== Top header - full-width bar, mirrors the status bar's strip look ===== */
  .topbar{display:flex;align-items:center;gap:8px;padding:5px 10px;background:var(--chrome);border-bottom:1px solid var(--chrome-edge);z-index:30;flex-wrap:wrap;row-gap:4px;min-width:0}
  .tb-group{display:flex;align-items:center;gap:2px;padding:0;background:none;border:none;border-radius:0;box-shadow:none}
  /* line-height:1 is what keeps the row's icons on one line. place-items:center
     centres the LINE BOX, and with line-height:normal that box is sized from
     whichever font supplies the glyph - these are symbols, so they come from
     fallback fonts, not from --sans. On Windows ⌕ ◐ ⤓ ▭ resolve to Segoe UI
     Symbol and ⚙ ⛶ ♥ to Segoe UI Emoji, whose taller ascent pushed their
     baseline down: the theme icon sat 1.5px above the gear beside it. A fixed
     line-height takes the glyph's own font out of the box calculation, so every
     button centres identically whatever font answers for its character. */
  .tb{border:none;background:none;color:var(--ink);width:36px;height:34px;border-radius:8px;display:grid;place-items:center;font-size:16px;line-height:1;white-space:nowrap}
  .tb:hover{background:var(--hover)}
  .language-menu{position:fixed;z-index:11000;min-width:150px;padding:5px;background:var(--chrome);border:1px solid var(--chrome-edge);border-radius:10px;box-shadow:var(--shadow-lg);display:grid;gap:2px}
  .language-option{width:100%;border:0;border-radius:6px;padding:9px 12px;background:transparent;color:var(--ink);font:inherit;text-align:left;cursor:pointer}
  .language-option:hover,.language-option:focus-visible{background:var(--hover)}
  .language-option.active{color:var(--accent-text);font-weight:700}
  .language-option.active::before{content:'✓';margin-right:8px}
  .login-language{position:absolute;top:16px;right:16px;border:1px solid var(--chrome-edge);border-radius:8px;padding:6px 9px;background:var(--chrome);color:var(--ink);font:inherit;cursor:pointer}
  .login-language:hover{background:var(--hover)}
  .tb:disabled{opacity:.32;cursor:default}
  .tb.wide{width:auto;padding:0 12px;font-size:13.5px;font-weight:600;gap:7px}
  .tb.primary{background:var(--accent);color:var(--accent-fg, #fff)}
  .tb.primary:hover{background:var(--accent-deep)}
  .title-edit{font-family:var(--serif);font-weight:600;font-size:19px;border:none;background:transparent;color:var(--ink);padding:6px 12px;min-width:120px;max-width:340px;border-radius:9px;outline:none}
  .title-edit:hover,.title-edit:focus{background:var(--chrome)}
  /* Hairline separator between topbar clusters (outside any .tb-group) */
  .tb-sep{width:1px;height:26px;background:var(--chrome-edge);flex:0 0 auto}
  .spacer{flex:1}
  /* When the bar wraps (narrow window), the spacer stays on line one and the
     last cluster is left alone on line two with nothing to push it right, so it
     would sit under the undo buttons instead of at the end of the bar. Auto
     margins only take free space that flex-grow did not already claim, so this
     is a no-op while everything fits. The rail makes the bar a column, where
     margin-left would shove the cluster off its centre - hence the reset. */
  .topbar > .tb-group:last-child{margin-left:auto}
  body.ui-rail .topbar > .tb-group:last-child{margin-left:0}
  .search-wrap{display:flex;align-items:center;background:none;border:none;border-radius:0;box-shadow:none;overflow:hidden;width:0;transition:width .22s,background-color .3s ease,border-color .3s ease,color .3s ease}
  .search-wrap.open{width:270px;background:var(--paper-2);border-radius:9px}
  .search-wrap.open.replace-mode{width:460px}
  .search-wrap input{border:none;background:none;outline:none;font-family:inherit;font-size:12px;color:var(--ink);padding:9px 12px;width:100%;min-width:0}
  .search-wrap #replace,.search-wrap .search-act{display:none}
  .search-wrap.replace-mode #replace{display:block;border-left:1px solid var(--chrome-edge)}
  .search-wrap.replace-mode .search-act{display:inline-flex}
  .search-count{font-size:11px;color:var(--ink-soft);white-space:nowrap;padding:0 8px;flex-shrink:0}
  .search-toggle{border:none;background:none;cursor:pointer;color:var(--ink-soft);font-size:14px;padding:8px 10px;flex-shrink:0;border-radius:0}
  .search-toggle:hover{background:var(--hover);color:var(--ink)}
  /* ▲/▼ stay in place while typing (so the bar never jumps) and fade out
     until there is a match to step to; all-maps mode lists its own results */
  .search-nav{display:inline-flex;align-items:center;border:none;background:none;cursor:pointer;color:var(--ink-soft);font-size:9px;line-height:1;padding:8px 5px;flex-shrink:0;border-radius:0}
  .search-nav:hover{background:var(--hover);color:var(--ink)}
  .search-wrap:not(.has-matches) .search-nav{opacity:.35;pointer-events:none}
  .search-wrap.all-mode .search-nav{display:none}
  .search-act{border:none;background:none;cursor:pointer;color:var(--accent-text);font-size:12px;font-weight:600;padding:8px 11px;flex-shrink:0;font-family:inherit;align-items:center}
  .search-act:hover{background:var(--hover)}
  .node.match{outline:2.5px solid var(--accent);outline-offset:2px}
  .node.match-current{outline:3px solid var(--accent);outline-offset:2px;box-shadow:0 0 0 6px color-mix(in srgb, var(--accent) 22%, transparent)}
  .node.dim{opacity:.32;transition:opacity .15s}
  .save-pill{font-size:12px;color:var(--ink-soft);background:var(--chrome);border:1px solid var(--chrome-edge);border-radius:20px;padding:6px 13px;box-shadow:var(--shadow);font-weight:500;display:flex;align-items:center;gap:6px}
  .save-pill .ring{width:8px;height:8px;border-radius:50%;background:var(--teal)}
  .save-pill.saving .ring{background:var(--accent);animation:pulse 1s infinite}
  @keyframes pulse{50%{opacity:.3}}

  /* ===== Bottom controls - one "overview" card =====
     Minimap and zoom controls live inside the status bar as a single chip.
     Hovering it expands the breadcrumb (see .breadcrumb.shown). Collapses to
     a stub via the chevron (state persisted in JS). */
  .overview{flex:0 0 auto;display:flex;align-items:center;gap:7px;padding:2px 6px;border-radius:8px}
  .overview:hover{background:var(--hover)}
  .overview .minimap{position:static;border-radius:6px;box-shadow:none}
  .zoom-row{display:flex;align-items:center;gap:3px}
  .zoom-row button{border:none;background:none;width:28px;height:26px;border-radius:7px;font-size:15px;color:var(--ink);flex:0 0 auto;display:grid;place-items:center}
  .zoom-row button:hover{background:var(--hover)}
  #zoomSlider{flex:1;min-width:0;width:70px;accent-color:var(--accent);height:16px;margin:0;cursor:pointer}
  .zoom-val{font-size:11px;text-align:center;color:var(--ink-soft);font-weight:600;padding:3px 4px;border-radius:5px;cursor:pointer;min-width:38px;outline:none;flex:0 0 auto}
  .zoom-val:hover{background:var(--hover);color:var(--ink)}
  .zoom-val[contenteditable="true"]{background:var(--node-bg);box-shadow:0 0 0 2px var(--accent);color:var(--ink)}
  .overview-toggle{border:none;background:none;color:var(--ink-soft);font-size:11px;width:22px;height:22px;flex:0 0 auto;border-radius:6px;cursor:pointer;display:grid;place-items:center;transition:transform .18s}
  .overview-toggle:hover{background:var(--hover);color:var(--ink)}
  .overview.collapsed{padding:2px}
  .overview.collapsed .minimap,
  .overview.collapsed .zoom-row{display:none}
  .overview.collapsed .overview-toggle{transform:rotate(-90deg)}

  /* ===== Status bar - bottom strip under the canvas ===== */
  /* min-width:0 is load bearing. The hint below is one nowrap line of keyboard
     tips (~850px), so as an in-flow grid item the bar's min-content was wider
     than the space the sidebar leaves: the fr track took that as its floor, the
     grid grew past the right edge, and the top bar - stretched onto the same
     track - carried its right cluster (search, theme, settings, Support
     MindSpark) off-screen. Collapsing the sidebar pulled it back, so the donate
     button looked like it vanished when the sidebar was open and came back when
     it was not. The bar is the only item in this grid that had not already
     opted out: .topbar has min-width:0 and #tabStrip has overflow:hidden. With
     it, the bar may be narrower than its content and the hint ellipsizes
     instead of dragging the layout wide. */
  .statusbar{display:flex;align-items:center;gap:14px;padding:5px 14px;background:var(--chrome);border-top:1px solid var(--chrome-edge);min-height:34px;min-width:0;font-family:var(--sans)}
  .statusbar .hint{
    display:flex;align-items:center;gap:6px;position:static;margin:0;
    background:none;border:none;box-shadow:none;border-radius:0;padding:0;
    font-size:12px;color:var(--ink-soft);max-width:70%;line-height:1.5;flex:1;min-width:0;
  }
  .statusbar .hint b{color:var(--ink);font-weight:600}
  /* The hint's text lives in its own span so it can shrink and ellipsize as a
     single unit - the bar shrinks when the overview collapses (the chip is
     content-sized), so without this the text would re-wrap into a ragged
     multi-line mess every time the overview is toggled. */
  .statusbar .hint .hint-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
  .statusbar .hint .close{border:none;background:none;color:var(--ink-soft);font-size:14px;line-height:1;padding:2px 5px;border-radius:5px;cursor:pointer;flex:0 0 auto}
  .statusbar .hint .close:hover{background:var(--hover);color:var(--ink)}
  .statusbar .save-pill,
  .statusbar .token-total{font-size:11.5px;padding:4px 11px;box-shadow:none}
  .statusbar .user-pill{height:28px;font-size:12px}
  .statusbar .user-pill img{width:20px;height:20px}
  .sb-right{margin-left:auto;display:flex;align-items:center;gap:10px;flex:0 0 auto}
  .hint b{color:var(--ink);font-weight:600}
  .hint kbd{font-family:var(--sans);background:var(--paper-2);border:1px solid var(--line);border-radius:5px;padding:1px 6px;font-size:11px;font-weight:600;color:var(--ink)}
  .hint .close{float:right;margin-left:8px;cursor:pointer;color:var(--ink-soft);border:none;background:none;font-size:14px}

  /* ===== Nodes ===== */
  .node{position:absolute;background:var(--card-bg);border:1.5px solid var(--line);border-radius:var(--node-radius, 12px);padding:var(--node-pad-y, 9px) var(--node-pad-x, 15px);font-size:calc(15px * var(--look-node-size, 1));font-weight:500;line-height:1.35;box-shadow:var(--shadow);width:max-content;max-width:240px;min-width:34px;user-select:none;transition:background-color .3s ease,color .3s ease,box-shadow .15s,border-color .15s,transform .06s;white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box}
  .node:hover{box-shadow:var(--shadow-lg)}
  .node.root{font-family:var(--serif);font-weight:600;font-size:calc(19px * var(--look-node-size, 1));padding:var(--node-pad-y, 12px) var(--node-pad-x, 22px);border-width:0}
  .node.sel{outline:2.5px solid var(--accent);outline-offset:2px}
  .node.editing{cursor:text;user-select:text;outline:none !important;box-shadow:none}
  .node.editing .node-text{outline:none}
  .node.dim{opacity:.28}
  .node.match{outline:2.5px solid var(--teal);outline-offset:2px}
  .node .badge{display:none}  /* legacy, no longer rendered */
  /* Zebra striping - XMind-style alternating tint by tree depth. Only affects
     uncoloured nodes: the root and user-picked card colours carry inline
     background styles that win over these classes. Subtle color-mix so the
     stripes follow every theme, and they ride the theme-switch transition. */
  :root{
    --zebra-1:color-mix(in srgb, var(--card-bg) 93%, var(--accent));
    --zebra-2:color-mix(in srgb, var(--card-bg) 93%, var(--teal));
  }
  .node[data-depth="1"],.node[data-depth="3"],.node[data-depth="5"],
  .node[data-depth="7"]{background:var(--zebra-1)}
  .node[data-depth="2"],.node[data-depth="4"],.node[data-depth="6"],
  .node[data-depth="8"]{background:var(--zebra-2)}
  /* Hover-only "created/last edited" watermark - sits just above the node (not inside it,
     which used to collide with the node's own vertically-centered text on shorter nodes)
     so it never overlaps the actual content. */
  .node .node-watermark{
    position:absolute; left:0; right:0; bottom:100%; margin-bottom:3px;
    pointer-events:none; overflow:hidden;
    font-family:var(--sans); font-size:9px; font-weight:600; letter-spacing:.02em;
    color:var(--ink-soft); opacity:0;
    transition:opacity .18s ease;
    white-space:nowrap; text-overflow:ellipsis;
    text-align:center;
  }
  .node:hover .node-watermark{opacity:.85}
  .node.editing .node-watermark{opacity:0 !important}   /* never fight with the caret while typing */
  /* Formula nodes ("=SUM(children)" etc): a small fx badge marks the node as a live
     calculation, and the result shows in a tabular-numeral style so a column of computed
     values lines up like a spreadsheet. The title tooltip (native, on hover) reveals the
     actual formula text. */
  .node.formula-node{position:relative}
  .node.formula-node::before{
    content:'\0192x'; position:absolute; top:-7px; left:-7px;
    width:16px; height:16px; border-radius:50%;
    background:var(--accent); color:var(--accent-fg, #fff);
    font-size:9px; font-weight:700; font-style:normal;
    display:flex; align-items:center; justify-content:center;
    box-shadow:0 1px 3px rgba(0,0,0,.25);
    pointer-events:none;
  }
  .node.formula-node .node-text{font-variant-numeric:tabular-nums}
  .node.formula-error{border-color:var(--danger)}
  .node.formula-error::before{background:var(--danger)}
  .node.formula-error .node-text{color:var(--danger)}
  /* Frontmatter node (a skill.md's YAML name/description block, etc.): a small badge marks
     it as metadata rather than regular outline content - the table itself is the normal
     block-node table rendering, this is purely a visual "what is this" hint. */
  .node.frontmatter-node{position:relative}
  .node.frontmatter-node::before{
    content:'YAML'; position:absolute; top:-9px; left:8px;
    padding:1px 5px; border-radius:4px;
    background:var(--teal); color:var(--teal-fg, #fff);
    font-size:8px; font-weight:700; font-style:normal; letter-spacing:.03em;
    box-shadow:0 1px 3px rgba(0,0,0,.25);
    pointer-events:none;
  }
  /* Formula function autocomplete popup (Excel-style "=SU" -> suggests SUM) */
  .formula-ac{
    position:fixed; z-index:1200; min-width:220px; max-width:320px; max-height:220px;
    overflow-y:auto; background:var(--chrome); border:1px solid var(--line);
    border-radius:8px; box-shadow:var(--shadow-lg); padding:4px; font-family:var(--sans);
  }
  .formula-ac-row{
    display:flex; flex-direction:column; gap:1px; padding:5px 8px; border-radius:5px;
    cursor:pointer; line-height:1.3;
  }
  .formula-ac-row.active,.formula-ac-row:hover{background:var(--accent); }
  .formula-ac-row.active .formula-ac-sig,.formula-ac-row:hover .formula-ac-sig,
  .formula-ac-row.active .formula-ac-desc,.formula-ac-row:hover .formula-ac-desc{color:var(--accent-fg, #fff)}
  .formula-ac-sig{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12.5px;font-weight:700;color:var(--ink)}
  .formula-ac-desc{font-size:11px;color:var(--ink-soft)}

  /* ===== Quick-action handles on nodes (appear on hover) ===== */
  .node .handle{
    position:absolute; width:22px; height:22px; border-radius:50%;
    display:flex; align-items:center; justify-content:center;
    font-size:15px; font-weight:600; line-height:1; cursor:pointer;
    user-select:none; box-shadow:0 2px 5px rgba(0,0,0,.22);
    border:2px solid var(--card-bg, #fff); opacity:0; transition:opacity .12s, transform .1s;
    z-index:8;
  }
  .node:hover .handle{opacity:1}
  .node .handle:hover{transform:scale(1.15)}
  .node.editing .handle{display:none}

  /* Collapse / expand - at the outer edge, always visible (subtle) */
  .node .h-collapse{
    background:var(--card-bg, #fff); color:var(--ink); border:1.5px solid var(--line-2);
    width:18px; height:18px; font-size:13px; opacity:.8;
    right:-10px; top:50%; margin-top:-9px;
  }
  .node:hover .h-collapse{opacity:1}
  .node .h-collapse.collapsed{background:var(--accent);color:var(--accent-fg, #fff);border-color:var(--accent);opacity:1}
  .node.left .h-collapse{left:-10px;right:auto}

  /* Add child - outer edge (right for right-side / root, left for left-side) */
  .node .h-child{background:var(--accent);color:var(--accent-fg, #fff);right:-32px;top:50%;margin-top:-11px}
  .node.left .h-child{left:-32px;right:auto}

  /* Add sibling - below the node, centered */
  .node .h-sibling{background:var(--teal);color:var(--teal-fg, #fff);bottom:-30px;left:50%;margin-left:-11px}

  /* Resize grip - bottom-right corner, visible only on selected nodes */
  .node .resize-grip{
    position:absolute; right:-2px; bottom:-2px; width:14px; height:14px;
    cursor:nwse-resize; opacity:0; pointer-events:none; transition:opacity .12s;
    background:
      linear-gradient(135deg, transparent 40%, var(--accent) 40%, var(--accent) 55%, transparent 55%,
                              transparent 65%, var(--accent) 65%, var(--accent) 80%, transparent 80%);
    border-bottom-right-radius:12px;
  }
  .node.sel .resize-grip{opacity:.9; pointer-events:auto}
  .node .resize-grip:hover{opacity:1}
  .node.editing .resize-grip{display:none}

  /* Drop-target highlight while drag-and-drop reparenting. The three modes
     need to be clearly distinguishable at a glance during a fast drag, not
     just technically different - before/after (sibling insertion) previously
     had only a bare 3px line hugging the node's own edge, much weaker than
     the child-nest treatment below it. Strengthened to a thicker line that
     extends past the node's own width (box-shadow spread, not just blur) so
     it reads as a real insertion marker, plus a background tint so there's
     more than one signal to notice mid-drag. */
  .node.drop-before{
    box-shadow:0 -5px 0 3px var(--accent), var(--shadow);
    background:color-mix(in srgb, var(--accent) 12%, var(--card-bg)) !important;
  }
  .node.drop-after{
    box-shadow:0 5px 0 3px var(--accent), var(--shadow);
    background:color-mix(in srgb, var(--accent) 12%, var(--card-bg)) !important;
  }
  /* File-drop target - deliberately distinct from .drop-target above.
     That one means "nest as child" during node dragging; this one means
     "attach the image you are holding". Using the same visual would
     promise the wrong action. */
  .node.file-drop{
    outline:3px dashed var(--teal, #2f6f6a); outline-offset:5px;
    box-shadow:0 0 0 6px color-mix(in srgb, var(--teal, #2f6f6a) 22%, transparent);
  }
  .node.drop-target{
    outline:3px dashed var(--accent); outline-offset:5px;
    background:linear-gradient(0deg,
      color-mix(in srgb, var(--accent) 22%, var(--card-bg)),
      color-mix(in srgb, var(--accent) 22%, var(--card-bg))) !important;
    box-shadow:0 0 0 6px color-mix(in srgb, var(--accent) 20%, transparent);
  }

  /* Notes indicator - small page-icon at the top-right of nodes with a note */
  .node .notes-mark{
    position:absolute; top:-9px; right:-9px;
    width:22px; height:22px; border-radius:4px;
    background:var(--sticky, #fff7b0); border:1.5px solid var(--sticky-edge, #f2e27a);
    display:flex; align-items:center; justify-content:center;
    font-size:11px; cursor:pointer; z-index:6;
    box-shadow:0 1px 3px rgba(0,0,0,.18); transform:rotate(-6deg);
  }
  .node .notes-mark:hover{transform:rotate(0deg) scale(1.12)}
  .node.left .notes-mark{right:auto; left:-9px}

  /* List rendering inside a node */
  .node-text-list .list-marker{display:inline-block; min-width:1.6em; opacity:.7; user-select:none}

  /* Alignment-picker glyphs */
  .align-icon{display:inline-block; font-family:monospace; font-size:13px; line-height:1}
  .align-icon.align-left{text-align:left}
  .align-icon.align-center{text-align:center}
  .align-icon.align-right{text-align:right}

  /* ===== Notes editor - styled as a sticky note ===== */
  :root{ --sticky:#fff7b0; --sticky-edge:#f2e27a; --sticky-ink:#4a431f; }
  :root[data-theme="dark"], :root[data-theme="night-owl"], :root[data-theme="dracula"], :root[data-theme="catppuccin-dark"],
  :root[data-theme="rose-pine-moon"], :root[data-theme="nord"], :root[data-theme="github-dark"],
  :root[data-theme="slate-steel"], :root[data-theme="vscode-onedark"], :root[data-theme="monokai-pro"], :root[data-theme="amazon-aws"],
  :root[data-theme="synthwave"], :root[data-theme="matrix-green"],
  :root[data-theme="carbon-amber"], :root[data-theme="cyber-quantum"], :root[data-theme="blueprint"], :root[data-theme="obsidian"],
  :root[data-theme="deep-ocean"], :root[data-theme="google-material"], :root[data-theme="github-modern"], :root[data-theme="aurora-frost"],
  :root[data-theme="espresso"], :root[data-theme="copper-lab"], :root[data-theme="forest-night"], :root[data-theme="cobalt"],
  :root[data-theme="ruby-night"], :root[data-theme="electric-indigo"], :root[data-theme="steel-orange"], :root[data-theme="cosmic-rose"],
  :root[data-theme="plasma-crimson"], :root[data-theme="neon-noir"], :root[data-theme="ember-forge"], :root[data-theme="violet-circuit"],
  :root[data-theme="twilight-amber"], :root[data-theme="ocean-depth"], :root[data-theme="charcoal-gold"], :root[data-theme="azure-horizon"],
  :root[data-theme="jade-temple"], :root[data-theme="magenta-pulse"], :root[data-theme="graphite-terminal"], :root[data-theme="frosted-pine"],
  :root[data-theme="quantum-violet"], :root[data-theme="copper-rust"], :root[data-theme="lunar-silver"]{
    --sticky:#3a3520; --sticky-edge:#5c5430; --sticky-ink:#f3ecc4;
  }
  .notes-popup{
    position:fixed; z-index:1100;
    background:var(--sticky);
    color:var(--sticky-ink);
    border-radius:4px 4px 6px 6px;
    box-shadow:0 14px 34px rgba(0,0,0,.34), 0 2px 5px rgba(0,0,0,.16);
    width:340px; max-width:92vw;
    border:1px solid var(--sticky-edge);
    border-top:none;
    font-family:var(--sans);
    overflow:hidden;
    transform:rotate(-1deg);          /* slight sticky-note tilt */
    transform-origin:top center;
  }
  .notes-popup::before{
    content:''; display:block; height:8px;
    background:linear-gradient(180deg, color-mix(in srgb, var(--sticky-edge) 80%, #000 6%), var(--sticky-edge));
  }
  .notes-popup .np-toolbar{
    display:flex; align-items:center; gap:1px;
    padding:7px 9px;
    border-bottom:1px solid color-mix(in srgb, var(--sticky-edge) 70%, transparent);
    background:color-mix(in srgb, var(--sticky-edge) 30%, var(--sticky));
  }
  .notes-popup .np-toolbar button{
    border:none; background:transparent; cursor:pointer;
    width:32px; height:30px; border-radius:6px;
    font-family:inherit; font-size:13px; color:var(--sticky-ink);
    display:inline-flex; align-items:center; justify-content:center;
  }
  .notes-popup .np-toolbar button:hover{background:color-mix(in srgb, var(--sticky-ink) 12%, transparent)}
  .notes-popup .np-toolbar button b,
  .notes-popup .np-toolbar button i,
  .notes-popup .np-toolbar button s{font-family:var(--serif); font-size:14px}
  .notes-popup .np-toolbar button i{font-style:italic}
  .notes-popup .np-div{width:1px; height:20px; background:color-mix(in srgb, var(--sticky-edge) 80%, transparent); margin:0 4px}
  .notes-popup .np-editor{
    padding:14px 16px; min-height:170px; max-height:50vh; overflow-y:auto;
    font-family:var(--sans); font-size:14px; line-height:1.6;
    color:var(--sticky-ink); outline:none; background:transparent;
  }
  .notes-popup .np-editor:empty:before{
    content:attr(data-placeholder); color:color-mix(in srgb, var(--sticky-ink) 50%, transparent); pointer-events:none;
  }
  .notes-popup .np-editor h1{font-size:20px; margin:.4em 0 .3em; font-family:var(--serif); font-weight:700}
  .notes-popup .np-editor h2{font-size:16px; margin:.4em 0 .3em; font-family:var(--serif); font-weight:600}
  .notes-popup .np-editor p{margin:.3em 0}
  .notes-popup .np-editor ul,
  .notes-popup .np-editor ol{margin:.3em 0; padding-left:1.6em}
  .notes-popup .np-editor a{color:var(--link); text-decoration:underline}
  /* A formula rendered in place inside the editor (see npMathSpan). It is not
     editable itself; clicking it swaps its $ source back in. */
  .notes-popup .np-math{
    display:inline-block; padding:0 3px; border-radius:4px; cursor:pointer;
    font-size:1.05em; line-height:1.3; vertical-align:baseline;
  }
  .notes-popup .np-math[data-display="1"]{display:block; text-align:center; margin:.2em 0}
  .notes-popup .np-math:hover{background:color-mix(in srgb, var(--sticky-ink) 10%, transparent); outline:1px dashed color-mix(in srgb, var(--sticky-ink) 40%, transparent)}
  .notes-popup .np-actions{
    display:flex; gap:8px; padding:10px 12px;
    border-top:1px solid color-mix(in srgb, var(--sticky-edge) 70%, transparent);
    background:color-mix(in srgb, var(--sticky-edge) 22%, var(--sticky));
    align-items:center;
  }
  .notes-popup button{
    padding:7px 14px; border-radius:7px; border:1px solid var(--sticky-edge);
    background:color-mix(in srgb, var(--sticky-edge) 25%, var(--sticky)); cursor:pointer;
    font-family:inherit; font-size:13px; font-weight:500; color:var(--sticky-ink);
  }
  .notes-popup .np-actions > button:hover{background:color-mix(in srgb, var(--sticky-ink) 12%, transparent)}
  .notes-popup button.primary{background:var(--accent); color:var(--accent-fg, #fff); border-color:var(--accent)}
  .notes-popup button.primary:hover{background:var(--accent-deep)}
  .notes-popup .np-clear{margin-right:auto; color:var(--ink-soft); border-color:transparent; background:transparent}
  .notes-popup .np-clear:hover{color:var(--danger); background:var(--hover)}

  /* ===== Map style variants (applied to viewport[data-style]) ===== */
  #viewport[data-style="modern"]{--edge-color:var(--line-2);--edge-width:2.2}
  #viewport[data-style="classic"] .node{border-radius:var(--node-radius, 4px); border-width:1.5px}
  #viewport[data-style="classic"]{--edge-color:var(--line-2);--edge-width:1.6}
  #viewport[data-style="classic"] .node.root{border-radius:var(--node-radius, 6px)}
  #viewport[data-style="bubble"]  .node{border-radius:var(--node-radius, 50px); padding:var(--node-pad-y, 10px) var(--node-pad-x, 10px); border-width:0; box-shadow:0 0 var(--node-glow, 0) var(--node-glow, 0) color-mix(in srgb, var(--accent) 20%, transparent)}
  #viewport[data-style="bubble"] {--edge-color:var(--accent);--edge-width:2}
  #viewport[data-style="bubble"]  .node.root{padding:calc(var(--node-pad-y, 10px) + 3px) calc(var(--node-pad-x, 10px) + 4px)}
  #viewport[data-style="sketch"]  .node{border-radius:var(--node-radius, 3px); border-width:2px; border-color:var(--ink); box-shadow:none; transform:rotate(var(--tilt,0deg))}
  #viewport[data-style="sketch"]  .node.root{border-width:0}
  #viewport[data-style="sketch"] {--edge-color:var(--ink-soft);--edge-width:1.6}
  #viewport[data-style="dashed"]   {--edge-color:var(--line-2);--edge-width:2.2}
  #viewport[data-style="dashed"]   .node{border-radius:var(--node-radius, 14px)}
  #viewport[data-style="minimal"]  {--edge-color:var(--line-2);--edge-width:1.1}
  #viewport[data-style="minimal"]  .node{border-radius:var(--node-radius, 6px); border-width:1px; box-shadow:none}
  #viewport[data-style="minimal"]  .node.root{border-radius:var(--node-radius, 8px)}
  #viewport[data-style="zigzag"]   {--edge-color:var(--line-2);--edge-width:2}
  #viewport[data-style="zigzag"]   .node{border-radius:var(--node-radius, 2px); border-width:2px; box-shadow:none}
  #viewport[data-style="zigzag"]   .node.root{border-radius:var(--node-radius, 2px)}
  #viewport[data-style="neon"]     {--edge-color:var(--accent);--edge-width:1.5}
  #viewport[data-style="neon"]     .node{border-radius:var(--node-radius, 10px); border-width:1px; border-color:color-mix(in srgb, var(--accent) 55%, transparent); box-shadow:0 0 0 1px color-mix(in srgb, var(--accent) 18%, transparent), 0 0 var(--node-glow, 6px) color-mix(in srgb, var(--accent) 38%, transparent)}
  #viewport[data-style="neon"]     .node.root{box-shadow:0 0 0 1px color-mix(in srgb, var(--accent) 32%, transparent), 0 0 var(--node-glow, 6px) color-mix(in srgb, var(--accent) 55%, transparent)}
  #viewport[data-style="neon"]     #edges path{filter:drop-shadow(0 0 3px color-mix(in srgb, var(--accent) 65%, transparent))}
  #viewport[data-style="circuit"]  {--edge-color:var(--accent);--edge-width:1.4}
  #viewport[data-style="circuit"]  .node{border-radius:var(--node-radius, 4px); border-width:1.5px; border-color:color-mix(in srgb, var(--accent) 45%, transparent); box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--accent) 10%, transparent), 0 1px var(--node-glow, 3px) color-mix(in srgb, var(--accent) 12%, transparent)}
  #viewport[data-style="circuit"]  .node.root{border-width:1.5px; box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--accent) 14%, transparent), 0 0 var(--node-glow, 10px) color-mix(in srgb, var(--accent) 18%, transparent)}
  #viewport[data-style="blueprint"]{--edge-color:var(--ink-soft);--edge-width:1.2}
  #viewport[data-style="blueprint"] .node{border-radius:var(--node-radius, 11px); border-width:1.5px; border-style:dashed; border-color:var(--ink); background:color-mix(in srgb, var(--paper) 82%, transparent); box-shadow:none}
  #viewport[data-style="blueprint"] .node.root{border-radius:var(--node-radius, 13px); border-style:dashed}
  #viewport[data-style="clay"]     {--edge-color:var(--line-2);--edge-width:1.6}
  #viewport[data-style="clay"]     .node{border-radius:var(--node-radius, 16px); border-width:1px; border-color:color-mix(in srgb, var(--line) 40%, transparent); background:var(--card-bg); box-shadow:inset 2px 2px 4px color-mix(in srgb, #fff 70%, transparent), inset -2px -2px 6px color-mix(in srgb, var(--ink) 12%, transparent), 0 8px var(--node-glow, 20px) color-mix(in srgb, var(--ink) 14%, transparent)}
  #viewport[data-style="clay"]     .node.root{border-radius:var(--node-radius, 18px); box-shadow:inset 3px 3px 6px color-mix(in srgb, #fff 75%, transparent), inset -3px -3px 8px color-mix(in srgb, var(--ink) 14%, transparent), 0 12px var(--node-glow, 28px) color-mix(in srgb, var(--ink) 16%, transparent)}
  #viewport[data-style="clay"]     #edges path{opacity:.95}
  #viewport[data-style="ink"]      {--edge-color:var(--ink);--edge-width:2.6}
  #viewport[data-style="ink"]      .node{border-radius:var(--node-radius, 8px); border-width:3px; border-color:var(--ink); box-shadow:4px 4px 0 var(--ink)}
  #viewport[data-style="ink"]      .node.root{border-radius:var(--node-radius, 10px); border-width:3px; box-shadow:4px 4px 0 var(--ink)}
  #viewport[data-style="ink"]      #edges path{stroke-linecap:square}
  #viewport[data-style="paper"]    {--edge-color:var(--line-2);--edge-width:1.5}
  /* Legal pad: punch holes and the accent margin live in a reserved left strip
     (hence padding-left) and rules run full width under them like a real pad.
     Background layers paint first-listed on top. */
  #viewport[data-style="paper"]    .node{border-radius:var(--node-radius, 6px); border-width:1px; border-color:var(--line); background-color:var(--card-bg); padding-left:max(18px, var(--node-pad-x, 15px)); padding-right:max(18px, var(--node-pad-x, 15px)); background-image:radial-gradient(circle at 6px 25%, color-mix(in srgb, var(--ink) 16%, transparent) 0 3px, transparent 3.4px), radial-gradient(circle at 6px 50%, color-mix(in srgb, var(--ink) 16%, transparent) 0 3px, transparent 3.4px), radial-gradient(circle at 6px 75%, color-mix(in srgb, var(--ink) 16%, transparent) 0 3px, transparent 3.4px), linear-gradient(90deg, transparent 0 15px, color-mix(in srgb, var(--accent) 65%, transparent) 15px 16.5px, transparent 16.5px), repeating-linear-gradient(180deg, transparent 0 22px, color-mix(in srgb, var(--line) 22%, transparent) 23px); background-size:100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 23px; box-shadow:0 2px 6px color-mix(in srgb, var(--ink) 10%, transparent), 0 1px 2px color-mix(in srgb, var(--ink) 8%, transparent)}
  #viewport[data-style="paper"]    .node.root{border-radius:var(--node-radius, 8px)}
  #viewport[data-style="paper"]    #edges path{stroke-linecap:round}
  #viewport[data-style="terminal"] {--edge-color:var(--accent);--edge-width:1.6}
  #viewport[data-style="terminal"] .node{
    background:transparent;
    border:none;
    border-bottom:2px solid var(--accent);
    border-radius:0;
    box-shadow:none;
    text-shadow:0 0 6px color-mix(in srgb, var(--accent) 40%, transparent);
    padding:8px 10px 6px 10px;
    justify-content:flex-start;
    text-align:left;
  }
  #viewport[data-style="terminal"] .node::before{
    content:">";
    color:var(--accent-text);
    font-weight:700;
    margin-right:8px;
    opacity:0.9;
    text-shadow:0 0 8px color-mix(in srgb, var(--accent) 60%, transparent);
  }
  #viewport[data-style="terminal"] .node::after{ content:none; }
  #viewport[data-style="terminal"] .node.root{
    background:transparent;
    border:none;
    border-bottom:2.5px solid var(--accent);
    border-radius:0;
    box-shadow:none;
    text-shadow:0 0 8px color-mix(in srgb, var(--accent) 50%, transparent);
    padding:10px 14px 8px 14px;
    justify-content:flex-start;
    text-align:left;
  }
  #viewport[data-style="terminal"] .node.root::before{
    content:"▌";
    color:var(--accent-text);
    font-weight:700;
    margin-right:8px;
    opacity:1;
    text-shadow:0 0 10px color-mix(in srgb, var(--accent) 70%, transparent);
  }
  #viewport[data-style="terminal"] .node.root::after{ content:none; }
  #viewport[data-style="terminal"] #edges path{stroke-linecap:round}

  /* Cascades the pencil font to every part of the app that already
     references --sans/--serif (sidebar, modals, buttons, topbar, and
     nodes/edges too, since those never had their own font override) -
     not a per-component override, the same two variables everything
     already uses. This is now the ONLY thing "Back to School" controls
     via color/font variables - no palette override, see below. */
  :root[data-look="handwritten"]{ --sans:"Caveat",cursive; --serif:"Caveat",cursive; }

  /* ===== "Back to School" look and feel - font + chrome texture only =====
     Deliberately does NOT touch .node/.nodebar border-radius or #viewport's
     --edge-color/--edge-width: card shape and branch style are Map Style's
     job exclusively (Modern/Classic/Bubble/Sketch), independent of which
     Look and Feel is active - picking "Back to School" must never lock out
     or override a Map Style choice. Canvas texture uses var(--paper)/
     var(--line) as already defined by whichever Colour Theme is active, not
     a hardcoded palette - Back to School no longer sets colors at all, so
     the ruled paper automatically follows Light/Dark/Nord/etc.
     font-size on .node: NOT a shape override (no border-radius/transform
     here), just legibility compensation - handwriting fonts need more size
     than a geometric sans for equivalent readability. */
   :root[data-look="handwritten"] .node{ font-size:calc(1.2em * var(--look-node-size, 1)); }
  :root[data-look="handwritten"] .stage{
    background-image:repeating-linear-gradient(var(--paper) 0px, var(--paper) 27px, var(--line) 28px);
    background-size:100% 28px; background-color:var(--paper);
  }
  :root[data-look="handwritten"] .nodebar{
    font-weight:700; font-size:1.15em;
    border-radius:16px 9px 16px 9px;
    border:2px solid var(--ink); box-shadow:none;
  }
  /* Sidebar - thicker ink-colored edge instead of the default hairline */
  :root[data-look="handwritten"] .side{border-right:2px solid var(--ink)}
  /* Modal cards (citation form, due-date form, etc.) - same sketchy-border
     language as the toolbar, for visual consistency across the app */
  :root[data-look="handwritten"] .vf-card{
    border-radius:20px 8px 20px 8px; border:2px solid var(--ink); box-shadow:var(--shadow-lg);
  }
  :root[data-look="handwritten"] .vf-card h2{font-weight:700}
  /* Remaining containers audited and covered in this pass - context menus,
     pickers, the donation card, export/template popups, search box, minimap.
     Grouped since they share the same underlying structure (chrome/node-bg
     background, chrome-edge border, rounded corners) - same sketchy-border
     language as the rest of the app, not restyled individually from scratch. */
  :root[data-look="handwritten"] .row-pop,
  :root[data-look="handwritten"] .picker,
  :root[data-look="handwritten"] .donate-card,
  :root[data-look="handwritten"] .export-pop{
    border-radius:16px 6px 16px 6px; border:2px solid var(--ink);
  }
  :root[data-look="handwritten"] .donate-card{ border-radius:20px 8px 20px 8px; }
  :root[data-look="handwritten"] .search-wrap{ border-radius:14px 6px 14px 6px; border:2px solid var(--ink); }
  :root[data-look="handwritten"] .minimap{ border-radius:12px 4px 12px 4px; border:2px solid var(--ink); }
  :root[data-look="handwritten"] .toast{ border-radius:14px 5px 14px 5px; border:2px solid var(--ink); font-weight:500; }

  /* ===== "At Coffee Shop" look and feel - same principles, lighter touch =====
     Font-only color/font override, same as Back to School - no palette
     override here either. Differentiated from Back to School through
     restraint, not a new color: a lighter 1.5px border (vs 2px) and a
     quieter diagonal-hatch canvas texture instead of overt ruled-paper
     lines.
     Nunito (not a handwriting font, deliberately) needs no font-size
     compensation on .node - unlike Caveat/Patrick Hand, it's designed to
     read well at normal body-text size, so there's no bump here at all.
     That also means this look carries much less risk of the
     overflow/overlap class of bug a size bump can cause elsewhere (nodes
     growing taller than manually-set sizes, edges drawn against stale
     measurements) - not eliminated everywhere, but not something this
     specific look needs to fight. Nunito genuinely has a bold weight
     (unlike Patrick Hand's single weight), so emphasis below uses
     font-weight:700 directly instead of a size-based workaround. */
  :root[data-look="coffee-shop"]{ --sans:"Nunito",sans-serif; --serif:"Nunito",sans-serif; }
  :root[data-look="coffee-shop"] .stage{
    background-image:repeating-linear-gradient(135deg, var(--paper) 0px, var(--paper) 22px, var(--line) 23px);
    background-size:32px 32px; background-color:var(--paper);
  }
  :root[data-look="coffee-shop"] .nodebar{
    font-weight:700;
    border-radius:14px 8px 14px 8px;
    border:1.5px solid var(--ink); box-shadow:none;
  }
  :root[data-look="coffee-shop"] .side{border-right:1.5px solid var(--ink)}
  :root[data-look="coffee-shop"] .vf-card{
    border-radius:18px 7px 18px 7px; border:1.5px solid var(--ink); box-shadow:var(--shadow-lg);
  }
  :root[data-look="coffee-shop"] .vf-card h2{font-weight:700}
  :root[data-look="coffee-shop"] .row-pop,
  :root[data-look="coffee-shop"] .picker,
  :root[data-look="coffee-shop"] .donate-card,
  :root[data-look="coffee-shop"] .export-pop{
    border-radius:14px 5px 14px 5px; border:1.5px solid var(--ink);
  }
  :root[data-look="coffee-shop"] .donate-card{ border-radius:18px 7px 18px 7px; }
  :root[data-look="coffee-shop"] .search-wrap{ border-radius:12px 5px 12px 5px; border:1.5px solid var(--ink); }
  :root[data-look="coffee-shop"] .minimap{ border-radius:10px 4px 10px 4px; border:1.5px solid var(--ink); }
  :root[data-look="coffee-shop"] .toast{ border-radius:12px 4px 12px 4px; border:1.5px solid var(--ink); }

  /* ===== "In the Lab" look and feel - technical, squared-off =====
      Font-only override plus a quiet blueprint grid on the canvas, same
      principles as the other looks: no palette change, no card-shape or
      edge-style interference (those belong to Map Style). Everything is
      squared and precise to match a monospace: 3px corners instead of
      organic rounded ones, 1.5px ink borders, a lab-notebook grid texture
      (both axes) instead of dots or ruled lines. JetBrains Mono is a
      designed text face, so no font-size compensation on .node. */
   :root[data-look="lab"]{ --sans:"JetBrains Mono",monospace; --serif:"JetBrains Mono",monospace; }
   :root[data-look="lab"] .stage{
     background-image:linear-gradient(color-mix(in srgb, var(--line) 55%, transparent) 1px, transparent 1px),
       linear-gradient(90deg, color-mix(in srgb, var(--line) 55%, transparent) 1px, transparent 1px);
     background-size:26px 26px; background-color:var(--paper);
   }
   :root[data-look="lab"] .nodebar{
     border-radius:3px;
     border:1.5px solid var(--ink); box-shadow:none;
   }
   :root[data-look="lab"] .side{border-right:1.5px solid var(--ink)}
   :root[data-look="lab"] .new-map{border:1.5px dashed var(--ink-soft)}
   :root[data-look="lab"] .vf-card{
     border-radius:3px; border:1.5px solid var(--ink); box-shadow:var(--shadow-lg);
   }
   :root[data-look="lab"] .vf-card h2{font-weight:700}
   :root[data-look="lab"] .row-pop,
   :root[data-look="lab"] .picker,
   :root[data-look="lab"] .donate-card,
   :root[data-look="lab"] .export-pop,
   :root[data-look="lab"] .tpl-pop{
     border-radius:3px; border:1.5px solid var(--ink);
   }
   :root[data-look="lab"] .donate-card{ border-radius:4px; }
   :root[data-look="lab"] .search-wrap{ border-radius:3px; border:1.5px solid var(--ink); }
   :root[data-look="lab"] .minimap{ border-radius:3px; border:1.5px solid var(--ink); }
:root[data-look="lab"] .toast{ border-radius:3px; border:1.5px solid var(--ink); }

  /* ===== "The Scientist" - Schrödinger's graph paper =====
      Quantum lab: crisp measurement grid + interference fringes +
      probability clouds + lattice dots. Glowing accent instrumentation
      rather than flat ink: tighter monospace tracking, luminous borders,
      specimen-label breadcrumb. Distinct from "In the Lab" (flat grid,
      ink borders) - here the grid is accent-laced, clouds drift, and
      chrome glows. No palette or Map Style interference - all
      var(--paper)/--line/--accent. */
   :root[data-look="scientist"]{ --sans:"Space Mono",monospace; --serif:"Space Mono",monospace; }
    :root[data-look="scientist"] .stage{
      background-image:
        /* crisp 28px measurement grid */
        linear-gradient(color-mix(in srgb, var(--line) 52%, transparent) 1px, transparent 1px),
        linear-gradient(90deg, color-mix(in srgb, var(--line) 52%, transparent) 1px, transparent 1px),
        /* interference fringes - faint diagonal wave */
        repeating-linear-gradient(-14deg, transparent 0 34px, color-mix(in srgb, var(--accent) 7%, transparent) 35px, transparent 36px),
        /* pinpoint lattice - sub-pixel dots at intersections */
        radial-gradient(circle at 50% 50%, color-mix(in srgb, var(--accent) 18%, transparent) 1.2px, transparent 1.7px);
      background-size:28px 28px, 28px 28px, auto, 52px 52px;
      background-color:var(--paper);
    }
   :root[data-look="scientist"] .nodebar{
     border-radius:3px;
     border:1.5px solid var(--accent);
     box-shadow:0 0 0 1px color-mix(in srgb, var(--accent) 14%, transparent), 0 0 12px color-mix(in srgb, var(--accent) 18%, transparent);
     letter-spacing:0.01em;
   }
   :root[data-look="scientist"] .side{border-right:1.5px solid var(--accent); box-shadow:inset -1px 0 0 color-mix(in srgb, var(--accent) 10%, transparent)}
   :root[data-look="scientist"] .new-map{border:1.5px dashed var(--accent); background:color-mix(in srgb, var(--accent) 6%, transparent)}
   :root[data-look="scientist"] .vf-card{
     border-radius:3px; border:1.5px solid var(--accent); box-shadow:0 0 0 1px color-mix(in srgb, var(--accent) 10%, transparent), var(--shadow-lg);
   }
   :root[data-look="scientist"] .vf-card h2{font-weight:800; letter-spacing:0.04em; text-transform:uppercase; font-size:13px; color:var(--accent-text)}
   :root[data-look="scientist"] .row-pop,
   :root[data-look="scientist"] .picker,
   :root[data-look="scientist"] .donate-card,
   :root[data-look="scientist"] .export-pop,
   :root[data-look="scientist"] .tpl-pop{
     border-radius:3px; border:1.5px solid var(--accent);
     box-shadow:0 0 0 1px color-mix(in srgb, var(--accent) 10%, transparent), var(--shadow);
   }
   :root[data-look="scientist"] .donate-card{ border-radius:4px; }
   :root[data-look="scientist"] .search-wrap{ border-radius:3px; border:1.5px solid var(--accent); box-shadow:0 0 0 1px color-mix(in srgb, var(--accent) 8%, transparent) }
   :root[data-look="scientist"] .minimap{ border-radius:3px; border:1.5px solid var(--accent); box-shadow:0 0 8px color-mix(in srgb, var(--accent) 20%, transparent) }
   :root[data-look="scientist"] .toast{ border-radius:3px; border:1.5px solid var(--accent); box-shadow:0 0 10px color-mix(in srgb, var(--accent) 16%, transparent) }
   /* specimen-label breadcrumb - monospace, upper + tracked, accent pill */
   :root[data-look="scientist"] .breadcrumb{
     background:var(--paper); border:1.5px solid var(--accent);
     box-shadow:0 0 0 1px color-mix(in srgb, var(--accent) 12%, transparent), 0 4px 16px color-mix(in srgb, var(--accent) 14%, transparent);
     border-radius:6px; padding:3px 6px; gap:2px;
      font-family:"Space Mono",monospace; font-size:11.5px; letter-spacing:0.06em; text-transform:uppercase;
   }
   :root[data-look="scientist"] .breadcrumb .bc-crumb{ border-radius:4px; padding:3px 8px; font-weight:700; letter-spacing:0.05em; }
   :root[data-look="scientist"] .breadcrumb .bc-crumb:hover{ background:color-mix(in srgb, var(--accent) 10%, transparent); color:var(--ink) }
   :root[data-look="scientist"] .breadcrumb .bc-sep{ color:var(--accent-text); opacity:1; font-weight:900; font-size:10px; }

  /* ===== "The Architect" - blueprint system drafting table =====
      Software-architecture blueprint: dual grid (major 32px + minor 8px),
      faint 45° construction haze, snapshot component ghosts, and title-block
      corners. Heavy ink structure with accent spine - not the lab's flat
      grid. Space Grotesk for confident, structural sans (distinct from
      office Bricolage / studio Fraunces). Title-block breadcrumb like a
      drawing sheet label. No palette override - follows active Colour Theme. */
   :root[data-look="architect"]{ --sans:"Space Grotesk",sans-serif; --serif:"Space Grotesk",sans-serif; }
   :root[data-look="architect"] .stage{
     background-image:
       /* major 32px blueprint grid */
       linear-gradient(color-mix(in srgb, var(--line) 48%, transparent) 1px, transparent 1px),
       linear-gradient(90deg, color-mix(in srgb, var(--line) 48%, transparent) 1px, transparent 1px),
       /* minor 8px subdivision - lighter */
       linear-gradient(color-mix(in srgb, var(--line) 20%, transparent) 1px, transparent 1px),
       linear-gradient(90deg, color-mix(in srgb, var(--line) 20%, transparent) 1px, transparent 1px),
       /* 45° construction haze - very faint */
       repeating-linear-gradient(45deg, transparent 0 48px, color-mix(in srgb, var(--line) 9%, transparent) 49px, transparent 50px),
       /* blueprint title-block corners - L-shaped faint ink markers */
       linear-gradient(90deg, color-mix(in srgb, var(--ink) 14%, transparent) 2px, transparent 2px),
       linear-gradient(color-mix(in srgb, var(--ink) 14%, transparent) 2px, transparent 2px),
       /* ghost system blocks - faint accent rectangles hinting at components */
       linear-gradient(color-mix(in srgb, var(--accent) 4.5%, transparent) 1px, transparent 1px),
       linear-gradient(90deg, color-mix(in srgb, var(--accent) 4.5%, transparent) 1px, transparent 1px);
     background-size:32px 32px, 32px 32px, 8px 8px, 8px 8px, 64px 64px, 100% 100%, 100% 100%, 220px 140px, 220px 140px;
     background-position:0 0, 0 0, 0 0, 0 0, 0 0, 0 0, 0 0, 18% 42%, 72% 68%;
     background-repeat:repeat, repeat, repeat, repeat, repeat, no-repeat, no-repeat, no-repeat, no-repeat;
     background-color:var(--paper);
   }
   :root[data-look="architect"] .nodebar{
      border-radius:6px;
      border:1.5px solid var(--ink); border-left:4px solid var(--accent);
      box-shadow:inset 0 1px 0 color-mix(in srgb, var(--paper) 60%, transparent), 0 1px 0 color-mix(in srgb, var(--ink) 8%, transparent);
    }
   :root[data-look="architect"] .nodebar::before{
     content:""; position:absolute; top:6px; right:6px; width:6px; height:6px;
     border-top:1.5px solid var(--accent); border-right:1.5px solid var(--accent); opacity:.55; border-radius:1px;
   }
   :root[data-look="architect"] .side{border-right:2px solid var(--ink); box-shadow:inset -2px 0 0 color-mix(in srgb, var(--accent) 18%, transparent)}
   :root[data-look="architect"] .side .side-tabs{ border-bottom:1px solid var(--line) }
   :root[data-look="architect"] .new-map{border:1.5px dashed var(--ink); border-left:3px solid var(--accent); background:color-mix(in srgb, var(--paper-2) 60%, transparent)}
   :root[data-look="architect"] .vf-card{
     border-radius:6px; border:2px solid var(--ink); border-left:4px solid var(--accent); box-shadow:var(--shadow-lg);
   }
   :root[data-look="architect"] .vf-card h2{font-weight:800; letter-spacing:0.06em; text-transform:uppercase; font-size:12.5px; color:var(--ink)}
   :root[data-look="architect"] .row-pop,
   :root[data-look="architect"] .picker,
   :root[data-look="architect"] .donate-card,
   :root[data-look="architect"] .export-pop,
   :root[data-look="architect"] .tpl-pop{
     border-radius:6px; border:1.5px solid var(--ink);
   }
   :root[data-look="architect"] .donate-card{ border-radius:8px; border-width:2px; }
   :root[data-look="architect"] .search-wrap{ border-radius:6px; border:1.5px solid var(--ink); border-left:3px solid var(--accent); }
   :root[data-look="architect"] .minimap{ border-radius:6px; border:2px solid var(--ink); box-shadow:inset 0 0 0 2px color-mix(in srgb, var(--accent) 14%, transparent) }
   :root[data-look="architect"] .toast{ border-radius:6px; border:1.5px solid var(--ink); border-left:3px solid var(--accent); }
   /* title-block breadcrumb - architectural drawing label */
   :root[data-look="architect"] .breadcrumb{
     background:var(--paper); border:2px solid var(--ink); border-left:4px solid var(--accent);
     box-shadow:var(--shadow); border-radius:4px; padding:2px 5px; gap:1px;
      font-family:"Space Grotesk",sans-serif; font-size:11px; letter-spacing:0.1em; text-transform:uppercase; font-weight:800;
   }
   :root[data-look="architect"] .breadcrumb .bc-crumb{ border-radius:3px; padding:3px 8px; font-weight:800; letter-spacing:0.08em; background:color-mix(in srgb, var(--paper-2) 70%, transparent); }
   :root[data-look="architect"] .breadcrumb .bc-crumb.current{ background:var(--accent); color:var(--accent-fg, #fff); }
   :root[data-look="architect"] .breadcrumb .bc-sep{ color:var(--ink); opacity:.45; font-weight:900; font-size:10px; letter-spacing:0; }
    :root[data-look="architect"] .breadcrumb .bc-crumb:hover{ background:color-mix(in srgb, var(--accent) 12%, transparent); color:var(--ink); }

  /* ===== "A Psycho" - midnight slasher =====
      Grindhouse: high contrast, VHS tracking lines, blood spatter and a glitch.
      Oswald is condensed and tense like a thriller poster. No palette override. */
   :root[data-look="psycho"]{ --sans:"Oswald",system-ui,sans-serif; --serif:"Oswald",system-ui,sans-serif;
    --look-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='480' height='480' viewBox='0 0 480 480'%3E%3Cg fill='none' stroke='%23000' stroke-width='1.1' stroke-linecap='round' stroke-linejoin='round'%3E%3Cg transform='translate(88 96) rotate(-10)'%3E%3Cpath d='M -14 -8 Q -18 -18 0 -20 Q 18 -18 14 -8 Q 10 8 0 12 Q -10 8 -14 -8'/%3E%3Ccircle cx='-5' cy='-2' r='2.8' fill='%23000' stroke='none'/%3E%3Ccircle cx='5' cy='-2' r='2.8' fill='%23000' stroke='none'/%3E%3Cpath d='M -6 6 Q 0 9 6 6' fill='none' stroke='%23000' stroke-width='1'/%3E%3Cpath d='M -16 -12 L -18 -18 M -10 -14 L -12 -20 M 10 -14 L 12 -20 M 16 -12 L 18 -18'/%3E%3C/g%3E%3Cg transform='translate(318 64) rotate(14)'%3E%3Cpath d='M -22 0 Q 0 -14 22 0 Q 0 14 -22 0'/%3E%3Cpath d='M -10 0 A 10 10 0 0 0 10 0 A 7 7 0 0 1 -7 3 A 4 4 0 0 0 7 -1' stroke='%23000' stroke-width='0.9'/%3E%3Ccircle cx='0' cy='0' r='1.6' fill='%23000' stroke='none'/%3E%3C/g%3E%3Cg transform='translate(428 178) rotate(-16)'%3E%3Cpath d='M -9 -7 C -11 -11 -7 -15 0 -15 C 7 -15 11 -11 9 -7 L 7 4 L 5 8 L -5 8 L -7 4 Z'/%3E%3Ccircle cx='-3.5' cy='-2' r='1.9' fill='%23000' stroke='none'/%3E%3Ccircle cx='3.5' cy='-2' r='1.9' fill='%23000' stroke='none'/%3E%3Cpath d='M -1 2 L 0 4 L 1 2' fill='none'/%3E%3Cpath d='M -5 6 H -3 M -1 6 H 1 M 3 6 H 5'/%3E%3C/g%3E%3Cg transform='translate(196 214) rotate(8)'%3E%3Cpath d='M -7 -10 L -7 -4 L -10 -2 L -10 9 L 10 9 L 10 -2 L 7 -4 L 7 -10 Z'/%3E%3Crect x='-4.5' y='-13' width='9' height='3.2' rx='0.6'/%3E%3Ctext x='0' y='4' font-family='monospace' font-size='7' text-anchor='middle' fill='%23000' stroke='none' opacity='0.9'%3E×%3C/text%3E%3C/g%3E%3Cg transform='translate(72 322) rotate(-12)'%3E%3Cpath d='M -12 -28 C -14 -32 -8 -38 0 -38 C 8 -38 14 -32 12 -28 L 8 28 L -8 28 Z'/%3E%3Cline x1='-10' y1='-18' x2='10' y2='-16'/%3E%3Cline x1='-11' y1='-8' x2='11' y2='-6'/%3E%3Cline x1='-10' y1='2' x2='10' y2='4'/%3E%3Cline x1='-8' y1='12' x2='8' y2='14'/%3E%3Ccircle cx='0' cy='-32' r='7'/%3E%3Ccircle cx='-2.2' cy='-33' r='1' fill='%23000' stroke='none'/%3E%3Ccircle cx='2.2' cy='-33' r='1' fill='%23000' stroke='none'/%3E%3C/g%3E%3Cg transform='translate(356 348) rotate(18)'%3E%3Cpath d='M -11 -6 C -15 -10 -12 -16 0 -14 C 12 -16 15 -10 11 -6 C 15 -2 12 6 0 8 C -12 6 -15 -2 -11 -6' fill='none'/%3E%3Cpath d='M 0 -14 L 0 8' stroke='%23000' stroke-width='0.7'/%3E%3Cpath d='M -6 -8 C -8 -4 -6 0 -4 2'/%3E%3Cpath d='M 6 -8 C 8 -4 6 0 4 2'/%3E%3C/g%3E%3Cg transform='translate(250 30) rotate(20)'%3E%3Cpath d='M 0 -4 L 1.2 -1.2 L 4 0 L 1.2 1.2 L 0 4 L -1.2 1.2 L -4 0 L -1.2 -1.2 Z' fill='%23000' stroke='none' opacity='0.8'/%3E%3C/g%3E%3Cg transform='translate(30 210) rotate(-15)'%3E%3Cpath d='M -3 0 Q 0 -3 3 0 Q 0 3 -3 0' stroke='%23000' stroke-width='0.7'/%3E%3C/g%3E%3Cg transform='translate(460 60) rotate(25)'%3E%3Cpath d='M -3 -3 L 3 3 M 3 -3 L -3 3' stroke='%23000' stroke-width='0.7'/%3E%3C/g%3E%3Cg transform='translate(150 160) rotate(-8)'%3E%3Cpath d='M 0 -5 Q 2 0 0 5' stroke='%23000' stroke-width='0.7'/%3E%3C/g%3E%3Cg transform='translate(390 260) rotate(12)'%3E%3Cpath d='M -4 0 H 4 M 0 -4 V 4' stroke='%23000' stroke-width='0.7'/%3E%3C/g%3E%3Cg transform='translate(250 380) rotate(-20)'%3E%3Ccircle cx='0' cy='0' r='2.5' stroke='%23000' stroke-width='0.7'/%3E%3C/g%3E%3Cg transform='translate(30 460) rotate(10)'%3E%3Cpath d='M -3 -2 Q 0 2 3 -2' stroke='%23000' stroke-width='0.7'/%3E%3C/g%3E%3Ctext x='150' y='448' font-family='monospace' font-size='11' fill='%23000' stroke='none' opacity='0.8' transform='rotate(-14 150 448)'%3EPSY%3C/text%3E%3Cpath d='M 452 440 Q 458 448 452 456 Q 446 448 452 440' stroke='%23000' stroke-width='0.8' opacity='0.8' fill='none'/%3E%3C/g%3E%3C/svg%3E");
    --look-mask-size:480px 480px;
    --look-mask-ink:7.5%;
  }
:root[data-look="psycho"] .stage{
      background-image:
      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='480' height='480' viewBox='0 0 480 480'%3E%3Cg opacity='0.09' fill='none' stroke='%231a1a1a' stroke-width='1.1' stroke-linecap='round' stroke-linejoin='round'%3E%3Cg transform='translate(88 96) rotate(-10)'%3E%3Cpath d='M -14 -8 Q -18 -18 0 -20 Q 18 -18 14 -8 Q 10 8 0 12 Q -10 8 -14 -8'/%3E%3Ccircle cx='-5' cy='-2' r='2.8' fill='none' stroke='none'/%3E%3Ccircle cx='5' cy='-2' r='2.8' fill='none' stroke='none'/%3E%3Cpath d='M -6 6 Q 0 9 6 6' fill='none' stroke='%231a1a1a' stroke-width='1'/%3E%3Cpath d='M -16 -12 L -18 -18 M -10 -14 L -12 -20 M 10 -14 L 12 -20 M 16 -12 L 18 -18'/%3E%3C/g%3E%3Cg transform='translate(318 64) rotate(14)'%3E%3Cpath d='M -22 0 Q 0 -14 22 0 Q 0 14 -22 0'/%3E%3Cpath d='M -10 0 A 10 10 0 0 0 10 0 A 7 7 0 0 1 -7 3 A 4 4 0 0 0 7 -1' stroke='%231a1a1a' stroke-width='0.9'/%3E%3Ccircle cx='0' cy='0' r='1.6' fill='none' stroke='none'/%3E%3C/g%3E%3Cg transform='translate(428 178) rotate(-16)'%3E%3Cpath d='M -9 -7 C -11 -11 -7 -15 0 -15 C 7 -15 11 -11 9 -7 L 7 4 L 5 8 L -5 8 L -7 4 Z'/%3E%3Ccircle cx='-3.5' cy='-2' r='1.9' fill='none' stroke='none'/%3E%3Ccircle cx='3.5' cy='-2' r='1.9' fill='none' stroke='none'/%3E%3Cpath d='M -1 2 L 0 4 L 1 2' fill='none'/%3E%3Cpath d='M -5 6 H -3 M -1 6 H 1 M 3 6 H 5'/%3E%3C/g%3E%3Cg transform='translate(196 214) rotate(8)'%3E%3Cpath d='M -7 -10 L -7 -4 L -10 -2 L -10 9 L 10 9 L 10 -2 L 7 -4 L 7 -10 Z'/%3E%3Crect x='-4.5' y='-13' width='9' height='3.2' rx='0.6'/%3E%3Ctext x='0' y='4' font-family='monospace' font-size='7' text-anchor='middle' fill='%231a1a1a' stroke='none' opacity='0.9'%3E%C3%97%3C/text%3E%3C/g%3E%3Cg transform='translate(72 322) rotate(-12)'%3E%3Cpath d='M -12 -28 C -14 -32 -8 -38 0 -38 C 8 -38 14 -32 12 -28 L 8 28 L -8 28 Z'/%3E%3Cline x1='-10' y1='-18' x2='10' y2='-16'/%3E%3Cline x1='-11' y1='-8' x2='11' y2='-6'/%3E%3Cline x1='-10' y1='2' x2='10' y2='4'/%3E%3Cline x1='-8' y1='12' x2='8' y2='14'/%3E%3Ccircle cx='0' cy='-32' r='7'/%3E%3Ccircle cx='-2.2' cy='-33' r='1' fill='none' stroke='none'/%3E%3Ccircle cx='2.2' cy='-33' r='1' fill='none' stroke='none'/%3E%3C/g%3E%3Cg transform='translate(356 348) rotate(18)'%3E%3Cpath d='M -11 -6 C -15 -10 -12 -16 0 -14 C 12 -16 15 -10 11 -6 C 15 -2 12 6 0 8 C -12 6 -15 -2 -11 -6' fill='none'/%3E%3Cpath d='M 0 -14 L 0 8' stroke='%231a1a1a' stroke-width='0.7'/%3E%3Cpath d='M -6 -8 C -8 -4 -6 0 -4 2'/%3E%3Cpath d='M 6 -8 C 8 -4 6 0 4 2'/%3E%3C/g%3E%3Cg transform='translate(250 30) rotate(20)'%3E%3Cpath d='M 0 -4 L 1.2 -1.2 L 4 0 L 1.2 1.2 L 0 4 L -1.2 1.2 L -4 0 L -1.2 -1.2 Z' fill='%231a1a1a' stroke='none' opacity='0.8'/%3E%3C/g%3E%3Cg transform='translate(30 210) rotate(-15)'%3E%3Cpath d='M -3 0 Q 0 -3 3 0 Q 0 3 -3 0' stroke='%231a1a1a' stroke-width='0.7'/%3E%3C/g%3E%3Cg transform='translate(460 60) rotate(25)'%3E%3Cpath d='M -3 -3 L 3 3 M 3 -3 L -3 3' stroke='%231a1a1a' stroke-width='0.7'/%3E%3C/g%3E%3Cg transform='translate(150 160) rotate(-8)'%3E%3Cpath d='M 0 -5 Q 2 0 0 5' stroke='%231a1a1a' stroke-width='0.7'/%3E%3C/g%3E%3Cg transform='translate(390 260) rotate(12)'%3E%3Cpath d='M -4 0 H 4 M 0 -4 V 4' stroke='%231a1a1a' stroke-width='0.7'/%3E%3C/g%3E%3Cg transform='translate(250 380) rotate(-20)'%3E%3Ccircle cx='0' cy='0' r='2.5' stroke='%231a1a1a' stroke-width='0.7'/%3E%3C/g%3E%3Cg transform='translate(30 460) rotate(10)'%3E%3Cpath d='M -3 -2 Q 0 2 3 -2' stroke='%231a1a1a' stroke-width='0.7'/%3E%3C/g%3E%3Ctext x='150' y='448' font-family='monospace' font-size='11' fill='%231a1a1a' stroke='none' opacity='0.8' transform='rotate(-14 150 448)'%3EPSY%3C/text%3E%3Cpath d='M 452 440 Q 458 448 452 456 Q 446 448 452 440' stroke='%231a1a1a' stroke-width='0.8' opacity='0.8' fill='none'/%3E%3C/g%3E%3C/svg%3E"),
      repeating-linear-gradient(0deg,
      transparent 0 2px,
      color-mix(in srgb,
      var(--accent) 5%, transparent) 3px, transparent 4px),
      linear-gradient(90deg,
      transparent 0 1px,
      color-mix(in srgb,
      var(--line) 10%, transparent) 1px);
      background-size:60% 50%, 40px 20px;
      background-color:var(--paper);
    }
    :root[data-look="psycho"] .node{
     border-radius:4px;
     border:2px solid var(--ink);
     box-shadow:4px 4px 0 var(--ink);
     transform:rotate(-0.4deg);
   }
   :root[data-look="psycho"] .node:nth-child(even){
     transform:rotate(0.4deg);
   }
   :root[data-look="psycho"] .node.root{
     border-radius:6px;
     border-width:3px;
     box-shadow:5px 5px 0 var(--ink);
     transform:rotate(0deg);
     font-weight:700; letter-spacing:0.02em; text-transform:uppercase;
   }
   :root[data-look="psycho"] .nodebar{
     border-radius:4px;
     border:2px solid var(--ink);
     box-shadow:4px 4px 0 var(--ink);
     transform:rotate(0.3deg);
   }
   :root[data-look="psycho"] .side{border-right:2px solid var(--ink); box-shadow:4px 0 0 var(--ink);}
   :root[data-look="psycho"] .vf-card{ border-radius:4px; border:2px solid var(--ink); box-shadow:4px 4px 0 var(--ink); transform:translate(-50%,-50%) rotate(-0.2deg); }
   :root[data-look="psycho"] .row-pop,
   :root[data-look="psycho"] .picker,
   :root[data-look="psycho"] .donate-card,
   :root[data-look="psycho"] .export-pop{ border-radius:4px; border:2px solid var(--ink); box-shadow:3px 3px 0 var(--ink); }
   :root[data-look="psycho"] .search-wrap{ border-radius:4px; border:2px solid var(--ink); box-shadow:2px 2px 0 var(--ink); }
   :root[data-look="psycho"] .minimap{ border-radius:4px; border:2px solid var(--ink); box-shadow:3px 3px 0 var(--ink); }
   :root[data-look="psycho"] .toast{ border-radius:4px; border:2px solid var(--ink); box-shadow:3px 3px 0 var(--ink); font-weight:700; letter-spacing:0.03em; text-transform:uppercase; }
   :root[data-look="psycho"] .breadcrumb{
     background:var(--paper); border:2px solid var(--ink); box-shadow:3px 3px 0 var(--ink);
     font-family:"Oswald",system-ui,sans-serif; letter-spacing:0.08em; text-transform:uppercase;
   }

/* Forest - birch grove: staggered vertical trunk lines on the canvas,
     rounded asymmetric corners, teal-green chrome. Fredoka is rounded and
     friendly, like a nature walk sign. */
  :root[data-look="forest"]{ --sans:"Fredoka",system-ui,sans-serif; --serif:"Fredoka",system-ui,sans-serif; }
  :root[data-look="forest"] .stage{
    background-image:
      radial-gradient(ellipse 7px 10px at 18% 22%, color-mix(in srgb, var(--teal) 10%, transparent) 98%, transparent 100%),
      radial-gradient(ellipse 5px 7px at 68% 18%, color-mix(in srgb, var(--teal) 7%, transparent) 98%, transparent 100%),
      radial-gradient(ellipse 9px 6px at 42% 38%, color-mix(in srgb, var(--teal) 8%, transparent) 98%, transparent 100%),
      radial-gradient(ellipse 6px 8px at 82% 52%, color-mix(in srgb, var(--teal) 6%, transparent) 98%, transparent 100%),
      radial-gradient(ellipse 8px 5px at 12% 68%, color-mix(in srgb, var(--teal) 7%, transparent) 98%, transparent 100%),
      radial-gradient(ellipse 5px 9px at 55% 78%, color-mix(in srgb, var(--teal) 6%, transparent) 98%, transparent 100%),
      radial-gradient(ellipse 6px 6px at 30% 88%, color-mix(in srgb, var(--teal) 5%, transparent) 98%, transparent 100%);
    background-size:220px 220px, 240px 240px, 200px 200px, 260px 260px, 210px 210px, 230px 230px, 190px 190px;
    background-color:var(--paper);
  }
  :root[data-look="forest"] .nodebar{
    border-radius:22px 10px 22px 10px;
    border:2px solid var(--teal); box-shadow:var(--shadow);
  }
  :root[data-look="forest"] .side{border-right:2px solid var(--teal)}
  :root[data-look="forest"] .new-map{border:2px dashed var(--teal)}
  :root[data-look="forest"] .vf-card{
    border-radius:22px 10px 22px 10px; border:2px solid var(--teal); box-shadow:var(--shadow);
  }
  :root[data-look="forest"] .vf-card h2{font-weight:600}
  :root[data-look="forest"] .row-pop,
  :root[data-look="forest"] .picker,
  :root[data-look="forest"] .donate-card,
  :root[data-look="forest"] .export-pop,
  :root[data-look="forest"] .tpl-pop{
    border-radius:18px 8px 18px 8px; border:2px solid var(--teal);
  }
  :root[data-look="forest"] .donate-card{ border-radius:24px 10px 24px 10px; }
  :root[data-look="forest"] .search-wrap{ border-radius:18px 8px 18px 8px; border:2px solid var(--teal); }
  :root[data-look="forest"] .minimap{ border-radius:18px 8px 18px 8px; border:2px solid var(--teal); }
  :root[data-look="forest"] .toast{ border-radius:16px 8px 16px 8px; border:2px solid var(--teal); }

  /* Beach - the shore itself: a foam curl, a scallop shell, a starfish, a sand
     dollar and a few pebbles scattered on plain sand-coloured paper. The
     artwork is a MASK rather than a background image, so .stage::before tints
     it with the theme's ink and nothing here may name a hue; drawLookBg reads
     the same computed mask for the PNG export, so there is no second copy to
     keep in step. The default dot grid has to be cleared - background:none,
     not background-image:none, which would look like a painted texture to
     looksWithStageTexture() and demand a painter branch. Oswald is tall and
     condensed, like seaside signage. */
  :root[data-look="beach"]{ --sans:"Oswald",system-ui,sans-serif; --serif:"Oswald",system-ui,sans-serif;
    --look-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='320' height='320' viewBox='0 0 320 320'%3E%3Cg fill='none' stroke='%23000' stroke-width='1.3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cg transform='translate(72 68) rotate(-12)'%3E%3Cpath d='M-26 -2 Q-24 -30 0 -32 Q24 -30 26 -2 Q18 4 13 0 Q6 6 0 0 Q-6 6 -13 0 Q-18 4 -26 -2 Z'/%3E%3Cpath d='M0 0 L-20 -21 M0 0 L-10 -28 M0 0 L0 -31 M0 0 L10 -28 M0 0 L20 -21' stroke-width='1'/%3E%3Cpath d='M-8 -3 L-15 -7 M8 -3 L15 -7'/%3E%3C/g%3E%3Cg transform='translate(232 52) rotate(10)'%3E%3Cpath d='M0 -20 L5.3 -7.3 L19 -6.2 L8.6 2.8 L11.8 16.2 L0 9 L-11.8 16.2 L-8.6 2.8 L-19 -6.2 L-5.3 -7.3 Z'/%3E%3Ccircle cx='0' cy='-11' r='1.1' fill='%23000' stroke='none'/%3E%3Ccircle cx='8' cy='3' r='1.1' fill='%23000' stroke='none'/%3E%3Ccircle cx='-8' cy='3' r='1.1' fill='%23000' stroke='none'/%3E%3C/g%3E%3Cg transform='translate(96 214)'%3E%3Ccircle r='17'/%3E%3Cpath d='M0 -13 Q4 -7 0 -2 Q-4 -7 0 -13' stroke-width='1.1'/%3E%3Cpath d='M0 -13 Q4 -7 0 -2 Q-4 -7 0 -13' transform='rotate(72)' stroke-width='1.1'/%3E%3Cpath d='M0 -13 Q4 -7 0 -2 Q-4 -7 0 -13' transform='rotate(144)' stroke-width='1.1'/%3E%3Cpath d='M0 -13 Q4 -7 0 -2 Q-4 -7 0 -13' transform='rotate(216)' stroke-width='1.1'/%3E%3Cpath d='M0 -13 Q4 -7 0 -2 Q-4 -7 0 -13' transform='rotate(288)' stroke-width='1.1'/%3E%3Ccircle r='2.4' fill='%23000' stroke='none'/%3E%3C/g%3E%3Cg transform='translate(200 140) rotate(-8)'%3E%3Cpath d='M-26 2 Q-17 -9 -9 1 Q0 -10 9 1 Q17 -9 26 2' stroke-width='1.4'/%3E%3Ccircle cx='-33' cy='-6' r='1.5' fill='%23000' stroke='none'/%3E%3Ccircle cx='33' cy='-4' r='1.3' fill='%23000' stroke='none'/%3E%3Ccircle cx='1' cy='-15' r='1.4' fill='%23000' stroke='none'/%3E%3C/g%3E%3Cellipse cx='250' cy='190' rx='10' ry='6.5' transform='rotate(-15 250 190)' stroke-width='1.1'/%3E%3Cellipse cx='46' cy='268' rx='7' ry='4.5' transform='rotate(20 46 268)' stroke-width='1.1'/%3E%3Ccircle cx='172' cy='270' r='4.5' stroke-width='1.1'/%3E%3Cg transform='translate(284 296) rotate(-18) scale(0.75)'%3E%3Cpath d='M0 -20 L5.3 -7.3 L19 -6.2 L8.6 2.8 L11.8 16.2 L0 9 L-11.8 16.2 L-8.6 2.8 L-19 -6.2 L-5.3 -7.3 Z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
    --look-mask-size:320px 320px;
    --look-mask-ink:7%;
  }
  :root[data-look="beach"] .stage{
    background:none;
    background-color:var(--paper);
  }
  :root[data-look="beach"] .nodebar{
    border-radius:999px;
    border:1px solid var(--teal); box-shadow:var(--shadow);
  }
  :root[data-look="beach"] .side{border-right:1px solid var(--teal)}
  :root[data-look="beach"] .new-map{border:1.5px dashed var(--teal)}
  :root[data-look="beach"] .vf-card{
    border-radius:18px; border:1px solid var(--teal); box-shadow:var(--shadow);
  }
  :root[data-look="beach"] .vf-card h2{font-weight:600}
  :root[data-look="beach"] .row-pop,
  :root[data-look="beach"] .picker,
  :root[data-look="beach"] .donate-card,
  :root[data-look="beach"] .export-pop,
  :root[data-look="beach"] .tpl-pop{
    border-radius:18px; border:1px solid var(--teal);
  }
  :root[data-look="beach"] .donate-card{ border-radius:24px; }
  :root[data-look="beach"] .search-wrap{ border-radius:999px; border:1px solid var(--teal); }
  :root[data-look="beach"] .minimap{ border-radius:999px; border:1px solid var(--teal); }
  :root[data-look="beach"] .toast{ border-radius:999px; border:1px solid var(--teal); }

  /* Studio - artist's sketchbook: diagonal crosshatch on the canvas, dashed
     pencil-outline chrome. Fraunces is a warm, wonky editorial serif. */
  :root[data-look="studio"]{ --sans:"Fraunces",serif; --serif:"Fraunces",serif; }
  :root[data-look="studio"] .stage{
    background-image:
      repeating-linear-gradient(45deg, color-mix(in srgb, var(--ink) 12%, transparent) 0 1px, transparent 1px 22px),
      repeating-linear-gradient(-45deg, color-mix(in srgb, var(--ink) 12%, transparent) 0 1px, transparent 1px 22px);
    background-size:31px 31px; background-color:var(--paper);
  }
  :root[data-look="studio"] .nodebar{
    border-radius:6px;
    border:1.5px dashed var(--ink); box-shadow:none;
  }
  :root[data-look="studio"] .side{border-right:1.5px dashed var(--ink)}
  :root[data-look="studio"] .new-map{border:1.5px dashed var(--ink-soft)}
  :root[data-look="studio"] .vf-card{
    border-radius:6px; border:1.5px dashed var(--ink); box-shadow:none;
  }
  :root[data-look="studio"] .vf-card h2{font-style:italic}
  :root[data-look="studio"] .row-pop,
  :root[data-look="studio"] .picker,
  :root[data-look="studio"] .donate-card,
  :root[data-look="studio"] .export-pop,
  :root[data-look="studio"] .tpl-pop{
    border-radius:6px; border:1.5px dashed var(--ink);
  }
  :root[data-look="studio"] .donate-card{ border-radius:8px; }
  :root[data-look="studio"] .search-wrap{ border-radius:6px; border:1.5px dashed var(--ink); }
  :root[data-look="studio"] .minimap{ border-radius:6px; border:1.5px dashed var(--ink); }
  :root[data-look="studio"] .toast{ border-radius:6px; border:1.5px dashed var(--ink); }

  /* Mountain - gondola cabins crossing on a sagging cable above stratified
     bands, peaked angular cards. Roboto Condensed is compact and alpine. */
  :root[data-look="mountain"]{ --sans:"Roboto Condensed",system-ui,sans-serif; --serif:"Roboto Condensed",system-ui,sans-serif; }
  /* The band lattice stops at the gondola wire: the wire hangs at
     max(64px, 15%) of the stage and sags at most 26px more, so a paper lid
     to 15% + 70px hides whichever 120px band would land immediately above
     the curve at any stage height, while every band below the wire stays.
     Both layers carry the paper opaquely, and that is load-bearing. A
     background-image cannot interpolate, so it takes a new theme in one step
     while background-color fades over 0.3s (see the transition block at the
     end of this file). While the lid was the only opaque layer, the top
     15% + 70px was already the new paper and everything below it was still
     crossing to it - a hard seam straight across the map, just under the
     wire, on every theme switch and on the boot repaint that applies the
     saved theme. Painting the band tile on paper too leaves nothing for the
     fade to show through, so the stage changes colour in one piece, the same
     way the ruled-paper looks above already do. */
  :root[data-look="mountain"] .stage{
    background-image:
      linear-gradient(180deg, var(--paper) 0 calc(15% + 70px), transparent calc(15% + 70px)),
      linear-gradient(180deg, var(--paper) 60%, color-mix(in srgb, var(--ink-soft) 12%, var(--paper)) 61%, var(--paper) 62%);
    background-size:100% 100%, 100% 120px; background-color:var(--paper);
  }
  .mountain-layer{
    position:absolute; inset:0; pointer-events:none; z-index:0;   /* scenery: under #viewport (see _syncLookFx) */
    will-change:transform; contain:paint;
    display:none;
  }
  /* The cable and the cabins are painted by JS into this bitmap
     (CANVAS_FX in app.js); the rule only stretches it to fill the host. */
  .mountain-layer-canvas{ display:block; width:100%; height:100%; }
  :root[data-look="mountain"] .mountain-layer{ display:block; }
  :root[data-look="mountain"] .nodebar{
    border-radius:12px 12px 0 0;
    border:2px solid var(--ink-soft); box-shadow:var(--shadow-lg);
  }
  :root[data-look="mountain"] .side{border-right:2px solid var(--ink-soft)}
  :root[data-look="mountain"] .new-map{border:2px dashed var(--ink-soft)}
  :root[data-look="mountain"] .vf-card{
    border-radius:12px 12px 0 0; border:2px solid var(--ink-soft); box-shadow:var(--shadow-lg);
  }
  :root[data-look="mountain"] .vf-card h2{font-weight:700; text-transform:uppercase; letter-spacing:.04em}
  :root[data-look="mountain"] .row-pop,
  :root[data-look="mountain"] .picker,
  :root[data-look="mountain"] .donate-card,
  :root[data-look="mountain"] .export-pop,
  :root[data-look="mountain"] .tpl-pop{
    border-radius:10px 10px 0 0; border:2px solid var(--ink-soft);
  }
  :root[data-look="mountain"] .donate-card{ border-radius:14px 14px 0 0; }
  :root[data-look="mountain"] .search-wrap{ border-radius:10px 10px 0 0; border:2px solid var(--ink-soft); }
  :root[data-look="mountain"] .minimap{ border-radius:10px 10px 0 0; border:2px solid var(--ink-soft); }
  :root[data-look="mountain"] .toast{ border-radius:10px 10px 0 0; border:2px solid var(--ink-soft); }

  /* ---- in the Desert: wind-sculpted dunes - two soft parabolic ellipses
     at the horizon plus fine sand speck. Nunito is warm and dusty. Curved
     dunes distinguish from mountain sharp stratified bands. ---- */
  :root[data-look="desert"]{ --sans:"Nunito",system-ui,sans-serif; --serif:"Nunito",system-ui,sans-serif; }
  :root[data-look="desert"] .stage{
    background-image:
      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1000 200'%3E%3Cg opacity='0.15' fill='none' stroke='%231a1a1a' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M118 162 Q117 122 122 68'/%3E%3Cpath d='M122 68 Q92 58 76 42'/%3E%3Cpath d='M122 68 Q102 48 86 30'/%3E%3Cpath d='M122 68 Q122 40 118 22'/%3E%3Cpath d='M122 68 Q142 48 160 36'/%3E%3Cpath d='M122 68 Q152 60 170 52'/%3E%3Ccircle cx='118' cy='72' r='3.2' fill='%231a1a1a' stroke='none'/%3E%3Ccircle cx='127' cy='74' r='2.6' fill='%231a1a1a' stroke='none'/%3E%3Cpath d='M348 158 Q346 118 352 64'/%3E%3Cpath d='M352 64 Q322 54 306 38'/%3E%3Cpath d='M352 64 Q332 44 316 28'/%3E%3Cpath d='M352 64 Q352 36 348 18'/%3E%3Cpath d='M352 64 Q372 44 390 34'/%3E%3Cpath d='M352 64 Q382 56 400 48'/%3E%3Ccircle cx='348' cy='68' r='3.2' fill='%231a1a1a' stroke='none'/%3E%3Ccircle cx='357' cy='70' r='2.6' fill='%231a1a1a' stroke='none'/%3E%3Cpath d='M618 160 Q616 120 622 66'/%3E%3Cpath d='M622 66 Q592 56 576 40'/%3E%3Cpath d='M622 66 Q602 46 586 30'/%3E%3Cpath d='M622 66 Q622 38 618 20'/%3E%3Cpath d='M622 66 Q642 46 660 36'/%3E%3Cpath d='M622 66 Q652 58 670 50'/%3E%3Ccircle cx='618' cy='70' r='3.2' fill='%231a1a1a' stroke='none'/%3E%3Ccircle cx='627' cy='72' r='2.6' fill='%231a1a1a' stroke='none'/%3E%3Cpath d='M848 162 Q846 122 852 70'/%3E%3Cpath d='M852 70 Q822 60 806 44'/%3E%3Cpath d='M852 70 Q832 50 816 34'/%3E%3Cpath d='M852 70 Q852 42 848 24'/%3E%3Cpath d='M852 70 Q872 50 890 40'/%3E%3Cpath d='M852 70 Q882 62 900 54'/%3E%3Ccircle cx='848' cy='74' r='3.2' fill='%231a1a1a' stroke='none'/%3E%3Ccircle cx='857' cy='76' r='2.6' fill='%231a1a1a' stroke='none'/%3E%3C/g%3E%3C/svg%3E"),
      radial-gradient(ellipse 520px 140px at 22% 108%, color-mix(in srgb, var(--line) 14%, transparent) 98%, transparent 100%),
      radial-gradient(ellipse 460px 120px at 78% 115%, color-mix(in srgb, var(--line) 11%, transparent) 98%, transparent 100%),
      radial-gradient(ellipse 620px 100px at 50% 102%, color-mix(in srgb, var(--accent) 6%, transparent) 98%, transparent 100%),
      radial-gradient(circle at 12px 12px, color-mix(in srgb, var(--ink) 9%, transparent) 1px, transparent 1.6px),
      radial-gradient(circle at 34px 28px, color-mix(in srgb, var(--ink) 7%, transparent) 0.9px, transparent 1.4px);
    background-size:100% 180px, 100% 100%, 100% 100%, 100% 100%, 46px 46px, 52px 52px;
    background-position:left bottom, 0 0, 0 0, 0 0, 0 0, 0 0;
    background-repeat:no-repeat, no-repeat, no-repeat, no-repeat, repeat, repeat;
    background-color:var(--paper);
  }
  :root[data-look="desert"] .nodebar{
    border-radius:12px;
    border:1.5px solid var(--ink-soft); box-shadow:var(--shadow);
  }
  :root[data-look="desert"] .side{border-right:1.5px solid var(--ink-soft)}
  :root[data-look="desert"] .new-map{border:1.5px dashed var(--ink-soft)}
  :root[data-look="desert"] .vf-card{
    border-radius:12px; border:1.5px solid var(--ink-soft); box-shadow:var(--shadow-lg);
  }
  :root[data-look="desert"] .vf-card h2{font-weight:700}
  :root[data-look="desert"] .row-pop,
  :root[data-look="desert"] .picker,
  :root[data-look="desert"] .donate-card,
  :root[data-look="desert"] .export-pop,
  :root[data-look="desert"] .tpl-pop{
    border-radius:12px; border:1.5px solid var(--ink-soft);
  }
  :root[data-look="desert"] .donate-card{ border-radius:14px; }
  :root[data-look="desert"] .search-wrap{ border-radius:12px; border:1.5px solid var(--ink-soft); }
  :root[data-look="desert"] .minimap{
    border-radius:12px;
    border:2px solid var(--ink-soft);
    border-bottom:3px solid var(--accent);
    box-shadow:0 4px 12px color-mix(in srgb, var(--ink-soft) 16%, transparent), inset 0 1px 0 color-mix(in srgb, #fff 55%, transparent);
    overflow:hidden;
    position:relative;
    background:var(--paper);
    transform:scaleY(-1);
  }
  :root[data-look="desert"] .minimap svg,
  :root[data-look="desert"] .minimap canvas{
    transform:scaleY(-1);
  }
  :root[data-look="desert"] .minimap::after{
    content:"";
    position:absolute; left:8px; right:8px; bottom:5px; height:2px;
    background:color-mix(in srgb, var(--accent) 20%, transparent);
    border-radius:999px; opacity:.6; pointer-events:none; z-index:2;
  }
  :root[data-look="desert"] .toast{ border-radius:12px; border:1.5px solid var(--ink-soft); }
  :root[data-look="desert"] .breadcrumb{
    background:var(--paper);
    border:1.5px solid var(--ink-soft);
    border-bottom:3px solid var(--accent);
    box-shadow:var(--shadow);
    border-radius:12px 12px 16px 16px;
    padding:4px 8px; gap:4px;
    font-family:"Nunito",system-ui,sans-serif; font-size:11px; letter-spacing:0.04em; text-transform:uppercase; font-weight:700;
  }
  :root[data-look="desert"] .breadcrumb .bc-crumb{
    border-radius:999px; padding:3px 10px; border:1.5px solid var(--ink-soft);
    background:color-mix(in srgb, var(--paper) 92%, var(--accent) 8%);
    font-weight:800;
  }
  :root[data-look="desert"] .breadcrumb .bc-crumb.current{
    background:var(--accent); color:var(--accent-fg, #fff); border-color:var(--accent);
    box-shadow:0 0 0 2px color-mix(in srgb, var(--accent) 16%, transparent);
  }
  :root[data-look="desert"] .breadcrumb .bc-crumb:hover{
    background:color-mix(in srgb, var(--accent) 12%, var(--paper));
    color:var(--ink);
  }
  :root[data-look="desert"] .breadcrumb .bc-sep{
    position:relative; width:14px; text-align:center; color:transparent; opacity:1;
  }
  :root[data-look="desert"] .breadcrumb .bc-sep::after{
    content:"🌴";
    position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
    font-size:8px; opacity:.75;
  }

/* Groot - Baby Groot faces peeking from the bottom corners, vine speckle
     on the canvas. Rounded, organic chrome with a warm bark-green accent. */
  /* Moss and bark are TONES, not colours: mixing the theme's own teal and ink
     keeps them recognisable on Matrix Green, Dracula and Synthwave, where a
     fixed green and brown looked pasted on. A Look is colour-independent by
     contract - colour comes from whichever Colour Theme is active. */
  :root[data-look="groot"]{
    --sans:"Fredoka",system-ui,sans-serif; --serif:"Fredoka",system-ui,sans-serif;
    --groot-moss: color-mix(in srgb, var(--teal) 74%, var(--ink));
    /* the same tone pulled far enough toward the ink to carry --paper text on
       a light theme whose teal is pale (honeycomb needs 80% or less) */
    --groot-moss-deep: color-mix(in srgb, var(--groot-moss) 75%, var(--ink));
    --groot-bark: color-mix(in srgb, var(--ink) 62%, var(--teal));
  }
  :root[data-look="groot"] .stage{
    background-image:
      var(--groot-face, none),
      radial-gradient(circle at 20px 20px, color-mix(in srgb, var(--teal) 6%, transparent) 1px, transparent 1.6px),
      radial-gradient(circle at 40px 36px, color-mix(in srgb, var(--teal) 5%, transparent) 0.9px, transparent 1.4px);
    background-size:180px 180px, 58px 58px, 64px 64px;
    background-position:left -12px bottom -8px, 0 0, 0 0;
    background-repeat:no-repeat, repeat, repeat;
    background-color:var(--paper);
  }
  /* The face is not in this file. It is a 200x200 grid of tone indices, gzipped
     and base64'd in app.js (GROOT_FACE), rebuilt into an SVG data URI on first
     use and handed here as --groot-face. See the note there for why.

     Short version: the artwork is a dithered 5,261-pixel image, which needs
     ~3,600 separate path elements however tightly they are packed, and no path
     syntax fits that in under ~18 KB. Inlined it cost ~25 KB here and another
     ~25 KB in the export painter. As a compressed grid it is 4 KB, stored once.
     Same artwork to the pixel. */  :root[data-look="groot"] .nodebar{
    border-radius:14px;
    border:2px solid color-mix(in srgb, var(--groot-moss) 85%, var(--paper));
    background:color-mix(in srgb, var(--ink) 92%, var(--groot-moss) 8%);
    background-image:repeating-linear-gradient(90deg, transparent 0 8px, color-mix(in srgb, var(--paper) 8%, transparent) 8px 9px);
    box-shadow:0 6px 16px color-mix(in srgb, #000 22%, transparent), inset 0 1px 0 color-mix(in srgb, var(--paper) 14%, transparent);
    color:var(--paper);
  }
  :root[data-look="groot"] .nodebar button{
    border-color:color-mix(in srgb, var(--groot-moss) 28%, transparent);
    color:var(--paper);
  }
  :root[data-look="groot"] .nodebar button.on{
    background:color-mix(in srgb, var(--paper) 92%, var(--groot-moss) 8%);
    color:var(--ink); border-color:color-mix(in srgb, var(--paper) 85%, transparent);
  }
  :root[data-look="groot"] .nodebar button:hover{
    background:color-mix(in srgb, var(--paper) 14%, transparent);
  }
  :root[data-look="groot"] .nodebar .sep{
    background:color-mix(in srgb, var(--paper) 70%, transparent); opacity:.22;
  }
  :root[data-look="groot"] .side{border-right:2px solid var(--teal)}
  :root[data-look="groot"] .new-map{border:2px dashed var(--teal)}
  :root[data-look="groot"] .vf-card{
    border-radius:16px; border:2px solid var(--teal); box-shadow:var(--shadow-lg);
  }
  :root[data-look="groot"] .vf-card h2{font-weight:700}
  :root[data-look="groot"] .row-pop,
  :root[data-look="groot"] .picker,
  :root[data-look="groot"] .donate-card,
  :root[data-look="groot"] .export-pop,
  :root[data-look="groot"] .tpl-pop{
    border-radius:16px; border:2px solid var(--teal);
  }
  :root[data-look="groot"] .donate-card{ border-radius:18px; }
  :root[data-look="groot"] .search-wrap{ border-radius:16px; border:2px solid var(--teal); }
  :root[data-look="groot"] .minimap{
    border-radius:14px;
    border:2px solid var(--groot-moss);
    background:var(--paper);
    background-image:
      repeating-linear-gradient(90deg, transparent 0 2px, color-mix(in srgb, var(--groot-moss) 6%, transparent) 2px 3px);
    box-shadow:0 6px 16px color-mix(in srgb, var(--groot-moss) 20%, transparent), inset 0 1px 0 color-mix(in srgb, #fff 65%, transparent);
    overflow:hidden;
    position:relative;
    transform:scaleY(-1);
  }
  :root[data-look="groot"] .minimap svg,
  :root[data-look="groot"] .minimap canvas{
    transform:scaleY(-1);
  }
  :root[data-look="groot"] .minimap::after{
    content:"";
    position:absolute; left:8px; right:8px; bottom:5px; height:2px;
    background:color-mix(in srgb, var(--groot-moss) 22%, transparent);
    border-radius:999px; opacity:.7; pointer-events:none; z-index:2;
  }
  :root[data-look="groot"] .toast{ border-radius:16px; border:2px solid var(--teal); }
  :root[data-look="groot"] .breadcrumb{
    background:var(--paper);
    background-image:
      repeating-linear-gradient(90deg, transparent 0 10px, color-mix(in srgb, var(--groot-moss) 5%, transparent) 10px 11px);
    border:2px solid var(--groot-moss);
    border-left:4px solid var(--groot-bark);
    box-shadow:0 4px 14px color-mix(in srgb, var(--groot-moss) 18%, transparent), inset 0 1px 0 color-mix(in srgb, #fff 70%, transparent);
    border-radius:18px 10px 18px 10px;
    padding:6px 12px; gap:6px;
    font-family:"Fredoka",system-ui,sans-serif; font-size:11px; letter-spacing:0.06em; text-transform:uppercase; font-weight:800;
  }
  :root[data-look="groot"] .breadcrumb .bc-crumb{
    border-radius:12px 6px 12px 6px;
    padding:4px 12px; border:1.5px solid var(--groot-moss);
    background:color-mix(in srgb, var(--paper) 96%, var(--groot-moss) 4%);
    font-weight:800;
    box-shadow:inset 0 1px 0 color-mix(in srgb, #fff 60%, transparent);
  }
  :root[data-look="groot"] .breadcrumb .bc-crumb.current{
    background:var(--groot-moss-deep); color:var(--paper); border-color:var(--groot-moss-deep);
    box-shadow:0 2px 8px color-mix(in srgb, var(--groot-moss) 24%, transparent), 0 0 0 2px color-mix(in srgb, var(--groot-moss) 14%, transparent);
  }
  :root[data-look="groot"] .breadcrumb .bc-crumb:hover{
    background:color-mix(in srgb, var(--groot-moss) 12%, var(--paper));
    color:var(--ink);
    transform:translateY(-1px);
  }
  :root[data-look="groot"] .breadcrumb .bc-sep{
    position:relative; width:18px; height:14px; color:transparent; opacity:1;
  }
  :root[data-look="groot"] .breadcrumb .bc-sep::after{
    content:"";
    position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
    width:16px; height:10px;
    background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 10'%3E%3Cpath d='M1 5 H15' stroke='%231a1a1a' stroke-width='1.2' stroke-linecap='round' opacity='0.7'/%3E%3Cpath d='M5 3 Q3 1 2 2 Q4 4 5 3' fill='%231a1a1a' opacity='0.55'/%3E%3Cpath d='M11 7 Q13 9 15 8 Q13 6 11 7' fill='%231a1a1a' opacity='0.55'/%3E%3C/svg%3E") center/contain no-repeat;
    opacity:1;
  }


  /* Scanlines and pixel grid tint from --ink rather than a fixed black. Black
     lines are invisible on a dark theme, and on one a CRT should glow light
     anyway - taking the theme's ink gets both for free. */


/* ===== "On a Sailboat" look - nautical: a drifting fleet of sailboats riding
     the swell, compass rose watermark, rope-knot breadcrumb crumbs. Quicksand
     is clean and rounded like sea-smoothed glass. */
  :root[data-look="sailboat"]{
    --sans:"Quicksand",sans-serif; --serif:"Quicksand",sans-serif;
    /* The fleet, ONCE: one mask per boat, read through both mask-image
       properties by its own layer and read back off the live element by the
       PNG export, so screen and export share one source. 400x400 - four
       swell tiles wide, two tall - with the boat clear of every edge, so the
       tile seams invisibly as it drifts.
       Three layers, three boats, three speeds: .wave-layer carries the near
       boat, .wave-layer-mid the middle one, .wave-layer-far the far one,
       drifting at 25, 16.67 and 12.5px/s against the water's 12.5px/s. That
       is depth parallax - the near boat overtakes the swell, the middle one
       creeps past it, the far one rides along at exactly the water's pace.
       Each layer repeats its single-boat tile sideways ONLY and pins it to
       the bottom of its box, so the fleet lives in a bottom band and the
       rest of the stage carries only the swell; one boat per 400px also
       halves how often a wave line carries a boat, so the sea reads calmer
       than the old three-per-200px tile did.
       Hull bottoms sit 41, 160.2 and 281px up from the tile's bottom edge:
       the first, fourth and sixth wave lines - one boat per row, rows
       119-121px apart. The rows matter more than they used to: the layers
       drift at different speeds, so the boats slide past each other
       horizontally and only the row gaps (81px and 88px, hull box to hull
       box) hold at every phase. The top 92.4px of the tile carries no boat
       at all (the far mast is the highest), and that emptiness IS the band's
       upper edge - which is why no hull can ever be cut in half by it. A clip
       or a fade at a fixed height could, for most window heights.
       Every hull sits ON a line of --swell-mask, measured in this tile:
       near y=359 on the line at 360, mid (scale .85) y=239.8 on 240, far
       (scale .7) y=119 on 120 - the same 1px / 0.2px / 1px the old pair
       held, because 8*scale comes off the origin the same way. */
    --wave-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 400'%3E%3Cg fill='%23000'%3E%3Cg transform='translate(60 351)'%3E%3Cpath d='M-13 0L13 0L9 5Q0 8 -8 5Z'/%3E%3Cpath d='M0 -2L0 -30L13 -4Z'/%3E%3Cpath d='M-2 -3L-2 -22L-11 -5Z'/%3E%3Cpath d='M-16 5L-10 5M-25 6.5L-20 6.5M-34 8L-30 8' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
    /* The middle boat, .85 scale, row two. x=240 puts it centre-right; with
       near at x=60 and far at x=360 the three sit left, centre and right, so
       the sideways repeat cannot band them into columns. */
    --wave-mask-mid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 400'%3E%3Cg fill='%23000'%3E%3Cg transform='translate(240 233) scale(.85)'%3E%3Cpath d='M-13 0L13 0L9 5Q0 8 -8 5Z'/%3E%3Cpath d='M0 -2L0 -30L13 -4Z'/%3E%3Cpath d='M-2 -3L-2 -22L-11 -5Z'/%3E%3Cpath d='M-16 5L-10 5M-25 6.5L-20 6.5M-34 8L-30 8' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
    /* The far boat, .7 scale - smaller for depth, and its mast (30*.7) then
       stops 12px under the seventh wave line instead of crowding it. Row
       three, x=360. */
    --wave-mask-far:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 400'%3E%3Cg fill='%23000'%3E%3Cg transform='translate(360 113.4) scale(.7)'%3E%3Cpath d='M-13 0L13 0L9 5Q0 8 -8 5Z'/%3E%3Cpath d='M0 -2L0 -30L13 -4Z'/%3E%3Cpath d='M-2 -3L-2 -22L-11 -5Z'/%3E%3Cpath d='M-16 5L-10 5M-25 6.5L-20 6.5M-34 8L-30 8' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
    /* The swell tile, ONCE: four sine lines per 200x200, the wave art the
       sailboat look shipped before the fleet replaced it. Same contract as the
       fleet tile - one source, read by .swell-layer and by the PNG export -
       and the same seam rule: the lines start and end exactly on the tile edge
       so the repeat is invisible while the layer drifts. The old version baked
       opacity and a near-black stroke into the SVG; as a mask the shape is
       full-alpha black and the tint comes from background-color, so the lines
       follow the theme the way the hulls do. */
    --swell-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cg fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round'%3E%3Cpath d='M0 40 Q25 30 50 40 Q75 50 100 40 Q125 30 150 40 Q175 50 200 40'/%3E%3Cpath d='M0 80 Q25 70 50 80 Q75 90 100 80 Q125 70 150 80 Q175 90 200 80'/%3E%3Cpath d='M0 120 Q25 110 50 120 Q75 130 100 120 Q125 110 150 120 Q175 130 200 120'/%3E%3Cpath d='M0 160 Q25 150 50 160 Q75 170 100 160 Q125 150 150 160 Q175 170 200 160'/%3E%3C/g%3E%3C/svg%3E");
  }
  :root[data-look="sailboat"] .stage{
    background:
      radial-gradient(circle at 85% 15%, color-mix(in srgb, var(--teal) 4%, transparent) 0, transparent 40%);
    background-size:100% 100%;
    background-color:var(--paper);
  }
  /* Motion is a single compositor-only transform. background-position CANNOT be
     composited, so animating it repainted this whole full-viewport layer every
     frame: measured at 220 paints, 110 style recalcs and 859ms of main thread
     over 5s on a 6x-throttled CPU, against 0/0/0 for a static look. A keyframed
     translate3d runs on the compositor thread instead and touches neither.

     Each layer is oversized only where it actually travels: one tile to the
     LEFT (the drift is rightward, so that is the edge that would be exposed)
     and 8px top and bottom. The layers' Y runs -4.3 to 11.2, so the bottom
     edge always hangs past the stage bottom (the hull band never lifts out)
     and only the top edge can cross the stage top, by ~3px, over the empty
     top of the tile: boats live in its bottom ~310px and nothing repeats
     upward. A 400px tile makes that ~1680x816 at this viewport; insetting a
     full tile on all four sides would be ~2080x1600, and that is GPU memory
     a low-end device does not have to spare. contain:paint stops any
     invalidation escaping into the stage. */
/* A look can paint a texture as a MASK here instead of a background image, so
   its colour comes from --ink and follows the theme. The artwork itself lives
   in --look-mask on the look's own :root rule, ONCE: writing the data URI into
   both the prefixed and unprefixed property doubled it, which pushed a
   masked look past the per-look CSS size guard on its own. --look-mask-ink
   defaults to 0%, so this pseudo-element paints nothing for looks that do
   not use it. */
.stage::before{
  content:""; position:absolute; inset:0; pointer-events:none; z-index:0;
  background-color:color-mix(in srgb, var(--ink) var(--look-mask-ink, 0%), transparent);
  -webkit-mask-image:var(--look-mask, none); mask-image:var(--look-mask, none);
  -webkit-mask-repeat:repeat;               mask-repeat:repeat;
  -webkit-mask-size:var(--look-mask-size, auto); mask-size:var(--look-mask-size, auto);
}
  /* One rule for the three boat layers: the near boat lives in .wave-layer
     (the name the registry and the tests have always used), the others in
     .wave-layer-mid and .wave-layer-far. Each layer's own look rule below
     picks its mask and its speed; everything here is shared - the box, the
     promotion, the tint. The left inset is one FULL tile now (400px) because
     each layer travels one 400px tile per loop and the drift is rightward,
     so the left edge is the one that would be exposed.
     Colour follows the theme: the SVG is a full-opacity silhouette used as a
     mask, and background-color supplies the tint. Matches drawLookBg's teal
     fleet at alpha 0.06. A hardcoded near-black silhouette was invisible on
     dark paper. */
  /* Width is pinned to the VIEWPORT, not the stage (right:auto overrides the
     inset's right edge; left and bottom - the mask origin - are unchanged).
     Tied to the stage, every frame of the sidebar's 220ms width transition
     resized all four full-stage masked layers, and a resized layer is
     re-rasterised from scratch: measured at 6x CPU throttle, raster time per
     six toggles 2866ms against 1430ms without the layers, 24fps against the
     static looks' 48. A box that never changes size keeps its tiles, and the
     stage's overflow:hidden clips the part past its right edge. The stage is
     never wider than the viewport, and --ui-zoom converts viewport pixels
     into this subtree's zoomed pixels, so the layer always reaches past the
     right edge. */
  .wave-layer,.wave-layer-mid,.wave-layer-far{
    position:absolute; inset:-8px 0 -8px -400px; pointer-events:none; z-index:0;   /* scenery: under #viewport (see _syncLookFx) */
    right:auto; width:calc(100vw / var(--ui-zoom, 1) + 400px);
    will-change:transform; contain:paint;
    background-color:color-mix(in srgb, var(--teal) 6%, transparent);
    -webkit-mask-repeat:repeat;               mask-repeat:repeat;
    -webkit-mask-size:400px 400px;            mask-size:400px 400px;
    display:none;
  }
  /* The swell the fleet rides. The bottom half of the inset matches the boat
     layers on purpose (-8px, room for the bob), and the two masks are pinned
     to the same edge (see the look rules below): the fleet's tiles are 400px
     tall and this one 200, but hanging both off the bottom of the box puts
     them on one coordinate system, so a hull sits on a wave line by counting
     pixels in the two masks at ANY window height. The left insets differ by
     design - each layer hides exactly one of its own tiles (200 here, 400 for
     the boats) while it drifts. This layer still repeats upward, so the water
     covers the whole stage and only the fleet is a bottom band.
     _syncLookFx inserts this layer last, which lands it first in the tree,
     underneath the boats: scenery under scenery under the map.
     10% teal rather than the hulls' 6%: a 1.5px stroke needs the tint a
     filled hull gets for free from its area. */
  .swell-layer{
    position:absolute; inset:-8px 0 -8px -200px; pointer-events:none; z-index:0;
    right:auto; width:calc(100vw / var(--ui-zoom, 1) + 200px);   /* viewport-pinned like the boat layers above: a sidebar toggle must not resize it */
    will-change:transform; contain:paint;
    background-color:color-mix(in srgb, var(--teal) 10%, transparent);
    -webkit-mask-image:var(--swell-mask, none);
    mask-image:var(--swell-mask, none);
    -webkit-mask-repeat:repeat;               mask-repeat:repeat;
    -webkit-mask-size:200px 200px;             mask-size:200px 200px;
    display:none;
  }
  /* Four layers, one sea. Each drifts exactly ONE tile per loop so the tile
     seam stays invisible - the water 200px over 16s, the boats 400px over
     16, 24 and 32s - and each layer's Y is built so the hull stays IN
     CONTACT with the wave line for the whole loop.

     The speeds are set against --swell-mask, which repeats its pattern every
     100px. Whole pattern periods must pass under a hull per loop, or the bob
     lands off-phase at the seam: crest-passings per loop =
     (speed - 12.5) * loop / 100. That gives the near boat 25px/s -> 2 per
     16s, the middle 16.67px/s -> 1 per 24s, the far 12.5px/s -> 0. Zero is
     the point: the far boat moves at exactly the water's speed, so its wave
     never passes under it and it rides one crest for good.

     Contact is enforced by construction, not by eye. Every 1s keyframe says
       Yb = Yw + dev(p) + static + Dc
     where Yw is the water layer's own bob at that instant, dev(p) is the
     swell profile directly under the hull centre (pattern position
     p = (mask gap + Xb - Xw) mod 100), static is the hull's deep point
     above its nearest line, and Dc is the bite: how far the hull presses
     through the line - 1.5px near, 1.25px mid, 1px far, the deepest each
     hull face absorbs without the line crossing the deck. At every sample
     the daylight gap is therefore exactly Dc (positive = pressed in,
     never floating); between samples the linear keyframe chords miss the
     true curve by at most ~0.7px, which the line's own 1.5px stroke
     covers. test/sailboat-contact.test.mjs re-derives all of this from
     the CSS and fails if a hull floats, a background gap shows, or the
     line rises above a deck. */
  @keyframes sailboat-waves{
    0%     { transform:translate3d(0,0,0); }
    6.25%  { transform:translate3d(12.5px,2.1px,0); }
    12.5%  { transform:translate3d(25px,3px,0); }
    18.75% { transform:translate3d(37.5px,2.1px,0); }
    25%    { transform:translate3d(50px,0,0); }
    31.25% { transform:translate3d(62.5px,-2.1px,0); }
    37.5%  { transform:translate3d(75px,-3px,0); }
    43.75% { transform:translate3d(87.5px,-2.1px,0); }
    50%    { transform:translate3d(100px,0,0); }
    56.25% { transform:translate3d(112.5px,2.1px,0); }
    62.5%  { transform:translate3d(125px,3px,0); }
    68.75% { transform:translate3d(137.5px,2.1px,0); }
    75%    { transform:translate3d(150px,0,0); }
    81.25% { transform:translate3d(162.5px,-2.1px,0); }
    87.5%  { transform:translate3d(175px,-3px,0); }
    93.75% { transform:translate3d(187.5px,-2.1px,0); }
    100%   { transform:translate3d(200px,0,0); }
  }
  @keyframes sailboat-fleet-near{
    0%     { transform:translate3d(0,7.2px,0); }
    6.25%  { transform:translate3d(25px,11px,0); }
    12.5%  { transform:translate3d(50px,11.2px,0); }
    18.75% { transform:translate3d(75px,7px,0); }
    25%    { transform:translate3d(100px,0.8px,0); }
    31.25% { transform:translate3d(125px,-3px,0); }
    37.5%  { transform:translate3d(150px,-3.2px,0); }
    43.75% { transform:translate3d(175px,1px,0); }
    50%    { transform:translate3d(200px,7.2px,0); }
    56.25% { transform:translate3d(225px,11px,0); }
    62.5%  { transform:translate3d(250px,11.2px,0); }
    68.75% { transform:translate3d(275px,7px,0); }
    75%    { transform:translate3d(300px,0.8px,0); }
    81.25% { transform:translate3d(325px,-3px,0); }
    87.5%  { transform:translate3d(350px,-3.2px,0); }
    93.75% { transform:translate3d(375px,1px,0); }
    100%   { transform:translate3d(400px,7.2px,0); }
  }
  @keyframes sailboat-fleet-mid{
    0%     { transform:translate3d(0,-0.5px,0); }
    6.25%  { transform:translate3d(25px,3.9px,0); }
    12.5%  { transform:translate3d(50px,5.8px,0); }
    18.75% { transform:translate3d(75px,4.6px,0); }
    25%    { transform:translate3d(100px,3.9px,0); }
    31.25% { transform:translate3d(125px,6.6px,0); }
    37.5%  { transform:translate3d(150px,9.8px,0); }
    43.75% { transform:translate3d(175px,9.7px,0); }
    50%    { transform:translate3d(200px,5.9px,0); }
    56.25% { transform:translate3d(225px,1.6px,0); }
    62.5%  { transform:translate3d(250px,-0.3px,0); }
    68.75% { transform:translate3d(275px,0.9px,0); }
    75%    { transform:translate3d(300px,1.5px,0); }
    81.25% { transform:translate3d(325px,-1.1px,0); }
    87.5%  { transform:translate3d(350px,-4.3px,0); }
    93.75% { transform:translate3d(375px,-4.2px,0); }
    100%   { transform:translate3d(400px,-0.5px,0); }
  }
  @keyframes sailboat-fleet-far{
    0%     { transform:translate3d(0,6.2px,0); }
    6.25%  { transform:translate3d(25px,9.2px,0); }
    12.5%  { transform:translate3d(50px,6.2px,0); }
    18.75% { transform:translate3d(75px,3.2px,0); }
    25%    { transform:translate3d(100px,6.2px,0); }
    31.25% { transform:translate3d(125px,9.2px,0); }
    37.5%  { transform:translate3d(150px,6.2px,0); }
    43.75% { transform:translate3d(175px,3.2px,0); }
    50%    { transform:translate3d(200px,6.2px,0); }
    56.25% { transform:translate3d(225px,9.2px,0); }
    62.5%  { transform:translate3d(250px,6.2px,0); }
    68.75% { transform:translate3d(275px,3.2px,0); }
    75%    { transform:translate3d(300px,6.2px,0); }
    81.25% { transform:translate3d(325px,9.2px,0); }
    87.5%  { transform:translate3d(350px,6.2px,0); }
    93.75% { transform:translate3d(375px,3.2px,0); }
    100%   { transform:translate3d(400px,6.2px,0); }
  }
  :root[data-look="sailboat"] .swell-layer{
    display:block;
    /* Pinned to the bottom edge of the box, the same edge the fleet's tiles
       hang from: the tiles are different heights (200 against 400) but then
       share one origin, so the fleet hulls keep landing on these lines
       whatever the window height is. It still repeats upward, so the
       water stays a full-canvas texture - only the fleet is banded. */
    -webkit-mask-position:0 100%;               mask-position:0 100%;
    animation:sailboat-waves 16s linear infinite;
  }
  :root[data-look="sailboat"] .wave-layer{
    display:block;
    /* The fleet is a bottom BAND, not a texture: one 400px square tile per
       layer, pinned to the bottom edge of the box and not repeated upward,
       so the boats live in the bottom ~310px of the stage (the bottom six
       wave lines, the far boat's mast under the seventh) and everything
       above them is only the swell.
       The upper edge of that band is where the tile runs out of boat, not a
       cut through one: a clip at a fixed height slices a hull for about a
       third of window heights, and the bob then wobbles the slice. The anchor
       has to match .swell-layer's - pinning one layer and not the other slides
       the fleet off its water by (box height mod tile height) the moment the
       window is resized. */
    -webkit-mask-image:var(--wave-mask, none);
    mask-image:var(--wave-mask, none);
    -webkit-mask-repeat:repeat-x;               mask-repeat:repeat-x;
    -webkit-mask-size:400px 400px;              mask-size:400px 400px;
    -webkit-mask-position:0 100%;               mask-position:0 100%;
    animation:sailboat-fleet-near 16s linear infinite;
  }
  :root[data-look="sailboat"] .wave-layer-mid{
    display:block;
    /* Same band, same anchor, second boat: only the mask and the speed
       differ. 24s over one 400px tile is the 16.67px/s middle of the
       parallax, and exactly one crest slides under it per loop; its Y
       tracks the water's bob plus the swell under it, contact keyframes
       like every layer (see the comment above the blocks). */
    -webkit-mask-image:var(--wave-mask-mid, none);
    mask-image:var(--wave-mask-mid, none);
    -webkit-mask-repeat:repeat-x;               mask-repeat:repeat-x;
    -webkit-mask-size:400px 400px;              mask-size:400px 400px;
    -webkit-mask-position:0 100%;               mask-position:0 100%;
    animation:sailboat-fleet-mid 24s linear infinite;
  }
  :root[data-look="sailboat"] .wave-layer-far{
    display:block;
    /* Third boat, slowest: 32s over the tile is exactly the water's
       12.5px/s, so no wave passes under it and its bob simply copies the
       water's own. */
    -webkit-mask-image:var(--wave-mask-far, none);
    mask-image:var(--wave-mask-far, none);
    -webkit-mask-repeat:repeat-x;               mask-repeat:repeat-x;
    -webkit-mask-size:400px 400px;              mask-size:400px 400px;
    -webkit-mask-position:0 100%;               mask-position:0 100%;
    animation:sailboat-fleet-far 32s linear infinite;
  }
  :root[data-look="sailboat"] .node{
    border-radius:16px;
    border:1.5px solid var(--teal);
    box-shadow:0 2px 8px color-mix(in srgb, var(--teal) 12%, transparent);
  }
  :root[data-look="sailboat"] .node.root{
    border-radius:20px;
    border-width:2px;
    box-shadow:0 2px 12px color-mix(in srgb, var(--teal) 18%, transparent);
  }
  :root[data-look="sailboat"] .nodebar{
    border-radius:16px;
    border:1.5px solid var(--teal); box-shadow:var(--shadow);
  }
  :root[data-look="sailboat"] .side{border-right:1.5px solid var(--teal)}
  :root[data-look="sailboat"] .new-map{border:1.5px dashed var(--teal)}
  :root[data-look="sailboat"] .vf-card{
    border-radius:16px; border:1.5px solid var(--teal); box-shadow:var(--shadow-lg);
  }
  :root[data-look="sailboat"] .vf-card h2{font-weight:700}
  :root[data-look="sailboat"] .row-pop,
  :root[data-look="sailboat"] .picker,
  :root[data-look="sailboat"] .donate-card,
  :root[data-look="sailboat"] .export-pop,
  :root[data-look="sailboat"] .tpl-pop{
    border-radius:14px; border:1.5px solid var(--teal);
  }
  :root[data-look="sailboat"] .donate-card{ border-radius:18px; }
  :root[data-look="sailboat"] .search-wrap{ border-radius:999px; border:1.5px solid var(--teal); }
  :root[data-look="sailboat"] .minimap{
    border-radius:999px; border:1.5px solid var(--teal);
    box-shadow:0 0 12px color-mix(in srgb, var(--teal) 15%, transparent);
  }
  :root[data-look="sailboat"] .toast{ border-radius:999px; border:1.5px solid var(--teal); }
  :root[data-look="sailboat"] .breadcrumb{
    background:var(--paper); border:1.5px solid var(--teal);
    font-family:"Quicksand",sans-serif; font-size:11px; font-weight:600;
  }
  :root[data-look="sailboat"] .breadcrumb .bc-crumb{
    border-radius:999px; padding:3px 10px; font-weight:700;
  }
  :root[data-look="sailboat"] .breadcrumb .bc-crumb.current{
    background:var(--teal); color:var(--teal-fg, #fff);
    box-shadow:0 0 8px color-mix(in srgb, var(--teal) 25%, transparent);
  }
  :root[data-look="sailboat"] .breadcrumb .bc-crumb:hover{
    background:color-mix(in srgb, var(--teal) 12%, var(--paper));
    color:var(--ink);
  }
  :root[data-look="sailboat"] .breadcrumb .bc-sep{
    color:var(--teal-text); opacity:.5; font-weight:900;
  }

              :root[data-look="mathematician"]{ --sans:"Cambria",Cambria,"Cambria Math",Georgia,serif; --serif:"Cambria",Cambria,"Cambria Math",Georgia,serif;
    --look-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='600' viewBox='0 0 600 600'%3E%3Cg fill='none' stroke='%23000' stroke-width='1.3' stroke-linecap='round' stroke-linejoin='round' font-family='Cambria,Cambria Math,Georgia,serif' font-style='italic'%3E%3Cg transform='translate(150 293)'%3E%3Cpath d='M-64 0 H64 M0 -46 V46'/%3E%3Cpath d='M-21 -3 V3 M21 -3 V3 M-43 -3 V3 M43 -3 V3 M-3 -15 H3 M-3 -31 H3 M-3 15 H3 M-3 31 H3' stroke-width='1'/%3E%3Cpath d='M-64 0 L-48 -20 L-32 -28 L-16 -20 L0 0 L16 20 L32 28 L48 20 L64 0'/%3E%3Cpath d='M-64 0 L-48 -20 L-32 -28 L-16 -20 L0 0 Z' fill='%23000' fill-opacity='0.35' stroke='none'/%3E%3Cpath d='M0 0 L16 -9 L32 -15 L48 -20 L64 -23'/%3E%3Ctext x='70' y='-24' font-size='10' fill='%23000' stroke='none'%3Ey = sin x%3C/text%3E%3Ctext x='70' y='-10' font-size='10' fill='%23000' stroke='none'%3Ey = ln x%3C/text%3E%3Ctext x='-64' y='58' font-size='9' fill='%23000' stroke='none'%3Eshaded = ∫ sin x dx%3C/text%3E%3C/g%3E%3Cg transform='translate(85 107)'%3E%3Cpath d='M-45 0 H45 M0 -36 V36'/%3E%3Cpath d='M-15 -3 V3 M15 -3 V3 M-3 -12 H3 M-3 12 H3' stroke-width='1'/%3E%3Cpath d='M-41 -27 Q0 27 41 -27'/%3E%3Ctext x='16' y='-30' font-size='10' fill='%23000' stroke='none'%3Ey = x²%3C/text%3E%3C/g%3E%3Cg transform='translate(505 107)'%3E%3Ccircle r='34'/%3E%3Cpath d='M-42 0 H42 M0 -42 V42' stroke-width='1'/%3E%3Cpath d='M0 0 L26 -22'/%3E%3Cpath d='M12 0 A12 12 0 0 0 9 -8'/%3E%3Cpath d='M26 -22 L26 0' stroke-dasharray='3 3'/%3E%3Ctext x='30' y='-8' font-size='10' fill='%23000' stroke='none'%3Esin θ%3C/text%3E%3C/g%3E%3Cg transform='translate(0 -48)'%3E%3Cpath d='M74.2 538 L74.3 539.3 L73.5 540.7 L72 541.6 L69.9 541.7 L67.7 540.5 L66.2 538 L66.1 534.6 L68 531 L72 528.6 L77.5 528.4 L83.3 531.5 L87.3 538 L87.5 547 L82.5 556.2 L72 562.7 L57.5 563.1 L42.5 555 L32 538'/%3E%3Ctext x='95' y='545' font-size='11' fill='%23000' stroke='none'%3Eφ%3C/text%3E%3Ctext x='415' y='545' font-size='17' fill='%23000' stroke='none'%3Ee%3C/text%3E%3Ctext x='421' y='538' font-size='11' fill='%23000' stroke='none'%3Eiπ%3C/text%3E%3Ctext x='433' y='545' font-size='17' fill='%23000' stroke='none'%3E + 1 = 0%3C/text%3E%3C/g%3E%3Cg transform='translate(300 81)'%3E%3Ccircle r='30'/%3E%3Cpath d='M0 -30 L26 -15 L26 15 L0 30 L-26 15 L-26 -15 Z'/%3E%3Cpath d='M-28 -30 H28'/%3E%3C/g%3E%3Cg transform='translate(0 -48)'%3E%3Cpath d='M260 548 H340'/%3E%3Cpath d='M300 515 A52 52 0 0 1 300 581'/%3E%3Cpath d='M300 581 A52 52 0 0 1 300 515'/%3E%3Cpath d='M300 503 V593' stroke-dasharray='4 3'/%3E%3C/g%3E%3Ctext x='40' y='180' font-size='15' fill='%23000' fill-opacity='0.6' stroke='none' font-style='italic' transform='rotate(-8 40 180)'%3Eπ%3C/text%3E%3Ctext x='140' y='240' font-size='14' fill='%23000' fill-opacity='0.6' stroke='none' font-style='italic' transform='rotate(6 140 240)'%3EΣ%3C/text%3E%3Ctext x='235' y='150' font-size='16' fill='%23000' fill-opacity='0.6' stroke='none' font-style='italic' transform='rotate(0 235 150)'%3E∫%3C/text%3E%3Ctext x='360' y='120' font-size='14' fill='%23000' fill-opacity='0.6' stroke='none' font-style='italic' transform='rotate(-10 360 120)'%3E√%3C/text%3E%3Ctext x='430' y='200' font-size='13' fill='%23000' fill-opacity='0.6' stroke='none' font-style='italic' transform='rotate(4 430 200)'%3E∞%3C/text%3E%3Ctext x='540' y='250' font-size='12' fill='%23000' fill-opacity='0.6' stroke='none' font-style='italic' transform='rotate(-6 540 250)'%3Eα%3C/text%3E%3Ctext x='60' y='320' font-size='13' fill='%23000' fill-opacity='0.6' stroke='none' font-style='italic' transform='rotate(8 60 320)'%3Eβ%3C/text%3E%3Ctext x='250' y='300' font-size='12' fill='%23000' fill-opacity='0.6' stroke='none' font-style='italic' transform='rotate(-4 250 300)'%3Eδ%3C/text%3E%3Ctext x='390' y='350' font-size='14' fill='%23000' fill-opacity='0.6' stroke='none' font-style='italic' transform='rotate(10 390 350)'%3Eλ%3C/text%3E%3Ctext x='545' y='340' font-size='13' fill='%23000' fill-opacity='0.6' stroke='none' font-style='italic' transform='rotate(0 545 340)'%3E∂%3C/text%3E%3Ctext x='160' y='470' font-size='13' fill='%23000' fill-opacity='0.6' stroke='none' font-style='italic' transform='rotate(-8 160 470)'%3E≈%3C/text%3E%3Ctext x='330' y='460' font-size='14' fill='%23000' fill-opacity='0.6' stroke='none' font-style='italic' transform='rotate(5 330 460)'%3E∑%3C/text%3E%3Ctext x='470' y='470' font-size='13' fill='%23000' fill-opacity='0.6' stroke='none' font-style='italic' transform='rotate(-5 470 470)'%3Eφ%3C/text%3E%3Ctext x='560' y='430' font-size='12' fill='%23000' fill-opacity='0.6' stroke='none' font-style='italic' transform='rotate(8 560 430)'%3Eψ%3C/text%3E%3Ctext x='220' y='560' font-size='13' fill='%23000' fill-opacity='0.6' stroke='none' font-style='italic' transform='rotate(-7 220 560)'%3Eθ%3C/text%3E%3C/g%3E%3C/svg%3E");
    --look-mask-size:600px 600px;
    --look-mask-ink:7%;
   }
   :root[data-look="mathematician"] .stage{
     background-image:
       linear-gradient(color-mix(in srgb, var(--line) 12%, transparent) 1px, transparent 1px),
       linear-gradient(90deg, color-mix(in srgb, var(--line) 12%, transparent) 1px, transparent 1px);
     background-size:24px 24px;
     background-color:var(--paper);
   }
    :root[data-look="mathematician"] .node{
      border-radius:12px;
      border:1.5px solid color-mix(in srgb, var(--accent) 55%, var(--ink));
      box-shadow:none;
    }
    :root[data-look="mathematician"] .node.root{
      border-radius:18px;
      border-width:2px;
      box-shadow:none;
    }
   :root[data-look="mathematician"] .nodebar{
     font-size:11px; font-family:var(--sans); letter-spacing:0.3px;
   }
   :root[data-look="mathematician"] .side{border-right:1.5px solid color-mix(in srgb, var(--accent) 55%, var(--ink))}
   :root[data-look="mathematician"] .new-map{border:1.5px dashed var(--accent)}
   :root[data-look="mathematician"] .vf-card{
     border:1.5px solid color-mix(in srgb, var(--accent) 40%, var(--ink));
     border-radius:16px;
   }
   :root[data-look="mathematician"] .vf-card h2{font-weight:700}
   :root[data-look="mathematician"] .row-pop,
   :root[data-look="mathematician"] .picker,
   :root[data-look="mathematician"] .donate-card,
   :root[data-look="mathematician"] .export-pop,
   :root[data-look="mathematician"] .tpl-pop{
     border:1.5px solid color-mix(in srgb, var(--accent) 40%, var(--ink));
     border-radius:12px;
   }
   :root[data-look="mathematician"] .donate-card{ border-radius:16px; }
   :root[data-look="mathematician"] .search-wrap{ border-radius:999px; border:1.5px solid var(--accent); }
 :root[data-look="mathematician"] .minimap{
      border-radius:10px; border:1.5px solid color-mix(in srgb, var(--accent) 35%, var(--ink));
      box-shadow:none;
      background:
        linear-gradient(color-mix(in srgb, var(--accent) 10%, transparent) 1px, transparent 1px),
        linear-gradient(90deg, color-mix(in srgb, var(--accent) 10%, transparent) 1px, transparent 1px),
        color-mix(in srgb, var(--chrome) 92%, transparent);
      background-size:8px 8px;
    }
   :root[data-look="mathematician"] .toast{ border-radius:999px; border:1.5px solid var(--accent); }
:root[data-look="mathematician"] .breadcrumb{
      border-radius:0; border:none;
      background:transparent; box-shadow:none;
      font-family:"Cambria",Cambria,"Cambria Math",Georgia,serif; font-size:13px; gap:0;
    }
    :root[data-look="mathematician"] .breadcrumb .bc-crumb{
      border-radius:0; padding:2px 6px;
      color:var(--ink); background:transparent;
      border-bottom:2px solid transparent;
      transition:all 0.15s ease;
    }
    :root[data-look="mathematician"] .breadcrumb .bc-crumb.current{
      color:var(--accent-text); font-weight:700;
      border-bottom:2px solid var(--accent);
    }
    /* the underline is this look's hover feedback, so the surface stays empty
       on purpose - but the rule still owns the text, otherwise the accent
       'you are here' colour rides along onto a surface a bright theme can
       render nearly as light as the text. */
    :root[data-look="mathematician"] .breadcrumb .bc-crumb:hover{
      background:transparent; color:var(--ink);
      border-bottom-color:var(--accent);
    }
    :root[data-look="mathematician"] .breadcrumb .bc-sep{
      color:var(--ink); opacity:0.5; font-size:16px;
      margin:0 1px; font-weight:300;
    }

/* ===== "In the Matrix" - digital rain =====
   Terminal green on black, falling katakana/hex rain with shining heads,
   scanline flicker, phosphor glow. Courier New, sharp terminal windows,
   glowing breadcrumb cursor. No palette override - follows active Colour Theme
   (greenest with matrix-green). */
:root[data-look="matrix"]{ --sans:"Courier New",monospace; --serif:"Courier New",monospace; }
:root[data-look="matrix"] .stage{
  background-color:var(--paper);
  background-image:
    repeating-linear-gradient(0deg, transparent 0 2px, color-mix(in srgb, var(--accent) 5%, transparent) 3px, transparent 4px),
    linear-gradient(color-mix(in srgb, var(--accent) 7%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--accent) 4%, transparent) 1px, transparent 1px);
  background-size:4px 4px, 28px 28px, 28px 28px;
  position:relative;
  overflow:hidden;
}
:root[data-look="matrix"] #viewport{ position:absolute; z-index:1; }
:root[data-look="matrix"] .stage::before,
:root[data-look="matrix"] .stage::after{
  content:"ﾊﾐﾋｰｳｼﾅﾓﾆｻﾜﾂｵﾘｱﾎﾃﾏﾐｶｷﾑﾕﾗｾﾈｽﾀﾇﾍ0123456789ABCDEFｱｲｳｴｵｶｷｸｹｺｻﾊﾐﾋｰｳｼﾅﾓﾆｻﾜﾂｵﾘｱﾎﾃﾏﾐｶｷﾑﾕﾗｾﾈｽﾀﾇﾍｦｧｨｩｪｫｬｭｮｯｰﾊﾐﾋｰｳｼﾅﾓﾆ0123456789ﾊﾐﾋｰｳｼﾅﾓﾆｻﾜﾂｵﾘｱﾎﾃﾏﾐｶｷﾑﾕﾗｾﾈｽﾀﾇﾍｦｧｨｩｪｫｬｭｮｯｰｱｲｳｴｵｶｷｸｹｺｻｼｽｾｿﾀﾁﾂﾃﾄﾅﾆﾇﾈﾉﾊﾋﾌﾍﾎﾏﾐﾑﾒﾓﾔﾕﾖﾗﾘﾙﾚﾛﾜ";
  position:absolute;
  inset:-70% -6% -10% -6%;
  font-family:"Courier New",monospace;
  font-size:13px;
  line-height:15px;
  letter-spacing:7px;
  color:color-mix(in srgb, var(--accent) 68%, transparent);
  text-shadow:0 0 8px color-mix(in srgb, var(--accent) 70%, transparent), 0 0 2px color-mix(in srgb, var(--paper) 50%, transparent);
  opacity:0.16;
  pointer-events:none;
  white-space:pre;
  word-break:break-all;
  writing-mode:vertical-rl;
  text-orientation:upright;
  will-change:transform;
  contain:paint;
}
:root[data-look="matrix"] .stage::before{
  animation:matrixFall1 13s linear infinite;
  transform:translateY(-28%);
}
:root[data-look="matrix"] .stage::after{
  content:"ｳｴｵｶｷｸｹｺｻｼｽｾｿﾀﾁﾂﾃﾄﾅﾆﾇﾈﾉﾊﾋﾌﾍﾎﾏﾐﾑﾒﾓﾔﾕﾖﾗﾘﾙﾚﾛﾜｦｧｨｩｪｫｬｭｮｯｰ1234567890ZYXWVUTSRQPONMLKJIHGFEDCBAﾊﾐﾋｰｳｼﾅﾓﾆｻﾜﾂｵﾘABCDEFGHIJKLMNOﾊﾐﾋｰｳｼﾅﾓﾆｻﾜﾂｵﾘｱﾎﾃﾏﾐｶｷﾑﾕﾗｾ";
  font-size:11px;
  letter-spacing:9px;
  color:color-mix(in srgb, var(--accent) 42%, transparent);
  opacity:0.09;
  animation:matrixFall2 21s linear infinite;
  transform:translateY(-42%);
}
@keyframes matrixFall1{ 0%{transform:translateY(-32%)} 100%{transform:translateY(32%)} }
@keyframes matrixFall2{ 0%{transform:translateY(-44%)} 100%{transform:translateY(44%)} }
@media (prefers-reduced-motion:reduce){ :root[data-look="matrix"] .stage::before, :root[data-look="matrix"] .stage::after{animation-play-state:paused} }
.matrix-rain{ position:absolute; inset:0; pointer-events:none; z-index:0; overflow:hidden; }
.matrix-rain-canvas{ display:block; width:100%; height:100%; }
@media (prefers-reduced-motion:reduce){ .matrix-rain{ display:none } }
:root[data-look="matrix"] .nodebar{
  border-radius:4px;
  border:1px solid var(--accent);
  box-shadow:0 0 10px color-mix(in srgb, var(--accent) 24%, transparent);
  background:color-mix(in srgb, var(--paper) 92%, var(--accent) 8%);
  color:var(--ink);
  font-family:"Courier New",monospace;
  letter-spacing:0.03em;
}
:root[data-look="matrix"] .nodebar button{ color:var(--ink); }
:root[data-look="matrix"] .nodebar button:hover{ background:color-mix(in srgb, var(--ink) 12%, transparent); color:var(--ink); }
:root[data-look="matrix"] .nodebar button.on{ background:var(--accent); color:var(--accent-fg, #fff); }
:root[data-look="matrix"] .nodebar .nb-div{ background:color-mix(in srgb, var(--ink) 18%, transparent); }
:root[data-look="matrix"] .side{border-right:1px solid var(--accent); box-shadow:inset -1px 0 0 color-mix(in srgb, var(--accent) 18%, transparent), 0 0 12px color-mix(in srgb, var(--accent) 12%, transparent)}
:root[data-look="matrix"] .side .side-tabs{border-bottom:1px solid color-mix(in srgb, var(--accent) 22%, transparent)}
:root[data-look="matrix"] .new-map{border:1px dashed var(--accent); background:color-mix(in srgb, var(--accent) 8%, transparent); color:var(--accent-text); text-shadow:0 0 4px var(--accent)}
:root[data-look="matrix"] .vf-card{
  border-radius:4px; border:1px solid var(--accent); box-shadow:0 0 0 1px color-mix(in srgb, var(--accent) 12%, transparent), 0 0 18px color-mix(in srgb, var(--accent) 22%, transparent), var(--shadow-lg);
  background:color-mix(in srgb, var(--paper) 94%, var(--accent) 6%);
}
:root[data-look="matrix"] .vf-card h2{font-family:"Courier New",monospace; font-weight:700; letter-spacing:0.06em; text-transform:uppercase; color:var(--accent-text); text-shadow:0 0 6px color-mix(in srgb, var(--accent) 50%, transparent)}
:root[data-look="matrix"] .row-pop,
:root[data-look="matrix"] .picker,
:root[data-look="matrix"] .donate-card,
:root[data-look="matrix"] .export-pop,
:root[data-look="matrix"] .tpl-pop{
  border-radius:4px; border:1px solid var(--accent);
  box-shadow:0 0 0 1px color-mix(in srgb, var(--accent) 12%, transparent), 0 0 14px color-mix(in srgb, var(--accent) 18%, transparent);
  background:color-mix(in srgb, var(--paper) 96%, var(--accent) 4%);
}
:root[data-look="matrix"] .donate-card{ border-radius:6px; }
:root[data-look="matrix"] .search-wrap{ border-radius:4px; border:1px solid var(--accent); box-shadow:0 0 8px color-mix(in srgb, var(--accent) 18%, transparent); background:color-mix(in srgb, var(--paper) 94%, var(--accent) 6%); }
:root[data-look="matrix"] .minimap{
  border-radius:4px; border:1px solid var(--accent);
  box-shadow:0 0 14px color-mix(in srgb, var(--accent) 30%, transparent), inset 0 0 12px color-mix(in srgb, var(--accent) 12%, transparent);
  background:color-mix(in srgb, var(--paper) 90%, black);
  overflow:hidden;
  position:relative;
}
:root[data-look="matrix"] .minimap::after{
  content:""; position:absolute; inset:0;
  background:repeating-linear-gradient(0deg, transparent 0 2px, color-mix(in srgb, var(--accent) 10%, transparent) 3px);
  opacity:0.18; pointer-events:none;
}
:root[data-look="matrix"] .toast{
  border-radius:4px; border:1px solid var(--accent);
  box-shadow:0 0 14px color-mix(in srgb, var(--accent) 28%, transparent);
  background:color-mix(in srgb, var(--paper) 88%, var(--accent) 12%);
  color:var(--accent-text); font-family:"Courier New",monospace; letter-spacing:0.04em; text-shadow:0 0 4px var(--accent);
}
:root[data-look="matrix"] .breadcrumb{
  background:color-mix(in srgb, var(--paper) 92%, black);
  border:1px solid var(--accent);
  box-shadow:0 0 12px color-mix(in srgb, var(--accent) 28%, transparent), 0 0 0 1px color-mix(in srgb, var(--accent) 14%, transparent);
  border-radius:4px; padding:4px 8px; gap:3px;
  font-family:"Courier New",monospace; font-size:11px; letter-spacing:0.08em; text-transform:uppercase; color:var(--accent-text);
}
:root[data-look="matrix"] .breadcrumb::before{
  content:">"; color:var(--accent-text); font-weight:900; margin-right:4px; text-shadow:0 0 6px var(--accent);
}
:root[data-look="matrix"] .breadcrumb .bc-crumb{
  border-radius:3px; padding:2px 6px; font-weight:700; letter-spacing:0.06em;
  text-shadow:0 0 5px color-mix(in srgb, var(--accent) 60%, transparent);
}
:root[data-look="matrix"] .breadcrumb .bc-crumb.current{
  background:var(--accent); color:var(--accent-fg, #fff);
  box-shadow:0 0 8px color-mix(in srgb, var(--accent) 50%, transparent);
  text-shadow:none;
}
:root[data-look="matrix"] .breadcrumb .bc-crumb:hover{
  background:color-mix(in srgb, var(--accent) 12%, var(--paper));
  color:var(--ink); text-shadow:none;
}
:root[data-look="matrix"] .breadcrumb .bc-crumb.current::after{
  content:"_"; animation:matrixBlink 1s steps(2) infinite; margin-left:2px; font-weight:900;
}
:root[data-look="matrix"] .breadcrumb .bc-sep{ color:var(--accent-text); opacity:0.9; font-weight:900; }
@keyframes matrixBlink{ 0%,50%{opacity:1} 50.01%,100%{opacity:0} }

/* ===== "IITian" look - the answer booklet ===== */
  /* Anek Latin is an Indian Type Foundry face, so the one look here whose type
     comes from the same place the name does. The rest is an exam answer
     booklet: ruled paper with the double margin rule down the left and an
     accent rule over every fifth line, squared chrome, uppercase tracked
     labels, a shell-style breadcrumb and a reticle minimap.
     No palette, no .node shape and no branch style - colour belongs to Colour
     Theme and card/branch shape belongs to Map Style, as in every other look. */
  :root[data-look="iitian"]{ --sans:"Anek Latin",system-ui,sans-serif; --serif:"Anek Latin",system-ui,sans-serif; }
  /* Three layers, topmost first: the double margin, the fifth-line rule, the
     rules themselves. Every layer fades to transparent rather than laying an
     opaque colour over part of the stage, which is the pairing that leaves a
     seam mid-fade - test/stage-theme-fade.test.mjs checks exactly that. */
  :root[data-look="iitian"] .stage{
    background-image:
      linear-gradient(90deg, transparent 0 64px, color-mix(in srgb, var(--accent) 55%, transparent) 64px 65px,
        transparent 65px 68px, color-mix(in srgb, var(--accent) 30%, transparent) 68px 69px, transparent 69px),
      repeating-linear-gradient(180deg, transparent 0 129px, color-mix(in srgb, var(--accent) 32%, transparent) 129px 130px),
      repeating-linear-gradient(180deg, transparent 0 25px, color-mix(in srgb, var(--line) 75%, transparent) 25px 26px);
    background-size:100% 100%,100% 130px,100% 26px;
    background-color:var(--paper);
  }
  /* Squared chrome. No rule below declares background or color: the sweep in
     test/color-contrast.test.mjs measures the winning background and text
     through the cascade, so a look that repainted them would move every
     pairing it touched. Borders, radii and tracking only. */
  :root[data-look="iitian"] .nodebar{
    font-weight:700; letter-spacing:0.03em;
    border-radius:6px; border:1.5px solid var(--ink); box-shadow:none;
  }
  :root[data-look="iitian"] .side{border-right:1.5px solid var(--ink)}
  :root[data-look="iitian"] .vf-card{border-radius:6px; border:1.5px solid var(--ink); box-shadow:var(--shadow-lg)}
  :root[data-look="iitian"] .vf-card h2{font-weight:700; letter-spacing:0.05em; text-transform:uppercase}
  :root[data-look="iitian"] .row-pop,
  :root[data-look="iitian"] .picker,
  :root[data-look="iitian"] .donate-card,
  :root[data-look="iitian"] .export-pop,
  :root[data-look="iitian"] .tpl-pop{border-radius:6px; border:1.5px solid var(--ink)}
  :root[data-look="iitian"] .search-wrap{border-radius:6px; border:1.5px solid var(--ink)}
  :root[data-look="iitian"] .toast{border-radius:6px; border:1.5px solid var(--ink); font-weight:600}
  :root[data-look="iitian"] .new-map{border:1.5px dashed var(--ink-soft); border-radius:6px}
  /* The breadcrumb becomes a shell path: a tilde root, square tracked crumbs
     and a solidus instead of the chevron. Geometry and type only - neither
     crumb rule declares background or color, so the base `.bc-crumb:hover`
     pair is still what test/breadcrumb-contrast.test.mjs measures here. */
  :root[data-look="iitian"] .breadcrumb{
    border-radius:0; border:1.5px solid var(--ink);
    box-shadow:none; padding:2px 5px; gap:0;
    font-family:var(--sans);
  }
  :root[data-look="iitian"] .breadcrumb::before{
    content:"~"; color:var(--ink); font-weight:700; font-size:12px; margin:0 5px 0 1px;
  }
  :root[data-look="iitian"] .breadcrumb .bc-crumb{
    border-radius:0; padding:3px 6px;
    font-size:11px; letter-spacing:0.07em; text-transform:uppercase;
    font-variant-numeric:tabular-nums;
  }
  :root[data-look="iitian"] .breadcrumb .bc-crumb.current{font-weight:700}
  :root[data-look="iitian"] .breadcrumb .bc-sep{font-size:0; margin:0 3px; opacity:.75}
  :root[data-look="iitian"] .breadcrumb .bc-sep::after{content:"/"; font-size:12px; font-weight:600}
  /* The minimap reads as an instrument panel: a squared ink frame with an
     accent top edge, a centre crosshair and viewfinder brackets in the two
     pseudo layers. The box keeps the base background so the sweep still
     measures the same surface it does for every other look. */
  :root[data-look="iitian"] .minimap{
    position:relative;
    border-radius:0; border:1.5px solid var(--ink);
    border-top:2px solid color-mix(in srgb, var(--accent) 65%, var(--ink));
    box-shadow:none;
  }
  :root[data-look="iitian"] .minimap::before{
    content:""; position:absolute; inset:0; pointer-events:none; opacity:.5;
    background:
      linear-gradient(var(--accent),var(--accent)) 50% 0/1px 100% no-repeat,
      linear-gradient(var(--accent),var(--accent)) 0 50%/100% 1px no-repeat;
  }
  :root[data-look="iitian"] .minimap::after{
    content:""; position:absolute; inset:3px; pointer-events:none;
    background:
      linear-gradient(var(--accent),var(--accent)) 0 0/9px 1px no-repeat,
      linear-gradient(var(--accent),var(--accent)) 0 0/1px 9px no-repeat,
      linear-gradient(var(--accent),var(--accent)) 100% 0/9px 1px no-repeat,
      linear-gradient(var(--accent),var(--accent)) 100% 0/1px 9px no-repeat,
      linear-gradient(var(--accent),var(--accent)) 0 100%/9px 1px no-repeat,
      linear-gradient(var(--accent),var(--accent)) 0 100%/1px 9px no-repeat,
      linear-gradient(var(--accent),var(--accent)) 100% 100%/9px 1px no-repeat,
      linear-gradient(var(--accent),var(--accent)) 100% 100%/1px 9px no-repeat;
  }

/* A sonar ping, because the static rings above are a DIAGRAM of sonar rather
     than sonar: the whole idea is a pulse. One ring expands from the centre and
     fades, every 5s, so the canvas is still for most of that.

     scale() and opacity only, so it runs on the compositor like the sailboat
     swell and costs no main thread. The ::after is the same ring at half phase,
     which reads as a repeating sweep rather than a lonely blip, and costs one
     more composited layer rather than any script.

     It starts at scale .12 rather than 0 so the 1.5px border never renders
     sub-pixel, which flickers at the start of each cycle. */


/* Sketchpad - expressive, bold pen strokes on a clean canvas. Thick borders,
     very asymmetric radii for a hand-drawn feel, and slightly larger node text
     to give the writing more presence. */
  :root[data-look="sketchpad"]{ --sans:system-ui,sans-serif; --serif:system-ui,sans-serif; }
  :root[data-look="sketchpad"] .node{ font-size:calc(1.15em * var(--look-node-size, 1)); }
  :root[data-look="sketchpad"] .nodebar{
    border-radius:14px 4px 14px 4px;
    border:2px solid var(--ink); box-shadow:none;
  }
  :root[data-look="sketchpad"] .side{border-right:2px solid var(--ink)}
  :root[data-look="sketchpad"] .vf-card{
    border-radius:16px 5px 16px 5px; border:2px solid var(--ink); box-shadow:var(--shadow-lg);
  }
  :root[data-look="sketchpad"] .vf-card h2{font-weight:700}
  :root[data-look="sketchpad"] .row-pop,
  :root[data-look="sketchpad"] .picker,
  :root[data-look="sketchpad"] .donate-card,
  :root[data-look="sketchpad"] .export-pop{
    border-radius:14px 4px 14px 4px; border:2px solid var(--ink);
  }
  :root[data-look="sketchpad"] .donate-card{ border-radius:18px 6px 18px 6px; }
  :root[data-look="sketchpad"] .search-wrap{ border-radius:12px 4px 12px 4px; border:2px solid var(--ink); }
  :root[data-look="sketchpad"] .minimap{ border-radius:10px 4px 10px 4px; border:2px solid var(--ink); }
  :root[data-look="sketchpad"] .toast{ border-radius:12px 5px 12px 5px; border:2px solid var(--ink); }

  /* ===== Theme panel - multi-section layout ===== */
  /* ===== Theme picker panel ===== */
  .theme-panel{
    background:var(--chrome); border:1px solid var(--chrome-edge); border-radius:14px;
    box-shadow:var(--shadow-lg); padding:8px; z-index:1000;
    min-width:340px; font-family:var(--sans);
  }
  .theme-panel-large{
    min-width:580px; max-width:700px; display:flex; flex-direction:column;
    gap:0; padding:0; overflow-x:hidden; overflow-y:auto;
  }
  .theme-panel-large .tp-section{padding:7px 12px; border-top:1px solid var(--chrome-edge)}
  .theme-panel-large .tp-section:first-child{border-top:none}
  .theme-panel-large .tp-section-special{
    background:color-mix(in srgb, var(--accent) 6%, transparent);
    border-top:1px solid color-mix(in srgb, var(--accent) 30%, transparent);
    border-bottom:1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  }
  .theme-panel-large .tp-section-special .tp-label{color:var(--accent-text)}
  .theme-panel-large .tp-label{
    font-size:11px; text-transform:uppercase; letter-spacing:.11em;
    color:var(--ink-soft); font-weight:700; margin-bottom:5px;
  }
  /* Cog beside a section label - opens that section's JSON settings. */
  .theme-panel-large .tp-label{ display:flex; align-items:center; gap:6px; }
  .theme-panel-large .tp-cog{
    background:none; border:none; cursor:pointer; padding:0 2px; line-height:1;
    font-size:12px; color:var(--ink-soft); opacity:.7;
  }
  .theme-panel-large .tp-cog:hover{ opacity:1; color:var(--accent-text); }
  /* Explanatory line and inline error inside a .var-form card. */
  .var-form .vf-hint{
    font-size:12px; line-height:1.5; color:var(--ink-soft); margin:-2px 0 12px;
  }
  .var-form .vf-err{
    font-size:12.5px; color:var(--danger); margin:8px 0 0;
    background:color-mix(in srgb, var(--danger) 10%, transparent);
    border:1px solid color-mix(in srgb, var(--danger) 35%, transparent);
    border-radius:8px; padding:7px 10px;
  }
  .var-form .vf-json{
    font-family:ui-monospace, SFMono-Regular, Menlo, monospace; font-size:12.5px;
    line-height:1.5; white-space:pre; overflow:auto;
  }
  /* ===== Inline colour swatches (attachColorSwatches in app.js) =====
     .cs-wrap is the textarea's own box: the mirror measures, the layer draws.
     The mirror carries the text a second time and must stay invisible but
     laid out, so visibility rather than display. */
  .cs-wrap{position:relative}
  /* Block, not inline-block: the inline baseline gap made the wrap (and so
     the layer) a few px taller than the textarea, and a square for the first
     line scrolled out of view showed in that gap under the box. */
  .cs-wrap > textarea{display:block}
  .cs-mirror{
    position:absolute; left:0; top:0; margin:0; overflow:hidden;
    visibility:hidden; pointer-events:none;
    border-style:solid; border-color:transparent;
  }
  .cs-layer{position:absolute; inset:0; overflow:hidden; pointer-events:none}
  .cs-swatch{
    position:absolute; width:11px; height:11px; padding:0; overflow:hidden;
    pointer-events:auto; cursor:pointer; border-radius:3px;
    border:1px solid color-mix(in srgb, var(--ink) 35%, transparent);
    /* Chequerboard under the fill, so a translucent colour reads as translucent. */
    background:
      linear-gradient(45deg, rgba(128,128,128,.45) 25%, transparent 25%,
                             transparent 75%, rgba(128,128,128,.45) 75%) 0 0/6px 6px,
      #fff;
  }
  .cs-swatch:hover{border-color:var(--accent); box-shadow:0 0 0 2px color-mix(in srgb, var(--accent) 30%, transparent)}
  .cs-fill{display:block; width:100%; height:100%}

  /* The picker itself. Above .var-form (z-index 6000), since it opens over one. */
  .cs-pop{
    position:fixed; z-index:6100; width:196px; padding:10px;
    background:var(--chrome); color:var(--ink);
    border:1px solid var(--chrome-edge); border-radius:12px;
    box-shadow:var(--shadow-lg); font-family:var(--sans);
    user-select:none; touch-action:none;
  }
  .cs-sv{position:relative; height:118px; border-radius:8px; overflow:hidden; cursor:crosshair}
  .cs-sv span{position:absolute; inset:0}
  .cs-sv span:first-child{background:linear-gradient(to right, #fff, rgba(255,255,255,0))}
  .cs-sv span:nth-child(2){background:linear-gradient(to top, #000, rgba(0,0,0,0))}
  .cs-bar{position:relative; height:12px; margin-top:10px; border-radius:6px; cursor:pointer}
  .cs-hue{background:linear-gradient(to right,#f00,#ff0,#0f0,#0ff,#00f,#f0f,#f00)}
  .cs-alpha{
    background:
      linear-gradient(45deg, rgba(128,128,128,.45) 25%, transparent 25%,
                             transparent 75%, rgba(128,128,128,.45) 75%) 0 0/8px 8px,
      #fff;
  }
  .cs-agrad{position:absolute; inset:0; border-radius:6px}
  .cs-dot{
    position:absolute; top:50%; width:12px; height:12px; margin:-6px 0 0 -6px;
    border-radius:50%; border:2px solid #fff; box-sizing:border-box;
    box-shadow:0 0 0 1px rgba(0,0,0,.4); pointer-events:none;
  }
  .cs-text{
    width:100%; box-sizing:border-box; margin-top:10px; padding:5px 7px;
    font-family:ui-monospace, SFMono-Regular, Menlo, monospace; font-size:12px;
    color:var(--ink); background:var(--paper);
    border:1px solid var(--line); border-radius:6px; outline:none;
  }
  .cs-text:focus{border-color:var(--accent)}

  /* While a picker is open the dialog stops hiding the map. Choosing a colour
     for the map through a card that covers it is the wrong way round, so the
     backdrop goes clear, and the card itself steps aside for the length of a
     drag (never for a click: see peek() in app.js). The picker is a child of
     body, not of the card, so it stays put and stays legible throughout. */
  .var-form .vf-backdrop{transition:background .18s ease, backdrop-filter .18s ease}
  .var-form.cs-peek .vf-backdrop{background:transparent; backdrop-filter:none}
  .var-form .vf-card{transition:opacity .16s ease}
  .var-form.cs-peek-drag .vf-card{opacity:.12}
  /* Import tile in the Layout row - a dashed square marking it as an action
     rather than one more layout to choose. */
  .theme-panel-large .theme-opt.layout-import .li-tile{
    width:70px; height:40px; flex-shrink:0; display:flex; align-items:center; justify-content:center;
    border:1.5px dashed var(--line-2); border-radius:10px;
    font-size:18px; line-height:1; color:var(--ink-soft);
  }
  .theme-panel-large .theme-opt.layout-import:hover .li-tile{
    border-color:var(--accent); color:var(--accent-text);
  }
  /* Saved-layout chips inside the import dialog. */
  .var-form .li-list{ display:flex; flex-wrap:wrap; gap:6px; margin-top:6px; }
  .var-form .li-chip{
    display:inline-flex; align-items:center; gap:6px; font-size:12.5px;
    background:var(--paper-2); border:1px solid var(--line); border-radius:999px; padding:4px 6px 4px 11px;
  }
  .var-form .li-chip button{
    background:none; border:none; cursor:pointer; color:var(--ink-soft); font-size:14px; line-height:1; padding:0 2px;
  }
  .var-form .li-chip button:hover{ color:var(--danger); }
  .theme-panel-large .tp-grid{
    display:grid; gap:3px;
    grid-template-columns:repeat(4,minmax(160px,1fr));
  }
  /* One-line scrolling row (Layout). Keeps the panel from gaining a whole
     extra line for a fifth option. Overrides the grid above rather than
     replacing it, so every other section is untouched. */
  .theme-panel-large .tp-grid.tp-scroll-row{
    display:flex; overflow-x:auto; overflow-y:hidden;
    scroll-behavior:smooth; scrollbar-width:none;   /* Firefox */
    position:relative;
  }
  .theme-panel-large .tp-grid.tp-scroll-row::-webkit-scrollbar{ display:none; }
  .theme-panel-large .tp-grid.tp-scroll-row > .theme-opt{
    flex:0 0 160px; width:160px; min-width:0;
  }
   /* Colour theme: 3 rows (4 columns) visible, horizontally scrollable like
      the single-row sections. A grid fills down before across, so it gets
      explicit rows and auto-flowing columns; overflow lands in new columns
      past the visible four rather than wrapping to a 4th row. The column min
      (166px) matches what the "I am" cards measure on a full-width panel:
      (700px panel - 24px padding - 12px gaps) / 4 columns. The fifth column
      (custom + Add theme tiles) is what forces every column to its minimum -
      with it the fr share never expands, so the minimum IS the card width. */
  .theme-panel-large .tp-grid.tp-scroll-row.tp-scroll-3row{
    display:grid; gap:4px; overflow-x:auto; overflow-y:hidden;
    grid-template-columns:none; grid-template-rows:repeat(3,auto);
    grid-auto-flow:column; grid-auto-columns:minmax(166px,1fr);
    scroll-behavior:smooth; scrollbar-width:none; position:relative;
  }
  .theme-panel-large .tp-grid.tp-scroll-row.tp-scroll-3row::-webkit-scrollbar{ display:none; }
  .theme-panel-large .tp-grid.tp-scroll-row.tp-scroll-3row > .theme-opt{
    flex:none; width:auto;
  }
  /* Edge fades: the only cue that more options exist off-screen. Applied with
     a mask so they tint whatever is behind them, and only on the side that
     actually has more to show - a permanent fade would imply content that
     is not there. */
  .theme-panel-large .tp-grid.tp-scroll-row.has-more-right{
    -webkit-mask-image:linear-gradient(to right, #000 calc(100% - 34px), transparent 100%);
    mask-image:linear-gradient(to right, #000 calc(100% - 34px), transparent 100%);
  }
  .theme-panel-large .tp-grid.tp-scroll-row.has-more-left{
    -webkit-mask-image:linear-gradient(to left, #000 calc(100% - 34px), transparent 100%);
    mask-image:linear-gradient(to left, #000 calc(100% - 34px), transparent 100%);
  }
  .theme-panel-large .tp-grid.tp-scroll-row.has-more-left.has-more-right{
    -webkit-mask-image:linear-gradient(to right, transparent 0, #000 34px, #000 calc(100% - 34px), transparent 100%);
    mask-image:linear-gradient(to right, transparent 0, #000 34px, #000 calc(100% - 34px), transparent 100%);
  }
  /* A focused option must never be hidden behind a fade. */
  .theme-panel-large .tp-grid.tp-scroll-row > .theme-opt:focus-visible{ outline-offset:-2px; }
  /* Two-row horizontal scrolling grid (Colour theme): only two rows are ever
     shown and extra columns scroll sideways - same fades and behaviour as the
     one-line rows, just with a taller window. Items keep stretching to fill
     the column track (the one-line row's fixed 160px flex width doesn't
     apply to grid items). */
  .theme-panel-large .tp-grid.tp-scroll-row.tp-scroll-2rows{
    display:grid;
    grid-template-rows:repeat(2,auto);
    grid-auto-flow:column;
    grid-auto-columns:minmax(160px,1fr);
  }
  .theme-panel-large .tp-grid.tp-scroll-row.tp-scroll-2rows > .theme-opt{
    width:auto; flex:none;
  }
  .style-thumb{
    width:70px; height:40px; border-radius:6px; border:1px solid var(--line);
    background:var(--paper); display:inline-flex; align-items:center; justify-content:center;
    flex-shrink:0; overflow:hidden;
  }
  .theme-opt{
    display:flex; align-items:center; gap:8px;
    padding:6px 8px; border:1.5px solid transparent; border-radius:9px;
    background:transparent; cursor:pointer; text-align:left;
    font-family:inherit; font-size:13px; font-weight:500; color:var(--ink);
  }
  .theme-opt:hover{background:var(--hover)}
  .theme-opt.active{border-color:var(--accent); background:var(--paper-2)}
  .theme-thumb{
    width:70px; height:40px; border-radius:6px;
    position:relative; border:1px solid var(--line); flex-shrink:0; overflow:hidden;
    display:flex; align-items:center; justify-content:center; gap:6px;
  }
  .theme-thumb .t1{position:static; width:14px; height:14px; border-radius:3px; flex-shrink:0}
  .theme-thumb .t2{position:static; width:11px; height:11px; border-radius:50%; flex-shrink:0}
  .theme-thumb.look-thumb{
    display:flex; align-items:center; justify-content:center;
    background:var(--paper); color:var(--ink); font-size:17px; font-weight:600;
  }
  /* pre-line: tileName() breaks two-word names with a newline. keep-all: a
     Chinese name ("现代工具栏") stays whole instead of wrapping its last character. */
  .theme-name{flex:1; white-space:pre-line; word-break:keep-all; line-height:1.25}

  .theme-opt.add-theme-opt{
    border:1.5px dashed var(--line-2);
    flex-direction:column; justify-content:center; text-align:center; gap:2px;
  }
  .theme-opt.add-theme-opt .theme-name{flex:0 0 auto; font-weight:600}
  .theme-opt.add-theme-opt .add-theme-icon{
    width:26px; height:26px; border-radius:50%;
    border:1.5px dashed var(--line-2);
    display:flex; align-items:center; justify-content:center;
    font-size:16px; line-height:1; font-weight:600; color:var(--ink-soft);
  }
  .theme-opt.add-theme-opt:hover .add-theme-icon,
  .theme-opt.add-theme-opt.active .add-theme-icon{border-color:var(--accent); color:var(--accent-text)}
  .theme-opt.custom-theme-opt .theme-name{
    white-space:normal; overflow-wrap:break-word; min-width:0;
  }
  /* Add-a-theme dialog: the one-click library picker. */
  .lib-grid{
    display:grid; grid-template-columns:repeat(3,minmax(0,1fr));
    gap:4px; max-height:216px; overflow-y:auto; margin-top:6px; padding:2px;
    scrollbar-width:thin;
  }
  .lib-card{
    display:flex; align-items:center; gap:6px; padding:5px 6px;
    border:1.5px solid transparent; border-radius:8px;
    background:transparent; cursor:pointer; text-align:left;
    font-family:inherit; font-size:12px; font-weight:500; color:var(--ink);
    transition: background-color 0.2s ease, border-color 0.2s ease;
  }
  .lib-card:hover{background:var(--hover); border-color:var(--line-2)}
  .lib-card .lib-name{flex:1; min-width:0; white-space:pre-line; word-break:keep-all; overflow:hidden; text-overflow:ellipsis; line-height:1.2}   /* as .theme-name */


  /* ===== Context toolbar on selected node ===== */
  .nodebar{position:absolute;z-index:25;display:flex;align-items:center;gap:2px;background:var(--toolbar-bg);color:var(--toolbar-text);border-radius:10px;padding:4px;box-shadow:var(--shadow-lg)}
  .nodebar .nb-group{display:flex;gap:1px}
  .nodebar .nb-div{width:1px;height:22px;background:color-mix(in srgb, var(--toolbar-text) 22%, transparent);margin:0 4px}
  .nodebar button{border:none;background:none;color:var(--toolbar-text);min-width:30px;height:30px;padding:0;border-radius:7px;font-size:14px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;font-family:inherit}
  .nodebar button:hover{background:color-mix(in srgb, var(--toolbar-text) 16%, transparent)}
  .nodebar button.on{background:var(--accent);color:var(--accent-fg, #fff)}
  .nodebar button.size-btn{padding:0 8px;gap:3px;font-size:12.5px;font-weight:500;font-variant-numeric:tabular-nums}
  .nodebar button.color-btn{padding:0 6px;gap:2px}
  .nodebar button .caret{font-size:9px;opacity:.7}
  .nodebar button .A-mark{font-family:var(--serif);font-weight:700;font-size:13px;line-height:1;display:inline-block;padding-bottom:1px}
  .nodebar button b,.nodebar button s,.nodebar button u,.nodebar button i{font-family:var(--serif);font-size:14px}
  .nodebar button i{font-style:italic}
  .node-text math{ font-size:1.04em; }
  .node-text math[display="block"]{ display:block; margin:3px auto; }
  .node-text{min-width:0;max-width:100%;overflow-wrap:anywhere;word-break:break-word}
  .node-text code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.9em;background:var(--paper-2,rgba(127,127,127,.14));padding:.1em .35em;border-radius:4px}
  /* Block nodes: a fenced code block or a table imported as its own node */
  .node-block{max-width:100%;overflow:auto;text-align:left}
  .node-block pre{margin:0;white-space:pre;overflow-x:auto;background:var(--paper-2,rgba(0,0,0,.05));padding:6px 9px;border-radius:6px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px;line-height:1.5}
  .node-block pre code{font-family:inherit}
  .node-block table{border-collapse:collapse;font-size:11.5px}
  .node-block th,.node-block td{border:1px solid var(--line,#d9dee6);padding:2px 7px;text-align:left;white-space:nowrap}
  .node-block th{background:var(--paper-2,rgba(0,0,0,.05));font-weight:600}
  .node.block-node{max-width:none}                 /* grow to fit code/table; no scrollbar unless resized smaller */
  .block-node .node-text{cursor:default}
  .node .img-alt{display:block;font-size:12px;font-style:italic;color:var(--ink-soft,#667);white-space:normal;max-width:220px;line-height:1.4}
  .node.img-missing{border-style:dashed}
  .node.editing-block .node-block{cursor:text;user-select:text}
  .node.editing-block{border-color:var(--accent,#e0613a)}
  .node-text .node-link{color:var(--link,var(--accent));text-decoration:underline;text-underline-offset:2px;cursor:pointer;word-break:break-all}
  .node-text .node-link:hover{color:var(--accent-text)}
  .node.editing .node-text .node-link{cursor:text;color:inherit;text-decoration:underline}
  .node-text .node-link-fav{width:14px;height:14px;border-radius:3px;object-fit:cover;vertical-align:-2px;margin-right:5px;display:inline-block;background:var(--line-2,#e2e8f0)}
  .node-text .node-link-label{word-break:break-all}
  .node.editing .node-text .node-link-fav{display:none}
  .swatches{display:flex;gap:3px;align-items:center;padding:0 2px}
  .sw{width:18px;height:18px;border-radius:50%;border:2px solid transparent;cursor:pointer}
  .sw:hover{transform:scale(1.18)}
  .sw.sw-clear{display:inline-flex;align-items:center;justify-content:center;background:transparent;border:1.5px dashed var(--line);color:color-mix(in srgb, var(--toolbar-text) 75%, transparent);font-size:11px;line-height:1}
  .sw.sw-clear:hover{border-color:var(--accent);color:var(--toolbar-text)}

  /* ===== Picker popovers (font size / text color / highlight) ===== */
  .picker{background:var(--node-bg, #fff);color:var(--ink);border-radius:10px;box-shadow:0 10px 32px rgba(0,0,0,.30),0 1px 3px rgba(0,0,0,.10);padding:6px;display:flex;gap:3px;align-items:center;z-index:1000;border:1px solid var(--chrome-edge)}
  .picker.size{flex-direction:column;max-height:240px;overflow-y:auto;padding:4px}
  .picker.size button{min-width:54px;justify-content:flex-start;padding:6px 12px}
  .picker button{border:none;background:none;cursor:pointer;padding:6px 10px;font-family:inherit;font-size:13px;border-radius:6px;color:var(--ink);display:inline-flex;align-items:center;justify-content:center}
  .picker button:hover{background:var(--hover)}
  .picker button.on{background:var(--paper-2);font-weight:600;color:var(--ink)}
  .picker .p-sw{width:22px;height:22px;padding:0;border-radius:50%;border:2px solid var(--node-bg, #fff);box-shadow:0 0 0 1px var(--line-2)}
  .picker .p-sw.on{box-shadow:0 0 0 2px var(--accent)}
  .picker .p-default{color:var(--ink-soft);font-size:12px;padding:6px 8px;border-right:1px solid var(--line);margin-right:2px;border-radius:0}

  /* ===== Empty state ===== */
  .empty{position:absolute;inset:0;display:grid;place-items:center;text-align:center;color:var(--ink-soft);z-index:5}
  .empty .e-card{background:var(--chrome);border:1px solid var(--chrome-edge);border-radius:20px;padding:40px 44px;box-shadow:var(--shadow-lg);max-width:380px}
  .empty h2{font-family:var(--serif);font-size:26px;color:var(--ink);margin:0 0 8px}
  .empty p{margin:0 0 20px;line-height:1.6}

  .toast{position:absolute;bottom:64px;left:50%;transform:translateX(-50%) translateY(20px);background:var(--toolbar-bg);color:var(--toolbar-text);padding:11px 20px;border-radius:11px;font-size:13.5px;font-weight:500;box-shadow:var(--shadow-lg);opacity:0;pointer-events:none;transition:opacity .25s,transform .25s,background-color .3s ease,border-color .3s ease,color .3s ease;z-index:50}
  .toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
  /* ===== Login overlay (cloud / static-deploy mode only) ===== */
  .login-overlay{
    position:fixed; inset:0; z-index:5000;
    display:flex; align-items:center; justify-content:center;
    background:var(--paper);
    font-family:var(--sans);
    padding:20px; box-sizing:border-box;
  }
  .login-card{
    position:relative;
    background:var(--node-bg, #fff); color:var(--ink);
    border-radius:16px; box-shadow:0 22px 70px rgba(0,0,0,.25), 0 1px 3px rgba(0,0,0,.06);
    padding:32px 36px; width:520px; max-width:92vw;
    border:1px solid var(--chrome-edge);
    /* Never taller than the window: the card scrolls inside itself instead of
       pushing the sign-in button off-screen where it cannot be reached. The
       collapsed <details> below keep it short enough that this rarely engages.

       Sized against the overlay (100% of a padded, inset:0 flex parent), NOT
       against 100vh. The app scales its whole UI at small viewports - at 0.8x a
       600px window is 750 CSS px of usable space, so a vh-based cap clips the
       card several hundred pixels short of the room it actually has. */
    max-height:100%;
    overflow-y:auto; overscroll-behavior:contain;
  }
  .login-brand{display:flex; align-items:center; gap:10px; margin-bottom:18px}
  .login-brand .mark{flex:0 0 auto; filter:drop-shadow(0 2px 6px rgba(67,158,241,.3))}
  .login-brand h1{
    font-size:26px; margin:0;
  }
  .login-card h2{
    font-family:var(--serif); font-weight:700; font-size:22px;
    margin:0 0 10px; color:var(--ink);
  }
  .login-card > p{
    color:var(--ink-soft); line-height:1.55; margin:0 0 14px; font-size:14px;
  }
  .login-card code{
    background:var(--paper-2); padding:1px 6px; border-radius:4px;
    font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:12.5px;
  }
  .login-steps{
    counter-reset:step; list-style:none; padding:0; margin:18px 0;
  }
  .login-steps > li{
    position:relative; padding-left:40px; margin-bottom:18px;
    counter-increment:step;
  }
  .login-steps > li:before{
    content:counter(step);
    position:absolute; left:0; top:-2px;
    width:28px; height:28px; border-radius:50%;
    background:var(--accent); color:var(--accent-fg, #fff);
    display:flex; align-items:center; justify-content:center;
    font-weight:700; font-size:14px;
  }
  .login-step-link{
    color:var(--accent-text); font-weight:600; text-decoration:none; font-size:15px;
  }
  .login-step-link:hover{text-decoration:underline}
  .login-step-hint{font-size:13px; color:var(--ink-soft); margin-top:6px; line-height:1.5}
  .login-steps label{display:block; font-size:14px; color:var(--ink); margin-bottom:6px; font-weight:500}
  .token-input{display:flex; gap:8px}
  .token-input input{
    flex:1; padding:10px 12px;
    font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:13px;
    border:1.5px solid var(--line); border-radius:8px;
    background:var(--paper); color:var(--ink); outline:none;
  }
  .token-input input:focus{border-color:var(--accent); background:var(--node-bg, #fff)}
  .token-input button{
    padding:10px 20px; background:var(--accent); color:var(--accent-fg, #fff);
    border:none; border-radius:8px; font-family:inherit; font-weight:600;
    cursor:pointer; font-size:14px;
  }
  .token-input button:hover{background:var(--accent-deep)}
  .token-input button:disabled{opacity:.65; cursor:wait}
  .login-error{
    color:var(--danger); font-size:13px; margin-top:8px; min-height:16px; line-height:1.4;
  }
  /* Each host offers two sign-in methods. The one that needs no setup leads;
     the other collapses, so the card stays short enough to fit on a laptop
     window. showLoginOverlay() opens the token method automatically whenever
     OAuth isn't usable, so nobody lands on a card with nothing actionable. */
  .login-token{margin-top:14px; border-top:1px solid var(--chrome-edge); padding-top:12px}
  .login-token > summary{
    cursor:pointer; list-style:none; color:var(--accent-text); font-size:13.5px; font-weight:600; padding:2px 0;
  }
  .login-token > summary::-webkit-details-marker{display:none}
  .login-token > summary::before{content:'▸ '; display:inline-block; transition:transform .15s}
  .login-token[open] > summary::before{content:'▾ '}
  .login-token > summary:hover{text-decoration:underline}
  .login-token .login-steps{margin-top:12px}
  .pane-label{display:block; font-size:14px; color:var(--ink); margin:16px 0 6px; font-weight:500}
  /* The repository target is a setting, not the sign-in action, so it folds
     away and the card opens on the button. The summary always shows the
     current value, and showLoginOverlay() opens the section whenever that
     value is not the default, so a custom target is never out of sight. */
  .login-repo{margin-top:14px}
  .login-repo > summary{
    cursor:pointer; list-style:none; color:var(--ink-soft); font-size:13px; padding:2px 0;
  }
  .login-repo > summary::-webkit-details-marker{display:none}
  .login-repo > summary::before{content:'\25B8 '; display:inline-block}
  .login-repo[open] > summary::before{content:'\25BE '}
  .login-repo > summary:hover{color:var(--ink)}
  .login-repo > summary .repo-current{color:var(--ink); font-size:12.5px}
  .login-repo > .repo-input{margin-top:8px}

  /* "No account yet?" line under each pane's sign-in controls. A link is all
     MindSpark can offer - neither host exposes an API to create an account, so
     the alternative to this line is a visitor with nowhere to go. Muted until
     it has a target: the Gitea one stays href-less until an instance is typed. */
  .login-signup{margin:10px 0 0; font-size:12.5px; color:var(--ink-soft); line-height:1.55}
  /* Link on its own line, explanation beneath - the same shape as the numbered
     steps' .login-step-link + .login-step-hint. Inline, the arrow ran straight
     into the next sentence and read as one run-on; worse in the Gitea pane,
     where the parked link is muted and stops looking like a link at all. */
  .login-signup a{display:block; margin-bottom:1px; color:var(--accent-text); font-weight:600; text-decoration:none}
  .login-signup a:hover{text-decoration:underline}
  .login-signup a[aria-disabled="true"]{color:var(--ink-soft); font-weight:500; cursor:default}

  /* Forge picker - GitHub vs Gitea/Forgejo. A segmented control rather than a
     <select> so both options are visible: users arriving from the "does this
     work with my Forgejo?" thread need to see the answer without opening a menu. */
  .forge-tabs{display:flex; gap:4px; margin:16px 0 4px; padding:4px; border-radius:10px; background:var(--paper-2)}
  .forge-tab{
    flex:1; padding:8px 10px; border:none; border-radius:7px; cursor:pointer;
    background:transparent; color:var(--ink-soft); font-family:inherit; font-size:13.5px; font-weight:600;
  }
  .forge-tab:hover{color:var(--ink)}
  .forge-tab.is-on{background:var(--paper); color:var(--ink); box-shadow:0 1px 3px rgba(0,0,0,.10)}
  .instance-input{
    width:100%; box-sizing:border-box; padding:10px 12px; margin-bottom:2px;
    font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:13px;
    border:1.5px solid var(--line); border-radius:8px;
    background:var(--paper); color:var(--ink); outline:none;
  }
  .instance-input:focus{border-color:var(--accent); background:var(--node-bg, #fff)}

  /* Classic-token fallback tucked inside step 2 - present but not the default path. */
  .login-fallback{margin-top:8px}
  .login-fallback summary{cursor:pointer; color:var(--ink-soft); font-size:12.5px}
  .login-fallback summary:hover{color:var(--ink)}
  .login-fallback p{margin:8px 0 0; font-size:12.5px; color:var(--ink-soft); line-height:1.55}
  .login-fallback a{color:var(--accent-text)}
  .login-extra{margin-top:18px; padding-top:16px; border-top:1px solid var(--chrome-edge)}
  .login-extra summary{cursor:pointer; color:var(--ink-soft); font-size:13px; font-weight:500}
  .login-extra summary:hover{color:var(--ink)}
  .login-extra p{margin:10px 0 0; font-size:13px; color:var(--ink-soft); line-height:1.6}
  .login-extra a{color:var(--accent-text)}

  /* Sign-in to a self-hosted server with user accounts (AUTH=1). One short
     form, so the card is narrower than the git-host card above. */
  .server-login{width:420px}
  .sl-form{display:flex; flex-direction:column; gap:12px; margin-top:6px}
  .sl-field{display:flex; flex-direction:column; gap:5px; font-size:13.5px; font-weight:500; color:var(--ink)}
  .sl-field[hidden]{display:none}   /* display:flex above would otherwise beat the hidden attribute */
  .sl-field input{
    padding:10px 12px; font-family:inherit; font-size:14px; font-weight:400;
    border:1.5px solid var(--line); border-radius:8px;
    background:var(--paper); color:var(--ink); outline:none;
  }
  .sl-field input:focus{border-color:var(--accent); background:var(--node-bg, #fff)}
  .sl-field input[readonly]{color:var(--ink-soft)}
  .sl-field .login-step-hint{margin-top:0; font-weight:400}
  .sl-form .login-error{margin-top:0}
  .sl-submit{
    padding:11px 20px; background:var(--accent); color:var(--accent-fg, #fff);
    border:none; border-radius:8px; font-family:inherit; font-weight:600; font-size:14px; cursor:pointer;
  }
  .sl-submit:hover{background:var(--accent-deep)}
  .sl-submit:disabled{opacity:.65; cursor:wait}
  .sl-switch{text-align:center; margin-top:14px}

  /* User pill - shown in topbar after GitHub sign-in */
  .user-pill{
    display:flex; align-items:center; gap:8px;
    padding:3px 4px 3px 4px;
    background:var(--chrome); border:1px solid var(--chrome-edge);
    border-radius:22px; font-size:13px; font-weight:500;
    color:var(--ink); height:32px;
  }
  .user-pill img{width:24px; height:24px; border-radius:50%; background:var(--paper-2)}
  /* Shown instead of the avatar when the image can't load. A self-hosted Gitea
     or Forgejo serves avatars from its own origin, which img-src cannot know in
     advance, so for those users this is the normal state - not an error state. */
  .user-pill .user-initial{
    width:24px; height:24px; border-radius:50%; padding:0;
    display:flex; align-items:center; justify-content:center;
    background:var(--accent); color:var(--accent-fg, #fff); font-size:12px; font-weight:700; flex:0 0 auto;
  }
  .statusbar .user-pill .user-initial{width:20px; height:20px; font-size:11px}
  .user-pill span{padding:0 4px}
  .user-pill button{
    background:transparent; border:none; color:var(--ink-soft);
    cursor:pointer; font-family:inherit; padding:2px 8px;
    border-radius:6px; font-size:14px; height:26px;
  }
  .user-pill button:hover{background:var(--hover); color:var(--ink)}

  /* ===== Donate button + modal ===== */
  /* The two tinted buttons in the toolbar row. Both carry a plain text glyph
     on purpose: a colour emoji is painted by the emoji font, which ignores
     `color` entirely, so #themeBtn kept its own rainbow palette in every
     theme while every neighbour followed the palette. --accent-text is the
     accent hue that is guaranteed to read on --chrome (see
     test/color-contrast.test.mjs); --accent itself is not, on a pale theme. */
  .tb#themeBtn{color:var(--accent-text)}
  .tb#themeBtn:hover{background:color-mix(in srgb, var(--accent) 12%, transparent); color:var(--accent-text)}
  .tb.donate-btn{color:var(--accent-text); font-size:17px}
  .tb.donate-btn:hover{background:color-mix(in srgb, var(--accent) 12%, transparent); color:var(--accent-text)}

  .donate-modal{position:fixed; inset:0; z-index:6000; font-family:var(--sans); animation:fadeIn .15s ease}
  @keyframes fadeIn{from{opacity:0}to{opacity:1}}
  .donate-backdrop{position:absolute; inset:0; background:rgba(20,15,8,.45); backdrop-filter:blur(2px)}
  .donate-card{
    position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
    background:var(--node-bg, #fff); color:var(--ink);
    border-radius:18px; box-shadow:0 30px 80px rgba(0,0,0,.35);
    padding:30px 32px; width:440px; max-width:92vw; max-height:90vh; overflow-y:auto;
    border:1px solid var(--chrome-edge);
  }
  .donate-close{
    position:absolute; top:12px; right:14px; width:32px; height:32px;
    border:none; background:transparent; color:var(--ink-soft);
    font-size:22px; line-height:1; cursor:pointer; border-radius:8px;
  }
  .donate-close:hover{background:var(--hover); color:var(--ink)}
  .donate-head{text-align:center; margin-bottom:22px}
  .donate-icon{
    width:54px; height:54px; border-radius:50%;
    background:radial-gradient(circle at 30% 25%, #ff8a5b, #e0613a);
    color:#fff; font-size:26px; display:flex; align-items:center; justify-content:center;
    margin:0 auto 14px; box-shadow:0 6px 18px rgba(224,97,58,.4);
  }
  .donate-head h2{font-family:var(--serif); font-weight:700; font-size:22px; margin:0 0 6px}
  .donate-head p{color:var(--ink-soft); font-size:14px; line-height:1.55; margin:0}
  .donate-label{font-size:11px; text-transform:uppercase; letter-spacing:.1em; color:var(--ink-soft); font-weight:700; margin-bottom:8px}
  .donate-amounts{margin-bottom:18px}
  .donate-amount-row{display:flex; gap:8px; flex-wrap:wrap}
  .donate-amt{
    flex:1; min-width:60px; padding:11px 6px;
    background:var(--paper-2); border:1.5px solid var(--line); border-radius:9px;
    font-family:inherit; font-weight:600; font-size:14px; color:var(--ink); cursor:pointer;
    transition:.15s;
  }
  .donate-amt:hover{border-color:var(--accent); color:var(--accent-text)}
  .donate-amt.on{background:var(--accent); color:var(--accent-fg, #fff); border-color:var(--accent)}
  .donate-custom{
    flex:1.4; min-width:90px; display:flex; align-items:center; gap:4px;
    background:var(--paper-2); border:1.5px solid var(--line); border-radius:9px;
    padding:0 10px; color:var(--ink-soft);
  }
  .donate-custom input{
    flex:1; width:0; border:none; background:transparent; outline:none;
    font-family:inherit; font-weight:600; font-size:14px; padding:10px 0; color:var(--ink);
  }
  .donate-providers{display:flex; flex-direction:column; gap:6px; margin-bottom:18px}
  .donate-provider{
    display:flex; align-items:center; gap:12px; padding:13px 14px;
    background:var(--paper-2); border:1px solid var(--line); border-radius:10px;
    font-family:inherit; font-size:14px; font-weight:500; color:var(--ink);
    cursor:pointer; transition:.15s; text-align:left;
  }
  .donate-provider:hover{background:var(--node-bg, #fff); border-color:var(--p-color); transform:translateX(2px)}
  .donate-provider .dp-icon{
    width:32px; height:32px; border-radius:8px;
    background:var(--p-color); color:var(--p-ink, #000); opacity:.95;
    display:flex; align-items:center; justify-content:center; font-weight:700; font-size:15px;
    flex-shrink:0;
  }
  .donate-provider .dp-label{flex:1}
  .donate-provider .dp-arrow{color:var(--ink-soft); font-size:16px}
  .donate-empty{
    background:var(--paper-2); border-radius:10px; padding:18px;
    font-size:13px; color:var(--ink-soft); line-height:1.6;
  }
  .donate-empty .small{font-size:12px}
  .donate-foot{
    text-align:center; padding-top:14px;
    border-top:1px solid var(--chrome-edge); font-size:12.5px;
  }
  .donate-foot a{color:var(--ink-soft); text-decoration:none; margin:0 6px}
  .donate-foot a:hover{color:var(--accent-text)}

  /* ===== MOBILE / TOUCH ===== */
  @media (max-width: 720px){
    /* Single-column grid: stage takes the whole screen; sidebar overlays it */
    .app{grid-template-columns:1fr}
    body.md-ready .app{grid-template-columns:1fr}                     /* single column; md pane overlays */
    #mdPane{position:fixed;top:0;left:0;bottom:0;width:min(94vw,560px);z-index:200;box-shadow:var(--shadow-lg);transform:translateX(-100%);transition:transform .25s cubic-bezier(.4,0,.2,1)}
    body.md-mode #mdPane{width:min(94vw,560px);transform:translateX(0)}
    #mdPane .md-resize{display:none}
    .side{
      position:fixed; top:0; left:0; bottom:0; width:min(280px, 86vw);
      z-index:200; box-shadow:var(--shadow-lg);
      transform:translateX(0);
      transition:transform .25s cubic-bezier(.4,0,.2,1);
    }
    .side.collapsed{
      width:min(280px, 86vw) !important;     /* keep width so transform works */
      transform:translateX(-100%);
      border:none; box-shadow:none;
    }
    /* Off-canvas overlay shows the sidebar contents again - but only the
       ACTIVE pane, and with !important to beat the collapsed-rail rules.
       (The brand stays hidden: the overlay is dismissed by tapping the dimmed
       stage, which closes the sidebar.) */
    .side.collapsed .side-tabs{display:flex !important}
    .side.collapsed .side-pane.active{display:flex !important}
    .side.collapsed .side-foot{display:block !important}
    .side.collapsed .side-rail,
    .side-resize{display:none !important}       /* no icon rail / resize on touch */
    /* Status bar: single-line, hint text shrinks to fit */
    .statusbar{padding:4px 10px; gap:8px; grid-column:1; grid-row:4}
    .statusbar .hint{max-width:58%}
    .statusbar .save-pill{display:none}
    .statusbar .token-total{padding:4px 9px; font-size:11px}
    /* Dim the canvas while the mobile sidebar is open */
    .app:has(.side:not(.collapsed)) .stage::after{
      content:""; position:absolute; inset:0; background:rgba(20,15,8,.35);
      z-index:150; backdrop-filter:blur(1px);
    }
    .stage{width:100%}

    /* Topbar: keep buttons readable on tiny screens */
    .topbar{padding:7px 8px; gap:4px; grid-column:1; grid-row:1}
    .side{grid-column:1; grid-row:2}
    .stage{grid-column:1; grid-row:3}
    #tabStrip, #tabsBtn{display:none}               /* no tabs on touch screens */
    .title-edit{min-width:60px; max-width:130px; font-size:16px; padding:4px 8px}
    .tb{width:38px; height:38px; font-size:18px}    /* bigger tap targets */
    .tb.wide{padding:0 10px}
    .save-pill{display:none}                        /* hide on phones to save room */
    .user-pill span{display:none}                   /* avatar only */
    .search-wrap.open{width:180px}
    .search-wrap.open.replace-mode{width:min(86vw,370px)}
    .donate-btn{display:flex}

    .hint{display:none}

    /* Format toolbar wraps and scrolls on narrow screens */
    .nodebar{max-width:calc(100vw - 16px); flex-wrap:wrap}
    .nodebar button{width:34px; height:34px; min-width:34px}

    /* Overview chip tighter on phones; minimap hidden */
    .overview{gap:4px; padding:2px 4px}
    .overview .minimap{display:none}
    .zoom-row button{width:32px; height:32px}

    /* Cards fit phone width */
    .login-card{padding:24px 22px}
    .login-brand h1{font-size:22px}
    .notes-popup{width:96vw}
    .donate-card{padding:24px 22px; border-radius:14px}
    .donate-head h2{font-size:20px}

    /* Theme panel becomes a bottom sheet */
    .theme-panel-large{
      min-width:0; width:100vw; max-width:100vw;
      position:fixed !important; left:0 !important; right:0 !important;
      bottom:0; top:auto !important;
      border-radius:16px 16px 0 0; max-height:80vh; overflow-y:auto;
    }
    .theme-panel-large .tp-grid{grid-template-columns:repeat(2, 1fr)}
  }

  /* Disable browser tap-highlight on interactive UI */
  .tb, .node, .handle, .sw, .donate-provider, .donate-amt, .theme-opt, .map-item,
  .nodebar button, .picker button, .login-card button {
    -webkit-tap-highlight-color:transparent;
    touch-action:manipulation;
  }
  /* Stage shouldn't double-tap-zoom on iOS */
  #stage{touch-action:none}

  /* ===== QR sub-view inside the donate modal ===== */
  .donate-back{
    position:absolute; top:14px; left:14px;
    border:none; background:transparent; cursor:pointer;
    color:var(--ink-soft); font-family:inherit; font-size:13px; padding:4px 8px;
    border-radius:6px;
  }
  .donate-back:hover{background:var(--hover); color:var(--ink)}
  .qr-view{text-align:center; padding-top:8px}
  .qr-view h2{font-family:var(--serif); font-weight:700; font-size:20px; margin:0 0 6px; color:var(--ink)}
  .qr-sub{color:var(--ink-soft); font-size:13.5px; line-height:1.5; margin:0 0 18px}
  .qr-frame{
    width:240px; max-width:90%; aspect-ratio:1/1; margin:0 auto;
    background:#fff; border-radius:12px; padding:14px;
    box-shadow:0 4px 20px rgba(0,0,0,.12); border:1px solid var(--chrome-edge);
  }
  .qr-image{width:100%; height:100%; object-fit:contain; display:block}
  .qr-note{
    margin-top:14px; font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
    font-size:13px; color:var(--ink); background:var(--paper-2);
    padding:7px 12px; border-radius:7px; display:inline-block;
  }
  .qr-deeplink{
    display:inline-block; margin-top:14px; font-size:13.5px;
    color:var(--accent-text); text-decoration:none; font-weight:500;
  }
  .qr-deeplink:hover{text-decoration:underline}
  .qr-foot{margin:18px 0 0; font-size:13px; color:var(--ink-soft)}

  /* ===== Export popover ===== */
  .export-pop{
    background:var(--chrome); border:1px solid var(--chrome-edge); border-radius:14px;
    box-shadow:var(--shadow-lg); padding:6px; z-index:1000;
    min-width:280px; max-width:300px; font-family:var(--sans);
    display:flex; flex-direction:column; gap:1px;
    max-height:calc(100vh - 72px); overflow-y:auto;
  }
  .export-pop button{
    display:flex; align-items:center; gap:12px;
    padding:9px 10px; background:transparent; border:none;
    border-radius:9px; cursor:pointer; text-align:left;
    color:var(--ink); font-family:inherit; font-size:13.5px;
  }
  .export-pop button:hover{background:var(--hover)}
  .export-pop .ex-ic{
    width:30px; height:30px; flex-shrink:0;
    display:flex; align-items:center; justify-content:center;
    background:var(--paper-2); border-radius:7px;
    font-size:15px; font-family:ui-monospace,monospace;
  }
  .export-pop button span:not(.ex-ic){display:flex; flex-direction:column; line-height:1.3}
  .export-pop button b{font-weight:600; font-size:13px}
  .export-pop button i{font-style:normal; color:var(--ink-soft); font-size:11.5px; margin-top:1px}
  .export-pop .ex-div{height:1px; background:var(--chrome-edge); margin:4px 6px}

  /* ===== Templates popover (classic floating) ===== */
  .tpl-pop{
    background:var(--chrome); border:1px solid var(--chrome-edge); border-radius:14px;
    box-shadow:var(--shadow-lg); padding:8px; z-index:1000;
    width:340px; font-family:var(--sans);
    display:flex; flex-direction:column; gap:1px;
    overflow-y:auto;
  }
  .tpl-pop .tpl-head{
    font-size:11px; text-transform:uppercase; letter-spacing:.11em;
    color:var(--ink-soft); font-weight:700; padding:6px 10px 8px;
  }
  .tpl-pop .tpl-divider{height:1px; background:var(--chrome-edge); margin:5px 6px}
  .tpl-pop .tpl-chev{margin-left:auto; color:var(--ink-soft); font-size:18px; font-weight:400; padding-left:6px}
  .tpl-cat:hover .tpl-chev{color:var(--ink)}
  .tpl-pop .tpl-back{
    display:flex; align-items:center; gap:4px;
    background:transparent; border:none; cursor:pointer;
    color:var(--ink-soft); font-family:inherit; font-size:12.5px; font-weight:600;
    padding:6px 8px; border-radius:8px; text-align:left;
  }
  .tpl-pop .tpl-back:hover{background:var(--hover); color:var(--ink)}
  .tpl-item{
    display:flex; align-items:center; gap:11px;
    padding:9px 10px; background:transparent; border:none;
    border-radius:9px; cursor:pointer; text-align:left;
    color:var(--ink); font-family:inherit;
  }
  .tpl-item:hover{background:var(--hover)}
  .tpl-ic{
    width:30px; height:30px; flex-shrink:0;
    display:flex; align-items:center; justify-content:center;
    color:#fff; border-radius:7px; font-weight:700;
    font-size:14px; font-family:var(--serif);
  }
  .tpl-item span:not(.tpl-ic){display:flex; flex-direction:column; line-height:1.3; min-width:0}
  .tpl-item b{font-weight:600; font-size:13px; color:var(--ink); white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
  .tpl-item i{font-style:normal; color:var(--ink-soft); font-size:11.5px; margin-top:2px}
  .tpl-pop .tpl-del{
    margin-left:auto; color:var(--ink-soft); font-size:12px; padding:2px 6px;
    border-radius:5px; opacity:0;
  }
  .tpl-pop .tpl-item:hover .tpl-del{opacity:.7}
  .tpl-pop .tpl-del:hover{opacity:1; background:var(--hover); color:var(--danger)}

  /* ===== Sidebar GitHub / bug links ===== */
  .side-links{
    display:flex; flex-direction:column; gap:8px; margin-top:10px; padding-top:10px;
    border-top:1px solid var(--chrome-edge);
  }
  .side-links a{
    display:flex; align-items:center; gap:7px;
    color:var(--ink-soft); text-decoration:none;
    font-size:12px; font-weight:500; white-space:nowrap;
  }
  .side-links a:hover{color:var(--accent-text)}
  /* Uniform 16px icon gutter so emoji and the GitHub SVG line up in a column. */
  .side-links a svg,
  .side-links a .li-ic{
    flex-shrink:0; width:16px; height:16px;
    display:inline-flex; align-items:center; justify-content:center;
    opacity:.85; font-size:13px; line-height:1;
  }
  .gh-stars{margin-left:auto;font-size:11px;font-weight:600;color:var(--ink-soft);background:var(--paper-2);border:1px solid var(--line);border-radius:20px;padding:1px 7px;line-height:1.6;white-space:nowrap}
  .gh-stars[hidden]{display:none}

  /* Inline lists inside a node (created via the selection-aware UL/OL button).
     Override the centered text-align since bulleted lines look better aligned left. */
  .node-text ul, .node-text ol{
    margin:4px 0; padding-left:22px;
    text-align:left; display:inline-block; vertical-align:top;
  }
  .node-text li{margin:2px 0}
  .node-text ul{list-style-type:disc}
  .node-text ol{list-style-type:decimal}

  /* ===== Focus mode - hide all chrome, full-bleed canvas ===== */
  body.focus-mode .topbar,
  body.focus-mode .side,
  body.focus-mode .hint,
  body.focus-mode .save-pill,
  body.focus-mode .user-pill,
  body.focus-mode .empty .btn-row,
  body.focus-mode .statusbar,
  body.focus-mode .search-wrap{display:none !important}
  body.focus-mode .app{grid-template-columns:1fr}
  body.focus-mode .stage{grid-column:1;grid-row:3;width:calc(100vw / var(--ui-zoom, 1)); height:calc(100vh / var(--ui-zoom, 1)); height:calc(100dvh / var(--ui-zoom, 1))}
  .focus-exit{
    position:fixed; top:16px; right:18px; z-index:5500;
    padding:8px 14px;
    background:var(--chrome); color:var(--ink);
    border:1px solid var(--chrome-edge);
    border-radius:22px;
    font-family:var(--sans); font-size:13px; font-weight:500;
    cursor:pointer; box-shadow:var(--shadow);
    opacity:.7; transition:opacity .2s;
  }
  .focus-exit:hover{opacity:1; background:var(--hover)}

  /* ===== Token-count badge (bottom-right of node) ===== */
  .node .token-badge{
    position:absolute; bottom:-9px; left:-6px;
    padding:1px 7px; border-radius:9px;
    background:var(--toolbar-bg); color:var(--toolbar-text);
    font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
    font-size:10px; font-weight:600; line-height:1.4;
    opacity:.85; pointer-events:auto; z-index:5;
    border:1.5px solid var(--card-bg, #fff);
    white-space:nowrap;
  }
  .node .token-badge:hover{opacity:1}
  .node.sel .token-badge{opacity:1}

  /* ===== Variable / placeholder form (Export as prompt) ===== */
  .var-form{position:fixed; inset:0; z-index:6000; font-family:var(--sans); animation:fadeIn .15s ease}
  .vf-backdrop{position:absolute; inset:0; background:rgba(20,15,8,.45); backdrop-filter:blur(2px)}
  .vf-card{
    position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
    background:var(--node-bg, #fff); color:var(--ink);
    border-radius:16px; box-shadow:0 30px 80px rgba(0,0,0,.35);
    padding:28px 30px; width:520px; max-width:92vw; max-height:88vh; overflow-y:auto;
    border:1px solid var(--chrome-edge);
  }
  .vf-close{
    position:absolute; top:12px; right:14px; width:32px; height:32px;
    border:none; background:transparent; color:var(--ink-soft);
    font-size:22px; line-height:1; cursor:pointer; border-radius:8px;
  }
  .vf-close:hover{background:var(--hover); color:var(--ink)}
  .vf-card h2{font-family:var(--serif); font-weight:700; font-size:22px; margin:0 0 6px}
  .vf-sub{color:var(--ink-soft); font-size:13.5px; margin:0 0 18px; line-height:1.55}
  .vf-fields{display:flex; flex-direction:column; gap:14px}
  .vf-row{display:flex; flex-direction:column; gap:6px}
  .vf-name{font-size:12px; color:var(--ink-soft); font-weight:500}
  .vf-name code{
    background:var(--paper-2); color:var(--ink);
    padding:2px 8px; border-radius:5px;
    font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:13px;
  }
  .vf-input{
    width:100%; box-sizing:border-box;
    padding:9px 12px; font-family:var(--sans); font-size:14px;
    line-height:1.45; color:var(--ink);
    background:var(--paper); border:1.5px solid var(--line); border-radius:8px;
    resize:vertical; outline:none; min-height:38px;
  }
  .vf-input:focus{border-color:var(--accent); background:var(--node-bg, #fff)}
  /* Preferences card (showPreferences): one section per PREFS group, each a
     label / value / action row. Chips list the user's own layouts and
     templates with a delete each; the meter is storage used against the
     ~5 MB the browser allows this origin. */
  /* Each section is a <details>: the summary holds the heading and a one-line
     digest so a folded section still tells you what is inside. Which start
     open is decided by prefsSectionOpen() in app.js. */
  .pf-section{margin:10px 0 0; padding-top:8px; border-top:1px solid var(--chrome-edge)}
  .pf-section:first-child{margin-top:0; padding-top:0; border-top:none}
  .pf-section > summary{display:flex; align-items:baseline; gap:10px; cursor:pointer; list-style:none; padding:4px 0; border-radius:6px}
  .pf-section > summary::-webkit-details-marker{display:none}
  .pf-section > summary::before{content:'\25B8'; color:var(--ink-soft); font-size:11px; flex:0 0 10px}
  .pf-section[open] > summary::before{content:'\25BE'}
  .pf-section > summary:hover{background:var(--hover)}
  .pf-section > summary:focus-visible{outline:2px solid var(--accent); outline-offset:2px}
  .pf-section h3{margin:0; font-size:12px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:var(--ink-soft); flex:0 0 auto}
  .pf-digest{font-size:12px; color:var(--ink-soft); min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; flex:1}
  .pf-section[open] .pf-digest{display:none}
  .pf-rows{padding-top:4px}
  .pf-actions{flex-wrap:wrap}   /* four buttons; on a phone they take two rows rather than the card scrolling sideways */
  .pf-acts{display:inline-flex; gap:6px; flex-wrap:wrap}
  .pf-addprov{margin-top:6px}
  .pf-addprov-form{display:flex; flex-direction:column; gap:6px; margin-top:6px; padding:10px; border:1px dashed var(--line); border-radius:8px}
  .pf-addprov-form[hidden]{display:none}   /* display:flex above would otherwise beat the hidden attribute */
  .pf-in{font-family:inherit; font-size:13px; padding:6px 8px; border-radius:6px; border:1px solid var(--line); background:var(--node-bg,#fff); color:var(--ink); width:100%; box-sizing:border-box}
  .pf-row{display:flex; align-items:center; gap:10px; padding:5px 0; font-size:13px; min-height:28px}
  .pf-label{flex:0 0 150px; color:var(--ink-soft)}
  .pf-value{flex:1; min-width:0; color:var(--ink); overflow-wrap:anywhere; display:flex; flex-wrap:wrap; gap:4px; align-items:center}
  .pf-act{padding:4px 10px; border-radius:6px; border:1px solid var(--line); background:var(--node-bg,#fff); color:var(--ink);
    font-family:inherit; font-size:12px; cursor:pointer; white-space:nowrap}
  .pf-act:hover{background:var(--hover)}
  .pf-chip{display:inline-flex; align-items:center; gap:4px; padding:2px 4px 2px 8px; border-radius:999px; border:1px solid var(--line); font-size:12px}
  .pf-chip-x{border:none; background:transparent; color:var(--ink-soft); cursor:pointer; font-size:14px; line-height:1; padding:0 4px; border-radius:999px}
  .pf-chip-x:hover{background:var(--hover); color:var(--ink)}
  .pf-who{display:inline-flex; flex-wrap:wrap; align-items:baseline; gap:6px}
  .pf-role{font-size:12px; color:var(--ink-soft)}
  .pf-meter{height:6px; border-radius:999px; background:var(--hover); overflow:hidden; margin:2px 0 6px}
  .pf-meter-bar{height:100%; background:var(--accent); border-radius:999px}
  .pf-meter-bar.warn{background:#c62828}
  .pf-note{font-size:12px; color:var(--ink-soft); margin-top:4px}
  .pf-select{font-family:inherit; font-size:13px; padding:4px 8px; border-radius:6px; border:1px solid var(--line);
    background:var(--node-bg,#fff); color:var(--ink); max-width:100%}
  .pf-check{display:inline-flex; align-items:center; gap:2px; font-size:13px; cursor:pointer}
  .pf-check input{accent-color:var(--accent)}
  .pf-scale{display:inline-flex; align-items:center; gap:10px; flex-wrap:wrap}
  .pf-scale input[type=range]{width:140px; accent-color:var(--accent)}
  .pf-scale-pct{min-width:64px; font-variant-numeric:tabular-nums}
  @media (max-width:520px){ .pf-row{flex-wrap:wrap} .pf-label{flex-basis:100%} }
  .vf-actions{display:flex; gap:8px; margin-top:18px; align-items:center}
  .vf-actions button{
    padding:9px 16px; border-radius:8px; border:1px solid var(--line);
    background:var(--node-bg, #fff); cursor:pointer;
    font-family:inherit; font-size:13px; font-weight:500; color:var(--ink);
  }
  .vf-actions button:hover{background:var(--hover)}
  .vf-actions .vf-go{background:var(--accent); color:var(--accent-fg, #fff); border-color:var(--accent)}
  .vf-actions .vf-go:hover{background:var(--accent-deep)}
  .vf-skip{margin-right:auto; color:var(--ink-soft); border-color:transparent !important; background:transparent !important}
  .vf-skip:hover{color:var(--ink); background:var(--hover) !important}
  .vf-clear{margin-right:auto; color:var(--ink-soft); border-color:transparent !important; background:transparent !important}
  .vf-clear:hover{color:var(--ink); background:var(--hover) !important}

  /* ===== Keyboard help overlay (press ?) ===== */
  .kb-help{position:fixed; inset:0; z-index:6000; font-family:var(--sans); animation:fadeIn .15s ease}
  .kb-backdrop{position:absolute; inset:0; background:rgba(20,15,8,.45); backdrop-filter:blur(2px)}
  .kb-card{
    position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
    background:var(--node-bg, #fff); color:var(--ink);
    border-radius:16px; box-shadow:0 30px 80px rgba(0,0,0,.35);
    padding:28px 32px; width:640px; max-width:92vw; max-height:88vh; overflow-y:auto;
    border:1px solid var(--chrome-edge);
  }
  .kb-close{
    position:absolute; top:12px; right:14px; width:32px; height:32px;
    border:none; background:transparent; color:var(--ink-soft);
    font-size:22px; line-height:1; cursor:pointer; border-radius:8px;
  }
  .kb-close:hover{background:var(--hover); color:var(--ink)}
  .kb-card h2{font-family:var(--serif); font-weight:700; font-size:22px; margin:0 0 16px}
  .kb-grid{display:grid; grid-template-columns:1fr 1fr; gap:18px 32px}
  .kb-card h3{
    font-size:11px; text-transform:uppercase; letter-spacing:.1em;
    color:var(--ink-soft); font-weight:700; margin:0 0 6px;
  }
  .kb-card table{border-collapse:collapse; width:100%; font-size:13.5px}
  .kb-card td{padding:5px 0; vertical-align:middle}
  .kb-card td:first-child{width:120px; padding-right:12px}
  .kb-card kbd{
    display:inline-block; padding:2px 7px;
    background:var(--paper-2); border:1px solid var(--chrome-edge);
    border-radius:5px; font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
    font-size:12px; line-height:1.4; color:var(--ink);
    box-shadow:0 1px 0 var(--chrome-edge);
  }
  .kb-foot{margin-top:18px; padding-top:14px; border-top:1px solid var(--chrome-edge);
    color:var(--ink-soft); font-size:12.5px; text-align:center}
  @media (max-width:720px){
    .kb-card{padding:22px 20px}
    .kb-grid{grid-template-columns:1fr}
  }

  /* ===== Whole-map token total (topbar) ===== */
  .token-total{
    font-size:12px; color:var(--ink-soft);
    background:var(--chrome); border:1px solid var(--chrome-edge);
    border-radius:20px; padding:6px 13px;
    box-shadow:var(--shadow); font-weight:500; white-space:nowrap;
    font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  }
  @media (max-width:720px){ .token-total{display:none !important} }

  /* ===== Multi-select ===== */
  .node.multi-sel{
    outline:2.5px dashed var(--accent); outline-offset:3px;
  }
  .node.multi-sel::after{
    content:'✓'; position:absolute; top:-9px; right:-9px;
    width:18px; height:18px; border-radius:50%;
    background:var(--accent); color:var(--accent-fg, #fff);
    font-size:11px; font-weight:700; line-height:18px; text-align:center;
    box-shadow:0 1px 4px rgba(0,0,0,.3); z-index:6;
  }
  .bulk-bar{
    position:fixed; bottom:22px; left:50%; transform:translateX(-50%);
    z-index:600; display:flex; align-items:center; gap:4px;
    background:var(--toolbar-bg); color:var(--toolbar-text);
    border-radius:14px; padding:6px 8px; box-shadow:0 12px 36px rgba(0,0,0,.4);
    font-family:var(--sans); animation:fadeIn .15s ease;
  }
  .bulk-bar .bulk-count{font-size:13px; font-weight:600; padding:0 10px; white-space:nowrap}
  .bulk-bar .bulk-sep{width:1px; height:22px; background:currentColor; opacity:.2; margin:0 3px}
  .bulk-bar button{
    border:none; background:transparent; color:var(--toolbar-text);
    cursor:pointer; font-family:inherit; font-size:13px; font-weight:500;
    padding:7px 11px; border-radius:9px; white-space:nowrap;
  }
  .bulk-bar button:hover{background:color-mix(in srgb, var(--toolbar-text) 16%, transparent)}
  .bulk-bar .bulk-danger:hover{background:#b3261e; color:#fff}
  .bulk-bar .bulk-cancel{font-size:14px; opacity:.75}

  /* ===== Cross-link mode ===== */
  .node.link-source{
    outline:2.5px solid var(--accent); outline-offset:3px;
    animation:linkpulse 1.1s ease-in-out infinite;
  }
  @keyframes linkpulse{50%{outline-color:color-mix(in srgb,var(--accent) 40%,transparent)}}

  /* ===== Print - take the map to a meeting ===== */
  @media print{
    /* The app is a fixed-viewport SPA (html,body{height:100%;overflow:hidden}) so the
       canvas never page-scrolls on screen - but that same constraint silently clips ANY
       print output to a single page's worth of content, however much there actually is.
       Print has no "viewport" in that sense, so drop the constraint entirely here. */
    html, body{ height:auto !important; overflow:visible !important; }
    .topbar, .side, .overview, .hint, .save-pill, .token-total, .statusbar,
    .nodebar, .search-wrap, .user-pill, .bulk-bar, .focus-exit,
    .picker, .tpl-pop, .export-pop, .theme-panel, #outlinePane, #deckBar, #tabStrip{ display:none !important; }
    .app{ grid-template-columns:1fr !important; }
    .stage{ width:100% !important; height:auto !important; overflow:visible !important; }
    body, .app, .stage{ background:#fff !important; }
    .node{ box-shadow:none !important; -webkit-print-color-adjust:exact; print-color-adjust:exact; }
    .node .token-badge, .node .notes-mark{ display:none !important; }
    @page{ margin:1cm; }
  }

  /* ===== Task state ===== */
  .node.has-image{flex-direction:column; align-items:stretch; padding-top:8px}
  /* Image nodes size themselves to the picture. Two changes from the
     original rule, both deliberate:
       - object-fit was `cover`, which CROPS the image to fill the box; an
         attached picture should be shown in full, so `contain` + auto
         dimensions let it keep its own aspect ratio.
       - .node caps at 240px, which squeezed every picture regardless of its
         real size. Images are down-scaled to 360px wide on attach
         (readImageFile), so 400px here lets one display at natural size
         including the node's 15px horizontal padding, while still bounding
         how large a card can get. */
  .node .node-image{
    max-width:100%; max-height:280px; width:auto; height:auto;
    border-radius:8px; object-fit:contain;
    margin-bottom:6px; cursor:zoom-in; display:block;
  }
  .node.has-image{ max-width:400px; }
  /* Marker badge - sits beside the task checkbox and uses the same
     flex-shrink:0 / vertical-align treatment so the two line up when a node
     has both. font-size is nudged up because emoji render smaller than
     surrounding text at the same size. */
  .node .node-marker{
    flex-shrink:0; margin-right:6px; font-size:17px; line-height:1;
    cursor:pointer; user-select:none; vertical-align:middle;
  }
  .node .node-marker:hover{ transform:scale(1.15); }
  .picker.marker-picker{
    display:grid; grid-template-columns:repeat(4, 1fr); gap:2px; padding:6px;
  }
  .picker.marker-picker button{
    font-size:19px; line-height:1; padding:6px; border-radius:8px;
    background:none; border:1.5px solid transparent; cursor:pointer;
  }
  .picker.marker-picker button:hover{ background:var(--hover); }
  .picker.marker-picker button.on{ border-color:var(--accent); background:var(--paper-2); }
  .picker.marker-picker button.mk-none{ grid-column:1 / -1; font-size:13px; color:var(--ink-soft); }
  .picker.marker-picker button.mk-custom{ grid-column:1 / -1; font-size:13px; color:var(--ink-soft); border:1.5px dashed var(--line-2); }
  .picker.marker-picker .mk-custom-row{ grid-column:1 / -1; display:flex; align-items:center; gap:6px; }
  .picker.marker-picker .mk-custom-row input{
    flex:1; min-width:0; font-size:16px; padding:5px 8px; border-radius:8px;
    border:1.5px solid var(--line-2); background:var(--paper); color:var(--ink); font-family:inherit;
  }
  .picker.marker-picker .mk-custom-row input:focus{ outline:none; border-color:var(--accent); }
  .picker.marker-picker .mk-custom-row button.mk-apply{ font-size:13px; padding:5px 10px; }
  .picker.marker-picker .mk-custom-row button.mk-apply:disabled{ opacity:.45; cursor:default; }
  .node .task-check{
    flex-shrink:0; width:18px; height:18px; margin-right:7px;
    border:2px solid var(--line-2); border-radius:5px;
    display:inline-flex; align-items:center; justify-content:center;
    font-size:12px; font-weight:700; cursor:pointer; vertical-align:middle;
    background:var(--card-bg);
  }
  /* Task-flow step number (map layout "Task flow") */
  .node .flow-step{
    flex-shrink:0; min-width:20px; height:20px; padding:0 5px; margin-right:8px;
    border-radius:10px; display:inline-flex; align-items:center; justify-content:center;
    font-size:12px; font-weight:700; line-height:1; font-variant-numeric:tabular-nums;
    background:var(--accent); color:var(--accent-fg, #fff);
  }
  .node .task-check.task-doing{border-color:var(--warn); color:var(--warn)}
  .node .task-check.task-done{background:#4a9d5b; border-color:#4a9d5b; color:#fff}
  .node.task-done .node-text{text-decoration:line-through; opacity:.62}
  .node .task-progress{
    position:absolute; top:-9px; left:-6px;
    padding:1px 7px; border-radius:9px;
    background:var(--toolbar-bg); color:var(--toolbar-text);
    font-size:10px; font-weight:700; line-height:1.4;
    border:1.5px solid var(--card-bg); white-space:nowrap; z-index:5; opacity:.9;
    font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  }
  .node .task-progress.complete{background:#4a9d5b; color:#fff}

  /* ===== Reference / citation nodes ===== */
  .node.ref-node{
    border-left:4px solid var(--accent);
    font-style:italic;
  }
  .node .ref-mark{
    position:absolute; top:-9px; left:-9px;
    width:22px; height:22px; border-radius:50%;
    background:var(--card-bg); border:1.5px solid var(--accent);
    display:flex; align-items:center; justify-content:center;
    font-size:11px; cursor:pointer; z-index:6;
    box-shadow:0 1px 3px rgba(0,0,0,.18);
  }
  .node .ref-mark:hover{transform:scale(1.1)}

  /* ===== Global search results panel ===== */
  .global-results{
    position:fixed; z-index:1200; display:none;
    width:380px; max-width:92vw; max-height:70vh; overflow-y:auto;
    background:var(--chrome); border:1px solid var(--chrome-edge);
    border-radius:14px; box-shadow:var(--shadow-lg);
    font-family:var(--sans); padding:6px;
  }
  .global-results .gs-status{padding:14px; color:var(--ink-soft); font-size:13px; text-align:center}
  .global-results .gs-head{
    padding:8px 10px 6px; font-size:11px; text-transform:uppercase;
    letter-spacing:.08em; color:var(--ink-soft); font-weight:700;
  }
  .global-results .gs-group{margin-bottom:4px}
  .global-results .gs-map{
    padding:6px 10px 3px; font-size:12px; font-weight:700; color:var(--accent-text);
  }
  .global-results .gs-cur{color:var(--ink-soft); font-weight:400; text-transform:none}
  .global-results .gs-item{
    display:block; width:100%; text-align:left;
    padding:7px 10px; border:none; background:transparent; cursor:pointer;
    font-family:inherit; font-size:13px; color:var(--ink); border-radius:7px;
    line-height:1.4;
  }
  .global-results .gs-item:hover{background:var(--hover)}
  .global-results .gs-item mark{background:color-mix(in srgb, var(--accent) 40%, transparent); color:inherit; border-radius:2px; padding:0 1px}
  .global-results .gs-more{padding:3px 10px 8px; font-size:11.5px; color:var(--ink-soft)}
  /* 'all maps' toggle active state */
  .search-toggle.on{color:var(--accent-text); background:var(--hover)}

  /* ===== Sidebar duplicate button ===== */

  /* ===== Breadcrumb (floating pill, anchored under the topbar's left edge) =====
     Hidden by default; expands (fades in) while the mouse is over the overview
     in the status bar - the .shown class is toggled from JS. */
  .breadcrumb{
    position:fixed; top:56px; left:14px; transform:none;
    display:flex; align-items:center; gap:1px; z-index:90;
    background:var(--chrome); border:1px solid var(--chrome-edge);
    border-radius:10px; box-shadow:var(--shadow); padding:3px 6px;
    max-width:min(70vw, 720px); overflow:hidden; font-family:var(--sans);
    opacity:0; pointer-events:none; transform:translateY(-4px);
    transition:opacity .16s ease, transform .16s ease;
  }
  .breadcrumb.shown{opacity:1; pointer-events:auto; transform:translateY(0)}
  .breadcrumb .bc-crumb{
    border:none; background:transparent; cursor:pointer;
    font-family:inherit; font-size:12px; color:var(--ink-soft);
    padding:3px 7px; border-radius:6px; white-space:nowrap;
    max-width:170px; overflow:hidden; text-overflow:ellipsis;
  }
  /* Hover paints both halves of a crumb: the surface is the paper-pole tint,
     so the hover rule has to name the text as well. The resting .current
     colours the text with the fill's light colour, and a hover rule that only
     repaints the background leaves that light text on the light surface, which
     is how the crumb washed out. Every look's breadcrumb block follows this;
     test/breadcrumb-contrast.test.mjs walks this cascade for each look in
     each colour theme and fails on a surface rule that does not also win
     color. */
  .breadcrumb .bc-crumb.current{color:var(--ink); font-weight:600}
  .breadcrumb .bc-crumb:hover{background:var(--hover); color:var(--ink)}
  .breadcrumb .bc-sep{color:var(--ink-soft); font-size:12px; opacity:.6; flex:none}

  /* ===== Minimap (inside the overview card, bottom-right) ===== */
  .minimap{
    position:relative; z-index:29;
    width:104px; height:50px;
    background:color-mix(in srgb, var(--chrome) 92%, transparent);
    border:1px solid var(--chrome-edge); border-radius:10px;
    box-shadow:var(--shadow); overflow:hidden; cursor:crosshair;
  }
  .minimap svg{display:block;width:100%;height:100%}
  .minimap #mmView{ stroke:var(--accent); stroke-width:1.5; fill:color-mix(in srgb, var(--accent) 12%, transparent); }
  .minimap .mm-sel{ stroke:var(--accent); stroke-width:1.5; }
  @media (max-width:760px){ .minimap{display:none} .breadcrumb{max-width:88vw} }

  /* ===== Read-only shared view ===== */
  .shared-banner{
    position:fixed; top:0; left:0; right:0; z-index:300;
    display:flex; align-items:center; gap:12px;
    padding:9px 16px; font-family:var(--sans); font-size:13px;
    background:var(--accent); color:var(--accent-fg, #fff);
    box-shadow:0 2px 12px rgba(0,0,0,.18);
  }
  .shared-banner .sb-eye{font-size:15px}
  .shared-banner .sb-text{opacity:.96}
  .shared-banner .sb-text b{font-weight:700}
  .shared-banner .sb-copy{
    margin-left:auto; border:1px solid var(--accent-fg, #fff); background:transparent;
    color:var(--accent-fg, #fff); font-family:inherit; font-size:12.5px; font-weight:600;
    padding:5px 12px; border-radius:8px; cursor:pointer;
  }
  .shared-banner .sb-copy:hover{background:var(--accent-fg, #fff); color:var(--accent)}
  .shared-banner .sb-brand{
    color:var(--accent-fg, #fff); opacity:.85; text-decoration:none;
    font-family:var(--brand-font);font-weight:700;letter-spacing:-.035em;white-space:nowrap;
  }
  .shared-banner .sb-brand:hover{opacity:1; text-decoration:underline}

  /* In shared view: hide all editing chrome, keep viewing aids (zoom, minimap) */
  body.shared-view .side,
  body.shared-view #newMap,
  body.shared-view #menuExport,
  body.shared-view #searchBtn,
  body.shared-view #varsBtn,
  body.shared-view #focusBtn,
  body.shared-view #collapseAll,
  body.shared-view #mdToggle,
  body.shared-view #undo,
  body.shared-view #redo,
  body.shared-view #layout,
  body.shared-view #donateBtn,
  body.shared-view .hint,
  body.shared-view .save-pill,
  body.shared-view .menu-toggle,
  body.shared-view #addChild,
  body.shared-view #prefsBtn,
  body.shared-view #tabsBtn,
  body.shared-view #tabRow,
  body.shared-view #toggleSide{ display:none !important; }
  /* Sidebar is hidden in shared view - collapse the grid to one column so the
     stage fills the full width (otherwise it auto-places into the 0-width track
     and the canvas clips to nothing). */
  body.shared-view .app{ grid-template-columns:1fr; padding-top:var(--shared-banner-h, 42px); }
  body.shared-view .topbar{ grid-column:1; grid-row:1; }
  body.shared-view .stage{ grid-column:1; grid-row:3; }
  body.shared-view .statusbar{ grid-column:1; grid-row:4; }
  body.shared-view .breadcrumb{ top:97px; left:14px; }
  body.shared-view #mapTitle{ pointer-events:none; max-width:none; }
  body.shared-view .node{ cursor:default; }
  /* Read-only: no node editing affordances (collapse toggle + notes stay). */
  body.shared-view .handle.h-child,
  body.shared-view .handle.h-sibling,
  body.shared-view .resize-grip{ display:none !important; }
  @media (max-width:760px){
    .shared-banner{ font-size:12px; gap:8px; padding:8px 10px; }
    .shared-banner .sb-text{ flex:1; min-width:0; }
  }

  /* ===== Display-size (UI scale) control in theme panel ===== */
  .theme-panel .tp-hint{ font-weight:400; text-transform:none; letter-spacing:0; color:var(--ink-soft); font-size:10.5px; margin-left:6px; }
  .theme-panel .tp-scale{ display:flex; gap:6px; }
  .theme-panel .scale-opt{
    flex:1; padding:5px 0; border:1px solid var(--chrome-edge); border-radius:8px;
    background:var(--node-bg); color:var(--ink); cursor:pointer;
    font-family:var(--sans); font-size:12.5px; font-weight:600;
  }
  .theme-panel .scale-opt:hover{ background:var(--hover); }
  .theme-panel .scale-opt.active{ background:var(--accent); color:var(--accent-fg, #fff); border-color:var(--accent); }

  /* ===== Version history panel ===== */
  .hist-panel{ position:fixed; top:64px; right:18px; width:320px; max-height:70vh; z-index:60;
    background:var(--node-bg); border:1px solid var(--chrome-edge); border-radius:14px;
    box-shadow:0 18px 50px rgba(0,0,0,.28); display:flex; flex-direction:column; overflow:hidden; font-family:var(--sans); }
  .hist-head{ display:flex; align-items:center; justify-content:space-between; padding:12px 14px; border-bottom:1px solid var(--chrome-edge); }
  .hist-x{ border:none; background:none; font-size:20px; cursor:pointer; color:var(--ink-soft); line-height:1; }
  .hist-list{ overflow-y:auto; padding:6px; }
  .hist-status{ padding:18px 14px; color:var(--ink-soft); font-size:13px; }
  .hist-sub{ color:var(--ink-soft); font-size:12px; }
  .hist-row{ display:flex; align-items:center; justify-content:space-between; gap:8px; padding:9px 10px; border-radius:9px; }
  .hist-row:hover{ background:var(--hover); }
  .hist-row.active{ background:var(--hover); outline:2px solid var(--accent); }
  .hist-when b{ display:block; font-size:13px; }
  .hist-when i{ display:block; font-size:11px; color:var(--ink-soft); font-style:normal; }
  .hist-actions{ display:flex; gap:6px; }
  .hist-actions button{ border:1px solid var(--chrome-edge); background:var(--node-bg); color:var(--ink);
    border-radius:7px; padding:5px 9px; font-size:12px; cursor:pointer; }
  .hist-actions button:hover{ background:var(--accent); color:var(--accent-fg, #fff); border-color:var(--accent); }
  .hist-actions button.disabled{ opacity:.4; pointer-events:none; }
  .hist-banner{ position:fixed; top:14px; left:50%; transform:translateX(-50%); z-index:61;
    background:#2d2d2d; color:#fff; border-radius:999px; padding:8px 10px 8px 16px; display:flex; align-items:center; gap:10px;
    font-family:var(--sans); font-size:13px; box-shadow:0 10px 30px rgba(0,0,0,.3); }
  .hist-banner button{ border:none; border-radius:999px; padding:6px 12px; font-size:12px; cursor:pointer; font-weight:600; }
  .hb-restore{ background:var(--accent); color:var(--accent-fg, #fff); }
  .hb-cancel{ background:rgba(255,255,255,.15); color:#fff; }

  /* ===== Build-prompt panel ===== */
  .bp-panel{ position:fixed; top:64px; right:18px; width:420px; max-width:calc(100vw - 36px); max-height:80vh; z-index:60;
    resize:both; min-width:300px; min-height:220px;
    background:var(--node-bg); border:1px solid var(--chrome-edge); border-radius:14px;
    box-shadow:0 18px 50px rgba(0,0,0,.28); display:flex; flex-direction:column; overflow:hidden; font-family:var(--sans); }
  .bp-head{ display:flex; align-items:center; justify-content:space-between; padding:12px 14px; border-bottom:1px solid var(--chrome-edge);
    cursor:move; user-select:none; }
  body.bp-dragging{ user-select:none; }
  body.bp-dragging .bp-panel{ box-shadow:0 24px 60px rgba(0,0,0,.34); }
  .bp-head b{ font-size:14px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .bp-x{ border:none; background:none; font-size:20px; cursor:pointer; color:var(--ink-soft); }
  /* flex-shrink:0 - opening "Run with API" must not resize this box. If it
     shrank, its content would overflow, a scrollbar would appear inside it and
     the text would re-wrap narrower. The run section is the one that scrolls. */
  .bp-text{ margin:12px 14px 0; min-height:160px; max-height:38vh; resize:vertical; flex-shrink:0; border:1px solid var(--chrome-edge);
    border-radius:9px; padding:10px; font-family:var(--mono,ui-monospace,monospace); font-size:12.5px; line-height:1.5;
    background:var(--paper); color:var(--ink); }
  .bp-meta{ padding:6px 14px; color:var(--ink-soft); font-size:11.5px; }
  .bp-row{ display:flex; gap:8px; padding:0 14px 12px; }
  .bp-row button, .bp-run button{ border:1px solid var(--chrome-edge); background:var(--node-bg); color:var(--ink);
    border-radius:8px; padding:8px 12px; font-size:13px; cursor:pointer; font-family:var(--sans); }
  .bp-row button.primary, .bp-run button.primary{ background:var(--accent); color:var(--accent-fg, #fff); border-color:var(--accent); }
  .bp-run{ border-top:1px solid var(--chrome-edge); padding:12px 14px; overflow-y:auto; min-height:0; }
  .bp-run-row{ display:flex; gap:8px; margin-bottom:8px; }
  .bp-run select, .bp-run input{ flex:1; border:1px solid var(--chrome-edge); border-radius:8px; padding:7px 9px;
    background:var(--paper); color:var(--ink); font-family:var(--sans); font-size:12.5px; min-width:0; }
  .bp-warn{ color:var(--ink-soft); font-size:11px; margin:4px 0 10px; line-height:1.4; }
  .bp-result{ margin-top:10px; font-size:12.5px; }
  .bp-answer{ white-space:pre-wrap; background:var(--paper); border:1px solid var(--chrome-edge); border-radius:9px;
    padding:10px; max-height:30vh; overflow-y:auto; line-height:1.5; }
  .bp-answer-acts{ display:flex; gap:8px; margin-top:8px; }
  .vf-doi-lookup{ display:flex; gap:8px; margin:0 0 12px; }
  .vf-doi-in{ flex:1; border:1px solid var(--chrome-edge); border-radius:8px; padding:8px 10px; background:var(--paper); color:var(--ink); font-family:var(--sans); font-size:13px; }
  .vf-doi-go{ border:1px solid var(--accent); background:var(--accent); color:var(--accent-fg, #fff); border-radius:8px; padding:8px 14px; cursor:pointer; font-weight:600; }
  .vf-doi-go:disabled{ opacity:.6; cursor:default; }

  /* ===== Presentation mode ===== */
  body.presenting .node{ opacity:.28; transition:opacity .25s; }
  body.presenting .node.pres-current{ opacity:1; outline:3px solid var(--accent); outline-offset:3px; box-shadow:0 10px 40px rgba(0,0,0,.3); }
  body.presenting .handle, body.presenting .resize-grip, body.presenting #nodebar{ display:none !important; }
  .pres-bar{ position:fixed; bottom:24px; left:50%; transform:translateX(-50%); z-index:62;
    background:#23201b; color:#fff; border-radius:999px; padding:8px 12px; display:flex; align-items:center; gap:12px;
    font-family:var(--sans); box-shadow:0 12px 40px rgba(0,0,0,.4); max-width:90vw; }
  .pres-bar button{ border:none; background:rgba(255,255,255,.12); color:#fff; width:34px; height:34px; border-radius:50%;
    font-size:14px; cursor:pointer; flex:none; }
  .pres-bar button:hover:not(:disabled){ background:var(--accent); }
  .pres-bar button:disabled{ opacity:.35; cursor:default; }
  .pres-exit{ background:rgba(255,80,60,.25) !important; }
  .pres-count{ font-size:12px; opacity:.8; flex:none; }
  .pres-title{ font-size:14px; font-weight:600; max-width:46vw; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

  /* Version diff panel */
  .diff-panel{ position:fixed; right:18px; top:64px; width:360px; max-height:70vh; overflow:auto;
    background:var(--paper); border:1px solid var(--line); border-radius:14px; box-shadow:var(--shadow-lg);
    z-index:120; padding:6px 0; font-size:13px; }
  .diff-head{ display:flex; align-items:center; justify-content:space-between; padding:8px 14px; border-bottom:1px solid var(--line); position:sticky; top:0; background:var(--paper); }
  .diff-head b{ color:var(--ink); }
  .diff-x{ border:0; background:transparent; font-size:18px; cursor:pointer; color:var(--ink-soft); line-height:1; }
  .diff-sec{ padding:6px 0; }
  .diff-h{ font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.04em; padding:4px 14px; }
  .diff-h.add{ color:var(--ok); } .diff-h.del{ color:var(--danger); } .diff-h.chg{ color:var(--warn); }
  .diff-row{ padding:4px 14px; border-left:3px solid transparent; color:var(--ink); word-break:break-word; }
  .diff-row.add{ border-left-color:var(--ok); background:color-mix(in srgb, var(--ok) 6%, transparent); }
  .diff-row.del{ border-left-color:var(--danger); background:color-mix(in srgb, var(--danger) 6%, transparent); text-decoration:line-through; color:var(--ink-soft); }
  .diff-row.chg{ border-left-color:var(--warn); background:color-mix(in srgb, var(--warn) 6%, transparent); }
  .diff-row .d-from{ color:var(--ink-soft); text-decoration:line-through; }
  .diff-row .d-arrow{ margin:0 6px; color:var(--ink-soft); }
  .diff-empty{ padding:16px 14px; color:var(--ink-soft); }

  /* OAuth ("Sign in with GitHub") button on the login overlay */
  .login-oauth{ margin:2px 0 4px; }
  .oauth-btn{ display:flex; align-items:center; justify-content:center; gap:9px; width:100%; padding:11px 14px;
    border-radius:11px; border:1px solid var(--chrome-edge); background:var(--ink); color:var(--paper);
    font-size:14px; font-weight:600; cursor:pointer; transition:opacity .15s; }
  .oauth-btn:hover{ opacity:.9; }
  .oauth-btn svg{ width:18px; height:18px; fill:currentColor; }
  .oauth-or{ display:flex; align-items:center; gap:10px; margin:15px 2px 2px; color:var(--ink-soft); font-size:12px; }
  .oauth-or::before, .oauth-or::after{ content:''; flex:1; height:1px; background:var(--line); }

/* Pinned maps: small badge before the title in the sidebar list. */
.map-item.pinned .nm::before{ content:'\1F4CC'; margin-right:6px; font-size:.85em; opacity:.9; }

/* Live collaboration: remote cursors + presence pill. */
#collabCursors{ position:fixed; inset:0; pointer-events:none; z-index:60; overflow:hidden; }
.collab-cursor{ position:absolute; top:0; left:0; will-change:transform; transition:transform .08s linear; }
.collab-cursor svg{ display:block; filter:drop-shadow(0 1px 1px rgba(0,0,0,.3)); }
.collab-cursor b{ position:absolute; left:15px; top:13px; font-size:11px; font-weight:600; color:#fff; padding:1px 6px; border-radius:8px; white-space:nowrap; }
#collabPill{ position:fixed; bottom:18px; left:18px; z-index:70; display:flex; align-items:center; gap:8px;
  background:var(--chrome); border:1px solid var(--chrome-edge); border-radius:999px; padding:5px 8px 5px 12px;
  box-shadow:var(--shadow); font-size:12px; font-weight:500; }
#collabPill .cp-dots{ display:flex; }
#collabPill .cp-dot{ width:13px; height:13px; border-radius:50%; border:2px solid var(--chrome); margin-left:-5px; }
#collabPill .cp-dot:first-child{ margin-left:0; }
#collabPill .cp-txt{ color:var(--ink-soft); }
#collabPill button{ border:none; background:transparent; cursor:pointer; font-size:12px; opacity:.7; padding:2px 5px; border-radius:6px; color:var(--ink-soft); }
#collabPill button:hover{ opacity:1; background:var(--hover, rgba(127,127,127,.18)); }
#collabPill .cp-save{ opacity:1; font-weight:600; color:var(--accent-text); padding:3px 9px; border:1px solid var(--chrome-edge); border-radius:999px; }
#collabPill .cp-save:hover{ background:var(--accent); color:var(--accent-fg, #fff); border-color:var(--accent); }

/* Export menu: small uppercase section labels grouping the items. */
.ex-grp{ font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.6px;
  color:var(--ink-soft); opacity:.65; padding:10px 14px 3px; user-select:none; }
.export-pop > .ex-grp:first-child{ padding-top:6px; }

/* Shared-with-me section in the sidebar. */
.map-group-label{ font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.6px; color:var(--ink-soft); opacity:.6; padding:12px 12px 4px; user-select:none; }
.map-item .shared-badge{ font-size:11px; opacity:.7; flex:0 0 auto; margin-right:2px; }

/* Cloud-edit keeps the editing toolbar visible, so push it clear of the shared banner. */
/* Cloud-edit shows the sidebar AND toolbar, so shift the whole app below the fixed
   shared banner (measured into --shared-banner-h so it adapts when the banner wraps). */
body.cloud-edit .app{ padding-top:var(--shared-banner-h, 44px); }

/* ---- Manage-access modal (identity ACLs) ---- */
.access-modal{ position:fixed; inset:0; background:rgba(0,0,0,.45); display:grid; place-items:center; z-index:600; }
.access-modal .am-card{ width:min(440px,92vw); max-height:86vh; overflow:auto; background:var(--paper); color:var(--ink);
  border:1px solid var(--line); border-radius:14px; box-shadow:var(--shadow-lg); padding:16px 18px; }
.access-modal .am-head{ display:flex; align-items:center; justify-content:space-between; margin-bottom:6px; font-size:16px; font-weight:700; }
.access-modal .am-x{ background:none; border:none; color:var(--ink-soft); font-size:22px; line-height:1; cursor:pointer; }
.access-modal .am-x:hover{ color:var(--ink); }
.access-modal .am-sec{ margin-top:14px; }
.access-modal .am-lbl{ font-size:12px; text-transform:uppercase; letter-spacing:.04em; color:var(--ink-soft); margin-bottom:8px; }
.access-modal .am-link label{ display:block; padding:5px 0; font-size:14px; cursor:pointer; }
.access-modal .am-link input{ margin-right:8px; accent-color:var(--accent); }
.access-modal .am-auth{ display:flex; align-items:center; gap:8px; margin-top:10px; font-size:13.5px; cursor:pointer; }
.access-modal .am-auth input{ margin:0; accent-color:var(--accent); }
.access-modal .am-auth input:disabled{ opacity:.4; }
.access-modal .am-list{ display:flex; flex-direction:column; gap:6px; margin-bottom:10px; }
.access-modal .am-row{ display:flex; align-items:center; gap:10px; padding:7px 10px; background:var(--paper-2); border:1px solid var(--line); border-radius:8px; }
.access-modal .am-who{ flex:1; font-size:14px; }
.access-modal .am-role{ font-size:12px; color:var(--ink-soft); }
.access-modal .am-rm{ background:none; border:1px solid var(--line-2); color:var(--ink-soft); border-radius:6px; padding:3px 9px; font-size:12px; cursor:pointer; }
.access-modal .am-rm:hover{ border-color:var(--accent); color:var(--accent-text); }
.access-modal .am-empty{ font-size:13px; color:var(--ink-soft); padding:6px 2px; }
.access-modal .am-add{ display:flex; gap:8px; }
.access-modal .am-user{ flex:1; background:var(--paper-2); border:1px solid var(--line); color:var(--ink); border-radius:8px; padding:8px 10px; font-size:14px; }
.access-modal .am-user::placeholder{ color:var(--ink-soft); }
.access-modal .am-user:focus{ outline:none; border-color:var(--accent); }
.access-modal .am-newrole{ background:var(--paper-2); border:1px solid var(--line); color:var(--ink); border-radius:8px; padding:8px; }
  .access-modal .am-addbtn{ background:var(--accent); border:none; color:var(--accent-fg, #fff); border-radius:8px; padding:8px 14px; font-weight:600; cursor:pointer; }
.access-modal .am-addbtn:hover{ background:var(--accent-deep); }
.access-modal .am-vis{ display:flex; flex-direction:column; gap:5px; }
.access-modal .am-visrow{ display:flex; align-items:center; justify-content:space-between; padding:5px 10px; background:var(--paper-2); border:1px solid var(--line); border-radius:8px; font-size:13px; }
.access-modal .am-vtime{ color:var(--ink-soft); font-size:12px; }
.access-modal .am-foot{ margin-top:14px; font-size:12px; color:var(--ink-soft); }

/* Contextual note on the login overlay when access requires sign-in (e.g. a shared map) */
.login-share-note{ margin:0 0 14px; padding:10px 13px; border-radius:10px; font-size:13.5px; line-height:1.4;
  background:color-mix(in srgb, var(--accent) 14%, transparent); border:1px solid color-mix(in srgb, var(--accent) 40%, transparent); color:var(--ink); }

/* Shared-map indicator: a compact themed pill (replaces the full-width banner). */
.user-pill.shared-pill{ background:var(--accent); color:var(--accent-fg, #fff); border-color:transparent; font-weight:600; padding:3px 12px; }
.user-pill.shared-pill img, .user-pill.shared-pill button{ display:none !important; }
.user-pill.shared-pill span{ display:inline-block !important; padding:0; }
/* Pill-mode has no banner, so reclaim the space the banner used to reserve. */
body.no-banner.cloud-edit .app{ padding-top:0; }
body.no-banner.shared-view .stage{ top:0; }

/* Themed scrollbars - desktop only (mobile keeps native overlay scrollbars) */
@media (min-width:721px){
  *::-webkit-scrollbar{width:12px;height:12px}
  *::-webkit-scrollbar-track{background:transparent}
  *::-webkit-scrollbar-thumb{background:var(--line-2,var(--line));border-radius:10px;border:3px solid transparent;background-clip:padding-box}
  *::-webkit-scrollbar-thumb:hover{background:var(--ink-soft)}
  *::-webkit-scrollbar-corner{background:transparent}
  html{scrollbar-color:var(--line-2,var(--line)) transparent;scrollbar-width:thin}
}

/* ===== PDF export (markdown preview -> browser print -> Save as PDF) =====
   #mdPrintRoot is populated on demand by mdDownloadPdf() and is otherwise empty/hidden.
   Forces light, ink-friendly colors regardless of the app's active theme. */
#mdPrintRoot{display:none}
@media print{
  body.md-printing > *:not(#mdPrintRoot){display:none !important}
  body.md-printing #mdPrintRoot{
    display:block !important;position:static !important;padding:0 32px;
    color:#1a1a1a;background:#fff;
    font:14px/1.6 -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;
  }
  /* Browsers drop background colors/images when printing unless told otherwise - this is
     what was silently stripping the code-block shading (and would strip any other
     background color here too, e.g. a table header tint) even though it renders fine
     on screen in the live preview. Applies to every descendant, not just #mdPrintRoot
     itself, since print-color-adjust doesn't reliably cascade in all engines. */
  #mdPrintRoot, #mdPrintRoot *{ -webkit-print-color-adjust:exact; print-color-adjust:exact; }
  #mdPrintRoot h1{font-size:1.9em;margin:0 0 .4em;line-height:1.25;color:#111;border-bottom:1px solid #ccc;padding-bottom:.2em}
  #mdPrintRoot h1,#mdPrintRoot h2,#mdPrintRoot h3{page-break-after:avoid}
  #mdPrintRoot h2,#mdPrintRoot h3{margin:.9em 0 .3em;line-height:1.25;color:#111}
  #mdPrintRoot h2{font-size:1.35em;border-bottom:1px solid #ccc;padding-bottom:.2em}
  #mdPrintRoot h3{font-size:1.1em}
  #mdPrintRoot p,#mdPrintRoot ul,#mdPrintRoot ol,#mdPrintRoot blockquote{margin:.5em 0}
  #mdPrintRoot pre.mp-code{background:#f2f2f2;padding:10px 12px;border-radius:6px;overflow:auto;font-size:12px;page-break-inside:avoid}
  #mdPrintRoot code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
  #mdPrintRoot table{border-collapse:collapse;font-size:.95em;page-break-inside:avoid;margin:.5em 0}
  #mdPrintRoot th,#mdPrintRoot td{border:1px solid #ccc;padding:5px 9px}
  #mdPrintRoot th{background:#eef0f3;font-weight:700;text-align:left}
  #mdPrintRoot blockquote{border-left:3px solid #ccc;margin-left:0;padding:.2em 0 .2em 12px;color:#555}
  #mdPrintRoot img{max-width:100%}
  #mdPrintRoot hr{border:0;border-top:1px solid #ccc;margin:1em 0}
  #mdPrintRoot a{color:#2a5db0;text-decoration:underline}
  #mdPrintRoot math{font-size:1.04em;color:#1a1a1a}
  #mdPrintRoot math[display="block"]{display:block;margin:.6em auto;text-align:center;page-break-inside:avoid}
}

/* ===== Floating-shell interface layouts (Classic / Side-toolbar) =====
   body.ui-classic and body.ui-rail both drop the status bar and float the
   chrome over the canvas: minimap card + vertical zoombar bottom-right, hint
   bottom-left, breadcrumb top-centre, collapsed sidebar vanishes (no rail).
   Classic keeps the upstream toolbar as one floating strip inside .stage;
   Side-toolbar docks the same toolbar as a vertical icon rail left of the
   canvas with the title, save pill and minimap floating over it. The same
   DOM elements are reparented by applyUiLayout() - nothing is hidden or
   cloned, only restyled and repositioned. */
body.ui-classic .app, body.ui-rail .app{grid-template-rows:minmax(0,1fr)}
body.ui-classic .side, body.ui-rail .side, body.ui-zen .side{grid-column:1;grid-row:1;width:268px}
body.ui-classic .side.collapsed, body.ui-rail .side.collapsed, body.ui-zen .side.collapsed{width:0 !important}
body.ui-classic .side-rail, body.ui-rail .side-rail, body.ui-zen .side-rail,
body.ui-classic .side-resize, body.ui-rail .side-resize, body.ui-zen .side-resize{display:none}
body.ui-classic .statusbar, body.ui-rail .statusbar, body.ui-zen .statusbar{display:none}
/* Floating bottom-right: the minimap as its own card (right:72) and the
   vertical zoom bar beside it (right:18) - the upstream zoombar. The zoom
   row is reparented out of the overview card by applyUiLayout(); the
   overview here is just the minimap chip. */
body.ui-classic .overview, body.ui-rail .overview, body.ui-zen .overview{
  position:absolute;bottom:18px;right:72px;z-index:29;
  flex-direction:column;align-items:stretch;gap:0;padding:0;
  background:none;border:none;box-shadow:none;
}
body.ui-classic .overview .minimap, body.ui-rail .overview .minimap, body.ui-zen .overview .minimap{width:168px;height:120px}
body.ui-classic .zoom-row, body.ui-rail .zoom-row, body.ui-zen .zoom-row{
  position:absolute;bottom:18px;right:18px;z-index:30;
  flex-direction:column;gap:2px;padding:4px;
  background:var(--chrome);border:1px solid var(--chrome-edge);border-radius:12px;
  box-shadow:var(--shadow);
}
body.ui-classic .zoom-row button, body.ui-rail .zoom-row button, body.ui-zen .zoom-row button{width:36px;height:34px;border-radius:8px;font-size:17px}
body.ui-classic #zoomSlider, body.ui-rail #zoomSlider, body.ui-zen #zoomSlider{display:none}   /* no slider here; the % label handles custom zoom */
body.ui-classic .zoom-row #zoomIn, body.ui-rail .zoom-row #zoomIn, body.ui-zen .zoom-row #zoomIn{order:1}  /* upstream order: ＋ 100% － ⊡ */
body.ui-classic .zoom-row #zoomVal, body.ui-rail .zoom-row #zoomVal, body.ui-zen .zoom-row #zoomVal{order:2}
body.ui-classic .zoom-row #zoomOut, body.ui-rail .zoom-row #zoomOut, body.ui-zen .zoom-row #zoomOut{order:3}
body.ui-classic .zoom-row #zoomFit, body.ui-rail .zoom-row #zoomFit, body.ui-zen .zoom-row #zoomFit{order:4}
body.ui-classic .overview-toggle, body.ui-rail .overview-toggle, body.ui-zen .overview-toggle{display:none}
/* Floating hint pill, bottom-left (upstream position). */
body.ui-classic .hint, body.ui-rail .hint, body.ui-zen .hint{
  position:absolute;bottom:18px;left:18px;z-index:20;
  background:var(--chrome);border:1px solid var(--chrome-edge);border-radius:11px;
  padding:9px 13px;box-shadow:var(--shadow);font-size:12px;color:var(--ink-soft);
  max-width:330px;line-height:1.55;
}
body.ui-classic .hint .hint-text, body.ui-rail .hint .hint-text, body.ui-zen .hint .hint-text{white-space:normal;overflow:visible}
/* Breadcrumb: top-centre. */
body.ui-classic .breadcrumb, body.ui-rail .breadcrumb, body.ui-zen .breadcrumb{top:60px;left:50%;transform:translateX(-50%) translateY(-4px)}
body.ui-classic .breadcrumb.shown, body.ui-rail .breadcrumb.shown, body.ui-zen .breadcrumb.shown{transform:translateX(-50%) translateY(0)}
body.ui-classic.focus-mode .overview, body.ui-rail.focus-mode .overview, body.ui-zen.focus-mode .overview{display:none !important}
body.ui-classic.shared-view .overview, body.ui-rail.shared-view .overview, body.ui-zen.shared-view .overview{display:none !important}

/* ---- Classic: the upstream floating toolbar ---- */
body.ui-classic .app{grid-template-columns:auto 1fr}
body.ui-classic .stage{grid-column:2;grid-row:1}
body.ui-classic .topbar{
  position:absolute;top:14px;left:14px;right:14px;z-index:30;pointer-events:none;
  background:none;border-bottom:none;padding:0;flex-wrap:nowrap;gap:8px;
}
body.ui-classic .topbar > *{pointer-events:auto}
body.ui-classic .tb-group{background:var(--chrome);border:1px solid var(--chrome-edge);border-radius:12px;padding:4px;box-shadow:var(--shadow)}
body.ui-classic .tb-sep{display:none}
body.ui-classic .search-wrap{background:var(--chrome);border:1px solid var(--chrome-edge);border-radius:12px;box-shadow:var(--shadow)}
body.ui-classic .search-wrap.open{background:var(--paper-2)}
/* Sidebar shell (as upstream): plain brand, new-map row with ▾ caret, no
   tabs - the caret toggles the Templates pane in and out. */
body.ui-classic .side-tabs{display:none}
body.ui-classic .new-map-row{display:flex;gap:0;margin:14px 14px 10px}
body.ui-classic .new-map-row #newMap{
  flex:1;height:auto;padding:11px;margin:0;
  border:1.5px dashed var(--line-2);background:none;color:var(--ink-soft);
  border-top-left-radius:11px;border-bottom-left-radius:11px;
  border-top-right-radius:0;border-bottom-right-radius:0;
  font-weight:600;font-size:14px;font-family:inherit;
}
body.ui-classic .new-map-row #newMap:hover{border-color:var(--accent);color:var(--accent-text);background:var(--node-bg)}
body.ui-classic .new-map-row .new-map-caret{
  width:36px;padding:0;background:var(--accent);color:var(--accent-fg, #fff);border:none;
  border-left:1px solid color-mix(in srgb, #000 18%, var(--accent));
  border-top-right-radius:11px;border-bottom-right-radius:11px;
  cursor:pointer;font-size:13px;font-family:inherit;
}
body.ui-classic .new-map-row .new-map-caret:hover{background:var(--accent-deep)}
body.ui-classic.md-ready .app{grid-template-columns:auto auto minmax(0,1fr)}
body.ui-classic #mdPane{grid-column:2;grid-row:1}
body.ui-classic.md-ready .stage{grid-column:3;grid-row:1}
body.ui-classic.focus-mode .app{grid-template-columns:1fr}
body.ui-classic.focus-mode .stage{grid-column:1;grid-row:1;width:calc(100vw / var(--ui-zoom, 1));height:calc(100dvh / var(--ui-zoom, 1))}
body.ui-classic.shared-view .app{grid-template-columns:1fr;padding-top:var(--shared-banner-h, 42px)}
body.ui-classic.shared-view .stage{grid-column:1;grid-row:1}
body.ui-classic.shared-view .breadcrumb{top:97px;left:50%;transform:translateX(-50%) translateY(-4px)}
body.ui-classic.shared-view .breadcrumb.shown{transform:translateX(-50%) translateY(0)}

/* ---- Side-toolbar: the toolbar is a vertical icon rail left of the canvas.
   Title, save pill, minimap card, zoombar and hint float over the canvas. ---- */
body.ui-rail .app{grid-template-columns:auto 46px minmax(0,1fr)}
body.ui-rail .side{grid-column:1;grid-row:1}
body.ui-rail .topbar{
  grid-column:2;grid-row:1;position:relative;z-index:20;   /* a solid column above the canvas, whatever overlaps it */
  display:flex;flex-direction:column;align-items:center;gap:5px;
  padding:10px 5px;background:var(--chrome);border-right:1px solid var(--chrome-edge);
  border-bottom:none;overflow:visible;scrollbar-width:none;
}
body.ui-rail .topbar::-webkit-scrollbar{display:none}
body.ui-rail .topbar .tb-group{flex-direction:column;gap:2px}
body.ui-rail .topbar .tb{width:36px;height:34px;padding:0;font-size:16px}
body.ui-rail .topbar .tb.wide{width:36px;padding:0;font-size:16px}
body.ui-rail .topbar .tb-sep{display:none}
body.ui-rail .topbar .spacer,
body.ui-rail .topbar .title-edit{display:none}   /* the title is moved out to float on the canvas */
body.ui-rail .topbar > .user-pill,
body.ui-rail .topbar > .token-total{display:none}  /* too wide for the rail (left over from classic) */
/* Search floats right of the rail, over the canvas - toggled by the
   ⌕ button exactly like the modern toolbar's own inline field. */
body.ui-rail .topbar .search-wrap{
  position:absolute;top:64px;left:calc(100% + 8px);right:auto;z-index:20;
  width:0; overflow:hidden;
  background:none; border:none; box-shadow:none;
}
body.ui-rail .topbar .search-wrap.open{
  width:270px;
  background:var(--chrome);border:1px solid var(--chrome-edge);border-radius:12px;
  box-shadow:var(--shadow);
}
body.ui-rail .topbar .search-wrap.open.replace-mode{width:460px}
body.ui-rail .stage{grid-column:3;grid-row:1}
body.ui-rail .title-edit{
  position:absolute;top:14px;left:14px;z-index:30;max-width:42vw;
  background:transparent;border:1px solid transparent;
}
body.ui-rail .title-edit:hover, body.ui-rail .title-edit:focus{background:var(--chrome);border-color:var(--chrome-edge)}
/* Top-right group on the canvas: signed-in pill + save feedback (see the rail
   branch of applyUiLayout). The pills themselves are unstyled here; the group
   is what floats, so they never overlap each other whatever their width. */
body.ui-rail .rail-float{position:absolute;top:14px;right:14px;z-index:30;display:flex;align-items:center;gap:8px}
body.ui-rail .rail-float .save-pill{margin:0}
body.ui-rail.md-ready .app{grid-template-columns:auto 46px auto minmax(0,1fr)}
body.ui-rail #mdPane{grid-column:3;grid-row:1}
body.ui-rail.md-ready .stage{grid-column:4;grid-row:1}
body.ui-rail.focus-mode .app{grid-template-columns:1fr}
body.ui-rail.focus-mode .topbar{display:none}
body.ui-rail.focus-mode .stage{grid-column:1;grid-row:1;width:calc(100vw / var(--ui-zoom, 1));height:calc(100dvh / var(--ui-zoom, 1))}
body.ui-rail.shared-view .app{grid-template-columns:auto 46px minmax(0,1fr);padding-top:var(--shared-banner-h, 42px)}
body.ui-rail.shared-view .topbar{grid-column:2;grid-row:1}
body.ui-rail.shared-view .stage{grid-column:3;grid-row:1}
body.ui-rail.shared-view .breadcrumb{top:97px;left:50%;transform:translateX(-50%) translateY(-4px)}
body.ui-rail.shared-view .breadcrumb.shown{transform:translateX(-50%) translateY(0)}

/* ── Side-toolbar popovers: fly out to the right of the rail ───────────────
   In the rail layout the toolbar is a 46px vertical strip. A classic dropdown
   (below the button, right-aligned) gets clamped to the left viewport edge and
   looks detached - the pattern every modern app with a vertical rail uses is a
   horizontal flyout 8px to the right of the rail, top-aligned with the trigger
   (VS Code activity bar, Figma tools, Slack navigation, Discord server list).
   The JS `positionPopup(…, {side:'right'})` places the popover there; these
   rules just polish the feel so the flyout reads as attached to the rail. */
@keyframes railPopIn{
  from{opacity:0; transform:translateX(-6px) scale(.98);}
  to{opacity:1; transform:translateX(0) scale(1);}
}
body.ui-rail .theme-panel,
body.ui-rail .export-pop{
  transform-origin:left center;
  animation:railPopIn .14s cubic-bezier(.2,0,.2,1);
  /* A slightly stronger shadow so the flyout lifts clearly off the canvas and
     the rail when it sits 8px away - matching VS Code / Slack elevation. */
  box-shadow:0 12px 40px rgba(0,0,0,.22), 0 2px 8px rgba(0,0,0,.14);
}
@media (prefers-reduced-motion: reduce){
  body.ui-rail .theme-panel,
  body.ui-rail .export-pop{animation:none;}
}

/* ---- Zen / write: the shell vanishes; the canvas fills the window. The
   toolbar strip, breadcrumb and save pill are hidden and slide in only while
   the pointer hovers the top edge (body.zen-chrome). Minimap card, zoombar
   and hint stay floating at all times. ---- */
body.ui-zen .app{grid-template-columns:1fr;grid-template-rows:minmax(0,1fr)}
body.ui-zen .side{
  position:fixed;top:0;left:0;bottom:0;width:min(320px,86vw);z-index:400;
  background:var(--chrome);border-right:1px solid var(--chrome-edge);
  box-shadow:var(--shadow-lg);transform:translateX(-100%);
  transition:transform .25s cubic-bezier(.4,0,.2,1);
  display:flex;flex-direction:column;
}
body.ui-zen.side-open .side{transform:translateX(0)}
body.ui-zen .statusbar{display:none}
body.ui-zen .stage{grid-column:1;grid-row:1}
body.ui-zen .topbar{
  position:absolute;top:14px;left:50%;right:auto;z-index:30;pointer-events:none;
  display:flex;align-items:center;gap:6px;flex-wrap:nowrap;overflow:hidden;
  background:var(--chrome);border:1px solid var(--chrome-edge);border-radius:12px;
  padding:4px 8px;box-shadow:var(--shadow);max-width:92vw;width:max-content;
  opacity:0;transform:translateX(-50%) translateY(-12px);
  transition:opacity .18s ease, transform .18s ease;
}
body:not(.ui-zen) #zenPin{display:none}
body.ui-zen.zen-chrome .topbar{opacity:1;transform:translateX(-50%) translateY(0);pointer-events:auto}
/* Pinned: bar stays visible without hovering the top edge (state in localStorage).
   Declared BEFORE the focus-mode rules below so focus mode still hides it. */
body.ui-zen.zen-pinned .topbar{opacity:1;transform:translateX(-50%) translateY(0);pointer-events:auto}
body.ui-zen #zenPin.on{background:var(--accent);color:var(--accent-fg, #fff)}
body.ui-zen .tb-group{background:none;border:none;border-radius:0;padding:0;box-shadow:none}
body.ui-zen .tb-sep{width:1px;height:22px;background:var(--chrome-edge);flex:0 0 auto}
body.ui-zen .search-wrap{background:transparent;border:none;box-shadow:none;flex:0 0 auto}
body.ui-zen .search-wrap.open{background:var(--paper-2);border:1px solid var(--chrome-edge);border-radius:8px;padding:2px}
/* Opening search must widen the centered bar instead of squeezing the field:
   lift the 92vw cap and absorb the extra width from the map title first. */
body.ui-zen .topbar:has(.search-wrap.open){max-width:98vw}
body.ui-zen .topbar:has(.search-wrap.open) .title-edit{min-width:60px;max-width:160px}
/* While the find field is open the least-used actions step aside, so the
   token counter and pin stay on the first row instead of being clipped.
   Tabbed workspace (▭) is the primary reclaim per spec; Donate (♥) is also
   low-priority while searching and avoids the ~2px clip at 1024px / ~50px at
   960px that otherwise hides the pin. (Present used to be reclaimed here too;
   that button is gone from the bar.) */
body.ui-zen .topbar:has(.search-wrap.open) #tabsBtn,
body.ui-zen .topbar:has(.search-wrap.open) #donateBtn{display:none}
@media (max-width:1024px){
  /* 960px still clips the pin by ~20px even after the three above; reclaim
     one more low-priority control only on narrow windows so wide screens
     keep the full bar. */
  body.ui-zen .topbar:has(.search-wrap.open) #varsBtn{display:none}
}
body.ui-zen .save-pill{display:flex;position:absolute;top:22px;right:14px;z-index:30;margin:0}
body.ui-zen .token-total{background:none;border:none;box-shadow:none;border-radius:0;padding:6px 4px;margin-left:4px}
body.ui-zen .breadcrumb{display:none}
body.ui-zen.md-ready .topbar{max-width:min(92vw, calc(100% - 28px))}
body.ui-zen.md-ready .topbar:has(.search-wrap.open){max-width:min(98vw, calc(100% - 16px))}
body.ui-zen.md-ready .app{grid-template-columns:auto minmax(0,1fr)}
body.ui-zen #mdPane{grid-column:1;grid-row:1}
body.ui-zen.md-ready .stage{grid-column:2;grid-row:1}
body.ui-zen.tabs-on .app{grid-template-rows:auto minmax(0,1fr)}
body.ui-zen.tabs-on #tabStrip{grid-column:1;grid-row:1;display:flex}
body.ui-zen.tabs-on .stage{grid-column:1;grid-row:2}
body.ui-zen.tabs-on.md-ready .app{grid-template-columns:auto minmax(0,1fr);grid-template-rows:auto minmax(0,1fr)}
body.ui-zen.tabs-on.md-ready #tabStrip{grid-column:1/-1;grid-row:1}
body.ui-zen.tabs-on #mdPane{grid-column:1;grid-row:2}
body.ui-zen.tabs-on.md-ready .stage{grid-column:2;grid-row:2}
body.ui-zen.focus-mode #mdPane{display:none}
@media (prefers-reduced-motion: reduce){ body.ui-zen .topbar{transition:none} }
body.ui-zen.focus-mode .topbar{opacity:0;transform:translateY(-12px);pointer-events:none}
body.ui-zen.focus-mode .stage{grid-column:1;grid-row:1;width:calc(100vw / var(--ui-zoom, 1));height:calc(100dvh / var(--ui-zoom, 1))}
body.ui-zen.shared-view .app{grid-template-columns:1fr;padding-top:var(--shared-banner-h, 42px)}
body.ui-zen.shared-view .topbar{display:none}
body.ui-zen.shared-view .stage{grid-column:1;grid-row:1}
body.ui-zen.shared-view .breadcrumb{top:97px;left:50%;transform:translateX(-50%) translateY(-4px)}
body.ui-zen.shared-view .breadcrumb.shown{transform:translateX(-50%) translateY(0)}

/* ---- Bottom-dock: the status bar becomes a full-width dock that owns the
   sidebar toggle, the Maps/Templates tabs, the hint and the overview chip;
   the sidebar turns into a slide-over panel instead of a permanent column. ---- */
body.ui-dock .app{grid-template-columns:1fr;grid-template-rows:auto auto minmax(0,1fr) auto}
body.ui-dock .topbar{grid-column:1;grid-row:1}
body.ui-dock #tabStrip{grid-column:1;grid-row:2}
body.ui-dock .stage{grid-column:1;grid-row:3}
body.ui-dock .statusbar{grid-column:1;grid-row:4;width:100%;gap:12px;padding:6px 12px}
body.ui-dock .side{
  position:fixed;top:0;left:0;bottom:0;width:min(320px,86vw);z-index:400;
  background:var(--chrome);border-right:1px solid var(--chrome-edge);
  box-shadow:var(--shadow-lg);transform:translateX(-100%);
  transition:transform .25s cubic-bezier(.4,0,.2,1);
}
body.ui-dock.side-open .side{transform:translateX(0)}
body.ui-dock .side-rail, body.ui-dock .side-resize{display:none}
body.ui-dock .side-tabs{display:flex;gap:2px;padding:2px;background:var(--paper-2);border-radius:10px}
body.ui-dock .hint{margin-left:auto}
body.ui-dock.md-ready .app{grid-template-columns:minmax(0,1fr) auto}
body.ui-dock.md-ready #mdPane{grid-column:2;grid-row:3; border-right:none; border-left:1px solid var(--line)}
body.ui-dock.md-ready .stage{grid-column:1;grid-row:3}
body.ui-dock.md-ready .topbar{grid-column:1/-1;grid-row:1}
body.ui-dock.md-ready #tabStrip{grid-column:1/-1;grid-row:2}
body.ui-dock.md-ready .statusbar{grid-column:1/-1;grid-row:4}
body.ui-dock.md-ready #mdPane .md-resize{left:0; right:auto}
body.ui-dock.md-mode #mdPane{width:var(--md-w,40vw)}
body.ui-dock.focus-mode .app{grid-template-columns:1fr}
body.ui-dock.focus-mode #mdPane{display:none}
body.ui-dock.focus-mode .stage{grid-column:1;grid-row:3;width:calc(100vw / var(--ui-zoom, 1));height:calc(100dvh / var(--ui-zoom, 1))}
body.ui-dock.shared-view .app{grid-template-columns:1fr}
body.ui-dock.shared-view #mdPane{display:none}
body.ui-dock.shared-view .stage{grid-column:1;grid-row:3}

/* ---- Split editor: the modern shell plus the markdown pane pinned open as a
   permanent third column (side | editor | canvas). ---- */
body.ui-split .app{grid-template-columns:auto auto minmax(0,1fr)}
body.ui-split .side{grid-column:1;grid-row:1/5}
body.ui-split .topbar{grid-column:2/-1;grid-row:1}
body.ui-split #mdPane{grid-column:2;grid-row:3;width:var(--md-w,40vw)}
body.ui-split .stage{grid-column:3;grid-row:3}
body.ui-split .statusbar{grid-column:2/-1;grid-row:4}
body.ui-split #mdToggle{display:none}
body.ui-split #mdPane .md-close{display:none}
 body.ui-split.focus-mode .app{grid-template-columns:1fr}
  body.ui-split.focus-mode #mdPane{display:none}
  body.ui-split.focus-mode .stage{grid-column:1;grid-row:3;width:calc(100vw / var(--ui-zoom, 1));height:calc(100dvh / var(--ui-zoom, 1))}
body.ui-split.tabs-on .app{grid-template-rows:auto auto minmax(0,1fr) auto}
body.ui-split.tabs-on #tabStrip{display:flex;grid-column:2/-1;grid-row:2}
body.ui-split.tabs-on #mdPane{grid-column:2;grid-row:3}
body.ui-split.tabs-on .stage{grid-column:3;grid-row:3}
body.ui-split.shared-view .app{grid-template-columns:1fr}
body.ui-split.shared-view #mdPane{display:none}
body.ui-split.shared-view .stage{grid-column:1;grid-row:3}

 /* ---- Minimal: the plain modern shell with the sidebar gone entirely - no
   column, no slide-over, nothing to toggle. ---- */
body.ui-minimal .app{grid-template-columns:1fr}
body.ui-minimal .topbar{grid-column:1;grid-row:1}
body.ui-minimal .stage{grid-column:1;grid-row:3}
body.ui-minimal .statusbar{grid-column:1;grid-row:4}
body.ui-minimal .side{
  position:fixed;top:0;left:0;bottom:0;width:min(320px,86vw);z-index:400;
  background:var(--chrome);border-right:1px solid var(--chrome-edge);
  box-shadow:var(--shadow-lg);transform:translateX(-100%);
  transition:transform .25s cubic-bezier(.4,0,.2,1);
}
body.ui-minimal.side-open .side{transform:translateX(0)}
body.ui-minimal .side-rail, body.ui-minimal .side-resize{display:none}
body.ui-minimal #toggleSide{display:none}
body.ui-minimal.md-ready .app{grid-template-columns:minmax(0,1fr) auto}
body.ui-minimal.md-ready #mdPane{grid-column:2;grid-row:3; border-right:none; border-left:1px solid var(--line)}
body.ui-minimal.md-ready .stage{grid-column:1;grid-row:3}
body.ui-minimal.md-ready .topbar{grid-column:1/-1;grid-row:1}
body.ui-minimal.md-ready #tabStrip{grid-column:1/-1;grid-row:2}
body.ui-minimal.md-ready .statusbar{grid-column:1/-1;grid-row:4}
body.ui-minimal.md-ready #mdPane .md-resize{left:0; right:auto}
body.ui-minimal.md-mode #mdPane{width:var(--md-w,40vw)}
body.ui-minimal.tabs-on .app{grid-template-rows:auto auto minmax(0,1fr) auto}
body.ui-minimal.tabs-on #tabStrip{display:flex;grid-column:1;grid-row:2}
body.ui-minimal.tabs-on .stage{grid-column:1;grid-row:3}
body.ui-minimal.tabs-on.md-ready #mdPane{grid-column:2;grid-row:3}
body.ui-minimal.tabs-on.md-ready .stage{grid-column:1;grid-row:3}
body.ui-minimal.focus-mode .app{grid-template-columns:1fr}
body.ui-minimal.focus-mode #mdPane{display:none}
body.ui-minimal.focus-mode .stage{grid-column:1;grid-row:3;width:calc(100vw / var(--ui-zoom, 1));height:calc(100dvh / var(--ui-zoom, 1))}
body.ui-minimal.shared-view .app{grid-template-columns:1fr}
body.ui-minimal.shared-view #mdPane{display:none}
body.ui-minimal.shared-view .stage{grid-column:1;grid-row:3}

  /* ---- Minimal launcher: a Raspberry-Pi-OS-style menu. The MindSpark logo
   sits beside the map title at the top-left and opens a context menu with
   Maps, Templates and Open source. Only the minimal layout shows the logo. ---- */
.minimal-menu-btn{display:none;align-items:center;justify-content:center;
  width:34px;height:34px;margin:6px 4px 6px 10px;padding:0;flex:0 0 auto;
  border:none;border-radius:9px;background:none;cursor:pointer}
.minimal-menu-btn:hover{background:var(--hover)}
.minimal-menu-btn:active{transform:scale(.94)}
.minimal-menu-btn svg{width:24px;height:24px}
body.ui-minimal .minimal-menu-btn{display:flex}
.minimal-menu{
  position:fixed;min-width:190px;z-index:500;padding:6px;
  background:var(--chrome);border:1px solid var(--chrome-edge);border-radius:12px;
  box-shadow:var(--shadow-lg);display:flex;flex-direction:column;gap:2px;
  animation:pop .12s ease-out;
}
.minimal-menu[hidden]{display:none}
.minimal-menu .mm-item{
  display:flex;align-items:center;gap:10px;padding:8px 10px;width:100%;
  border:none;background:none;border-radius:8px;font-family:inherit;font-size:13px;
  color:var(--ink);cursor:pointer;text-align:left}
.minimal-menu .mm-item:hover{background:var(--hover)}
.minimal-menu .mm-item.mm-has-sub:hover{background:var(--paper-2)}
.minimal-menu .mm-item.mm-has-sub:hover .mm-caret{color:var(--accent-text);opacity:1}
.minimal-menu .mm-item:active{transform:scale(.98)}
.minimal-menu .mm-item + .mm-item{border-top:1px solid transparent}
.minimal-menu .mm-item .mm-lbl{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.minimal-menu .mm-item .mm-caret{margin-left:auto;font-size:10px;opacity:.6;transform:translateY(-1px)}
.minimal-menu .mm-ic{width:18px;height:18px;flex:0 0 auto;display:flex;align-items:center;justify-content:center;color:var(--accent-text)}
.minimal-menu .mm-ic svg{width:16px;height:16px}
.minimal-menu .mm-ic .dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto;display:block}
.minimal-menu .mm-ic svg + .dot{display:none}
@keyframes pop{from{opacity:0}to{opacity:1}}

/* Cascading sub-panels anchored beside their parent item. They live on <body>
   (never inside the animated menu) so their fixed positioning is relative to
   the viewport; they flip to the left/bottom edge when they would otherwise
   run off the screen. */
.minimal-sub{
  position:fixed;min-width:190px;max-width:280px;max-height:min(70vh,420px);overflow-y:auto;
  z-index:501;padding:6px;background:var(--chrome);border:1px solid var(--chrome-edge);
  border-radius:12px;box-shadow:var(--shadow-lg);display:flex;flex-direction:column;gap:2px;
  animation:pop .12s ease-out;
}
.minimal-sub[hidden]{display:none}
.minimal-sub .mm-item{display:flex;align-items:center;gap:10px;padding:8px 10px;width:100%;border:none;background:none;border-radius:8px;font-family:inherit;font-size:13px;color:var(--ink);cursor:pointer;text-align:left}
.minimal-sub .mm-item:hover{background:var(--hover)}
.minimal-sub .mm-item:active{transform:scale(.98)}
.minimal-sub .mm-item.active{background:var(--paper-2);color:var(--accent-text);font-weight:600}
.minimal-sub .mm-item .mm-lbl{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.minimal-sub .mm-item .mm-pin{margin-left:auto;font-size:11px;opacity:.7}
.minimal-sub .mm-ic{width:18px;height:18px;flex:0 0 auto;display:flex;align-items:center;justify-content:center;color:var(--accent-text)}
.minimal-sub .mm-ic svg{width:16px;height:16px}
.minimal-sub .mm-ic .dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto;display:block}
.minimal-sub .mm-empty{padding:8px 10px;font-size:12px;color:var(--ink-soft);text-align:center}
.minimal-sub .map-group-label{font-size:10px;text-transform:uppercase;letter-spacing:.6px;color:var(--ink-soft);padding:8px 10px 4px;font-weight:700}

/* ---- Mirror classic: the same floating shell as classic, flipped to the
   left. applyUiLayout() keeps body.ui-classic applied too, so every classic
   rule already covers this layout - only the positions are mirrored. ---- */
body.ui-mirror .topbar{justify-content:flex-end}     /* actions left, title right */
body.ui-mirror .tb-group{flex-direction:row-reverse}
body.ui-mirror .overview{right:auto;left:72px}       /* minimap card bottom-left */
body.ui-mirror .zoom-row{right:auto;left:18px}       /* zoombar beside it, left edge */
body.ui-mirror .hint{left:auto;right:18px}           /* hint bottom-right */
body.ui-mirror.shared-view .breadcrumb{transform:translateX(-50%) translateY(-4px)}
body.ui-mirror.shared-view .breadcrumb.shown{transform:translateX(-50%) translateY(0)}

/* ---- Presentation: chrome vanishes, the canvas fills the window and the map
   is walked node by node (breadth-first). The current node gets an accent
   ring and the rest of the map dims; a slim bar top-centre shows the node
   title, the position in the walk and prev/next controls. ---- */
body.ui-deck .app{grid-template-columns:1fr;grid-template-rows:minmax(0,1fr)}
body.ui-deck .stage{grid-column:1;grid-row:1}
body.ui-deck .side, body.ui-deck .topbar, body.ui-deck .statusbar,
body.ui-deck .hint, body.ui-deck .overview, body.ui-deck .breadcrumb,
body.ui-deck .save-pill, body.ui-deck .search-wrap, body.ui-deck #mdPane,
body.ui-deck .focus-exit{display:none !important}
#deckBar{
  position:fixed;top:14px;left:50%;transform:translateX(-50%);z-index:500;
  display:flex;align-items:center;gap:12px;max-width:min(92vw, 640px);
  padding:6px 8px 6px 14px;background:var(--chrome);
  border:1px solid var(--chrome-edge);border-radius:12px;box-shadow:var(--shadow);
  font-size:13px;color:var(--ink);
}
#deckBar .deck-title{font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#deckBar .deck-pos{font-variant-numeric:tabular-nums;color:var(--ink-soft);font-size:12px;flex:0 0 auto}
#deckBar .deck-nav{display:flex;gap:4px}
#deckBar button{width:30px;height:30px;border-radius:8px;border:none;background:none;color:var(--ink-soft);cursor:pointer;font-size:13px;line-height:1}
#deckBar button:hover{background:var(--hover);color:var(--ink)}
#deckBar #deckExit:hover{background:color-mix(in srgb, var(--accent) 14%, transparent);color:var(--accent-text)}
body.ui-deck .node{transition:opacity .3s ease}
body.ui-deck .node:not(.deck-current){opacity:.28}
body.ui-deck .node.deck-current{box-shadow:0 0 0 3px var(--accent)}

/* ---- Outline dock: the modern shell plus a live tree panel between the
   sidebar and the canvas (side | outline | canvas). Folded by default - the
   ▤ toolbar button opens it. Rows select + centre the node; the twist folds
   a branch locally without touching the map. ---- */
body.ui-outline .app{grid-template-columns:auto 0 minmax(0,1fr)}
body.ui-outline.outline-open .app{grid-template-columns:auto auto minmax(0,1fr)}
body.ui-outline .topbar, body.ui-outline .statusbar{grid-column:2/-1}
body.ui-outline .stage{grid-column:3;grid-row:3}
body.ui-outline #mdToggle{display:none}
body:not(.ui-outline) #outlineBtn{display:none}
#outlinePane{
  grid-column:2;grid-row:3;display:flex;flex-direction:column;min-width:0;min-height:0;
  width:0;overflow:hidden;background:var(--paper);border-right:1px solid var(--line);
  transition:width .22s cubic-bezier(.4,0,.2,1);
}
body.ui-outline.outline-open #outlinePane{width:min(280px,24vw)}
/* box-sizing:border-box cannot shrink a border, so the collapsed pane still
   measured 1px wide and painted a stray hairline down the canvas edge (in
   minimal that was the last column of the screen). Drop the border while it
   is closed so the pane is genuinely zero-width. */
body.ui-outline:not(.outline-open) #outlinePane{border-right-width:0}
#outlinePane .ol-head{display:flex;align-items:center;gap:8px;padding:8px 12px;border-bottom:1px solid var(--line);font-size:12px;font-weight:700;color:var(--ink-soft);letter-spacing:.04em}
#outlinePane .ol-close{margin-left:auto;border:none;background:none;cursor:pointer;color:var(--ink-soft);font-size:13px;line-height:1;padding:4px 6px;border-radius:6px}
#outlinePane .ol-close:hover{background:var(--hover);color:var(--ink)}
#outlinePane .ol-body{flex:1;min-height:0;overflow:auto;padding:8px 6px 14px}
#outlinePane .ol-tree, #outlinePane .ol-sub{list-style:none;margin:0;padding:0}
#outlinePane .ol-sub{padding-left:16px}
#outlinePane .ol-node.collapsed > .ol-sub{display:none}
#outlinePane .ol-row{display:flex;align-items:center;gap:4px;padding:4px 8px;border-radius:8px;cursor:pointer;font-size:13px;line-height:1.45;color:var(--ink)}
#outlinePane .ol-row:hover{background:var(--hover)}
#outlinePane .ol-node.sel > .ol-row{background:color-mix(in srgb, var(--accent) 14%, transparent);color:var(--accent-text)}
#outlinePane .ol-twist{flex:0 0 14px;width:14px;text-align:center;color:var(--ink-soft);font-size:10px;user-select:none;transition:transform .15s ease}
#outlinePane .ol-leaf{visibility:hidden}
#outlinePane .ol-node.collapsed > .ol-row .ol-twist{transform:rotate(-90deg)}
#outlinePane .ol-label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
body.ui-outline.focus-mode .app{grid-template-columns:1fr}
body.ui-outline.focus-mode #outlinePane{display:none}
body.ui-outline.focus-mode .stage{grid-column:1;grid-row:3;width:calc(100vw / var(--ui-zoom, 1));height:calc(100dvh / var(--ui-zoom, 1))}
body.ui-outline.shared-view .app{grid-template-columns:auto minmax(0,1fr)}
body.ui-outline.shared-view #outlinePane{display:none}
body.ui-outline.shared-view .stage{grid-column:2;grid-row:3}

@media (max-width:720px){
  #deckBar{top:8px;left:8px;right:8px;transform:none;max-width:none}
  body.ui-outline .app{grid-template-columns:1fr}
  body.ui-outline .topbar, body.ui-outline .statusbar{grid-column:1}
  body.ui-outline .stage{grid-column:1;grid-row:3}
  body.ui-outline #outlinePane, body.ui-outline #outlineBtn{display:none}
  body.ui-classic .app{grid-template-columns:1fr}
  body.ui-classic .side{grid-column:1;grid-row:1;width:min(280px, 86vw)}
  body.ui-classic .stage{grid-column:1;grid-row:1}
  body.ui-classic .side.collapsed{width:min(280px, 86vw) !important}
  body.ui-classic .topbar{top:10px;left:10px;right:10px}
  /* Full-width strip on phones. The insets stretch the bar, so the desktop
     translateX(-50%) centering must go too - it shifted the strip half its
     own width off-screen (left edge unreachable). */
  body.ui-zen .topbar{top:10px;left:10px;right:10px;width:auto;max-width:none;transform:translateY(-12px)}
  body.ui-zen.zen-chrome .topbar,
  body.ui-zen.zen-pinned .topbar{transform:translateY(0)}
  body.ui-split .app{grid-template-columns:1fr}
  body.ui-split .topbar{grid-column:1;grid-row:1}
  body.ui-split .stage{grid-column:1;grid-row:3}
  body.ui-split .statusbar{grid-column:1;grid-row:4}
  body.ui-classic .hint, body.ui-rail .hint, body.ui-zen .hint{display:none}
  body.ui-classic .overview, body.ui-rail .overview, body.ui-zen .overview{display:none}   /* minimap is hidden on small screens; the zoombar stays */
  body.ui-classic .zoom-row, body.ui-rail .zoom-row, body.ui-zen .zoom-row{bottom:12px;right:12px}
  body.ui-mirror .zoom-row{bottom:12px;left:12px;right:auto}
  /* Rail keeps its slim column on phones; the sidebar is the off-canvas
     overlay and the canvas fills the remaining column. */
  body.ui-rail .app{grid-template-columns:40px minmax(0,1fr)}
  body.ui-rail .side{grid-column:1;grid-row:1;width:min(280px, 86vw)}
  body.ui-rail .topbar{grid-column:1;grid-row:1;padding:8px 2px}
  body.ui-rail .stage{grid-column:2;grid-row:1}
}

/* ===== Smooth theme switching =====
   When a Colour Theme (or a theme-config JSON) changes the custom
   properties, these surfaces interpolate their colors over 0.3s - the same
   easing as the theme cards - instead of snapping. Canvas textures
   (background-image) are not interpolatable, so they still swap instantly
   while the paper color underneath fades; node and edge strokes fade with
   the theme too. Interactive hover controls use the shorter 0.2s so hover
   feedback stays snappy. */
body, .app, .topbar, .statusbar, .stage, .nodebar, .brand, .save-pill,
.vf-card, .row-pop, .picker, .donate-card, .export-pop,
.search-wrap, .minimap, .toast, .theme-panel, .notes-popup{
  transition: background-color 0.3s ease, border-color 0.3s ease, color 0.3s ease;
}
#edges path{
  transition: stroke 0.3s ease;
}
.tb, .map-item, .scale-opt, .side-tab, .tpl-side-item, .title-edit,
.overview, .zoom-val, .zoom-row button,
input, textarea, select{
  transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
@media (prefers-reduced-motion: reduce){
  body, .app, .topbar, .statusbar, .stage, .nodebar, .brand, .save-pill,
  .vf-card, .row-pop, .picker, .donate-card, .export-pop,
  .minimap, .theme-panel, .notes-popup, #edges path,
  .tb, .map-item, .scale-opt, .side-tab, .tpl-side-item, .title-edit,
  .overview, .zoom-val, .zoom-row button,
  input, textarea, select{
    transition: none !important;
  }
}
@media (prefers-reduced-motion: reduce){
  .wave-layer,.wave-layer-mid,.wave-layer-far{ display:none !important; }
  .swell-layer{ display:none !important; }
  .mountain-layer{ display:none !important; }
}
.bp-panel:focus{ outline:none; }
.bp-task-wrap{ margin:10px 0 6px; padding:0 14px; }
.bp-task-label{ font-size:11px; opacity:.6; margin-bottom:2px; }
.bp-task-chips{ display:flex; gap:4px; flex-wrap:wrap; margin-bottom:4px; }
.bp-chip{ font-size:11px; padding:2px 6px; border-radius:3px; border:1px solid var(--chrome-edge); background:var(--node-bg); color:var(--ink); cursor:pointer; }
.bp-chip:hover{ background:var(--accent); color:var(--accent-fg, #fff); }
.bp-chip-wrap{ display:inline-flex; align-items:stretch; }
.bp-chip-wrap .bp-chip{ border-top-right-radius:0; border-bottom-right-radius:0; }
.bp-chip-del{ font-size:11px; line-height:1; padding:0 5px; cursor:pointer; color:var(--ink-soft);
  border:1px solid var(--chrome-edge); border-left:none; border-radius:0 3px 3px 0; background:var(--node-bg); }
  .bp-chip-del:hover{ background:var(--accent); border-color:var(--accent); color:var(--accent-fg, #fff); }
.bp-save{ font-size:11px; padding:2px 6px; border-radius:3px; cursor:pointer; color:var(--ink-soft);
  border:1px dashed var(--chrome-edge); background:transparent; }
.bp-save:hover{ border-color:var(--accent); color:var(--accent-text); }
.bp-chip.active{ background:var(--accent); color:var(--accent-fg, #fff); }
.bp-instruction{ width:100%; min-height:48px; font-family:inherit; font-size:12px; padding:4px 6px; border:1px solid var(--chrome-edge); border-radius:3px; background:var(--node-bg); color:var(--ink); resize:vertical; }
/* Expanded = the "Run with API" section is open. The panel may use more of the
   window than its 80vh default, but never more than fits below its own top
   anchor: 100vh minus the 64px top offset and an 18px bottom margin (the same
   gap it keeps on the right). A fixed height here once made it fill the whole
   screen and run 64px past the bottom edge, clipping the Send row. */
.bp-panel.bp-expanded{ max-height:calc(100vh - 82px); }

/* Look config radius override - only active when user has set a custom radius */
.user-look-radius .vf-card{ border-radius:var(--look-radius) !important; }
.user-look-radius .minimap{ border-radius:var(--look-radius) !important; }
.user-look-radius .toast{ border-radius:var(--look-radius) !important; }
.user-look-radius .search-wrap{ border-radius:var(--look-radius) !important; }
