:root{
  --editor-bg:#ffffff;
  --ws-dot:#b8b8bd;
  --bg:#f7f7f8;
  --bg-elev:#ffffff;
  --bg-elev-2:#eeeeef;
  --border:#d7d7da;
  --border-strong:#a9a9af;
  --text:#171719;
  --text-dim:#626269;
  /* Violet is the default accent and is never written to <html>, so the base palette is violet. */
  --accent:#6b3fd0;
  --accent-2:#a8309a;
  --accent-soft:#eee8ff;
  --danger:#c9362b;
  --success:#16804a;
  --warning:#965600;
  --cat-code:#245bd7;
  --cat-data:#6b4fc4;
  --cat-text:#0f8a7c;
  --cat-design:#c23b7a;
  --cat-utility:#b5650a;
  --cat-quality:#16804a;
  --cat-reference:#8a6d1f;
  --cat-productivity:#3d5ba9;
  --cat-entertainment:#d6532f;
  --cat-ai:#9a2fc9;
  --cat-api:#0f7fa3;
  --radius:3px;
  --radius-control:var(--radius);
  --control-height:32px;
  --sans:"Aptos","Segoe UI Variable","Segoe UI",sans-serif;
  --mono:"Inconsolata","SFMono-Regular",Consolas,monospace;
}
@font-face{font-family:"Inconsolata";font-style:normal;font-weight:200 900;font-display:swap;src:url("/assets/fonts/inconsolata-latin.woff2") format("woff2")}
@font-face{font-family:"Victor Mono";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/victor-mono-latin-400.woff2") format("woff2")}
@font-face{font-family:"Victor Mono";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/victor-mono-latin-700.woff2") format("woff2")}
@font-face{font-family:"Anonymous Pro";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/anonymous-pro-latin-400.woff2") format("woff2")}
@font-face{font-family:"Anonymous Pro";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/anonymous-pro-latin-700.woff2") format("woff2")}
/* Two ranges, one family: the Arabic file is only fetched by a page that
   actually paints Arabic. */
@font-face{font-family:"Cairo";font-style:normal;font-weight:200 1000;font-display:swap;unicode-range:U+0600-06FF,U+0750-077F,U+08A0-08FF,U+FB50-FDFF,U+FE70-FEFF;src:url("/assets/fonts/cairo-arabic.woff2") format("woff2")}
@font-face{font-family:"Cairo";font-style:normal;font-weight:200 1000;font-display:swap;src:url("/assets/fonts/cairo-latin.woff2") format("woff2")}
@font-face{font-family:"OpenDyslexic";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/opendyslexic-latin-400.woff2") format("woff2")}
@font-face{font-family:"OpenDyslexic";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/opendyslexic-latin-700.woff2") format("woff2")}
@font-face{font-family:"Departure Mono";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/departure-mono-latin.woff2") format("woff2")}
[data-theme="dark"]{
  --editor-bg:#1e1e1e;
  --ws-dot:#5a5a62;
  --bg:#0b0b0c;
  --bg-elev:#121214;
  --bg-elev-2:#1b1b1e;
  --border:#2e2e32;
  --border-strong:#55555c;
  --text:#f2f2f3;
  --text-dim:#a1a1a9;
  --accent:#b79bff;
  --accent-2:#f0a0e4;
  --accent-soft:#241b3d;
  --danger:#ff746c;
  --success:#5fd494;
  --warning:#e9a94b;
  --cat-code:#79a2ff;
  --cat-data:#b7a3ff;
  --cat-text:#4fd6c4;
  --cat-design:#f293bf;
  --cat-utility:#f0a955;
  --cat-quality:#5fd494;
  --cat-reference:#dcbf63;
  --cat-productivity:#9ab4f2;
  --cat-entertainment:#ff9a78;
  --cat-ai:#d68bf5;
  --cat-api:#5fc9ea;
}
/* Sepia and Dim re-tint the surfaces; data-theme stays light|dark so the embedded editors keep working. */
:root[data-mode="sepia"]{
  --bg:#F3EADA;
  --bg-elev:#FBF5E9;
  --bg-elev-2:#EADFCB;
  --border:#D8C9AE;
  --border-strong:#A9977A;
  --text:#2B2318;
  --text-dim:#6A5C46;
}
/* Comfort: dark text on an off-white ground at high, not maximum, contrast. Rides the light family. */
:root[data-mode="comfort"]{
  --bg:#E9EAEC;
  --bg-elev:#F2F3F5;
  --bg-elev-2:#DCDEE2;
  --border:#C2C6CC;
  --border-strong:#868C95;
  --text:#1E2023;
  --text-dim:#4E545C;
}
:root[data-mode="dim"]{
  --bg:#1A1D21;
  --bg-elev:#22262B;
  --bg-elev-2:#2C3138;
  --border:#3A4048;
  --border-strong:#5D6570;
  --text:#E6E9ED;
  --text-dim:#A3ABB6;
}

/* Accent palettes re-point --accent, --accent-2 and --accent-soft. Light values clear 4.5:1 on the light --bg,
   dark values on the dark one. Category colours never move. */
:root[data-accent="blue"]{--accent:#245bd7;--accent-2:#6b4fc4;--accent-soft:#e6ecfb}
[data-theme="dark"][data-accent="blue"]{--accent:#79a2ff;--accent-2:#b7a3ff;--accent-soft:#17213d}
:root[data-accent="violet"]{--accent:#6b3fd0;--accent-2:#a8309a;--accent-soft:#eee8ff}
[data-theme="dark"][data-accent="violet"]{--accent:#b79bff;--accent-2:#f0a0e4;--accent-soft:#241b3d}
:root[data-accent="red"]{--accent:#a8141b;--accent-2:#851d27;--accent-soft:#fbe9ea}
[data-theme="dark"][data-accent="red"]{--accent:#e34047;--accent-2:#ff7a85;--accent-soft:#2e1214}
:root[data-accent="orange"]{--accent:#a75405;--accent-2:#8f4520;--accent-soft:#fdedde}
[data-theme="dark"][data-accent="orange"]{--accent:#ffa257;--accent-2:#f0bd84;--accent-soft:#341f10}
:root[data-accent="green"]{--accent:#205B46;--accent-2:#2F6B52;--accent-soft:#E3EEE9}
[data-theme="dark"][data-accent="green"]{--accent:#205B46;--accent-2:#4A8F73;--accent-soft:#14241D}
:root[data-accent="cyan"]{--accent:#0a6490;--accent-2:#3f52a8;--accent-soft:#dceffa}
[data-theme="dark"][data-accent="cyan"]{--accent:#5cc2f0;--accent-2:#9fb2ff;--accent-soft:#102635}
:root[data-accent="pink"]{--accent:#b32a70;--accent-2:#7d3fbe;--accent-soft:#fbe6f1}
[data-theme="dark"][data-accent="pink"]{--accent:#ff92c2;--accent-2:#c9a8ff;--accent-soft:#331828}
/* Charcoal: --accent stays a legible grey (it is link and focus-ring text); its near-black primary button is overridden below. */
:root[data-accent="charcoal"]{--accent:#26262c;--accent-2:#3d3d46;--accent-soft:#e6e6ea}
[data-theme="dark"][data-accent="charcoal"]{--accent:#8d8d9a;--accent-2:#6e6e7b;--accent-soft:#0f0f12}
:root[data-accent="graphite"]{--accent:#3f3f46;--accent-2:#5d5d68;--accent-soft:#e7e7ea}
[data-theme="dark"][data-accent="graphite"]{--accent:#d4d4d8;--accent-2:#a5a5ae;--accent-soft:#272729}

/* Brighter row. Light-mode values stay dark enough to clear 4.5:1 as link text;
   the brightness people are after lives in the dark-mode pair. */
:root[data-accent="lime"]{--accent:#4a6b00;--accent-2:#3d7a1f;--accent-soft:#eef5d9}
[data-theme="dark"][data-accent="lime"]{--accent:#b8e04f;--accent-2:#d2e88a;--accent-soft:#23290f}
:root[data-accent="magenta"]{--accent:#b0158f;--accent-2:#8b2fbe;--accent-soft:#fbe4f5}
[data-theme="dark"][data-accent="magenta"]{--accent:#ff8ae0;--accent-2:#d7a5ff;--accent-soft:#33173a}
:root[data-accent="sky"]{--accent:#0b6ba8;--accent-2:#2f6fb5;--accent-soft:#ddeefb}
[data-theme="dark"][data-accent="sky"]{--accent:#7cc8ff;--accent-2:#a8d4ff;--accent-soft:#10263a}
:root[data-accent="coral"]{--accent:#b8402c;--accent-2:#a83f55;--accent-soft:#fdeae6}
[data-theme="dark"][data-accent="coral"]{--accent:#ff9b85;--accent-2:#ffb3a0;--accent-soft:#3a1e18}

/* Darker row. Deep, low-chroma faces in light mode; in dark mode they lift just
   far enough to stay readable, because an accent is text before it is decor. */
:root[data-accent="rust"]{--accent:#8b3a1a;--accent-2:#a8502a;--accent-soft:#f8e9e0}
[data-theme="dark"][data-accent="rust"]{--accent:#e09a70;--accent-2:#eebb98;--accent-soft:#2b1a12}
:root[data-accent="slate"]{--accent:#2f3e4e;--accent-2:#46586b;--accent-soft:#e6eaee}
[data-theme="dark"][data-accent="slate"]{--accent:#a9bccf;--accent-2:#c6d5e2;--accent-soft:#1a2029}

/* Appearance preferences, all set on <html> by theme.js before first paint.
   Text size is a root font-size so rem-based type scales; px layout stays put. */
:root[data-text-scale="80"]{font-size:80%}
:root[data-text-scale="90"]{font-size:90%}
:root[data-text-scale="110"]{font-size:110%}
:root[data-text-scale="120"]{font-size:120%}
:root[data-text-scale="130"]{font-size:130%}
:root[data-text-scale="140"]{font-size:140%}
:root[data-text-scale="150"]{font-size:150%}
:root[data-line-height="tight"] body{line-height:1.35}
:root[data-line-height="tight"] p,:root[data-line-height="tight"] li{line-height:1.45}
:root[data-line-height="relaxed"] body{line-height:1.75}
:root[data-line-height="relaxed"] p,:root[data-line-height="relaxed"] li{line-height:1.85}
:root[data-line-height="loose"] body{line-height:2}
:root[data-line-height="loose"] p,:root[data-line-height="loose"] li{line-height:2.15}
:root[data-contrast="medium"]{--border:#a7a7ae;--border-strong:#56565e;--text:#131318;--text-dim:#4f4f58}
[data-theme="dark"][data-contrast="medium"]{--border:#5c5c65;--border-strong:#95959f;--text:#f4f4f6;--text-dim:#c2c2ca}
:root[data-contrast="high"]{--border:#8b8b93;--border-strong:#3d3d44;--text:#000;--text-dim:#3a3a42}
[data-theme="dark"][data-contrast="high"]{--bg:#000;--bg-elev:#0c0c0e;--bg-elev-2:#191a1d;--border:#71717a;--border-strong:#b4b4bd;--text:#fff;--text-dim:#d6d6dd}
/* Maximum drops the elevation tints too: one flat ground, one ink, and a border
   that is the same value as the text so no divider can fade out. */
:root[data-contrast="max"]{--bg:#fff;--bg-elev:#fff;--bg-elev-2:#f2f2f4;--border:#000;--border-strong:#000;--text:#000;--text-dim:#1c1c22}
[data-theme="dark"][data-contrast="max"]{--bg:#000;--bg-elev:#000;--bg-elev-2:#101013;--border:#fff;--border-strong:#fff;--text:#fff;--text-dim:#ededf2}
/* A reading face, so it takes --sans and leaves --mono alone: code stays
   monospaced, which is what makes code legible in the first place. */
:root[data-font="dyslexic"]{--sans:"OpenDyslexic","Comic Sans MS",sans-serif;letter-spacing:.01em;word-spacing:.06em}
/* DIN Next LT Arabic is licensed, so it is used only when the computer has it; Bahnschrift is the stand-in. */
:root[data-font="cairo"]{--sans:"Cairo","Segoe UI",sans-serif}
:root[data-font="din"]{--sans:"DIN Next LT Arabic","DIN Next LT Arabic Regular","DIN Next LT Pro","DIN Next LT","DIN Next",Bahnschrift,"Cairo","Segoe UI",Tahoma,sans-serif}
:root[data-font="victor"]{--sans:"Victor Mono",ui-monospace,monospace;--mono:"Victor Mono",ui-monospace,monospace}
:root[data-font="inconsolata"]{--sans:"Inconsolata",ui-monospace,monospace;--mono:"Inconsolata",ui-monospace,monospace}
:root[data-font="anonymous"]{--sans:"Anonymous Pro",ui-monospace,monospace;--mono:"Anonymous Pro",ui-monospace,monospace}
:root[data-font="pixel"]{--sans:"Departure Mono",ui-monospace,monospace;--mono:"Departure Mono",ui-monospace,monospace;-webkit-font-smoothing:none;-moz-osx-font-smoothing:unset;font-smooth:never;font-kerning:none;font-variant-ligatures:none;font-synthesis:none}
/* Click targets: one --tap height drives every control. Small is below 44px on purpose, an opt-in for mouse users. */
:root[data-targets="small"]{--tap:26px;--tap-icon:30px;--tap-bar:36px}
:root[data-targets="large"]{--tap:40px;--tap-icon:40px;--tap-bar:49px}
:root[data-targets="xlarge"]{--tap:52px;--tap-icon:52px;--tap-bar:62px}
:root[data-targets] button,:root[data-targets] select,:root[data-targets] input.control,:root[data-targets] a.btn{min-height:var(--tap)}

[data-theme="dark"][data-accent="red"] button.btn.primary,[data-theme="dark"][data-accent="red"] a.btn.primary{background:#ad1d24;border-color:#ad1d24;color:#fff}
[data-theme="dark"][data-accent="red"] button.btn.primary:hover,[data-theme="dark"][data-accent="red"] a.btn.primary:hover{background:#981920;border-color:#981920;color:#fff}
/* The one accent whose button is not its --accent: charcoal asks for a
   near-black control, which no readable link colour could also be. */
:root[data-accent="charcoal"] button.btn.primary,:root[data-accent="charcoal"] a.btn.primary{background:#000;border-color:#000;color:#f4f4f7}
/* True black on a near-black page needs an edge, or the button dissolves into
   it -- the border is what makes "extra dark" read as a control. */
[data-theme="dark"][data-accent="charcoal"] button.btn.primary,[data-theme="dark"][data-accent="charcoal"] a.btn.primary{background:#000;border-color:#6e6e7b;color:#f4f4f7}
[data-theme="dark"][data-accent="charcoal"] button.btn.primary:hover,[data-theme="dark"][data-accent="charcoal"] a.btn.primary:hover{background:#000;border-color:#a9a9b6}
::selection{background:var(--accent-soft);color:var(--text)}
*{box-sizing:border-box;scrollbar-color:var(--border-strong) var(--bg);scrollbar-width:thin}
[hidden]{display:none!important}
.sr-only{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}
html,body{height:100%}
body{margin:0;background:var(--bg);color:var(--text);font-family:var(--sans);display:flex;flex-direction:column;position:relative}
a{color:inherit;text-decoration:none}
button,input,select,textarea{font:inherit}

/* ---------- the header: one row ----------
   Brand mark, tool name (the tool switcher), the page's toolbar, then the actions. Its markup is
   js/core/header.js; the build writes it into every page with the toolbar already in .topbar-tools
   (scripts/build/prerender-header.mjs), and js/core/theme.js patches what depends on the visitor. The row
   is --bar-h tall and never wraps; the side toolbar column starts below it (--head-h). */
:root{--bar-h:40px;--head-h:var(--bar-h)}
:root[data-targets]{--bar-h:var(--tap-bar)}
.topbar{--ctl:var(--tap,30px);position:sticky;top:0;z-index:40;flex:0 0 auto;display:flex;align-items:center;gap:6px;height:var(--bar-h);padding:0 clamp(8px,1.2vw,14px);background:var(--bg-elev);box-shadow:inset 0 -1px 0 var(--border);view-transition-name:topbar}
a.brand{display:flex;align-items:center;gap:8px;flex:0 0 auto;border-radius:var(--radius);font-weight:700;font-size:.9375rem;letter-spacing:-.15px;white-space:nowrap}
a.brand .brand-mark{width:28px;height:28px;display:block;flex:0 0 auto;color:var(--text);transition:color .2s ease}
.brand-text{display:none}
.topbar:not(:has(.topbar-page-name)) .brand-text{display:inline}
@media(min-width:1600px){.brand-text{display:inline}}

button.topbar-page-name{position:relative;display:inline-flex;align-items:center;gap:4px;flex:0 1 auto;min-width:0;max-width:240px;height:var(--ctl);margin-left:8px;padding:0 6px 0 8px;border:0;border-radius:var(--radius);background:transparent;color:var(--text);font:inherit;font-size:.8125rem;font-weight:650;cursor:pointer}
button.topbar-page-name::before{content:"";position:absolute;left:-8px;top:50%;width:1px;height:18px;margin-top:-9px;background:var(--border)}
button.topbar-page-name>span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
button.topbar-page-name:hover,button.topbar-page-name[aria-expanded="true"]{background:var(--bg-elev-2)}
button.topbar-page-name .ic{width:14px;height:14px;flex:0 0 auto;color:var(--text-dim);transition:transform var(--dur-2) var(--ease)}
button.topbar-page-name[aria-expanded="true"] .ic{transform:rotate(180deg)}

.topbar-tools{position:relative;flex:1 1 0;min-width:0;height:100%;display:flex;align-items:center;overflow-x:clip}
.topbar-tools:has(>.toolbar)::before{content:"";flex:0 0 1px;height:18px;margin-right:8px;background:var(--border)}
.topbar .topbar-tools>.toolbar{position:relative;flex:1 1 auto;min-width:0;height:100%;padding:0;border:0;background:none;gap:2px}
.topbar .toolbar :where(button.btn,a.btn,select,input.control){height:var(--ctl);min-height:var(--ctl);padding-block:0}
.topbar .toolbar :where(button.btn,a.btn){padding-inline:8px}
.topbar .toolbar .tb-sep{align-self:center;min-height:0;height:18px;margin-inline:4px}
.topbar .toolbar :focus-visible{outline-offset:-2px}
/* The last resort when icons alone do not fit: the toolbar scrolls sideways with a hidden
   scrollbar, and an overlay fades the edge where more is hidden. */
.topbar .topbar-tools>.toolbar[data-scroll]{overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:none}
.toolbar[data-scroll]::-webkit-scrollbar{display:none}
/* The arrows over the ends of a scrolling row (js/core/theme.js, setUpToolbar). Sticky, with a negative margin
   as wide as they are, so they lie over the row's ends instead of taking room in it; one shows only while there
   are buttons beyond its end. A soft shadow of the header colour fades the buttons that pass under it. */
.toolbar .strip-scroll{display:none}
.topbar .topbar-tools>.toolbar[data-scroll]>.strip-scroll{display:inline-flex;position:sticky;z-index:2;flex:0 0 auto;visibility:hidden;opacity:0;background:var(--bg-elev);color:var(--accent);transition:opacity var(--dur-2) var(--ease),visibility 0s linear var(--dur-2)}
.topbar .topbar-tools>.toolbar[data-scroll]>.strip-scroll[data-dir="start"]{order:-1;left:0;margin-right:calc(-1 * var(--ctl));box-shadow:10px 0 10px var(--bg-elev)}
.topbar .topbar-tools>.toolbar[data-scroll]>.strip-scroll[data-dir="end"]{order:99;right:0;margin-left:calc(-1 * var(--ctl));box-shadow:-10px 0 10px var(--bg-elev)}
.topbar .topbar-tools>.toolbar:is([data-fade="start"],[data-fade="both"])>.strip-scroll[data-dir="start"],
.topbar .topbar-tools>.toolbar:is([data-fade="end"],[data-fade="both"])>.strip-scroll[data-dir="end"]{visibility:visible;opacity:1;transition:opacity var(--dur-2) var(--ease)}
.topbar .topbar-tools>.toolbar>.strip-scroll .ic{color:var(--accent)}
/* A wide fade beside each arrow, so a button cut off under it fades out instead of showing half a glyph. */
.topbar .topbar-tools>.toolbar[data-scroll]>.strip-scroll::before{content:"";position:absolute;top:0;bottom:0;width:30px;pointer-events:none}
.topbar .topbar-tools>.toolbar[data-scroll]>.strip-scroll[data-dir="end"]::before{right:100%;background:linear-gradient(to right,transparent,var(--bg-elev) 70%)}
.topbar .topbar-tools>.toolbar[data-scroll]>.strip-scroll[data-dir="start"]::before{left:100%;background:linear-gradient(to left,transparent,var(--bg-elev) 70%)}
.topbar-tools:has(>.toolbar[data-fade])::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;background-repeat:no-repeat;background-size:28px 100%}
.topbar-tools:has(>.toolbar[data-fade="end"])::after{background-image:linear-gradient(270deg,var(--bg-elev),transparent);background-position:right}
.topbar-tools:has(>.toolbar[data-fade="start"])::after{background-image:linear-gradient(90deg,var(--bg-elev),transparent);background-position:left}
.topbar-tools:has(>.toolbar[data-fade="both"])::after{background-image:linear-gradient(90deg,var(--bg-elev),transparent),linear-gradient(270deg,var(--bg-elev),transparent);background-position:left,right}

.topbar-actions{position:relative;flex:0 0 auto;display:flex;align-items:center;gap:2px;margin-left:auto}
.topbar-tools:has(>.toolbar)+.topbar-actions::before{content:"";flex:0 0 1px;height:18px;margin-right:6px;background:var(--border)}
.topbar .icon-btn{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:var(--ctl);height:var(--ctl);min-height:0;padding:0;border:1px solid transparent;border-radius:var(--radius);background:transparent;color:var(--text);cursor:pointer;font-size:0;transition:background-color var(--dur-1) var(--ease),border-color var(--dur-1) var(--ease),transform var(--dur-1) var(--ease)}
.topbar .icon-btn:hover{background:var(--bg-elev-2);border-color:var(--border)}
.topbar .icon-btn:active{transform:scale(.93)}
.topbar .icon-btn svg{width:18px;height:18px;display:block}
.topbar .theme-btn{color:var(--accent)}
/* The theme button carries both glyphs, and the one it shows follows data-theme, which the blocking head script
   sets before the first paint: the sun offers the light theme from a dark one, the moon the dark theme from a
   light one. Drawing the right one in script instead meant a light-theme visitor's first frame showed the sun. */
.topbar .theme-btn .ic.theme-moon{display:none}
:root[data-theme="light"] .topbar .theme-btn .ic.theme-sun{display:none}
:root[data-theme="light"] .topbar .theme-btn .ic.theme-moon{display:block}

/* Inside a workspace tab the workspace's own bar carries the brand, theme and Settings. */
[data-in-workspace] .topbar>:not(.topbar-tools){display:none}
[data-in-workspace] .topbar-tools::before{display:none}
@media(min-width:900px){
  [data-in-workspace][data-toolbar]{--head-h:0px}
  [data-in-workspace][data-toolbar] .topbar{display:none}
}
@media(max-width:760px){
  .topbar-tools{position:static}
  /* Static here, so the fade would lie over the whole header; the strip's arrows mark its ends instead. */
  .topbar-tools::after{display:none}
}
@media(max-width:520px){
  .topbar .workspace-link{display:none}
  button.topbar-page-name>span{display:none}
  button.topbar-page-name{margin-left:4px;padding:0 4px}
  button.topbar-page-name::before{left:-4px}
}
/* A label that changes its words fades to them instead of jumping. */
@keyframes swap-in{from{opacity:0}to{opacity:1}}
.swap-in{animation:swap-in .24s ease both}
/* A region that opens by growing (one grid row from 0fr to 1fr). Flip data-open:
     <div class="fold" data-open="false"><div class="fold-in">...</div></div> */
.fold{display:grid;grid-template-rows:0fr;opacity:0;visibility:hidden;
  transition:grid-template-rows .3s cubic-bezier(.2,.7,.3,1),opacity .22s ease,visibility 0s linear .3s}
.fold[data-open="true"]{grid-template-rows:1fr;opacity:1;visibility:visible;
  transition:grid-template-rows .3s cubic-bezier(.2,.7,.3,1),opacity .22s ease .06s,visibility 0s}
.fold>.fold-in{min-height:0;overflow:hidden;padding:4px;margin:-4px}
/* Shared by every pane given to DevToolsUI.sidebar(). Each page sets how the pane
   shrinks; this makes what is inside it go quietly instead of being cut in half. */
.sidebar-fold{transition:opacity .2s ease,visibility 0s}
.sidebar-fold[data-collapsed="true"]{opacity:0;visibility:hidden;pointer-events:none;overflow:hidden;transition:opacity .16s ease,visibility 0s .32s}
@media(prefers-reduced-motion:reduce){.sidebar-fold,.sidebar-fold[data-collapsed="true"]{transition:none}}

/* Page changes and theme switches cross-fade while the header holds still. js/core/theme.js adds html.theme-swap
   during a theme switch so transitions are not blended twice. */
@view-transition{navigation:auto}
::view-transition-group(root),::view-transition-old(root),::view-transition-new(root){animation-duration:.2s;animation-timing-function:cubic-bezier(.2,.7,.3,1)}
::view-transition-group(topbar){animation-duration:0s}
html.theme-swap *,html.theme-swap *::before,html.theme-swap *::after{transition:none!important}
/* Anything you can point at eases its colour in and out. :where() keeps this at zero specificity. */
:where(a,button,input:not([type="color"]),select,textarea,summary,label,[role="button"],[role="tab"],[role="menuitem"]){transition:background-color .15s ease,color .15s ease,border-color .15s ease,box-shadow .15s ease,opacity .15s ease,fill .15s ease,stroke .15s ease}
@media(prefers-reduced-motion:reduce){
  @view-transition{navigation:none}
  ::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none!important}
  :where(a,button,input:not([type="color"]),select,textarea,summary,label,[role="button"],[role="tab"],[role="menuitem"]){transition:none}
}
.spacer{flex:1}
main{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:auto}

.toolbar{display:flex;align-items:center;gap:8px;flex:0 0 auto;flex-wrap:nowrap;background:var(--bg-elev)}
.toolbar>:where(:not(.spacer)){flex-shrink:0}
/* A toolbar is one line. When it does not fit, js/core/theme.js drops the words from its icon buttons one at
   a time (data-compact; the text stays as the button's name), and once every button is bare it hides the
   captions (data-fit="icons") and then lets the toolbar scroll sideways (data-scroll). */
.topbar .toolbar .btn[data-compact]{font-size:0;gap:0;min-width:var(--ctl);padding-inline:0;margin-inline-end:0}
.topbar .toolbar .btn[data-compact] :where(.kbd-hint,.lbl,.tb-rail-label){display:none}
.topbar .toolbar .btn[data-compact]:is([data-width-of],[data-label])::after{display:none}
.toolbar[data-fit="icons"] .tb-label{display:none}
/* Toolbar groups keep related controls together on one line. */
.tb-group{display:flex;align-items:center;gap:8px;min-width:0}
.tb-sep{flex:0 0 1px;align-self:stretch;min-height:22px;background:var(--border)}
.tb-label{font-size:.8125rem;color:var(--text-dim);white-space:nowrap}
/* The zoom control's percentage (js/core/zoom-controls.js), the same figure the Schema Designer shows. */
.zoom-level{font:600 .75rem var(--mono);min-width:38px;text-align:center;color:var(--text-dim)}
.tb-group.zoom-ctl{gap:2px}
/* The 100% button is square like the icons beside it, its figure boxed like an icon so it does not read as the
   level beside it; the figure keeps its size when a side toolbar hides words. */
.zoom-ctl .zoom-actual .zoom-actual-text{font:700 .5625rem/1 var(--mono);letter-spacing:-.03em;padding:3px 2px;border:1.5px solid currentColor;border-radius:3px}
@media(max-width:760px){.tb-sep{display:none}}
select,button.btn,a.btn,input.control{min-height:var(--control-height);background:var(--bg-elev);color:var(--text);border:1px solid var(--border);border-radius:var(--radius-control);padding:5px 10px;font-size:.8125rem}
select:not([multiple]):not([size]){-webkit-appearance:none;appearance:none;padding-right:32px;background-color:var(--bg-elev);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 14 14' fill='none'%3E%3Cpath d='m4 5.5 3 3 3-3' stroke='%23626269' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 9px center;background-size:14px 14px;line-height:1.2}
[data-theme="dark"] select:not([multiple]):not([size]){background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 14 14' fill='none'%3E%3Cpath d='m4 5.5 3 3 3-3' stroke='%23a1a1a9' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")}
/* Button-shaped controls centre their contents and keep one gap, even when something stretches them. */
button.btn,a.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;cursor:pointer}
a.btn{text-decoration:none}
button.btn:hover,a.btn:hover{border-color:var(--border-strong);background:var(--bg-elev-2)}button.btn.primary,a.btn.primary{background:var(--accent);border-color:var(--accent);color:#fff}[data-theme="dark"] button.btn.primary,[data-theme="dark"] a.btn.primary{color:#0b0b0c}[data-theme="dark"][data-accent="green"] button.btn.primary,[data-theme="dark"][data-accent="green"] a.btn.primary{color:#fff}
.tag{font-size:.75rem;color:var(--text-dim);background:var(--bg-elev-2);border:1px solid var(--border);padding:4px 8px;border-radius:var(--radius)}

/* A pane clips, so dragging its divider to the edge shuts it instead of scrolling the split sideways. */
.split{flex:1 1 auto;min-width:0;min-height:0;display:flex;overflow:hidden}.split.vertical{flex-direction:column}.pane{min-width:0;min-height:0;display:flex;flex-direction:column;flex:1;overflow:hidden}.pane+.pane{border-left:1px solid var(--border)}.split.vertical .pane+.pane{border-left:0;border-top:1px solid var(--border)}
.pane-header{min-height:37px;box-sizing:border-box;display:flex;align-items:center;gap:8px;font-size:.75rem;font-weight:650;color:var(--text-dim);padding:7px 10px;background:var(--bg-elev);border-bottom:1px solid var(--border);flex:0 0 auto}.pane-body{flex:1;min-height:0}
.pane-header.has-history{display:flex;align-items:center;gap:8px}
.pane-header.has-history>.editor-history{margin-left:auto}
/* The validity pill: one size on JSON, XML and YAML, wide enough for the longest
   verdict any of them shows, so no message length can move the toolbar. */
#validity{width:9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:center}
#validity.pass{color:var(--success)}
#validity.fail{color:var(--danger)}
#validity.warn{color:var(--warning)}
@media(max-width:700px){#validity{width:8rem}}
/* data-width-of reserves the width of the widest label a control ever carries, so a bar does not change
   width between tools. The ghost has no height. */
[data-width-of]::after{content:attr(data-width-of);display:block;height:0;overflow:hidden;visibility:hidden;pointer-events:none}
/* The ghost has to sit under the label, so such a control is a one-column grid: the label on one row and the
   ghost on the other. Spelled data-width-of, or data-label on the view tabs in JSON, XML and YAML Tools. */
button[data-width-of],button[data-label],a.btn[data-width-of],a.btn[data-label]{display:inline-grid;place-content:center;justify-items:center;gap:0}
/* With an icon, the icon and label share a row and the ghost spans underneath, so the button does not grow
   when its icon arrives. */
:is(button,a.btn):is([data-width-of],[data-label]):has(>.ic){grid-template-columns:auto 1fr;column-gap:6px;align-items:center}
:is(button,a.btn):is([data-width-of],[data-label]):has(>.ic)::after{grid-column:1/-1}
/* One toolbar strip for the panes: JSON, XML and YAML all put the same-sized
   buttons in the same place with it, rather than each page inventing a bar. */
.pane-tools{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:6px 10px}
.pane-tools .btn{font-size:12px;min-height:32px;padding:4px 10px}
.pane-tools input.control{flex:1 1 160px;min-width:0;font:12.5px var(--sans)}
.pane-title{font-size:.75rem;font-weight:650;color:var(--text-dim);flex:0 0 auto}
.pane-note{font:11.5px var(--mono);color:var(--text-dim);min-width:0}
.pane-count{font:11.5px var(--mono);color:var(--text-dim);white-space:nowrap}
@media(max-width:700px){.pane-tools .btn{flex:1 1 auto}.pane-tools input.control{flex:1 1 100%}}
/* Tree and Table views (JSON, XML, YAML Tools); the markup comes from js/data/data-views.js. .tree-bar is
   deliberately not a .pane-header: editor-history mounts on the first one in a pane. */
.tree-bar{border-bottom:1px solid var(--border);background:var(--bg-elev);flex:0 0 auto}
#path-input{font:12.5px var(--mono)}
#tree{flex:1 1 auto;min-height:0;overflow:auto;padding:12px 14px;font:13px/1.5 var(--mono)}
.tree-row{display:flex;align-items:flex-start;gap:6px;border-radius:var(--radius);padding:1px 4px;margin-left:-4px}
.tree-row:hover{background:var(--bg-elev)}
.tree-actions{margin-left:auto;display:none;gap:4px;flex:0 0 auto}
.tree-row:hover .tree-actions{display:flex}
.tree-actions button{border:1px solid var(--border);background:var(--bg-elev-2);color:var(--text-dim);border-radius:var(--radius);font:11px var(--mono);padding:0 6px;cursor:pointer;height:19px}
.tree-actions button:hover{border-color:var(--accent);color:var(--text)}
.tree-type{color:var(--text-dim);font-size:11.5px}
.tree-preview{color:var(--text-dim);opacity:.75;font-size:11.5px}
.t-string{color:var(--success)}
.t-number{color:var(--accent)}
.t-boolean{color:var(--accent-2)}
.t-null{color:var(--text-dim);font-style:italic}
.tree-hit{background:var(--accent-soft)}
#tree details{margin-left:14px}
#tree summary{cursor:pointer;color:var(--accent-2)}
#tree .key{color:var(--accent)}
#tree .value{color:var(--text-dim)}
/* Table view: the array underneath, one row per item, one column per key.
   Cells clip instead of stretching a column to fit one long string. */
#table{flex:1 1 auto;min-height:0;overflow:auto}
.data-table{border-collapse:collapse;font-size:12.5px;min-width:100%}
.data-table th,.data-table td{border-bottom:1px solid var(--border);padding:7px 10px;text-align:left;vertical-align:top;white-space:nowrap;max-width:320px;overflow:hidden;text-overflow:ellipsis}
.data-table th{position:sticky;top:0;z-index:1;background:var(--bg-elev);color:var(--text-dim);font:600 11.5px var(--mono)}
.data-table tbody tr:hover{background:var(--bg-elev)}
.data-table td.idx{color:var(--text-dim);font:11.5px var(--mono);text-align:right;width:1%}
.data-table td .t-string{color:var(--success)}
.data-table td .t-number{color:var(--accent)}
.data-table td .t-boolean{color:var(--accent-2)}
.data-table td .t-null{color:var(--text-dim);font-style:italic}
.table-empty{padding:16px 14px;color:var(--text-dim);font-size:13px}

.editor-history{display:inline-flex;gap:3px;flex:0 0 auto;transition:opacity .18s ease}
/* Dragging a pane shut fades its controls (js/core/resizable.js sets .is-narrow) instead of slicing them. */
.pane.is-narrow .editor-history{opacity:0;pointer-events:none}
.editor-history.floating{position:absolute;top:6px;right:14px;z-index:4}
/* A floating pair never covers text (js/core/editor-history.js): over a textarea it is stacked and the
   textarea's right padding grows to hold it; beside CodeMirror or Monaco the editor gives up a column of
   --history-gutter for it. */
.editor-history.floating.stacked,.history-beside>.editor-history.floating{flex-direction:column}
:root{--history-gutter:32px}
.history-beside{background:var(--editor-bg)}
.history-beside>.CodeMirror,.history-beside>#monaco-container{margin-right:var(--history-gutter)}
.history-beside>#monaco-container{width:auto}
.history-beside>.editor-history.floating{right:4px}
.editor-history-btn{display:inline-flex;align-items:center;justify-content:center;width:24px;height:22px;padding:0;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-elev);color:var(--text-dim);cursor:pointer}
.editor-history-btn:hover:not(:disabled){border-color:var(--border-strong);color:var(--text);background:var(--bg-elev-2)}
.editor-history-btn:disabled{opacity:.38;cursor:default}
.editor-history-btn svg{width:13px;height:13px;display:block}
.pane-body>textarea,.editor-wrap>textarea{width:100%;height:100%;box-sizing:border-box;border:0;border-radius:0;resize:none;background:var(--editor-bg);color:var(--text);padding:4px 0 4px 37px;font:.84375rem/1.55 var(--mono)}
iframe{width:100%;height:100%;border:0;background:var(--bg-elev)}.CodeMirror{height:100%!important;font-family:var(--mono)!important;font-size:.84375rem;line-height:1.55}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{font:inherit}.CodeMirror-gutters{border-right-color:var(--border)!important}.CodeMirror-hscrollbar{left:0!important}
/* One dark canvas for every editor. dracula sets its background with !important and loads after this file,
   so the html prefix carries the override. */
html .cm-s-dracula.CodeMirror,html .cm-s-dracula .CodeMirror-gutters{background-color:var(--editor-bg)!important}
/* Local HTML/JS hint menu. show-hint.css supplies position; these tokens
   keep the list on the site palette in light and dark. */
/* Only ever the second child of a .btn, whose gap now sets the space before it. */
/* The shortcut a button answers to. A keycap with a fixed height, centred like the icon and the
   label beside it: bare text in a smaller monospace face sat a pixel off the label's centre. */
.kbd-hint{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;height:18px;padding:0 5px;border-radius:var(--radius);background:color-mix(in srgb,currentColor 14%,transparent);opacity:.9;font:600 .625rem/1 var(--mono);letter-spacing:.02em;white-space:nowrap}
.save-badge{display:inline-flex;align-items:center;gap:6px;color:var(--text-dim)}
.save-badge:empty{display:none}
.save-badge svg{width:14px;height:14px;display:block;flex:0 0 auto}
.save-badge[data-save-state=warn]{color:var(--danger)}
/* ul/li in the selectors: show-hint.css loads after this file and would win a tie, giving the list its own white and blue. */
ul.CodeMirror-hints{z-index:20;background:var(--bg-elev);border:1px solid var(--border);border-radius:var(--radius-lg,var(--radius));box-shadow:0 0 0 1px var(--border-strong);padding:4px;font:12.5px/1.45 var(--mono);max-height:14.5rem}
ul.CodeMirror-hints li.CodeMirror-hint{color:var(--text);border-radius:var(--radius);padding:3px 8px;white-space:pre}
ul.CodeMirror-hints li.CodeMirror-hint-active{background:var(--accent-soft);color:var(--text);box-shadow:inset 2px 0 0 var(--accent-2)}
#monaco-container{flex:1 1 auto;min-height:0;width:100%;height:100%}
[data-resizable]{display:flex;min-height:0}[data-resizable][data-resize-direction="vertical"]{flex-direction:column}.resize-handle{flex:0 0 5px;position:relative;z-index:2;background:var(--border);cursor:col-resize;touch-action:none}.resize-handle:before{content:"";position:absolute;inset:0 -5px}.resize-handle:hover,.resize-handle:active,[data-resizable].is-resizing .resize-handle{background:var(--accent)}[data-resizable][data-resize-direction="vertical"] .resize-handle,[data-resizable].is-vertical-axis .resize-handle{cursor:row-resize}[data-resizable][data-resize-direction="vertical"] .resize-handle:before,[data-resizable].is-vertical-axis .resize-handle:before{inset:-5px 0}body.is-split-resizing,[data-resizable].is-resizing{cursor:col-resize}[data-resizable][data-resize-direction="vertical"].is-resizing,[data-resizable].is-vertical-axis.is-resizing,body.is-split-resizing:has([data-resizable][data-resize-direction="vertical"].is-resizing),body.is-split-resizing:has([data-resizable].is-vertical-axis.is-resizing){cursor:row-resize}body.is-split-resizing iframe,[data-resizable].is-resizing iframe,body.is-split-resizing .CodeMirror,[data-resizable].is-resizing .CodeMirror{pointer-events:none}
textarea{background:var(--bg-elev);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);padding:12px;font:.84375rem/1.55 var(--mono);resize:vertical}textarea:focus{border-color:var(--accent)}
/* A drag over a text box: an outline (the panes have no border), with a fixed label placed from JS. */
textarea.drop-target,.CodeMirror.drop-target{outline:2px solid var(--accent);outline-offset:-2px}
.drop-hint{position:fixed;left:var(--drop-x);top:var(--drop-y);width:var(--drop-w);height:var(--drop-h);display:flex;visibility:hidden;opacity:0;transition:opacity .18s ease,visibility 0s linear .18s;align-items:flex-start;justify-content:center;padding:14px;box-sizing:border-box;pointer-events:none;z-index:60;border-radius:var(--radius);background:color-mix(in srgb,var(--bg) 55%,transparent)}
.drop-hint[data-visible]{visibility:visible;opacity:1;transition:opacity .18s ease,visibility 0s}
/* The label sits at the top (a pane is tall) with its own background, so it reads in the dark modes. */
.drop-hint-label{max-width:100%;padding:6px 12px;border-radius:var(--radius);text-align:center;font:650 .8125rem/1.35 var(--sans);color:#fff;background:var(--accent);box-shadow:none}
[data-theme="dark"] .drop-hint-label{color:#0b0b0c}
[data-theme="dark"][data-accent="green"] .drop-hint-label{color:#fff}
.drop-hint[data-tone="ok"] .drop-hint-label,.drop-hint[data-tone="bad"] .drop-hint-label{color:#fff}
.drop-hint[data-tone="ok"] .drop-hint-label{background:var(--success)}
.drop-hint[data-tone="bad"] .drop-hint-label{background:var(--danger)}
.output-pane{background:var(--bg-elev);font:.78125rem var(--mono);overflow:auto;padding:8px 12px}.output-line{padding:4px 0;border-bottom:1px solid var(--border);white-space:pre-wrap;word-break:break-word}.output-error,.fail{color:var(--danger)}.output-warn{color:var(--warning)}.output-log{color:var(--text)}.pass{color:var(--success)}
/* Catalogue and start page: the footer follows the content. Tool pages hide it (html.chrome-compact). */
footer.note{position:static;padding:7px 14px;font-size:.75rem;line-height:1.4;color:var(--text-dim);text-align:center;border-top:1px solid var(--border);background:transparent;flex:0 0 auto}
footer.note a{color:var(--text);text-decoration:underline;text-underline-offset:3px}
@media(pointer:coarse){footer.note{padding:4px 14px}footer.note a{display:inline-block;padding-block:12px}}
.footer-separator{padding:0 6px;color:var(--border-strong)}
html.chrome-compact footer.note{display:none}
/* The footer scrolls away with the content but rests on the bottom edge of a short page. min-height is a
   viewport unit, as a percentage resolves to nothing against an auto-height <html>; dvh avoids a gap when
   mobile browser chrome collapses, vh is the fallback. */
html.landing-chrome{height:auto;min-height:100vh;min-height:100dvh;overflow-y:auto}
html.landing-chrome body{height:auto;min-height:100vh;min-height:100dvh;overflow:visible}
html.landing-chrome main{overflow:visible;flex:1 0 auto}
html.landing-chrome footer.note{margin-top:auto}

/* Homepage */
.catalog-page{width:min(1200px,100%);margin:0 auto;padding:48px clamp(16px,3vw,34px) 20px}
.catalog-count{font:.75rem/1 var(--mono);color:var(--text-dim);white-space:nowrap;font-variant-numeric:tabular-nums;padding-bottom:2px}
.note .catalog-count{font:inherit;padding:0}
/* A link that appears when it is reached by keyboard, so the tool list is one Tab from the top. */
.skip-link{position:absolute;left:12px;top:-56px;z-index:100;padding:8px 14px;border-radius:var(--radius);background:var(--text);color:var(--bg-elev);font-size:.8125rem;font-weight:650;text-decoration:none;transition:top .15s ease}
.skip-link:focus{top:8px}
@media(prefers-reduced-motion:reduce){.skip-link{transition:none}}
/* Search docks at the head of the category row. */
.catalog-find{display:flex;align-items:stretch;flex-wrap:wrap;gap:0 16px;border-bottom:1px solid var(--border);margin-bottom:15px}
/* A dock, not a pill: flat on the tabs' baseline, underlined in the accent. */
.catalog-search{display:grid;grid-template-columns:26px minmax(0,1fr);align-items:center;gap:8px;flex:0 1 260px;min-width:190px;padding:2px 2px 9px;border-bottom:2px solid transparent;margin-bottom:-1px;transition:border-color .18s ease}
.catalog-search:hover{border-bottom-color:var(--border-strong)}
.catalog-search:focus-within{border-bottom-color:var(--accent)}
/* The wrapper's accent underline IS this field's focus indicator; the shared
   ring would draw a second one as a pale box around the bare input. */
.catalog-search input:focus-visible{outline:none}
.catalog-search-mark{width:26px;height:26px;display:grid;place-items:center;background:transparent;color:var(--text-dim);cursor:pointer;transition:color .18s ease}
.catalog-search:focus-within .catalog-search-mark{color:var(--accent)}
.catalog-search-mark svg{width:16px;height:16px}
.catalog-search input{width:100%;min-width:0;height:26px;padding:0;border:0;border-radius:0;background:transparent;color:var(--text);font-size:.875rem;letter-spacing:-.1px;caret-color:var(--accent);appearance:none;outline:none}
.catalog-search input::placeholder{color:var(--text-dim)}
.catalog-search input::selection{background:var(--accent-soft);color:var(--text)}
/* overflow:auto on the base rule shows a vertical scrollbar once the strip is
   stretched to the dock's height; only sideways scrolling is ever wanted. */
.catalog-find .category-tabs{flex:1 1 auto;align-items:stretch;justify-content:safe center;border-bottom:0;padding-bottom:0;min-width:0;max-width:none;gap:2px;overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.catalog-find .category-tabs::-webkit-scrollbar::-webkit-scrollbar{display:none}
.catalog-find .collapse-all{margin-left:auto;align-self:center}
/* The tabs centre on the page: the dock and collapse control leave the flow above 760px and the nav reserves
   the same width each side. */
@media(min-width:1100px){.catalog-find{position:relative}
 /* A stacking order keeps the full-width nav from swallowing clicks on the search field. */
 .catalog-find>.catalog-search{position:absolute;left:0;bottom:0;width:260px;flex:none;z-index:1}
 .catalog-find .collapse-all{position:absolute;right:0;top:50%;transform:translateY(-50%);margin:0}
 .catalog-find .category-tabs{flex:1 1 100%;padding-inline:276px}}
/* Below the wide layout the tabs stay one row and scroll sideways, so the active rule stays under its tab. */
@media(max-width:1099px){.catalog-find .category-tabs{flex:1 1 0;flex-wrap:nowrap;justify-content:flex-start}.catalog-find .category-tabs button{flex:0 0 auto}.catalog-find .collapse-all{flex:0 0 auto;position:sticky;right:0;z-index:1;padding-left:12px;background:var(--bg)}}
@media(max-width:760px){.catalog-search{flex:1 1 100%}
 /* A phone shows every category: the tabs wrap onto a second row rather than scroll under Collapse all. */
 .catalog-find .category-tabs{flex-wrap:wrap;overflow:visible}.catalog-find .collapse-all{position:static;margin-left:auto;padding-left:4px;background:transparent}}
@media(max-width:760px),(pointer:coarse){.catalog-search{grid-template-columns:30px minmax(0,1fr)}.catalog-search input{height:34px}}
.category-tabs{position:relative;display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:2px;overflow:auto;padding-bottom:12px;border-bottom:1px solid var(--border)}/* Tabs sit on the same rule as the search dock and mark themselves with an
   underline in their own category colour, so the row reads as one control. */
.category-tabs button{border:0;border-radius:0;background:transparent;color:var(--text-dim);padding:8px 11px;font-size:.8125rem;font-weight:550;cursor:pointer;white-space:nowrap;border-bottom:2px solid transparent;margin-bottom:-1px;transition:background-color .15s ease,color .15s ease,box-shadow .15s ease}.category-tabs button:hover{background:transparent;color:var(--text)}
/* The active filter takes the category's own colour, so the chip, the section
   rail below it and the tool icons all agree on what that category looks like. */
.category-tabs button.active{background:transparent;color:var(--cc,var(--text));border-bottom-color:transparent}
.category-tabs button:not(.active):hover{border-bottom-color:color-mix(in srgb,var(--cc,var(--accent)) 24%,transparent)}
.category-indicator{position:absolute;left:0;bottom:-1px;width:var(--indicator-width,0);height:2px;background:var(--indicator-color,var(--text));transform:translate(var(--indicator-x,0),var(--indicator-y,0));pointer-events:none}
@media(prefers-reduced-motion:no-preference){.category-indicator[data-ready="true"]{transition:transform .3s cubic-bezier(.2,.8,.2,1),width .3s cubic-bezier(.2,.8,.2,1),background-color .3s ease}}
/* A quiet text control, not a boxed button; the chevron carries the state. */
.collapse-all{display:inline-flex;align-items:center;gap:6px;margin-left:10px;padding:5px 4px;font-size:.75rem!important;font-weight:550;color:var(--text-dim);background:transparent;border:0!important;border-radius:var(--radius);cursor:pointer;white-space:nowrap;transition:color .15s ease}
.collapse-all::before{content:"";width:7px;height:7px;border-left:1.6px solid currentColor;border-bottom:1.6px solid currentColor;transform:translateY(-2px) rotate(-45deg);transition:transform .2s cubic-bezier(.2,.7,.3,1)}
.collapse-all[data-state="collapsed"]::before{transform:translateY(1px) rotate(135deg)}
.collapse-all:hover{color:var(--text);background:transparent}
.collapse-all:focus-visible{color:var(--text)}

/* Category sections are drawers: a head with a pull, and a tray that slides out of it. */
.cat-group{position:relative;border-top:1px solid var(--border)}
.cat-group:first-child{border-top:0}
.cat-head{display:flex;align-items:center;gap:10px;width:100%;padding:18px 2px 12px;border:0;background:transparent;color:var(--text);cursor:pointer;text-align:left}
.cat-head .chev{width:13px;height:13px;flex:0 0 auto;color:var(--cc,var(--text-dim));transform:rotate(90deg);transition:transform .3s cubic-bezier(.2,.7,.3,1)}
.cat-head[aria-expanded="false"] .chev{transform:rotate(0deg)}
.cat-name{font-size:.9375rem;font-weight:650;color:var(--text);transition:color .15s ease}
.cat-head:hover .cat-name{color:var(--cc,var(--accent))}
.cat-count{font-size:.8125rem;color:var(--text-dim);font-variant-numeric:tabular-nums}
.cat-head::after{display:none}
/* The handle. It is the drawer's affordance, so it grows a little under the pointer. */
.cat-body{display:grid;grid-template-rows:0fr;visibility:hidden;opacity:0;border-top:0 solid var(--border);transition:grid-template-rows .34s cubic-bezier(.2,.7,.3,1),opacity .22s ease,visibility 0s .34s,border-top-width 0s .34s}
.cat-group[data-open="true"] .cat-body{grid-template-rows:1fr;visibility:visible;opacity:1;transition:grid-template-rows .34s cubic-bezier(.2,.7,.3,1),opacity .26s ease .06s,visibility 0s,border-top-width 0s}
.cat-body-in{min-height:0;overflow:hidden}
.cat-body .tool-index{padding:0 0 22px}
@media(prefers-reduced-motion:reduce){.cat-body,.cat-group[data-open="true"] .cat-body,.cat-head .chev{transition:none}}

.tool-index{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(300px,100%),1fr));gap:12px}
.tool-cell{position:relative;min-width:0;display:flex;border:1px solid color-mix(in srgb,var(--cc,var(--accent)) 14%,var(--border));border-radius:var(--radius-lg);background:var(--bg-elev);transition:border-color .16s ease,background-color .16s ease}
.tool-cell:hover,.tool-cell:focus-within{border-color:color-mix(in srgb,var(--cc,var(--accent)) 45%,var(--border))}
.tool-row{position:relative;flex:1;display:grid;grid-template-columns:20px minmax(0,1fr) auto;gap:12px;align-items:start;padding:15px 40px 15px 14px;border-radius:inherit;text-decoration:none;color:var(--text)}
.tool-row:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* The best search match, which Enter opens: its arrow shows as if hovered. */
.tool-row.top-hit .go{opacity:1;transform:none;color:var(--cc,var(--accent))}
.tool-text{min-width:0}
.pin-btn{position:absolute;top:6px;right:7px;width:26px;height:26px;display:grid;place-items:center;padding:0;border:0;border-radius:var(--radius);background:transparent;color:var(--text-dim);cursor:pointer;opacity:.55;transition:opacity .12s ease,color .12s ease}
.pin-btn svg{width:16px;height:16px}
.tool-cell:hover .pin-btn{opacity:.7}
.pin-btn:hover,.pin-btn:focus-visible{opacity:1;background:var(--bg-elev-2);color:var(--warning)}
.pin-btn.pinned,.tool-cell:hover .pin-btn.pinned{opacity:1;color:var(--warning)}
.fav-block{margin:0 0 22px}
.fav-head{display:flex;align-items:baseline;gap:12px;margin:0 0 10px}
.fav-head h2{font-size:.9375rem;font-weight:650;letter-spacing:-.02em;color:var(--text);margin:0}
.fav-count{font:.75rem var(--mono);color:var(--text-dim)}
.fav-head .btn{margin-left:auto;min-height:28px;padding:2px 10px;font-size:.75rem}
.fav-dock{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(200px,100%),1fr));gap:8px}
.fav-cell{position:relative;min-width:0}
.fav-chip{display:flex;align-items:center;gap:8px;width:100%;height:44px;padding:8px 36px 8px 8px;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-elev);text-decoration:none;color:var(--text);overflow:hidden}
.fav-chip:hover{border-color:var(--accent);background:var(--bg-elev-2)}
.fav-chip .tool-icon{width:28px;height:28px;border-radius:var(--radius);flex:0 0 auto}
.fav-chip .tool-icon svg{width:16px;height:16px}
.fav-name{flex:1;font-size:.8125rem;font-weight:650;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fav-cell .pin-btn{top:50%;right:7px;transform:translateY(-50%);opacity:.7;color:var(--warning)}
/* --cc is inherited from the [data-cat] ancestor; the fallback keeps an
   uncategorised icon on the accent rather than leaving it colourless. */
.tool-icon{width:20px;height:20px;display:grid;place-items:center;margin-top:1px;color:var(--cc,var(--accent));flex:0 0 auto}
.tool-icon svg{width:18px;height:18px}
.tool-row .tool-category{font-size:.75rem;font-weight:400;color:var(--text-dim)}
/* One hue per category, used by the icon tile, the section rail and the section
   heading, so a colour means the same thing everywhere on the page. */
[data-cat="Data & Code"]{--cc:var(--cat-data)}
[data-cat="Text"]{--cc:var(--cat-text)}
[data-cat="API"]{--cc:var(--cat-api)}
[data-cat="Utilities"]{--cc:var(--cat-utility)}
[data-cat="Design"]{--cc:var(--cat-design)}
[data-cat="Productivity"]{--cc:var(--cat-productivity)}
[data-cat="References"]{--cc:var(--cat-reference)}
[data-cat="Other"]{--cc:var(--cat-reference)}
.tool-row h2{font-size:.90625rem;margin:0 0 3px;letter-spacing:-.1px;display:flex;align-items:center;gap:7px;flex-wrap:wrap}.new-badge{font-size:.75rem;font-weight:650;color:var(--success)}
.tool-row p{font-size:.78125rem;line-height:1.4;color:var(--text-dim);margin:0;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.tool-row .go{color:var(--text-dim);font-size:1rem;opacity:0;transform:translateX(-4px);transition:opacity .16s ease,transform .16s cubic-bezier(.2,.7,.3,1)}.tool-row:hover .go,.tool-row:focus-visible .go{color:var(--cc,var(--accent));opacity:1;transform:none}.empty-state{padding:50px 16px;text-align:center;color:var(--text-dim);border:1px solid var(--border);border-radius:var(--radius);margin-top:16px}

@media (prefers-reduced-motion:no-preference){
  .fav-chip{transition:border-color .16s ease,background-color .16s ease}
  .pin-btn.pinned svg{animation:pinPop .32s cubic-bezier(.16,1,.3,1)}
  @keyframes pinPop{0%{transform:scale(.6)}60%{transform:scale(1.18)}100%{transform:none}}
}
@media(max-width:760px){
  .pin-btn{opacity:.65}
  .split:not(.vertical){flex-direction:column}
  .split:not(.vertical) .pane+.pane{border-left:0;border-top:1px solid var(--border)}
}
@media(max-width:1199px){.tool-row p{-webkit-line-clamp:3}}
@media(max-width:480px){.tool-row{padding-right:38px}.tool-row p{display:block;-webkit-line-clamp:unset}.catalog-page{padding-top:28px}}
/* Phones and touch screens: 44px touch targets are an accessibility floor, so the header row grows to hold them. */
@media(max-width:760px), (pointer:coarse){
button,.btn,a.btn{min-width:44px!important;min-height:44px!important}
:root{--history-gutter:52px}
:root,:root[data-targets]{--bar-h:max(44px,var(--tap-bar,0px))}
.topbar{--ctl:max(44px,var(--tap,0px));gap:4px}
.topbar .icon-btn svg{width:20px;height:20px}
select,input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]){min-height:44px!important}
.pin-btn,.wb-tool,.wb-sw,.wb-zoom button,.wb-zoom .level,.icon-btn{min-width:44px!important;height:44px!important;min-height:44px!important}
.fav-chip{padding-right:48px}
label:has(>input[type="checkbox"]),label:has(>input[type="radio"]){display:inline-flex;align-items:center;min-height:44px}
}
@media(prefers-reduced-motion:reduce){*,*:before,*:after{scroll-behavior:auto!important;transition:none!important;animation:none!important}}

/* The 404 page (404.html) carries no inline style, so its rules live here. */
.not-found{display:flex;align-items:center;justify-content:center;padding:60px 22px;flex:1}
.not-found>div{text-align:center;max-width:420px}
.not-found h1{font-size:1.625rem;letter-spacing:-.6px;margin:0 0 10px}
.not-found p{color:var(--text-dim);font-size:.875rem;line-height:1.6;margin:0 0 16px}
.not-found a{color:var(--text);text-decoration:underline;text-underline-offset:3px}

/* Keyboard focus stays visible across all reading modes and tool controls. */
:is(button,a,input,textarea,select,summary,[tabindex]):focus-visible{outline:2px solid var(--text);outline-offset:3px}
/* CodeMirror's touch input is the editable text block itself; the ring above drew two bright lines across
   the editor, clipped at its sides. The caret shows where focus is. */
.CodeMirror-code:focus-visible{outline:none}

/* Word wrap toggle on the CodeMirror tools (DevToolsWrap in theme.js). */
.btn.wrap-toggle[aria-pressed="true"]{border-color:var(--accent-soft);background:var(--accent-soft);color:var(--text)}
/* Data folder needs permission again after a restart (folder-storage.js). */

/* The canvas takes the theme's colour from the first frame (js/core/theme.js also sets color-scheme inline),
   so there is no white flash between pages. */
:root{color-scheme:dark;background:var(--bg)}
:root[data-theme="light"]{color-scheme:light}

/* ---------- shared controls ----------
   Appended last so these win on equal specificity. Motion, radius and shadow are tokens. */
:root{
  --ease:cubic-bezier(.2,.7,.2,1);
  --dur-1:.12s;--dur-2:.2s;--dur-3:.32s;
  --radius-lg:4px;
  --ctl:32px;
  /* No blurred shadows anywhere: a floating layer is set apart by a firmer hairline. */
  --shadow-pop:0 0 0 1px var(--border-strong);
  /* Syntax colours follow the category legend; strings get their own teal, as --cat-text is too light at code size on white. */
  --syn-keyword:var(--cat-data);
  --syn-string:#0a7a6d;
  --syn-number:var(--cat-utility);
  --syn-atom:var(--cat-design);
  --syn-key:var(--accent);
  --syn-tag:var(--cat-code);
  --syn-attr:var(--cat-api);
  --syn-comment:var(--text-dim);
}
[data-theme="dark"]{--syn-string:var(--cat-text);--syn-key:var(--accent-2)}
:root[data-targets]{--ctl:var(--tap)}

.ic{width:1.125rem;height:1.125rem;flex:0 0 auto;display:block}
.ic .f{fill:currentColor;fill-opacity:.14}
button.btn .ic,a.btn .ic{width:16px;height:16px}
button.btn .ic{color:var(--text-dim);transition:color var(--dur-1) var(--ease)}
button.btn:hover:not(:disabled) .ic{color:var(--text)}
button.btn.primary .ic,button.btn.danger .ic{color:inherit}

button.btn,a.btn,select,input.control{transition:background-color var(--dur-1) var(--ease),border-color var(--dur-1) var(--ease),color var(--dur-1) var(--ease),box-shadow var(--dur-1) var(--ease),transform var(--dur-1) var(--ease),filter var(--dur-1) var(--ease)}
button.btn:active:not(:disabled),a.btn:active{transform:translateY(.5px) scale(.985)}
button.btn:disabled{opacity:.45;cursor:default}
button.btn.primary:hover:not(:disabled),a.btn.primary:hover{filter:brightness(1.1)}
/* An icon button is a square. Never narrower than it is tall. */
button.btn.icon,a.btn.icon{width:var(--ctl);min-width:var(--ctl);padding:0}
button.btn.icon .ic,a.btn.icon .ic{width:18px;height:18px}
button.btn.danger{background:var(--danger);border-color:var(--danger);color:#fff}
[data-theme="dark"] button.btn.danger{color:#0b0b0c}
button.btn.danger:hover:not(:disabled){filter:brightness(1.08);background:var(--danger);border-color:var(--danger)}

textarea,input.control,select{transition:border-color var(--dur-1) var(--ease),box-shadow var(--dur-1) var(--ease),background-color var(--dur-1) var(--ease)}
input.control:hover,select:hover,textarea:hover{border-color:var(--border-strong)}
/* Text fields take a soft accent ring instead of the keyboard outline; the border changes too, so state never
   rests on the ring alone. */
input.control:focus-visible,textarea:focus-visible,select:focus-visible{outline:none;border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}
::placeholder{color:var(--text-dim);opacity:.8}

/* ---------- the strip: a toolbar that does not shout ----------
   Only the primary action is drawn as a button; the rest are quiet until hovered. Opt in with class="toolbar strip". */
.toolbar.strip{gap:2px}
.strip .btn:not(.primary){background:transparent;border-color:transparent;color:var(--text)}
.strip .btn:not(.primary):hover:not(:disabled){background:var(--bg-elev-2);border-color:transparent}
.strip .btn.danger{background:var(--danger);border-color:var(--danger);color:#fff;padding-inline:12px;margin-inline-end:4px}
[data-theme="dark"] .strip .btn.danger{color:#0b0b0c}
.strip .btn.danger:hover:not(:disabled){background:var(--danger);border-color:var(--danger);filter:brightness(1.08)}
/* A primary or danger button that cannot be pressed now gives up its fill: a filled button that does nothing
   draws the eye away from the one that does. */
button.btn.primary:disabled,button.btn.danger:disabled,.strip .btn.primary:disabled,.strip .btn.danger:disabled{background:transparent;border-color:var(--border);color:var(--text-dim);filter:none}
.strip .btn.primary:disabled,.strip .btn.danger:disabled{border-color:transparent}
.strip .btn[aria-pressed="true"]:not(.primary),.strip .btn.on:not(.primary){background:var(--accent-soft)}
.strip .btn[aria-pressed="true"]:not(.primary) .ic,.strip .btn.on .ic{color:var(--accent-2)}
.strip .btn.mode-tab.active:not(.primary){background:var(--accent-soft);color:var(--text);border-color:transparent}
.strip .btn.primary{padding-inline:12px;margin-inline-end:4px}
.strip select{background-color:transparent;border-color:transparent}
.strip select:hover{background-color:var(--bg-elev-2);border-color:transparent}
.strip .tb-group{gap:2px}
.strip .tb-sep{margin-inline:6px}
.strip .tb-label{margin-inline:4px 2px}

/* ---------- the toolbar as a column ----------
   Settings can put a tool's toolbar down the left or right edge instead of in the header. js/core/theme.js sets
   data-toolbar on <html> and moves the toolbar out of the header into <body>, which is the only place
   body>.toolbar matches. <body> makes room with padding and the header reaches back across the gap, so
   it still spans the window. The column starts under the header (--head-h). Below 900px it would eat the
   tool, so the toolbar stays in the header. */
.tb-rail-label{display:none}
button.btn.rail-toggle{display:none}
@media(min-width:900px){
  :root{--rail-w:188px}
  :root[data-rail="collapsed"]{--rail-w:56px}
  :root[data-toolbar="left"] body:has(>.toolbar){padding-left:var(--rail-w)}
  :root[data-toolbar="right"] body:has(>.toolbar){padding-right:var(--rail-w)}
  :root[data-toolbar="left"] body:has(>.toolbar)>.topbar{margin-left:calc(var(--rail-w)*-1)}
  :root[data-toolbar="right"] body:has(>.toolbar)>.topbar{margin-right:calc(var(--rail-w)*-1)}

  /* The build puts the toolbar in the header, and theme.js moves it out to the column as soon as the header has
     been parsed, before the first paint. Should a frame be painted in between (a slow connection delivering the
     page in pieces), it is not shown in the header first, only to jump to the side. */
  :root[data-toolbar] .topbar-tools>.toolbar{visibility:hidden}
  :root[data-toolbar] body>.toolbar{position:absolute;top:var(--head-h);bottom:0;z-index:3;width:var(--rail-w);flex-direction:column;flex-wrap:nowrap;align-items:stretch;gap:2px;padding:10px 8px;overflow-x:hidden;overflow-y:auto;border-bottom:0;scrollbar-width:thin}
  :root[data-toolbar="left"] body>.toolbar{left:0;border-right:1px solid var(--border)}
  :root[data-toolbar="right"] body>.toolbar{right:0;border-left:1px solid var(--border)}

  /* Each control is a full-width row. The primary action keeps its fill and leads the column. */
  :root[data-toolbar] body>.toolbar :where(button.btn,a.btn){width:100%;min-height:36px;padding:0 10px;justify-content:flex-start;gap:10px;margin:0;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  :root[data-toolbar] body>.toolbar .kbd-hint{margin-left:auto}
  /* A button that holds room for its longest label (data-width-of) is a grid, to stack that hidden copy
     under the real one. A full-width row needs no such reserve, so it goes back to an icon beside its words. */
  :root[data-toolbar] body>.toolbar :where(button,a.btn):is([data-width-of],[data-label]){display:inline-flex;place-content:normal;justify-content:flex-start;justify-items:normal}
  :root[data-toolbar] body>.toolbar :is([data-width-of],[data-label])::after{display:none}
  /* A button with only a glyph says what it is, from its tooltip. */
  :root[data-toolbar] body>.toolbar .tb-rail-label{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;overflow:hidden;white-space:normal;line-height:1.25;text-align:left}
  :root[data-toolbar] body>.toolbar .tb-icon{height:auto;padding-block:6px}
  /* The chosen one is marked on the edge that faces the page. */
  :root[data-toolbar="left"] body>.toolbar :where(.btn.active,.btn.on,.btn[aria-pressed="true"]):not(.primary){box-shadow:inset 3px 0 0 var(--accent-2)}
  :root[data-toolbar="right"] body>.toolbar :where(.btn.active,.btn.on,.btn[aria-pressed="true"]):not(.primary){box-shadow:inset -3px 0 0 var(--accent-2)}

  /* Dividers lie down, groups stack, and a select or a segmented control fills the width. */
  :root[data-toolbar] body>.toolbar .tb-sep{flex:0 0 1px;align-self:stretch;width:auto;min-height:0;height:1px;margin:6px 4px}
  :root[data-toolbar] body>.toolbar .tb-group{flex-direction:column;align-items:stretch;gap:2px}
  :root[data-toolbar] body>.toolbar .tb-label{margin:8px 4px 2px;white-space:normal}
  :root[data-toolbar] body>.toolbar select{width:100%;min-width:0}
  :root[data-toolbar] body>.toolbar .seg{display:flex;width:100%}
  :root[data-toolbar] body>.toolbar .spacer{flex:1 1 auto;min-height:8px;min-width:0}

  /* The button that folds the column to icons and back sits at the foot of the column and stays in view
     while the buttons above it scroll. */
  :root[data-toolbar] body>.toolbar .rail-toggle{display:inline-flex;margin-top:auto;position:sticky;bottom:-10px;flex:0 0 auto;background:var(--bg-elev);border-top:1px solid var(--border);border-radius:0;padding-block:8px;min-height:44px}
  :root[data-toolbar] body>.toolbar .rail-toggle:hover:not(:disabled){background:var(--bg-elev-2)}

  /* Collapsed: icons only. The words stay in the button as its name and a tooltip says them; a select
     shrinks to its first letters and status lines are left out. */
  :root[data-rail="collapsed"][data-toolbar] body>.toolbar{padding:8px 6px 0;gap:1px;scrollbar-width:none}
  :root[data-rail="collapsed"][data-toolbar] body>.toolbar .tb-group{gap:1px}
  :root[data-rail="collapsed"][data-toolbar] body>.toolbar :where(button.btn,a.btn){min-height:34px;justify-content:center;padding-inline:0;gap:0;font-size:0;overflow:visible}
  :root[data-rail="collapsed"][data-toolbar] body>.toolbar :where(button.btn,a.btn) :where(.kbd-hint,.tb-rail-label,.lbl){display:none}
  :root[data-rail="collapsed"][data-toolbar] body>.toolbar .rail-toggle{bottom:0;min-height:40px}
  :root[data-rail="collapsed"][data-toolbar] body>.toolbar .tb-icon{padding-block:0;height:34px}
  :root[data-rail="collapsed"][data-toolbar] body>.toolbar .tb-sep{margin:3px 4px}
  :root[data-rail="collapsed"][data-toolbar] body>.toolbar .tb-label{display:none}
  :root[data-rail="collapsed"][data-toolbar] body>.toolbar select{min-height:30px;padding:0 2px;font-size:.6875rem;text-align:center;text-align-last:center;text-overflow:clip;background-image:none}
  :root[data-rail="collapsed"][data-toolbar] body>.toolbar :where(.tag,.save-badge,.pd-meta,.dm-ready,#validity,output,.sd-zoom,.zoom-level){display:none}
}

.seg{position:relative;display:inline-flex;align-items:center;gap:2px;padding:2px;border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--bg-elev-2)}
.seg>button{position:relative;z-index:1;display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:calc(var(--ctl) - 6px);padding:0 12px;border:0;border-radius:var(--radius);background:transparent;color:var(--text-dim);font-size:.8125rem;font-weight:600;cursor:pointer;white-space:nowrap;transition:color var(--dur-1) var(--ease)}
.seg>button:hover{color:var(--text)}
.seg>button .ic{width:16px;height:16px}
.seg>button[aria-selected="true"],.seg>button[aria-pressed="true"]{color:var(--text)}
.seg>button[aria-selected="true"] .ic,.seg>button[aria-pressed="true"] .ic{color:var(--accent-2)}
.seg>button:focus-visible{outline-offset:1px}
.seg-thumb{position:absolute;z-index:0;top:2px;bottom:2px;left:0;width:var(--seg-w,0);transform:translateX(var(--seg-x,0));border-radius:var(--radius);background:var(--bg-elev);box-shadow:0 0 0 1px var(--border-strong),inset 0 0 0 1px var(--border);pointer-events:none}
.seg[data-ready="true"] .seg-thumb{transition:transform var(--dur-3) var(--ease),width var(--dur-3) var(--ease)}
/* Before script has measured anything the selected tab still looks selected. */
.seg:not([data-ready]) .seg-thumb{display:none}
.seg:not([data-ready])>button[aria-selected="true"],.seg:not([data-ready])>button[aria-pressed="true"]{background:var(--bg-elev);box-shadow:inset 0 0 0 1px var(--border)}

/* ---------- menus and the tool switcher ----------
   Fixed to the window and placed under their anchor by js/core/ui.js. They overlay the page
   and never move it. */
.pop{position:fixed;z-index:70;display:flex;flex-direction:column;min-width:200px;max-width:calc(100vw - 16px);overflow:auto;padding:4px;border-radius:var(--radius-lg);background:var(--bg-elev);box-shadow:var(--shadow-pop);animation:pop-in var(--dur-2) var(--ease)}
@keyframes pop-in{from{opacity:0;transform:translateY(-4px)}}

/* ---------- a note under a form field (rangeField, fieldNote in js/core/theme.js) ---------- */
.field-note{display:block;flex-basis:100%;margin:4px 0 0;padding-left:8px;border-left:2px solid var(--warning);color:var(--text);font-size:.75rem;line-height:1.4;animation:pop-in var(--dur-2) var(--ease)}
.field-note[data-tone="bad"]{border-left-color:var(--danger)}
input[aria-invalid="true"].control,input[aria-invalid="true"]:not([type=checkbox]):not([type=radio]){border-color:var(--danger)}
.menu{gap:1px}
.menu-item{display:flex;align-items:center;gap:10px;width:100%;min-height:32px;padding:0 10px;border:0;border-radius:var(--radius);background:transparent;color:var(--text);font-size:.8125rem;text-align:left;white-space:nowrap;cursor:pointer}
.menu-item:hover:not(:disabled),.menu-item:focus-visible{background:var(--bg-elev-2)}
.menu-item:focus-visible{outline-offset:-2px}
.menu-item .ic{width:16px;height:16px;flex:0 0 auto;color:var(--text-dim)}
.menu-item[aria-checked="true"] .ic:last-child{margin-left:auto;color:var(--accent-2)}
.menu-item:disabled{opacity:.45;cursor:default}
.menu-item .kbd{margin-left:auto;font:.6875rem var(--mono);color:var(--text-dim)}
.menu-sep{height:1px;margin:4px 2px;background:var(--border)}
.menu-label{padding:6px 10px 2px;font-size:.6875rem;font-weight:650;color:var(--text-dim)}

.switcher-pop{width:min(780px,calc(100vw - 16px));padding:0;overflow:hidden}
.switcher{display:flex;flex-direction:column;min-height:0;flex:1}
.switcher-find{display:flex;align-items:center;gap:8px;padding:0 12px;border-bottom:1px solid var(--border);color:var(--text-dim)}
.switcher-find input{flex:1;min-width:0;min-height:38px;border:0;border-radius:0;background:transparent;color:var(--text);font-size:.875rem}
.switcher-find input:focus-visible{outline:none}
.switcher-find:focus-within{color:var(--accent-2);box-shadow:inset 0 -2px 0 var(--accent)}
.switcher-list{flex:1;min-height:0;overflow:auto;overscroll-behavior:contain;display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:0 8px;align-content:start;padding:2px 8px 6px}
.switcher-group{grid-column:1/-1;padding:8px 8px 2px;font-size:.6875rem;font-weight:700;color:var(--cc,var(--text-dim))}
.switcher-item{display:flex;align-items:center;gap:8px;padding:3px 8px;border-radius:var(--radius);color:var(--text);font-size:.8125rem}
.switcher-item .tool-icon{width:24px;height:24px;border-radius:var(--radius)}
.switcher-item .tool-icon svg{width:14px;height:14px}
.switcher-item .ic-now{margin-left:auto;width:16px;height:16px;color:var(--accent-2)}
.switcher-item:hover,.switcher-item[aria-selected="true"]{background:var(--bg-elev-2)}
.switcher-empty{grid-column:1/-1;padding:22px 12px;text-align:center;color:var(--text-dim);font-size:.8125rem}
.switcher-loading{padding:22px 12px;text-align:center;color:var(--text-dim);font-size:.8125rem}
.switcher-foot{display:flex;flex-wrap:wrap;gap:4px;padding:4px 8px;border-top:1px solid var(--border)}
.switcher-foot .menu-item{width:auto;min-height:30px}
/* A finger needs 44px: the rows, the footer links and the home mark grow on a touch screen. */
@media(pointer:coarse){.switcher-item{min-height:44px}.switcher-foot .menu-item{min-height:44px}.topbar a.brand{min-width:44px;min-height:44px;justify-content:center}}

/* ---------- a notice at the right end of the header ----------
   js/core/theme.js showToast. It overlays the tail of the toolbar for a moment and takes no room. */
.topbar-note{position:absolute;top:50%;right:calc(100% + 8px);z-index:2;width:max-content;max-width:min(520px,calc(100vw - 170px));padding:3px 12px;border-radius:var(--radius-lg);background:var(--bg-elev-2);box-shadow:var(--shadow-pop);color:var(--text);font:600 .75rem/1.3 var(--sans);overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow-wrap:anywhere;pointer-events:none;opacity:0;transform:translateY(-50%);transition:opacity .18s ease}
.topbar-note[data-open="true"]{opacity:1}
.topbar-note.floating{position:fixed;top:calc(var(--bar-h)/2);right:12px}
/* Where the header spot would cover a control (js/core/theme.js placeNotice): just below the header, or at
   the bottom of the window. */
.topbar-note.below,.topbar-note.bottom{position:fixed;right:12px;transform:none;max-width:min(520px,calc(100vw - 24px))}
.topbar-note.bottom{top:auto;bottom:16px}
.topbar-note.at-left{right:auto;left:12px}
@media(max-width:760px){.topbar-note{max-width:calc(100vw - 130px)}}

/* ---------- a question: confirm and ask (js/core/ui.js) ----------
   It covers the toolbar region of the header (the whole row on a phone) until answered. */
.ask-inline{position:absolute;inset:0;z-index:3;display:flex;align-items:stretch;border-radius:var(--radius);background:var(--bg-elev-2);box-shadow:inset 0 0 0 1px var(--border);animation:fade-in var(--dur-2) var(--ease)}
.ask-inline.floating{position:fixed;inset:0 0 auto 0;height:var(--bar-h);z-index:60;border-radius:0}
@keyframes fade-in{from{opacity:0}}
.ask-inline .ask-bar-body{flex:1;min-width:0;display:flex;align-items:center;gap:10px;padding:0 6px 0 12px;border-left:1px solid var(--border)}
.ask-inline.danger .ask-bar-body{border-left-color:var(--danger)}
.ask-inline .ask-bar-text{flex:1 1 auto;min-width:0;display:flex;align-items:center;gap:10px;font-size:.8125rem}
.ask-inline .ask-bar-text strong{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ask-inline .ask-bar-text>span{flex:1 1 0;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-dim)}
.ask-inline input.control{flex:0 1 260px;min-width:80px;height:var(--ctl);min-height:var(--ctl)}
.ask-inline .ask-bar-actions{flex:0 0 auto;display:flex;gap:6px}
.ask-inline .btn{height:var(--ctl);min-height:var(--ctl);padding:0 12px}
/* A message too long for the row is shown in full under it while the question is open. */
.ask-more{display:none;position:absolute;top:calc(100% + 4px);left:0;width:max-content;max-width:100%;padding:8px 12px;border-radius:var(--radius);background:var(--bg-elev);box-shadow:var(--shadow-pop);color:var(--text-dim);font-size:.8125rem;line-height:1.5}
.ask-inline[data-clipped] .ask-more{display:block}

/* Panels a tool page writes itself (the Schema Designer's SQL import, the manifest's
   template list, the data-folder offer) open under the header, over the page. They
   toggle with data-open. */
.drawer,.ask-bar:not(.ask-inline){position:absolute;top:var(--head-h);left:clamp(8px,2vw,24px);z-index:45;width:min(720px,calc(100% - 16px));max-height:calc(100% - var(--head-h) - 12px);margin-top:4px;overflow:auto;border-radius:var(--radius-lg);background:var(--bg-elev);box-shadow:var(--shadow-pop);visibility:hidden;opacity:0;transform:translateY(-4px);transition:opacity var(--dur-2) var(--ease),transform var(--dur-2) var(--ease),visibility 0s linear var(--dur-2)}
.drawer[data-open="true"],.ask-bar[data-open="true"]:not(.ask-inline){visibility:visible;opacity:1;transform:none;transition-delay:0s}
.ask-bar.folder-banner{left:auto;right:12px;width:min(440px,calc(100% - 24px))}
.ask-bar:not(.ask-inline) .ask-bar-body{display:flex;flex-wrap:wrap;align-items:center;gap:12px;padding:12px 14px}
.ask-bar:not(.ask-inline) .ask-bar-text{flex:1 1 240px;min-width:0}
.ask-bar:not(.ask-inline) .ask-bar-text strong{display:block;font:600 .9375rem var(--sans)}
.ask-bar:not(.ask-inline) .ask-bar-text span{display:block;margin-top:3px;color:var(--text-dim);font-size:.8125rem;line-height:1.55}
.ask-bar:not(.ask-inline) .ask-bar-actions{display:flex;flex:0 0 auto;gap:8px}
@media(max-width:560px){.ask-bar:not(.ask-inline) .ask-bar-actions{width:100%}.ask-bar:not(.ask-inline) .ask-bar-actions .btn{flex:1 1 0}}

.tip{position:fixed;z-index:90;max-width:min(260px,70vw);padding:4px 8px;border-radius:var(--radius);background:var(--text);color:var(--bg-elev);font:500 .6875rem/1.35 var(--sans);pointer-events:none;animation:fade-in var(--dur-1) var(--ease)}

.cm-s-bench.CodeMirror{background:var(--editor-bg);color:var(--text)}
.cm-s-bench .CodeMirror-gutters{background:var(--editor-bg);border-right:1px solid var(--border)}
.cm-s-bench .CodeMirror-linenumber{color:var(--text-dim)}
.cm-s-bench .CodeMirror-cursor{border-left:2px solid var(--accent-2)}
.cm-s-bench .CodeMirror-selected{background:color-mix(in srgb,var(--accent) 16%,transparent)}
.cm-s-bench.CodeMirror-focused .CodeMirror-selected{background:color-mix(in srgb,var(--accent) 26%,transparent)}
.cm-s-bench .CodeMirror-activeline-background{background:color-mix(in srgb,var(--text) 4.5%,transparent)}
.cm-s-bench .CodeMirror-matchingbracket{color:var(--text)!important;background:color-mix(in srgb,var(--accent) 24%,transparent);border-radius:2px}
.cm-s-bench .CodeMirror-nonmatchingbracket{color:var(--danger)!important}
.cm-s-bench .cm-keyword,.cm-s-bench .cm-operator.cm-keyword{color:var(--syn-keyword)}
.cm-s-bench .cm-atom,.cm-s-bench .cm-builtin,.cm-s-bench .cm-qualifier{color:var(--syn-atom)}
.cm-s-bench .cm-number{color:var(--syn-number)}
.cm-s-bench .cm-string,.cm-s-bench .cm-string-2{color:var(--syn-string)}
.cm-s-bench .cm-property,.cm-s-bench .cm-def{color:var(--syn-key)}
.cm-s-bench .cm-variable,.cm-s-bench .cm-variable-2,.cm-s-bench .cm-variable-3,.cm-s-bench .cm-type{color:var(--text)}
.cm-s-bench .cm-tag{color:var(--syn-tag)}
.cm-s-bench .cm-attribute{color:var(--syn-attr)}
.cm-s-bench .cm-comment{color:var(--syn-comment);font-style:italic}
.cm-s-bench .cm-meta,.cm-s-bench .cm-bracket,.cm-s-bench .cm-punctuation{color:var(--text-dim)}
.cm-s-bench .cm-link{color:var(--syn-tag);text-decoration:underline}
.cm-s-bench .cm-header,.cm-s-bench .cm-strong{font-weight:700}
.cm-s-bench .cm-em{font-style:italic}
.cm-s-bench .cm-error,.cm-s-bench .CodeMirror-lint-mark-error{color:var(--danger);text-decoration:underline wavy var(--danger)}

/* Sentence case: capitals with tracking were used as a label style in three
   places; the words are short enough to read without shouting. */
.data-table th{text-transform:none;letter-spacing:0;font-family:var(--sans);font-size:12px;font-weight:650}
.tool-row .go{display:none}
.tool-row .go .ic{width:16px;height:16px}

/* ---------- the tree: a chevron in line with its row ----------
   The native marker goes (a block inside a <summary> pushes it onto its own line); an inline chevron turns as it opens. */
#tree summary{display:flex;align-items:flex-start;list-style:none;cursor:pointer}
#tree summary::-webkit-details-marker{display:none}
#tree summary::before{content:"";flex:0 0 14px;height:21px;margin-right:2px;background:var(--text-dim);-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 6l6 6-6 6'/%3E%3C/svg%3E") center/11px no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 6l6 6-6 6'/%3E%3C/svg%3E") center/11px no-repeat;transition:transform var(--dur-1) var(--ease)}
#tree details[open]>summary::before{transform:rotate(90deg)}
#tree summary:hover::before{background:var(--accent-2)}
#tree summary .tree-row{flex:1 1 auto;min-width:0}
/* A leaf lines up with the text of a branch, not with its chevron. */
#tree>.tree-row,#tree details>.tree-row{padding-left:20px}
#tree details{margin-left:16px}

/* A softer edge on the landing pages: a faint accent glow behind the clock and the catalogue head. */
.start-search #q,.catalog-search .control{border-radius:calc(var(--radius) * 3)}
.workbench .start-clock{color:var(--text)}
@media(forced-colors:active){.workbench .start-clock{background:none;color:CanvasText}}

/* Sort mark on a sorted table header (aria-sort), drawn rather than typed so it is not part of the header text. */
th[aria-sort="ascending"]::after,th[aria-sort="descending"]::after{content:"";display:inline-block;width:5px;height:5px;margin-left:8px;border-right:1.5px solid var(--accent-2);border-bottom:1.5px solid var(--accent-2);vertical-align:.12em}
th[aria-sort="ascending"]::after{transform:rotate(-135deg);vertical-align:-.05em}
th[aria-sort="descending"]::after{transform:rotate(45deg)}
