/* GuideStar Board - the parts of GuideStar Tutoring Helper's app.css the Board uses, in the same order.
   Made by stage 0 of docs/PLAN.md; each part is marked with where it came from. Edit freely from here on. */

/* ═══ the look: palettes, type packs, styles, glass, texture, the shell, type, cards (GuideStar app.css 1–1041) ═══ */
/* GuideStar Tutoring Helper — brand: Cormorant Garamond + Jost, ink on paper. */

@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,500;0,600;0,700;1,500&family=Jost:wght@300;400;500;600&display=swap');

/* ── Palettes ──────────────────────────────────────────────────────────
   Both are declared once here; the theme blocks below just point the
   working variables at one set or the other. Change a colour in one place. */
:root {
  /* light — ink on paper, straight off the business card.
     Toned down from the original near-white: the page was #fcfbf7 and cards
     were the same value, so a screenful was one flat sheet at 99% luminance
     with nothing to rest on. The page is now a shade darker than the cards,
     which both takes the glare off and lets a card read as a card. */
  --l-ink: #201e1a;
  --l-paper: #f1eee6;        /* page */
  --l-surface-lit: #faf8f3;  /* cards, masthead, dialogs — lifted off the page */
  --l-paper-2: #e7e3d8;      /* hovers, inputs, code */
  --l-muted: #6f6a61;
  --l-faint: #757066;
  --l-rule: #ddd8cb;
  --l-rule-strong: #c7c1b3;
  --l-accent: #7d6234;
  --l-accent-soft: #efe7d7;
  --l-accent-2: #4a6785;     /* measurements — progress, charts, scores */
  --l-green: #4f7a52;
  /* Darkened from #b07d2a, which sat at 3.41:1 on its own badge background
     — below AA, and had been since long before any of the glass work. */
  --l-amber: #946923;
  --l-red: #a4443c;
  --l-blue: #4a6785;
  --l-green-bg: #f3f8f3;  --l-green-br: #cdddce;
  --l-amber-bg: #fdf8ee;  --l-amber-br: #ecdcbe;
  --l-red-bg:   #fdf3f2;  --l-red-br:   #e8cfcd;
  --l-blue-bg:  #f3f6fa;  --l-blue-br:  #cfd9e4;
  /* An elevation scale, not one shadow. Every level is built the same way —
     a tight contact shadow plus a wider ambient one, both pulled in with a
     negative spread so they stay under the element instead of haloing out
     past it. Before this each component invented its own: cards carried a
     32px 50%-black cloud with no spread while buttons had a tight 8px one,
     which is why some things looked far heavier than others. */
  --l-shadow-1: 0 1px 1px rgb(32 30 26 / .04), 0 4px 10px -4px rgb(32 30 26 / .10);
  --l-shadow-2: 0 2px 3px -1px rgb(32 30 26 / .06), 0 10px 20px -8px rgb(32 30 26 / .14);
  --l-shadow-3: 0 4px 6px -2px rgb(32 30 26 / .08), 0 18px 36px -12px rgb(32 30 26 / .20);
  --l-shadow: var(--l-shadow-1);
  --l-logo-filter: none;
  --l-backdrop: rgba(32,30,26,.34);
  --l-surface: var(--l-surface-lit);

  /* dark — neutral graphite, not a dimmed version of the paper.
     Tinting the background warm turns it muddy brown, so the base stays a
     clean near-neutral and the brand warmth is carried entirely by the gold
     accent and the off-white text. */
  --d-ink: #eae8e3;
  --d-paper: #121216;        /* page */
  --d-surface: #2c2c32;      /* cards, masthead, dialogs — raised off the page */
  --d-paper-2: #37373e;      /* hovers, inputs, code */
  --d-muted: #adabac;        /* 4.7:1 on the card surface — clears AA */
  --d-faint: #918f97;
  --d-rule: #45454b;
  --d-rule-strong: #3b3b45;
  --d-accent: #dcb877;
  --d-accent-soft: #22222b;  /* highlight panels stay neutral; the accent shows
                                through the border and the text instead */
  --d-accent-2: #93b3d6;
  --d-green: #85c08d;
  --d-amber: #ddb46a;
  --d-red: #e0918a;
  --d-blue: #93b3d6;
  --d-green-bg: #182018;  --d-green-br: #2f4433;
  --d-amber-bg: #221e17;  --d-amber-br: #453a26;
  --d-red-bg:   #241a1a;  --d-red-br:   #4a302e;
  --d-blue-bg:  #181d24;  --d-blue-br:  #2d3b4c;
  /* Dark needs more alpha to register at all, but tighter — a wide soft
     shadow on a dark ground just muddies the page around the element. */
  --d-shadow-1: 0 1px 1px rgb(0 0 0 / .30), 0 4px 10px -4px rgb(0 0 0 / .45);
  --d-shadow-2: 0 2px 3px -1px rgb(0 0 0 / .35), 0 10px 20px -8px rgb(0 0 0 / .55);
  --d-shadow-3: 0 4px 6px -2px rgb(0 0 0 / .40), 0 18px 36px -12px rgb(0 0 0 / .65);
  --d-shadow: var(--d-shadow-1);
  --d-logo-filter: invert(1) hue-rotate(180deg) brightness(1.06) saturate(.7);
  --d-backdrop: rgba(0,0,0,.62);

  --serif: 'Cormorant Garamond', Georgia, serif;
  --sans: 'Jost', 'Segoe UI', system-ui, sans-serif;
  --radius: 3px;

  /* ── Working variables ────────────────────────────────────────────────
     Declared ONCE. light-dark() picks a side from `color-scheme`, so there
     is no second copy of this list to fall out of step — an earlier version
     kept the dark values in a duplicate block and a variable added to one
     and not the other made every card render light-on-light. */
  color-scheme: light dark;                    /* follow the OS by default */

  --ink:         light-dark(var(--l-ink),         var(--d-ink));
  --paper:       light-dark(var(--l-paper),       var(--d-paper));
  --surface:     light-dark(var(--l-surface),     var(--d-surface));
  --paper-2:     light-dark(var(--l-paper-2),     var(--d-paper-2));
  --muted:       light-dark(var(--l-muted),       var(--d-muted));
  --faint:       light-dark(var(--l-faint),       var(--d-faint));
  --rule:        light-dark(var(--l-rule),        var(--d-rule));
  --rule-strong: light-dark(var(--l-rule-strong), var(--d-rule-strong));
  --accent:      light-dark(var(--l-accent),      var(--d-accent));
  --accent-soft: light-dark(var(--l-accent-soft), var(--d-accent-soft));
  /* A second hue, with a job of its own. --accent means "you can act on this"
     — links, focus rings, today, the primary button. --accent-2 means "this is
     a measurement" — progress bars, score lines, the earnings chart. Splitting
     them is what lets a palette read as a palette instead of one tinted dot,
     and it stops a progress bar looking like something you can click. */
  --accent-2:    light-dark(var(--l-accent-2),    var(--d-accent-2));
  --backdrop:    light-dark(var(--l-backdrop),    var(--d-backdrop));

  --green:    light-dark(var(--l-green),    var(--d-green));
  --green-bg: light-dark(var(--l-green-bg), var(--d-green-bg));
  --green-br: light-dark(var(--l-green-br), var(--d-green-br));
  --amber:    light-dark(var(--l-amber),    var(--d-amber));
  --amber-bg: light-dark(var(--l-amber-bg), var(--d-amber-bg));
  --amber-br: light-dark(var(--l-amber-br), var(--d-amber-br));
  --red:      light-dark(var(--l-red),      var(--d-red));
  --red-bg:   light-dark(var(--l-red-bg),   var(--d-red-bg));
  --red-br:   light-dark(var(--l-red-br),   var(--d-red-br));
  --blue:     light-dark(var(--l-blue),     var(--d-blue));
  --blue-bg:  light-dark(var(--l-blue-bg),  var(--d-blue-bg));
  --blue-br:  light-dark(var(--l-blue-br),  var(--d-blue-br));

  /* A solid near-black button disappears on a dark page, so the primary
     action becomes gold there. Fields lift off the panel to stay findable. */
  --btn-bg:  light-dark(var(--l-ink),   var(--d-accent));
  --btn-fg:  light-dark(var(--l-paper), #16150f);
  /* A field is a hole, not a tile: it sits BELOW the surface it's cut into,
     which is the opposite of a card and is what stops a form reading as a
     stack of little panels. Mixed rather than set to the page colour flat,
     because plenty of fields sit directly on the page and would otherwise
     vanish into it. */
  --field-bg: light-dark(var(--l-paper),
                         color-mix(in srgb, var(--d-paper) 62%, var(--d-surface)));
  /* NOT light-dark(). That is a <color> function, so a shadow list inside it
     is invalid — and an invalid custom property takes the whole declaration
     with it, so `box-shadow: var(--field-inset)` computed to `none` and
     every input, select and textarea in the app rendered flat. Nothing
     announced it; the sunken look the rest of the design leans on simply
     was not there. Measured: CSS.supports('box-shadow', 'light-dark(inset
     0 1px 2px rgb(0 0 0 / .06), …)') is false, and getComputedStyle on any
     field returned boxShadow "none".
     Per-theme pair instead, selected with the other shadows below — which is
     what every other shadow token here already does. */
  --l-field-inset: inset 0 1px 2px rgb(0 0 0 / .06);
  --d-field-inset: inset 0 1px 3px rgb(0 0 0 / .32);
  --track:    light-dark(var(--l-rule),  var(--d-paper-2));
}
/* light-dark() in Safari. Safari before 17.5 - an iPad on iPadOS 16.4 to 17.4, a Mac's Safari as old - has none, and
   every colour above was invalid wherever it was used: pages, panes, dialogs and buttons with no colour, no border
   and no shadow, Glass with no glass, the words in the browser's own black, the Pen buttons' pen drawn solid black.
   So each rule in this file with a light-dark() in it is followed by its twin for a browser without one (held to it by
   tests/test_safari.py): light-dark(A, B) written `var(--not-light, A) var(--not-dark, B)`. On the light side
   --not-light is unset (its var() is A) and --not-dark a blank (its var() is nothing), on the dark side the other way
   round, by the theme's three branches as the shadows' below - so of the two only the side's own colour is left.
   A browser with light-dark() reads none of it (7 Oct 2026, the owner: "make sure it all works on safari too"). */
@supports not (color: light-dark(#000, #fff)) {
  :root {
    --ink:         var(--not-light, var(--l-ink)) var(--not-dark, var(--d-ink));
    --paper:       var(--not-light, var(--l-paper)) var(--not-dark, var(--d-paper));
    --surface:     var(--not-light, var(--l-surface)) var(--not-dark, var(--d-surface));
    --paper-2:     var(--not-light, var(--l-paper-2)) var(--not-dark, var(--d-paper-2));
    --muted:       var(--not-light, var(--l-muted)) var(--not-dark, var(--d-muted));
    --faint:       var(--not-light, var(--l-faint)) var(--not-dark, var(--d-faint));
    --rule:        var(--not-light, var(--l-rule)) var(--not-dark, var(--d-rule));
    --rule-strong: var(--not-light, var(--l-rule-strong)) var(--not-dark, var(--d-rule-strong));
    --accent:      var(--not-light, var(--l-accent)) var(--not-dark, var(--d-accent));
    --accent-soft: var(--not-light, var(--l-accent-soft)) var(--not-dark, var(--d-accent-soft));
    --accent-2:    var(--not-light, var(--l-accent-2)) var(--not-dark, var(--d-accent-2));
    --backdrop:    var(--not-light, var(--l-backdrop)) var(--not-dark, var(--d-backdrop));
    --green:    var(--not-light, var(--l-green)) var(--not-dark, var(--d-green));
    --green-bg: var(--not-light, var(--l-green-bg)) var(--not-dark, var(--d-green-bg));
    --green-br: var(--not-light, var(--l-green-br)) var(--not-dark, var(--d-green-br));
    --amber:    var(--not-light, var(--l-amber)) var(--not-dark, var(--d-amber));
    --amber-bg: var(--not-light, var(--l-amber-bg)) var(--not-dark, var(--d-amber-bg));
    --amber-br: var(--not-light, var(--l-amber-br)) var(--not-dark, var(--d-amber-br));
    --red:      var(--not-light, var(--l-red)) var(--not-dark, var(--d-red));
    --red-bg:   var(--not-light, var(--l-red-bg)) var(--not-dark, var(--d-red-bg));
    --red-br:   var(--not-light, var(--l-red-br)) var(--not-dark, var(--d-red-br));
    --blue:     var(--not-light, var(--l-blue)) var(--not-dark, var(--d-blue));
    --blue-bg:  var(--not-light, var(--l-blue-bg)) var(--not-dark, var(--d-blue-bg));
    --blue-br:  var(--not-light, var(--l-blue-br)) var(--not-dark, var(--d-blue-br));
    --btn-bg:  var(--not-light, var(--l-ink)) var(--not-dark, var(--d-accent));
    --btn-fg:  var(--not-light, var(--l-paper)) var(--not-dark, #16150f);
    --field-bg: var(--not-light, var(--l-paper)) var(--not-dark, color-mix(in srgb, var(--d-paper) 62%, var(--d-surface)));
    --track:    var(--not-light, var(--l-rule)) var(--not-dark, var(--d-paper-2));
  }
}
@supports not (color: light-dark(#000, #fff)) {
  :root { --not-light: initial; --not-dark: ; }
  @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --not-light: ; --not-dark: initial; } }
  :root[data-theme="dark"] { --not-light: ; --not-dark: initial; }
}

/* ── Type packs ────────────────────────────────────────────────────────
   A fourth appearance axis: which two families the whole app is set in.
   Everything already goes through --serif (headings, figures, the wordmark)
   and --sans (everything else), so a pack is nothing but a pair of stacks —
   no rule anywhere else has to know a pack exists.

   The webfonts these need are NOT in the @import at the top of this file.
   They are fetched by the boot script in base.html, and only when a pack is
   actually chosen, so the default set stays two families rather than seven.
   Each stack therefore ends in something already on the machine: if the
   request never lands, the app is set in a near neighbour instead of falling
   all the way back to Times. */

/* Vista-era Frutiger, which is where the glass style comes from in the first
   place. Segoe UI is already on every Windows machine, so on this one the
   pack costs no download at all. */
:root[data-font="aero"] {
  --serif: 'Segoe UI Semibold', 'Segoe UI', 'Source Sans 3', system-ui, sans-serif;
  --sans: 'Segoe UI', 'Source Sans 3', system-ui, sans-serif;
}

/* Angular Y2K. Chakra Petch rather than something wider like Orbitron: the
   headings here run to 34px and the figures to 30px, and a genuinely wide
   face at those sizes wraps the stat cards and the masthead. */
:root[data-font="techno"] {
  --serif: 'Chakra Petch', 'Rajdhani', 'Segoe UI', system-ui, sans-serif;
  --sans: 'Chakra Petch', 'Rajdhani', 'Segoe UI', system-ui, sans-serif;
  letter-spacing: .005em;
}

/* The media-player console: everything monospaced. Share Tech Mono rather
   than a true bitmap face like VT323 — the pixel fonts have a tiny x-height
   and this app is read at 13px for hours at a time. */
:root[data-font="terminal"] {
  --serif: 'Share Tech Mono', ui-monospace, Consolas, monospace;
  --sans: 'Share Tech Mono', ui-monospace, Consolas, monospace;
}
/* Monospace runs wide, so the two places with the least room to give get a
   little back rather than wrapping. */
:root[data-font="terminal"] h1 { font-size: 29px; letter-spacing: 0; }


/* The toggle pins a side by switching color-scheme; every light-dark() above
   follows automatically. */
/* The hand that moves things, open and held (28 Oct 2026, the owner: "the dragging hand is invisible, white on white"):
   the system's grab and grabbing are drawn by inverting what is under them in places - Windows with HDR on, for one -
   and showed as plain white on a white page, as its crosshair and text pointer did (--bd-cross, 7 Oct; Docs' I-beam,
   27 Oct). Dark lines with a white edge, seen on paper, on slate and on glass; the system's hands only as the fallback.
   Every grab and grabbing in this file is one of these. */
:root {
  --cur-grab: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M8.5 12.5V6a1.25 1.25 0 0 1 2.5 0v5.5M11 11V4.75a1.25 1.25 0 0 1 2.5 0V11M13.5 11V5.75a1.25 1.25 0 0 1 2.5 0V11.5M16 11.5V8.25a1.25 1.25 0 0 1 2.5 0v5.25c0 3.6-2.6 6.5-6.2 6.5h-.8c-2.4 0-3.9-1-5.2-2.9l-2.6-3.8a1.3 1.3 0 0 1 2.1-1.5l1.7 2.2' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M8.5 12.5V6a1.25 1.25 0 0 1 2.5 0v5.5M11 11V4.75a1.25 1.25 0 0 1 2.5 0V11M13.5 11V5.75a1.25 1.25 0 0 1 2.5 0V11.5M16 11.5V8.25a1.25 1.25 0 0 1 2.5 0v5.25c0 3.6-2.6 6.5-6.2 6.5h-.8c-2.4 0-3.9-1-5.2-2.9l-2.6-3.8a1.3 1.3 0 0 1 2.1-1.5l1.7 2.2' fill='none' stroke='%231c1f24' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 12 12, grab;
  --cur-grabbing: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M8.5 12.5V10a1.25 1.25 0 0 1 2.5 0v1.5M11 11.5V9a1.25 1.25 0 0 1 2.5 0v2.5M13.5 11.5V9.5a1.25 1.25 0 0 1 2.5 0v2M16 12v-1.25a1.25 1.25 0 0 1 2.5 0v2.75c0 3.6-2.6 6.5-6.2 6.5h-.8c-2.4 0-3.9-1-5.2-2.9l-1.4-2.3a1.3 1.3 0 0 1 2.1-1.5l1.5 1.7' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M8.5 12.5V10a1.25 1.25 0 0 1 2.5 0v1.5M11 11.5V9a1.25 1.25 0 0 1 2.5 0v2.5M13.5 11.5V9.5a1.25 1.25 0 0 1 2.5 0v2M16 12v-1.25a1.25 1.25 0 0 1 2.5 0v2.75c0 3.6-2.6 6.5-6.2 6.5h-.8c-2.4 0-3.9-1-5.2-2.9l-1.4-2.3a1.3 1.3 0 0 1 2.1-1.5l1.5 1.7' fill='none' stroke='%231c1f24' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 12 12, grabbing;
}
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark; }

/* light-dark() is colours only, so these two non-colour properties still need
   a branch each. Keep this list at exactly these two. */
/* The top-edge highlight. Barely there on paper, where a raised card is
   already obvious from its shadow; a real catch of light on dark, where it
   is doing most of the work of separating a panel from the page. */
:root { --l-edge-light: rgb(255 255 255 / .55); --d-edge-light: rgb(255 255 255 / .07); }
:root { --shadow: var(--l-shadow); --logo-filter: var(--l-logo-filter);
        --edge-light: var(--l-edge-light); --field-inset: var(--l-field-inset);
        --shadow-1: var(--l-shadow-1); --shadow-2: var(--l-shadow-2); --shadow-3: var(--l-shadow-3); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --shadow: var(--d-shadow); --logo-filter: var(--d-logo-filter);
                                     --edge-light: var(--d-edge-light); --field-inset: var(--d-field-inset);
                                     --shadow-1: var(--d-shadow-1); --shadow-2: var(--d-shadow-2); --shadow-3: var(--d-shadow-3); }
}
:root[data-theme="dark"]  { --shadow: var(--d-shadow); --logo-filter: var(--d-logo-filter);
                            --edge-light: var(--d-edge-light); --field-inset: var(--d-field-inset);
                            --shadow-1: var(--d-shadow-1); --shadow-2: var(--d-shadow-2); --shadow-3: var(--d-shadow-3); }
:root[data-theme="light"] { --shadow: var(--l-shadow); --logo-filter: var(--l-logo-filter);
                            --edge-light: var(--l-edge-light); --field-inset: var(--l-field-inset);
                            --shadow-1: var(--l-shadow-1); --shadow-2: var(--l-shadow-2); --shadow-3: var(--l-shadow-3); }

/* ── Palettes ──────────────────────────────────────────────────────────
   Colour and brightness are separate choices. A palette re-points the --l-*
   and --d-* inputs above; light-dark() then picks a side exactly as before,
   so every palette works in light AND dark without a second set of rules.

   Only the neutrals and the accent move. Green/amber/red/blue carry meaning —
   paid, overdue, needs review — and a palette that recoloured those would be
   changing what the interface says, not how it looks. */
/* Each palette below is built from the reference swatches, not invented near-
   greys with one tinted dot. Where a supplied colour is dark enough to be a
   page or light enough to be a card, it IS the page or the card — Meadow's
   dark ground is literally Onyx, Blossom's is Space Indigo. The saturated ones
   become the two accents; anything too light to sit on white becomes a soft
   panel instead of being darkened past recognition. */

/* Ghost White · Antique White · Peach Fuzz · Periwinkle · Soft Periwinkle */
:root[data-palette="periwinkle"] {
  --l-ink:#221f36; --l-paper:#eeebfa; --l-surface-lit:#f8f7ff; --l-paper-2:#ffeedd;
  --l-muted:#645e83; --l-faint:#726b95; --l-rule:#ddd7f2; --l-rule-strong:#c3bbe4;
  --l-accent:#5a45cc; --l-accent-soft:#ffeedd; --l-accent-2:#a4601e;
  --d-ink:#eceaff; --d-paper:#17132b; --d-surface:#2f2a49; --d-paper-2:#3b335a;
  --d-muted:#b2aad6; --d-faint:#948db8; --d-rule:#4b426f; --d-rule-strong:#4d4180;
  --d-accent:#b8b8ff; --d-accent-soft:#1e1836; --d-accent-2:#ffd8be;
}
/* Honeydew · Light Cyan · Frosted Blue · Wisteria Blue · Medium Slate Blue */
:root[data-palette="frost"] {
  --l-ink:#152630; --l-paper:#dff1f6; --l-surface-lit:#e0faff; --l-paper-2:#eaf7ed;
  --l-muted:#4f6f7d; --l-faint:#58727d; --l-rule:#c6e2ea; --l-rule-strong:#a3ccd8;
  --l-accent:#5a54a8; --l-accent-soft:#eaf7ed; --l-accent-2:#17788a;
  --d-ink:#e2f4f9; --d-paper:#0b1d29; --d-surface:#1f3646; --d-paper-2:#234155;
  --d-muted:#a6c2d0; --d-faint:#8aa7b6; --d-rule:#2f5166; --d-rule-strong:#33627d;
  --d-accent:#9fe2ee; --d-accent-soft:#102434; --d-accent-2:#80a4d4;
}
/* Lavender · Amethyst Smoke · Light Green · Dark Slate Grey · Onyx */
:root[data-palette="meadow"] {
  --l-ink:#0e2119; --l-paper:#e5efe8; --l-surface-lit:#f4faf6; --l-paper-2:#d8e4ff;
  --l-muted:#4e6b62; --l-faint:#5d766d; --l-rule:#cbdfd3; --l-rule-strong:#a8c6b5;
  --l-accent:#35605a; --l-accent-soft:#d8e4ff; --l-accent-2:#8e5a8d;
  --d-ink:#e2f2e7; --d-paper:#00120b; --d-surface:#192f24; --d-paper-2:#1d3b30;
  --d-muted:#9dbcaa; --d-faint:#80a08e; --d-rule:#2a4b3c; --d-rule-strong:#2a5a44;
  --d-accent:#59ee99; --d-accent-soft:#0b291c; --d-accent-2:#c79bc6;
}
/* Ivory · Powder Blush · Bubblegum Pink · Dark Magenta · Space Indigo */
:root[data-palette="blossom"] {
  --l-ink:#17183b; --l-paper:#eef1e4; --l-surface-lit:#f8faf1; --l-paper-2:#ffe4d9;
  --l-muted:#5f5f7d; --l-faint:#6f6e8d; --l-rule:#ddddcb; --l-rule-strong:#c0c0ab;
  --l-accent:#a11692; --l-accent-soft:#ffe4d9; --l-accent-2:#c22a52;
  --d-ink:#efeaf6; --d-paper:#17183b; --d-surface:#2b2c5f; --d-paper-2:#343571;
  --d-muted:#aeacd2; --d-faint:#918fb8; --d-rule:#444577; --d-rule-strong:#43448c;
  --d-accent:#ff6b8e; --d-accent-soft:#1f2050; --d-accent-2:#ffb49a;
}
/* Canary Yellow · Lemon Lime · Green Yellow · Forest Green · Ebony */
:root[data-palette="citrus"] {
  --l-ink:#22301c; --l-paper:#eef2dd; --l-surface-lit:#f9fbee; --l-paper-2:#e3ebc4;
  --l-muted:#5c6a4c; --l-faint:#6a765a; --l-rule:#d8e0bd; --l-rule-strong:#bcc79b;
  --l-accent:#487d34; --l-accent-soft:#e9f2d4; --l-accent-2:#8a6d12;
  --d-ink:#eaf2dd; --d-paper:#171e11; --d-surface:#2c3623; --d-paper-2:#344128;
  --d-muted:#abb999; --d-faint:#8d9b7c; --d-rule:#425035; --d-rule-strong:#4c5f38;
  --d-accent:#afff3e; --d-accent-soft:#1d2615; --d-accent-2:#fffa70;
}
/* Grapefruit Pink · Cinnabar · Atomic Tangerine · Clay Soil · Deep Mocha */
:root[data-palette="ember"] {
  --l-ink:#33201c; --l-paper:#f7e9e3; --l-surface-lit:#fef8f4; --l-paper-2:#f2d9cd;
  --l-muted:#7a5a51; --l-faint:#886a60; --l-rule:#ecd2c5; --l-rule-strong:#d7b3a3;
  --l-accent:#b8392a; --l-accent-soft:#fae2d6; --l-accent-2:#4c3535;
  --d-ink:#f6e6df; --d-paper:#201513; --d-surface:#3b2d2b; --d-paper-2:#4a3834;
  --d-muted:#c3a79d; --d-faint:#a58a81; --d-rule:#5a4541; --d-rule-strong:#61443c;
  --d-accent:#ff783e; --d-accent-soft:#281a17; --d-accent-2:#ffa39f;
}

/* ── Styles ─────────────────────────────────────────────────────────────
   A third axis, and a different kind of one. A palette answers "which
   colours"; brightness answers "how dark". A style answers "what are these
   surfaces MADE of" — and that is a question about gradients, blur, rim
   light and corner radius, none of which a colour token can express. Every
   style reads its colour from whichever palette is set, so the two combine
   rather than competing: Glass over Harbour and Glass over Dusk are the
   same material in different light.

   Everything below is expressed as overrides of a handful of surface
   variables, so a style never has to restate a component's layout — only
   what it is made of. */

/* ── Glass ──────────────────────────────────────────────────────────────
   Frutiger Aero, in the specific sense: things are panes, not cards. Light
   comes from above and catches the top edge, the panel is faintly
   transparent so the page glows through it, and every corner is generously
   round. The gloss is a real top-half highlight rather than a border
   trick, because that is what actually reads as glass. */
:root[data-style="glass"] {
  --radius: 18px;
  /* Panes let a little of the page through and blur what's behind. Kept at
     82% rather than something more dramatic: below about 75% the text
     contrast measured on the card stops holding against a busy backdrop. */
  /* 86%, not lower. Measured against the strongest point of the aurora
     behind it: below 85% the secondary text on Frost and GuideStar in light
     mode drops under 4.5:1. Glass you can't read is just a window. */
  --pane: color-mix(in srgb, var(--surface) 86%, transparent);
  /* Glass gets its own light, not the hairline's. --edge-light is tuned to
     be almost invisible because it draws a single pixel; a specular sheen
     across the top third of a pane has to be several times stronger before
     it reads as a highlight at all rather than as a slightly paler edge. */
  --sheen: light-dark(rgb(255 255 255 / .78), rgb(255 255 255 / .16));
  --sheen-soft: light-dark(rgb(255 255 255 / .30), rgb(255 255 255 / .05));
  /* The Aqua signature is a hard stop, not a fade. A gloss that dissolves
     smoothly reads as "slightly lighter at the top"; one that ends in a
     crisp line halfway down reads as a curved, lacquered surface with a
     reflection on it. That single edge is most of the look. */
  --gloss: linear-gradient(180deg,
             var(--sheen) 0,
             var(--sheen-soft) 48%,
             transparent 48.5%,
             transparent 100%);
  /* Panels get the softer version — a full hard-stop across a whole card
     is too much lacquer; it belongs on things you press. */
  --gloss-soft: linear-gradient(180deg,
             var(--sheen) 0,
             var(--sheen-soft) 26%,
             transparent 58%),
           radial-gradient(120% 60% at 50% -12%,
             light-dark(rgb(255 255 255 / .55), rgb(255 255 255 / .10)),
             transparent 70%);
  /* Bevel: lit from above, shaded below, the way a moulded plastic control
     was drawn for about a decade. */
  --bevel: inset 0 1px 0 light-dark(rgb(255 255 255 / .95), rgb(255 255 255 / .34)),
           inset 0 -2px 3px light-dark(rgb(10 60 80 / .10), rgb(0 0 0 / .28));
  /* A thicker, luminous edge. In the reference the panels read as slabs of
     glass with a genuinely lit rim all the way round and a soft bloom
     spilling outside them — not a 1px hairline. Four layers: a bright top
     highlight, a full inner outline, a wide inner glow that fades toward
     the middle, and an outer bloom in the accent. */
  --rim:
    inset 0 1px 0 light-dark(rgb(255 255 255 / 1), rgb(255 255 255 / .42)),
    inset 0 -1px 0 light-dark(rgb(255 255 255 / .45), rgb(255 255 255 / .10)),
    inset 0 0 0 1px light-dark(rgb(255 255 255 / .70), rgb(255 255 255 / .16)),
    inset 0 0 26px -6px light-dark(rgb(255 255 255 / .70), rgb(255 255 255 / .12)),
    0 0 30px -12px color-mix(in srgb, var(--accent) 65%, transparent);
  --glass-blur: blur(18px) saturate(1.4);
  /* How strongly the wash behind the glass is tinted. A palette may raise it
     as far as its own text contrast allows — Aero is built for a vivid sky
     and has the headroom; the muted palettes do not and keep the default. */
  --aurora: 42%;
}
@supports not (color: light-dark(#000, #fff)) {
  :root[data-style="glass"] {
    --sheen: var(--not-light, rgb(255 255 255 / .78)) var(--not-dark, rgb(255 255 255 / .16));
    --sheen-soft: var(--not-light, rgb(255 255 255 / .30)) var(--not-dark, rgb(255 255 255 / .05));
    --gloss-soft: linear-gradient(180deg,
               var(--sheen) 0,
               var(--sheen-soft) 26%,
               transparent 58%),
             radial-gradient(120% 60% at 50% -12%,
               var(--not-light, rgb(255 255 255 / .55)) var(--not-dark, rgb(255 255 255 / .10)),
               transparent 70%);
    --bevel: inset 0 1px 0 var(--not-light, rgb(255 255 255 / .95)) var(--not-dark, rgb(255 255 255 / .34)),
             inset 0 -2px 3px var(--not-light, rgb(10 60 80 / .10)) var(--not-dark, rgb(0 0 0 / .28));
    --rim:
      inset 0 1px 0 var(--not-light, rgb(255 255 255 / 1)) var(--not-dark, rgb(255 255 255 / .42)),
      inset 0 -1px 0 var(--not-light, rgb(255 255 255 / .45)) var(--not-dark, rgb(255 255 255 / .10)),
      inset 0 0 0 1px var(--not-light, rgb(255 255 255 / .70)) var(--not-dark, rgb(255 255 255 / .16)),
      inset 0 0 26px -6px var(--not-light, rgb(255 255 255 / .70)) var(--not-dark, rgb(255 255 255 / .12)),
      0 0 30px -12px color-mix(in srgb, var(--accent) 65%, transparent);
  }
}

/* An aurora behind everything — two soft washes of the palette's own two
   accents. This is what stops a glass panel looking like a grey box: glass
   needs something behind it to be glass ABOUT. Fixed and non-interactive,
   and it sits under all content. */
:root[data-style="glass"] body::before {
  content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  /* Promoted to its own compositing layer. Without this the fixed aurora
     and the many backdrop-filtered panels above it share a layer, and
     Chromium repaints only the strip it thinks changed while scrolling —
     which is the horizontal tear across the page. Forcing the layer makes
     the backdrop a texture the compositor can just re-sample. */
  transform: translateZ(0);
  will-change: transform;
  background:
    radial-gradient(56% 42% at 10% -4%,
      color-mix(in srgb, var(--accent) calc(var(--aurora) * 1.00), transparent), transparent 68%),
    radial-gradient(50% 38% at 94% 4%,
      color-mix(in srgb, var(--accent-2) calc(var(--aurora) * 0.90), transparent), transparent 66%),
    radial-gradient(64% 44% at 74% 52%,
      color-mix(in srgb, var(--accent-2) calc(var(--aurora) * 0.48), transparent), transparent 70%),
    radial-gradient(80% 56% at 40% 106%,
      color-mix(in srgb, var(--accent) calc(var(--aurora) * 0.62), transparent), transparent 72%);
}

/* Bubbles. Real elements, one per sphere, because the point is that they
   drift independently — a single element carrying nine gradients can only
   move as one sheet, and reads as a texture sliding about rather than as
   bubbles rising. Also, the gradient version lived on body::after, which
   the grain texture had already claimed: whichever rule lost simply
   vanished, so picking Grain silently deleted the bubbles.

   Hidden entirely unless the Glass style is on, so every other style pays
   nothing for them. */
.aero-bubbles { display: none; }
:root[data-style="glass"] .aero-bubbles {
  display: block; position: fixed; inset: 0; z-index: -1;
  pointer-events: none; overflow: hidden;
  transform: translateZ(0);          /* same reason as the aurora above */
  opacity: var(--bubble-opacity, .55);
}
/* Each bubble is two layers. The span is WHERE it is: the rise animation
   owns its `transform`, and the pointer (a mouse, a pen, a finger) nudges it
   about on `translate` (which composes with the animation rather than
   replacing it). The ::before is its skin — the paint, and WHAT SHAPE it is:
   the pointer squashes it through --squish, a 2-D matrix set from app.js
   (bubblesInTheWater; the two inline on the span, and taken off again once
   it is at rest), and a slow keyframe on `scale` keeps it faintly wobbling
   even when nothing has touched it, the way a bubble rising through water
   never is quite round. Left behind when app.js was cut down for the Board
   (stage 0), so for a while the bubbles only drifted; back 1 Oct 2026. */
:root[data-style="glass"] .aero-bubbles span {
  position: absolute;
  animation: aero-rise var(--dur, 34s) ease-in-out var(--delay, 0s) infinite;
  will-change: transform;
}
:root[data-style="glass"] .aero-bubbles span::before {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  /* Rim bright, body barely there, plus an off-centre specular dot. That
     dot is what makes a circle read as a sphere rather than a disc. */
  background:
    radial-gradient(circle at 32% 28%,
      light-dark(rgb(255 255 255 / .95), rgb(255 255 255 / .55)) 0 7%,
      transparent 8%),
    radial-gradient(circle at 68% 74%,
      light-dark(rgb(255 255 255 / .40), rgb(255 255 255 / .16)) 0 4%,
      transparent 5%),
    radial-gradient(circle at 50% 50%,
      transparent 58%,
      color-mix(in srgb, var(--accent) 45%, transparent) 76%,
      color-mix(in srgb, var(--accent) 18%, transparent) 88%,
      transparent 92%);
  box-shadow: 0 0 24px -6px color-mix(in srgb, var(--accent) 40%, transparent);
  transform: var(--squish, none);
  /* Smaller bubbles wobble quicker: the pace is tied to the rise, which is
     already quicker for the small ones. `alternate` with ease-in-out comes
     to rest at each end, so the reversal is a soft turn, not a bounce. */
  animation: aero-wobble calc(var(--dur, 34s) / 16) ease-in-out calc(var(--delay, 0s) / 3) infinite alternate;
  will-change: transform;
}
@supports not (color: light-dark(#000, #fff)) {
  :root[data-style="glass"] .aero-bubbles span::before {
    background:
      radial-gradient(circle at 32% 28%,
        var(--not-light, rgb(255 255 255 / .95)) var(--not-dark, rgb(255 255 255 / .55)) 0 7%,
        transparent 8%),
      radial-gradient(circle at 68% 74%,
        var(--not-light, rgb(255 255 255 / .40)) var(--not-dark, rgb(255 255 255 / .16)) 0 4%,
        transparent 5%),
      radial-gradient(circle at 50% 50%,
        transparent 58%,
        color-mix(in srgb, var(--accent) 45%, transparent) 76%,
        color-mix(in srgb, var(--accent) 18%, transparent) 88%,
        transparent 92%);
  }
}
/* Each bubble gets its own size, lane and pace. Written out rather than
   generated so the spread is deliberate — a few big slow ones at the back,
   smaller quicker ones in front, none of them in step with each other. */
:root[data-style="glass"] .aero-bubbles span:nth-child(1) { width: 190px; height: 190px; left:  6%; top: 68%; --dur: 41s; --delay: -3s; }
:root[data-style="glass"] .aero-bubbles span:nth-child(2) { width: 110px; height: 110px; left: 22%; top: 84%; --dur: 33s; --delay: -12s; }
:root[data-style="glass"] .aero-bubbles span:nth-child(3) { width:  64px; height:  64px; left: 38%; top: 76%; --dur: 26s; --delay: -7s; }
:root[data-style="glass"] .aero-bubbles span:nth-child(4) { width: 148px; height: 148px; left: 55%; top: 90%; --dur: 46s; --delay: -19s; }
:root[data-style="glass"] .aero-bubbles span:nth-child(5) { width:  86px; height:  86px; left: 71%; top: 72%; --dur: 30s; --delay: -1s; }
:root[data-style="glass"] .aero-bubbles span:nth-child(6) { width: 220px; height: 220px; left: 83%; top: 88%; --dur: 52s; --delay: -25s; }
:root[data-style="glass"] .aero-bubbles span:nth-child(7) { width:  48px; height:  48px; left: 14%; top: 58%; --dur: 22s; --delay: -14s; }
:root[data-style="glass"] .aero-bubbles span:nth-child(8) { width: 128px; height: 128px; left: 46%; top: 64%; --dur: 38s; --delay: -30s; }
:root[data-style="glass"] .aero-bubbles span:nth-child(9) { width:  72px; height:  72px; left: 92%; top: 60%; --dur: 28s; --delay: -9s; }

/* Rising, with a lazy sideways wander so they don't travel in straight
   lines. Ends where it starts so the loop never jumps. In lvh, the window's
   height with its toolbars put away (7 Oct 2026, "make sure it all works on
   safari too"): the height a bubble rises through is then one number whether
   Safari's toolbar is folded away or not, so none of them jumps as it folds. */
@keyframes aero-rise {
  0%   { transform: translate3d(0, 0, 0) scale(1); opacity: 0; }
  8%   { opacity: 1; }
  50%  { transform: translate3d(26px, -46lvh, 0) scale(1.06); }
  92%  { opacity: 1; }
  100% { transform: translate3d(-12px, -94lvh, 0) scale(.94); opacity: 0; }
}
@keyframes aero-wobble {
  from { scale: 1.035 .965; }
  to   { scale: .965 1.035; }
}
/* Held where they are while the page has the pen (app.js bubblesInTheWater, 19 Oct 2026): nothing else on the screen
   moves while writing, so the browser has the ink alone to draw. Paused, not stopped - they go on from there. The mark
   is on the bubbles' own box since 21 Oct 2026: on <html> it restyled the whole page at every pen down and up. */
:root[data-style="glass"] .aero-bubbles[data-inking] span,
:root[data-style="glass"] .aero-bubbles[data-inking] span::before { animation-play-state: paused; }
:root[data-motion="reduce"][data-style="glass"] .aero-bubbles span {
  animation: none;
  /* Still there, just still — the look survives without the motion. The
     pointer leaves them be as well: bubblesInTheWater does nothing at all. */
  transform: translate3d(0, -40vh, 0);
}
:root[data-motion="reduce"][data-style="glass"] .aero-bubbles span::before { animation: none; }
/* Held still while a whiteboard's page is moved about (board.js gives it
   .bd-panning: the middle button, a right-drag, a finger), and rising again
   from where they stopped once it is let go. Rising, every bubble was
   painted afresh at every frame the page was drawn - nine of them, behind
   paper that hides them - and that was eight times all the rest of the
   painting a pan did, measured in Chromium drawing without a graphics card
   (2 Oct 2026: "when holding middle mouse button to move around it jitters
   a bit"). They rise a few px a second: held for the length of a pan,
   nobody sees them stop. */
:root[data-style="glass"]:has(.bd-panning) .aero-bubbles span,
:root[data-style="glass"]:has(.bd-panning) .aero-bubbles span::before { animation-play-state: paused; }
/* Bubbles switched off (Settings → Look → Bubbles: data-bubbles="off" on <html>, lane L19). Not drawn at all
   rather than faded out: display: none stops the rise and the wobble outright, the animations are dropped as well
   so none is even computed, and bubblesInTheWater in app.js puts its loop to sleep while they are away. Same
   specificity as the Glass rules above, so it wins by coming after. (Until 1 Oct 2026 this said it was for
   GuideStar's desktop "see-through window", which the Board never had.) */
:root[data-bubbles="off"] .aero-bubbles { display: none; }
:root[data-bubbles="off"] .aero-bubbles span,
:root[data-bubbles="off"] .aero-bubbles span::before { animation: none; will-change: auto; }

:root[data-style="glass"] .card,
:root[data-style="glass"] .ctxmenu,
:root[data-style="glass"] dialog {
  background-color: var(--pane);
  background-image: var(--gloss-soft);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule));
  box-shadow: var(--rim), var(--shadow-2);
}
/* The card's own top hairline would double up with the rim, and two
   highlights a pixel apart looks like a rendering fault rather than light. */
/* Iridescence. Real glass — a jewel case, a MiniDisc shell, a soap film —
   splits light at its edges into faint rainbow bands, and it is that, not
   the transparency, that stops it reading as grey plastic. A conic gradient
   round the panel, masked to the border only so the interior stays clean
   and the text on it stays legible, at an opacity low enough to register as
   a sheen rather than as a rainbow. */
:root[data-style="glass"][data-iridescent="on"] .card::before {
  content: ''; position: absolute; inset: 0; z-index: 0;
  /* height and background must both be reset: the plain-style rule above
     makes this pseudo-element a 1px hairline pinned to the top edge, and
     inheriting that turned the ring into a stripe across the top. */
  height: auto; border-radius: inherit; pointer-events: none;
  padding: 1.5px;
  background: conic-gradient(from 210deg,
    #ff8fd0, #ffd08f, #b6ff9b, #8ff0ff, #a99bff, #ff8fd0);
  /* Mask to the ring: paint the whole box, then punch out everything but
     the border. Two masks intersected via xor. */
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  /* Not light-dark(): that is a <color> function, so in `opacity` the whole
     declaration is dropped and the ring has been painting at full strength
     rather than .42. Swapped per theme like --grain-strength instead. */
  opacity: var(--iris-ring-a);
  display: block;
}
/* The card's contents must sit above the ring. */
:root[data-style="glass"][data-iridescent="on"] .card > * { position: relative; z-index: 1; }
/* With iridescence off the hairline is unwanted here too — the glass rim
   already lights the top edge, and two highlights a pixel apart reads as a
   rendering fault rather than as light. */
:root[data-style="glass"]:not([data-iridescent="on"]) .card::before { display: none; }

:root[data-style="glass"] .masthead {
  /* A sticky element with a backdrop-filter has to re-read what is behind
     it on every scroll frame, and it is the one that tears first. Its own
     layer, so the compositor moves it rather than repainting it. */
  transform: translateZ(0);
  will-change: transform;
  background-color: color-mix(in srgb, var(--surface) 72%, transparent);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-bottom-color: color-mix(in srgb, var(--edge-light) 40%, var(--rule));
}

/* The settings search bar, which on glass was the one obvious flaw left: a
   sticky strip filled with an opaque, square-cornered var(--paper). Over a
   flat page that is invisible and correct — it is the page colour on the page
   colour, hiding whatever scrolls under it. Over the aurora it is a pale slab
   with hard corners pasted across the wash, which is the "weird square".

   So on this style it becomes what every other surface here already is: a
   translucent pane that blurs what passes behind it. The negative margin is
   what keeps the field and the buttons aligned with the cards below while the
   pane itself sits a little wider, the way a floating toolbar should. */
:root[data-style="glass"] .settings-bar {
  /* Sticky plus backdrop-filter is the combination that tears — the same one
     the masthead above had to be promoted for. */
  transform: translateZ(0);
  will-change: transform;
  padding: 10px 14px 12px;
  margin-left: -14px; margin-right: -14px;
  border-radius: var(--radius);
  background-color: var(--pane);
  background-image: var(--gloss-soft);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--rim), var(--shadow-1);
}

/* Buttons are little panes too — same light, same curve. */
/* Buttons get their OWN gloss, weaker than a panel's. The panel sheen is
   78% white, which is right over pale translucent glass and completely
   wrong over a saturated fill — it bleached a deep teal button into a
   muddy two-tone band. An Aqua button's highlight sits nearer 40%: enough
   to look lacquered, not enough to lose the colour underneath. */
:root[data-style="glass"] .btn {
  border-radius: 999px;
  /* Translucent, like everything else made of glass here. A solid fill was
     the last thing on the page still reading as painted plastic. Measured
     across every palette with the button sitting over the strongest part of
     the aurora: at 68% the worst label contrast is 4.91, so this is as far
     as it goes without the text starting to suffer. */
  background-color: color-mix(in srgb, var(--btn-bg) 68%, transparent);
  border-color: color-mix(in srgb, var(--btn-bg) 68%, transparent);
  backdrop-filter: blur(10px) saturate(1.3);
  -webkit-backdrop-filter: blur(10px) saturate(1.3);
  background-image: linear-gradient(180deg,
    rgb(255 255 255 / .42) 0,
    rgb(255 255 255 / .16) 48%,
    rgb(255 255 255 / .02) 48.5%,
    rgb(255 255 255 / .10) 100%);
  box-shadow: var(--bevel), var(--shadow-1);
}
/* The nav's current page is the same lozenge, so the bar reads as one set
   of controls rather than one solid tab among glass ones. Solid, though, and
   under a lighter gloss than .btn's: translucent at 68% under a 38% sheen,
   its light words in the light theme measured 2.9-3.2:1 in every palette
   (lane L19, tests/test_appearance.py). */
:root[data-style="glass"] nav.main a.on {
  border-radius: 999px;
  background-color: var(--btn-bg);
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%);
  box-shadow: var(--bevel);
}
/* A ghost button is mostly transparent already, so it takes the pale
   treatment the panels use rather than the button one. */
:root[data-style="glass"] .btn.ghost { background-image: var(--gloss); }

/* ── One rule for every tinted control ───────────────────────────────────
   Danger buttons and the coloured badges were the last things still
   painted in flat semantic colour, and the generic .btn rule above was
   actively making it worse: it put the solid --btn-bg fill behind a danger
   button that is meant to be transparent, which is why Pause came out as a
   dark olive lozenge with red text on it.

   They all become the same material: their own colour, heavily thinned,
   over blur, with the same gloss and pill as everything else. The colour
   still means what it meant — this only changes what it is made of. */
:root[data-style="glass"] .btn.danger {
  background-color: color-mix(in srgb, var(--red-bg) 82%, transparent);
  border-color: color-mix(in srgb, var(--red) 40%, var(--red-br));
  color: var(--red);
  box-shadow: var(--bevel), var(--shadow-1);
}
:root[data-style="glass"] .btn.danger:hover {
  background-color: var(--red-bg);
}

:root[data-style="glass"] .badge {
  border-radius: 999px;
  backdrop-filter: blur(8px) saturate(1.25);
  -webkit-backdrop-filter: blur(8px) saturate(1.25);
  background-image: linear-gradient(180deg,
    rgb(255 255 255 / .34) 0, rgb(255 255 255 / .10) 50%, transparent 50.5%);
  box-shadow: inset 0 1px 0 light-dark(rgb(255 255 255 / .85), rgb(255 255 255 / .22));
}
@supports not (color: light-dark(#000, #fff)) {
  :root[data-style="glass"] .badge {
    box-shadow: inset 0 1px 0 var(--not-light, rgb(255 255 255 / .85)) var(--not-dark, rgb(255 255 255 / .22));
  }
}
/* Built on each badge's OWN background colour rather than a thin wash of
   its foreground. Tinting with the strong colour looked right but measured
   badly — the dedicated -bg colours are pale for a reason, and thinning the
   foreground instead dropped every badge below AA. This keeps the existing
   colour relationship and only makes the material translucent. */
:root[data-style="glass"] .badge.green {
  background-color: color-mix(in srgb, var(--green-bg) 88%, transparent);
  border-color: color-mix(in srgb, var(--green) 40%, var(--green-br));
}
:root[data-style="glass"] .badge.amber {
  background-color: color-mix(in srgb, var(--amber-bg) 88%, transparent);
  border-color: color-mix(in srgb, var(--amber) 40%, var(--amber-br));
}
:root[data-style="glass"] .badge.red {
  background-color: color-mix(in srgb, var(--red-bg) 88%, transparent);
  border-color: color-mix(in srgb, var(--red) 40%, var(--red-br));
}
:root[data-style="glass"] .badge.blue {
  background-color: color-mix(in srgb, var(--blue-bg) 88%, transparent);
  border-color: color-mix(in srgb, var(--blue) 40%, var(--blue-br));
}
:root[data-style="glass"] .badge.ink {
  background-color: color-mix(in srgb, var(--btn-bg) 68%, transparent);
  border-color: color-mix(in srgb, var(--btn-bg) 68%, transparent);
}
/* The plain badge, which had no fill at all, gets a pane like the rest. */
:root[data-style="glass"] .badge:not(.green):not(.amber):not(.red):not(.blue):not(.ink) {
  background-color: color-mix(in srgb, var(--surface) 45%, transparent);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule-strong));
}
/* A press should look pressed: the gloss flips under, the bevel inverts. */
:root[data-style="glass"] .btn:active {
  background-image: linear-gradient(0deg, var(--sheen-soft) 0, transparent 55%);
  box-shadow: inset 0 2px 4px light-dark(rgb(10 60 80 / .22), rgb(0 0 0 / .40));
}
@supports not (color: light-dark(#000, #fff)) {
  :root[data-style="glass"] .btn:active {
    box-shadow: inset 0 2px 4px var(--not-light, rgb(10 60 80 / .22)) var(--not-dark, rgb(0 0 0 / .40));
  }
}
/* The primary button glows, because in 2007 everything important glowed. */
:root[data-style="glass"] .btn:not(.ghost):not(.danger) {
  box-shadow: var(--bevel), var(--shadow-1),
              0 0 18px -6px color-mix(in srgb, var(--accent) 70%, transparent);
}
:root[data-style="glass"] .btn.ghost {
  background-color: color-mix(in srgb, var(--surface) 42%, transparent);
  border-color: color-mix(in srgb, var(--edge-light) 60%, var(--rule-strong));
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
}
:root[data-style="glass"] .badge,
:root[data-style="glass"] .pill { border-radius: 999px; }

/* Fields stay recessed — a sunken pane is still sunken — but pick up the
   curve so they don't read as square holes in round furniture. */
:root[data-style="glass"] input,
:root[data-style="glass"] select,
:root[data-style="glass"] textarea { border-radius: 10px; }

@media (prefers-reduced-transparency: reduce) {
  :root[data-style="glass"] .card,
  :root[data-style="glass"] .ctxmenu,
  :root[data-style="glass"] dialog,
  :root[data-style="glass"] .masthead {
    background-color: var(--surface);
    backdrop-filter: none; -webkit-backdrop-filter: none;
  }
  :root[data-style="glass"] body::before { opacity: .35; }
}

/* ── Texture ────────────────────────────────────────────────────────────
   A large flat fill of one colour is what makes a dark interface read as
   plastic. A fine grain over the whole page breaks that up: it is far too
   faint to see as dots, but the eye stops reading the background as a
   perfectly uniform sheet and the whole thing gains a bit of tooth.

   Fixed to the viewport rather than tiled per element, so it doesn't move
   independently of the page or stack up double on a card. pointer-events
   none so it never intercepts a click; aria-hidden isn't needed because a
   pseudo-element on <body> is not in the accessibility tree. */
:root[data-texture="grain"] body::after {
  content: ''; position: fixed; inset: 0; z-index: 9999; pointer-events: none;
  opacity: var(--grain-strength);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* Lighter touch on paper, where grain reads much more strongly than it does
   over a dark ground. */
:root { --iris-ring-a: .42; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --iris-ring-a: .30; }
}
:root[data-theme="dark"]  { --iris-ring-a: .30; }
:root[data-theme="light"] { --iris-ring-a: .42; }

:root { --grain-strength: .035; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --grain-strength: .055; }
}
:root[data-theme="dark"]  { --grain-strength: .055; }
:root[data-theme="light"] { --grain-strength: .035; }
:root[data-motion="reduce"][data-texture="grain"] body::after { opacity: 0; }

/* Sky · Grass · White — the 2007 desktop wallpaper, more or less. Made to
   sit under the Glass style, though it works plain too. */
:root[data-style="glass"][data-palette="aero"] {
  --bubble-opacity: .8;
  /* Measured: at 75% the worst text contrast through the glass is 4.62,
     still clear of 4.5. A sky is meant to be a colour, not a suggestion. */
  --aurora: 75%;
}
:root[data-palette="aero"] {
  --l-ink:#0b2b38; --l-paper:#dff2fb; --l-surface-lit:#f7fdff; --l-paper-2:#e2f7e4;
  --l-muted:#40707f; --l-faint:#4d7e8c; --l-rule:#c4e6f2; --l-rule-strong:#98cfe2;
  --l-accent:#0a7ea8; --l-accent-soft:#e2f7e4; --l-accent-2:#3f8f3f;
  --d-ink:#e2f6fd; --d-paper:#04161f; --d-surface:#123a4c; --d-paper-2:#17495e;
  --d-muted:#9ccadb; --d-faint:#7fb2c6; --d-rule:#1f5d76; --d-rule-strong:#2b7b9b;
  --d-accent:#5fd8ff; --d-accent-soft:#0a2532; --d-accent-2:#7ae08a;
}

/* ── Complementary palettes ────────────────────────────────────────────
   The six above are each built around a single hue, which is restful but
   goes flat across a whole screen: page, card, accent and text all sit in
   the same family, so nothing pulls the eye. These three are built the
   other way — the accent sits 90-150 degrees round the wheel from the
   ground it stands on, so there is a warm note against a cool field (or
   the reverse) on every page rather than only inside a chart. */

/* Deep Teal · Sea Glass · Warm Amber — cool ground, warm signal */
:root[data-palette="harbour"] {
  --l-ink:#10262b; --l-paper:#e3eff0; --l-surface-lit:#f5fbfb; --l-paper-2:#ffeedd;
  --l-muted:#4a6b70; --l-faint:#587a7f; --l-rule:#cfe3e4; --l-rule-strong:#a9c9cb;
  --l-accent:#186b74; --l-accent-soft:#ffeedd; --l-accent-2:#b3651a;
  --d-ink:#e4f3f4; --d-paper:#08191c; --d-surface:#16333a; --d-paper-2:#1d424a;
  --d-muted:#a3c4c8; --d-faint:#87acb1; --d-rule:#2b5b66; --d-rule-strong:#3a7885;
  --d-accent:#5fd3c4; --d-accent-soft:#0d2429; --d-accent-2:#ffb066;
}
/* Indigo Night · Coral · Cornflower — the contrast Blossom only hints at */
:root[data-palette="dusk"] {
  --l-ink:#1e1b33; --l-paper:#eceaf3; --l-surface-lit:#faf9ff; --l-paper-2:#ffe8e4;
  --l-muted:#5e5a7d; --l-faint:#6d6889; --l-rule:#dbd8e8; --l-rule-strong:#bfbad4;
  --l-accent:#c2413f; --l-accent-soft:#ffe8e4; --l-accent-2:#4d63b5;
  --d-ink:#ece9f7; --d-paper:#12111f; --d-surface:#282641; --d-paper-2:#33314f;
  --d-muted:#aca7c6; --d-faint:#8f8aad; --d-rule:#453f68; --d-rule-strong:#5a5382;
  --d-accent:#ff8f7a; --d-accent-soft:#1d1a2e; --d-accent-2:#9fb4ff;
}
/* Olive · Terracotta · Sage — earthy, and the only warm ground here */
:root[data-palette="sagebrush"] {
  --l-ink:#232619; --l-paper:#eceee2; --l-surface-lit:#f9faf3; --l-paper-2:#f7e3d5;
  --l-muted:#5e6450; --l-faint:#6d7360; --l-rule:#dcdfcd; --l-rule-strong:#bec2ab;
  --l-accent:#a1512a; --l-accent-soft:#f7e3d5; --l-accent-2:#4f6f52;
  --d-ink:#eef0e4; --d-paper:#14170f; --d-surface:#2b2f22; --d-paper-2:#373c2c;
  --d-muted:#b3b9a2; --d-faint:#959c84; --d-rule:#454b36; --d-rule-strong:#5c6349;
  --d-accent:#f0a06a; --d-accent-soft:#1c2015; --d-accent-2:#9ecf9c;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* The `hidden` attribute has to actually hide things. The UA rule for it is
   `[hidden] { display: none }` — one class's worth of specificity, which any
   `.btn { display: inline-flex }` or `.row { display: flex }` in this file
   silently beats. That is why "Clear done" sat on the dashboard permanently
   with nothing to clear, and why .ctxmenu needed its own patch below. Fixed
   once, here, rather than per component. */
[hidden] { display: none !important; }

/* Always reserve the scrollbar gutter. Without it a short page has no
   scrollbar and a long one does, so the whole masthead jumps 15px sideways
   every time you move between them. */
html { scrollbar-gutter: stable; }

/* The scrollbar is part of the window, not a browser detail. Left alone it
   is drawn in the OS default — a hard dark slab down the edge of a pale
   glass page. Both syntaxes: the standard one for Firefox and modern
   Chromium, the -webkit- pseudo-elements for the rest, because the two
   overlap but neither covers everything. */
html {
  scrollbar-color: color-mix(in srgb, var(--rule-strong) 80%, transparent) transparent;
  scrollbar-width: thin;
}
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--rule-strong) 70%, transparent);
  border-radius: 999px;
  /* Transparent border with background-clip is the trick that insets a
     thumb without a wrapper element. */
  border: 3px solid transparent;
  background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover {
  background: color-mix(in srgb, var(--accent) 60%, var(--rule-strong));
  background-clip: content-box;
}
::-webkit-scrollbar-corner { background: transparent; }

/* Under glass the thumb becomes a little pane of its own. */
:root[data-style="glass"] ::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg,
      light-dark(rgb(255 255 255 / .75), rgb(255 255 255 / .30)),
      color-mix(in srgb, var(--accent) 45%, transparent));
  background-clip: content-box;
  box-shadow: inset 0 1px 0 light-dark(rgb(255 255 255 / .9), rgb(255 255 255 / .3));
}
@supports not (color: light-dark(#000, #fff)) {
  :root[data-style="glass"] ::-webkit-scrollbar-thumb {
    background: linear-gradient(180deg,
        var(--not-light, rgb(255 255 255 / .75)) var(--not-dark, rgb(255 255 255 / .30)),
        color-mix(in srgb, var(--accent) 45%, transparent));
    box-shadow: inset 0 1px 0 var(--not-light, rgb(255 255 255 / .9)) var(--not-dark, rgb(255 255 255 / .3));
  }
}

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 400;
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--ink); text-decoration: none; }
a:hover { color: var(--accent); }

/* ── Shell ─────────────────────────────────────────────────────────── */

.masthead {
  border-bottom: 1px solid var(--rule);
  background: var(--surface);
  position: sticky; top: 0; z-index: 50;
}

.masthead-inner {
  max-width: 1240px; margin: 0 auto; padding: 14px 28px;
  display: flex; align-items: center; gap: 28px;
}
.backbtn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; margin-right: -6px; padding: 0; cursor: pointer;
  background: transparent; border: 1px solid var(--rule-strong);
  border-radius: var(--radius); color: var(--muted);
  transition: background .12s, color .12s, border-color .12s;
}
.backbtn:hover { background: var(--paper-2); color: var(--ink); border-color: var(--muted); }
.backbtn svg { width: 16px; height: 16px; }
/* visibility, not display: the button keeps its place in the row either way,
   so a page with nowhere to go back to lays out identically to one that has
   somewhere. Hidden this way it is also out of the accessibility tree and
   can't be tabbed to, which display:none gave us and we shouldn't lose. */
.backbtn { visibility: hidden; }
:root[data-back="1"] .backbtn { visibility: visible; }

.brand { display: flex; align-items: center; gap: 11px; }
/* The mark is near-black line art on transparency, so it has to flip in dark. */
/* width:auto is load-bearing — without it the width="731" attribute in the
   markup becomes the used width and the mark renders enormous. The attribute
   is there for the aspect ratio, not the size; aspect-ratio states the same
   thing to CSS so the box is reserved before the PNG arrives, instead of the
   masthead reflowing the moment it does. */
.brand img { height: 30px; width: auto; aspect-ratio: 731 / 1185;
             filter: var(--logo-filter); }
.brand-text { line-height: 1; }
.brand-name {
  font-family: var(--serif); font-weight: 600; font-size: 20px;
  letter-spacing: .2em; text-transform: uppercase; padding-left: .2em;
}
/* --muted with a touch of ink, not --faint: at 8px --faint measured 3.6-4.5:1, and on Glass the aurora comes
   through the masthead - bare --muted still measured 4.37 on Aero's and Frost's vivid skies (lane L19,
   tests/test_appearance.py). */
.brand-sub {
  font-size: 8px; letter-spacing: .38em; text-transform: uppercase;
  color: color-mix(in srgb, var(--ink) 30%, var(--muted)); padding-left: .38em; margin-top: 3px;
}
nav.main { display: flex; gap: 4px; margin-left: auto; flex-wrap: wrap; }
/* --muted with a touch of ink, as .brand-sub: a page's link that is not the current one (People, beside
   Whiteboards - lane L3) measured 4.34-4.40:1 in bare --muted on Glass over the Aero, Frost and GuideStar skies. */
nav.main a {
  padding: 7px 13px; font-size: 12.5px; letter-spacing: .1em;
  text-transform: uppercase; color: color-mix(in srgb, var(--ink) 30%, var(--muted)); border-radius: var(--radius);
  transition: background .12s, color .12s;
}
nav.main a:hover { background: var(--paper-2); color: var(--ink); }
nav.main a.on { background: var(--btn-bg); color: var(--btn-fg); }
/* "← Helper" (10 Oct 2026, templates/base.html): back to the Tutoring Helper's page, in a window its BOARD button
   opened. A lozenge like the name chip's, not a page's name: in words that are read, --ink, not the pages' muted ones
   (tests/test_helper_look.py measures it as drawn in all 44 looks), a little apart from them. */
nav.main a.helper-back { display: inline-flex; align-items: center; gap: 6px; margin-right: 8px; padding: 6px 13px 6px 10px;
  border: 1px solid var(--rule-strong); border-radius: 999px; color: var(--ink); font-weight: 500; letter-spacing: .04em;
  text-transform: none; }
nav.main a.helper-back:hover { background-color: var(--paper-2); color: var(--ink); }
nav.main a.helper-back:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
:root[data-style="glass"] nav.main a.helper-back { background-image: var(--gloss); box-shadow: var(--bevel);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule-strong)); }

main { max-width: 1240px; margin: 0 auto; padding: 30px 28px 90px; }

/* ── Type ──────────────────────────────────────────────────────────── */

h1 { font-family: var(--serif); font-weight: 600; font-size: 34px; letter-spacing: -.01em; line-height: 1.1; }
h2 { font-family: var(--serif); font-weight: 600; font-size: 22px; letter-spacing: .01em; }
h3 { font-family: var(--serif); font-weight: 600; font-size: 17px; }
.eyebrow {
  font-size: 9.5px; letter-spacing: .3em; text-transform: uppercase;
  color: var(--faint); font-weight: 500;
}
.lede { color: var(--muted); font-size: 14px; }
.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 20px; margin-bottom: 26px; flex-wrap: wrap; }
.rule { height: 1px; background: var(--rule); margin: 22px 0; border: 0; }
.rule-dot { display: flex; align-items: center; gap: 7px; margin: 26px 0; }
.rule-dot::before, .rule-dot::after { content: ''; height: 1px; background: var(--rule); flex: 1; }
.rule-dot span { width: 4px; height: 4px; background: var(--rule-strong); transform: rotate(45deg); }
.mono { font-variant-numeric: tabular-nums; }

/* The sum row in "Owed to you". It is the figure the box exists to give, so
   it is the largest thing in it — the two rows above are its workings. Lining
   tabular figures, so the digits line up with those rows and sit on one
   baseline. */
.owed-total { padding-top: 13px; }
.owed-total > b { font-size: 19px; letter-spacing: -.01em; }
.owed-total .small b { font-size: 13px; }

/* ── Cards & grids ─────────────────────────────────────────────────── */

/* Raised, and made to look it. A panel only reads as sitting above the page
   if three things agree: it is lighter than the page, it casts a shadow, and
   its top edge catches a little light. The colours were carrying that on
   their own and only just — a card was 8-20% brighter than the page
   depending on palette, which is below the point at which the eye reads
   "separate object", so the whole screen collapsed into one flat field. */
.card {
  background: var(--surface); border: 1px solid var(--rule);
  border-radius: var(--radius); padding: 20px;
  box-shadow: var(--shadow);
  position: relative;
}
/* The hairline along the top edge. One pixel of the light the panel would
   catch if it really were tilted up out of the page — the cheapest honest
   depth cue there is, and it costs no contrast anywhere else. */
.card::before {
  content: ''; position: absolute; inset: 0 0 auto; height: 1px;
  border-radius: var(--radius) var(--radius) 0 0;
  background: linear-gradient(to right,
    transparent, var(--edge-light) 18%, var(--edge-light) 82%, transparent);
  pointer-events: none;
}
.card + .card { margin-top: 16px; }
/* ...but inside a grid the gap already does the spacing, and a stray margin on
   every card except the first knocks that column out of alignment. */
.grid > .card + .card { margin-top: 0; }
.card-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; margin-bottom: 14px; }

/* Cards sitting side by side stretch to the tallest in the row, so pin their
   closing block to the bottom and the rules and buttons line up across all. */
.card.stacked { display: flex; flex-direction: column; }
.card.stacked .card-foot { margin-top: auto; }
.card.stacked .card-foot > .rule:first-child { margin-top: 22px; }
.grid { display: grid; gap: 16px; }
.g2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.g3 { grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); }
.g4 { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.split { display: grid; grid-template-columns: minmax(0,1.9fr) minmax(0,1fr); gap: 22px; align-items: start; }
@media (max-width: 940px) { .split { grid-template-columns: 1fr; } }

/* ═══ tables, buttons and forms, drop-down lists, badges, progress (GuideStar app.css 1108–1454) ═══ */
/* ── Tables ────────────────────────────────────────────────────────── */

table { width: 100%; border-collapse: collapse; }
th {
  text-align: left; font-size: 9.5px; letter-spacing: .22em; text-transform: uppercase;
  color: var(--faint); font-weight: 500; padding: 0 10px 8px; border-bottom: 1px solid var(--rule-strong);
}
td { padding: 11px 10px; border-bottom: 1px solid var(--rule); vertical-align: middle; font-size: 14px; }
tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--paper-2); }
td.r, th.r { text-align: right; }
.table-wrap { overflow-x: auto; }

/* ── Buttons & forms ───────────────────────────────────────────────── */

.btn {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  font-family: var(--sans); font-size: 12px; font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase;
  padding: 8px 15px; border: 1px solid var(--btn-bg); background: var(--btn-bg);
  color: var(--btn-fg); border-radius: var(--radius);
  /* Explicit, not `normal`. With normal line-height a button's height comes
     from the font's own metrics, so the same class rendered 32, 34 and 36px
     across this page depending on the family — most visibly on the type
     picker, where each button previews a different one. 18px reproduces the
     36px every other button already was, and makes it font-independent. */
  line-height: 18px;
  transition: opacity .12s, background .12s, color .12s;
}
.btn:hover { opacity: .84; color: var(--btn-fg); }
.btn.ghost { background: transparent; color: var(--ink); border-color: var(--rule-strong); }
.btn.ghost:hover { background: var(--paper-2); color: var(--ink); opacity: 1; }
.btn.sm { padding: 5px 10px; font-size: 10.5px; line-height: 16px; }
.btn.danger { background: transparent; color: var(--red); border-color: var(--red-br); }
.btn.danger:hover { background: var(--red-bg); }
.btn[disabled] { opacity: .45; cursor: not-allowed; }

label { display: block; font-size: 9.5px; letter-spacing: .2em; text-transform: uppercase; color: var(--faint); margin-bottom: 5px; font-weight: 500; }
input, select, textarea {
  width: 100%; font-family: var(--sans); font-size: 14px; color: var(--ink);
  background: var(--field-bg); border: 1px solid var(--rule-strong);
  border-radius: var(--radius); padding: 8px 10px;
  box-shadow: var(--field-inset);
}
option, optgroup { background: var(--surface); color: var(--ink); }
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent); }
textarea { min-height: 84px; resize: vertical; line-height: 1.5; }
.field { margin-bottom: 14px; }
/* A field-shaped readout: the same sunken pane as an input, for a value the
   page is telling you rather than asking you for.

   It exists because hiding a control that cannot do anything also hid what
   it was reporting. With one model on the account there is nothing to pick
   — but you still need to know which model is writing your questions, and a
   missing row answers that with silence.

   Sunken, never raised: raised and glossy is this app's word for "press me".
   A softer edge than an input (--rule, not --rule-strong) and no focus ring
   is what separates "information" from "a field that won't let you type". */
.field-readout {
  width: 100%; font-size: 14px; color: var(--ink);
  background: var(--field-bg); border: 1px solid var(--rule);
  border-radius: var(--radius); padding: 8px 10px;
  box-shadow: var(--field-inset);
  display: flex; align-items: center; gap: 8px; cursor: default;
}
/* One line, exactly as tall as the input beside it: a readout that wraps
   makes its field taller than the rest of the row, and a row aligned to
   its bottom edge then lifts that one label above the others. What will
   not fit is in the tooltip, which is where the detail lives anyway. */
.field-readout.one-line {
  display: block; line-height: normal;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ── Drop-down lists, drawn by the page ─────────────────────────────────
   A native <select> on Windows opens its list in a separate OS window, and
   that window is shown black until its first frame has been composited. On
   a page with blur behind every card and bubbles always in motion, that
   frame is late — so every menu on the curriculum path went black for a
   beat before its names arrived. Nothing about the page's own speed could
   fix it; the delay was the second window.

   `appearance: base-select` has the browser draw the list inside the page
   instead. No second window, so nothing to flash; and the list is ordinary
   CSS, so it can be made of the same material as the app's other menu
   (.ctxmenu) instead of whatever the OS decided — and it is glass under the
   glass style, which an OS popup can never be.

   Wrapped in @supports on purpose. A browser without it must keep its
   native list exactly as it was, and an unknown pseudo-element in a
   selector list would otherwise invalidate every rule it shares one with. */
@supports (appearance: base-select) {
  select, select::picker(select) { appearance: base-select; }

  /* The closed control becomes a flex box in this mode, with its items
     aligned `normal` — which for a fixed-height item means the TOP of the
     line. That put the arrow in the control's top corner. Centre them: the
     label and the arrow both sit on the control's midline. */
  select { align-items: center; }

  /* The arrow. A chevron from two borders rather than a glyph, so it is
     the same crisp shape in every type pack. The rotated square's box is
     centred by the rule above; the visible V is the lower half of that
     box, so it is lifted a little to sit on the midline optically. */
  select::picker-icon {
    content: ''; display: block; flex: none; align-self: center;
    width: 7px; height: 7px; margin-left: 8px;
    border-right: 1.5px solid var(--muted); border-bottom: 1.5px solid var(--muted);
    transform: translateY(-2px) rotate(45deg);
    transition: transform .18s;
  }
  select:open::picker-icon { transform: translateY(2px) rotate(225deg); }

  /* The list itself: a .ctxmenu, exactly. */
  select::picker(select) {
    padding: 5px; margin: 4px 0;
    background: var(--surface); border: 1px solid var(--rule-strong);
    border-radius: var(--radius); box-shadow: var(--shadow-3);
    max-height: min(60vh, 420px); overflow-y: auto; overscroll-behavior: contain;
    /* Opens by unfolding from the control, closes the same way back. Short,
       eased out, and off entirely for anyone who has asked for no motion —
       a menu that appears instantly is the right answer there, not one
       that appears slowly. 7 Oct 2026: as the .ctxmenu it looks like moves
       ("Motion": down 6 px into place, in the menus' quick time, on
       --ease-out - its little overshoot and shrink were the one menu that
       bounced). @starting-style and allow-discrete stay here alone: a
       customisable <select> is Chromium's only, so a browser that can draw
       this list has them too (Safari draws its own). */
    transform-origin: top center;
    transition: opacity var(--t-quick) var(--ease-out), transform var(--t-quick) var(--ease-out),
                display var(--t-quick) allow-discrete, overlay var(--t-quick) allow-discrete;
  }
  select::picker(select):not(:popover-open) { opacity: 0; transform: translateY(-6px); }
  @starting-style {
    select::picker(select):popover-open { opacity: 0; transform: translateY(-6px); }
  }
  :root[data-motion="reduce"] select::picker(select) { transition: none; }

  select option {
    display: flex; align-items: center; gap: 8px;
    padding: 6px 9px; border-radius: 5px;
    font-size: 13px; color: var(--ink); background: transparent;
    /* Reset what a group label sets on its optgroup (below), because the
       options inside inherit it. */
    text-transform: none; letter-spacing: normal; font-weight: 400;
    cursor: pointer;
  }
  select option:hover, select option:focus-visible { background: var(--paper-2); outline: none; }
  select option:checked { font-weight: 600; }
  /* The tick. Accent-coloured, and reserved for every row whether checked
     or not, so the names line up down the list. */
  select option::checkmark { color: var(--accent); width: 1em; flex: none; }
  /* A group's label (.menu-label, in the context menu). Chromium draws the
     label itself, inside the optgroup, from its `label` attribute — there is
     no legend and no ::before to style, and the label is reachable only
     through what the optgroup passes down. So the group carries the label's
     type, and each option resets it (above). Verified: neither
     `optgroup::before` nor `optgroup legend` matches anything. */
  select optgroup {
    padding: 6px 9px 2px; font-size: 10px; letter-spacing: .1em;
    text-transform: uppercase; color: var(--faint);
  }
  select optgroup option { margin: 0 -9px; }

  /* Glass: the list is a pane like the cards, not a painted box over them. */
  :root[data-style="glass"] select::picker(select) {
    background-color: var(--pane); background-image: var(--gloss-soft);
    backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
    border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule));
    box-shadow: var(--rim), var(--shadow-2);
  }
  @media (prefers-reduced-transparency: reduce) {
    :root[data-style="glass"] select::picker(select) {
      background-color: var(--surface);
      backdrop-filter: none; -webkit-backdrop-filter: none;
    }
  }
}
/* Sits under the minutes box rather than inside it — a number input's spinner
   already owns the right-hand edge. Hidden when empty so a blank field has no
   stray line under it. */
.dur-hint {
  display: block; margin-top: 5px;
  font-size: 11.5px; color: var(--muted); font-variant-numeric: tabular-nums;
}
.dur-hint:empty { display: none; }
.form-row { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
.inline-form { display: inline; }
.check { display: flex; align-items: center; gap: 8px; }
.check input { width: auto; }
.check label { margin: 0; text-transform: none; letter-spacing: .02em; font-size: 13.5px; color: var(--ink); }

/* ── Badges ────────────────────────────────────────────────────────── */

.badge {
  display: inline-block; font-size: 9.5px; letter-spacing: .14em; text-transform: uppercase;
  padding: 3px 8px; border-radius: 100px; border: 1px solid var(--rule-strong);
  color: var(--muted); background: transparent; white-space: nowrap; font-weight: 500;
}
.badge.green { color: var(--green); border-color: var(--green-br); background: var(--green-bg); }
.badge.amber { color: var(--amber); border-color: var(--amber-br); background: var(--amber-bg); }
.badge.red   { color: var(--red);   border-color: var(--red-br);   background: var(--red-bg); }
.badge.blue  { color: var(--blue);  border-color: var(--blue-br);  background: var(--blue-bg); }
.badge.ink   { color: var(--btn-fg); background: var(--btn-bg); border-color: var(--btn-bg); }

/* The yellow a pane is tinted with. Deliberately NOT --amber: that token is #946923, a dark brown-gold, chosen to be
   read as TEXT on a pale ground. Mixed into a pane it does not tint it yellow, it darkens it. A glass tint has to be
   a light, saturated version of the hue, so that adding it to the pane moves the colour without moving the
   brightness. (GuideStar's schedule chips, which it was made for, did not come to the Board.) */
:root { --amber-glass: #ffc83c; }

.dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 7px; vertical-align: 1px; }

/* ── Progress ──────────────────────────────────────────────────────── */

.bar { height: 5px; background: var(--track); border-radius: 100px; overflow: hidden; }
.bar > i { display: block; height: 100%; background: var(--accent-2); border-radius: 100px; transition: width .25s; }
.bar.tall { height: 7px; }

.topic-tree { font-size: 14px; }
.topic-group { border-top: 1px solid var(--rule); padding: 12px 0; }
.topic-group:first-child { border-top: 0; }
.topic-group > summary { cursor: pointer; list-style: none; display: flex; align-items: center; gap: 12px; }
.topic-group > summary::-webkit-details-marker { display: none; }
.topic-group > summary::before { content: '›'; color: var(--faint); font-size: 17px; transition: transform .15s; line-height: 1; }
.topic-group[open] > summary::before { transform: rotate(90deg); }
.topic-title { font-family: var(--serif); font-size: 16.5px; font-weight: 600; flex: 1; }
.topic-code { font-size: 11px; color: var(--faint); font-variant-numeric: tabular-nums; }
.topic-list { margin: 10px 0 2px 22px; }
.topic-row {
  display: grid; grid-template-columns: 1fr auto auto; gap: 12px; align-items: center;
  padding: 6px 0; border-bottom: 1px dotted var(--rule);
}
/* A week of the teaching plan. Thirty-six of them on the page and all but
   three or four below the fold, so the ones out of view are not laid out
   until scrolled towards — the same arrangement as the Library's groups.
   The placeholder is a typical week's height; `auto` keeps the real height
   once a card has been rendered, so nothing jumps on the way back up.
   Anchors (#week-12, where "Move to" lands you) still work: the browser
   renders the target before scrolling to it. */
.week-card {
  content-visibility: auto;
  contain-intrinsic-size: auto 340px;
}
.topic-row:last-child { border-bottom: 0; }
.topic-row .name { font-size: 13.5px; }
.topic-row select { width: auto; font-size: 11.5px; padding: 3px 6px; color: var(--muted); }
.topic-meta { font-size: 11px; color: var(--faint); }


/* ═══ lists, flash, tabs, dialogs (GuideStar app.css 1577–1650) ═══ */
/* ── Lists ─────────────────────────────────────────────────────────── */

.list { list-style: none; }
.list li { padding: 11px 0; border-bottom: 1px solid var(--rule); display: flex; gap: 12px; align-items: center; }
.list li:last-child { border-bottom: 0; }
.list .grow { flex: 1; min-width: 0; }
.list .sub { font-size: 12px; color: var(--muted); }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.empty { text-align: center; padding: 34px 20px; color: var(--faint); font-size: 13.5px; }
.empty .serif { font-family: var(--serif); font-size: 18px; color: var(--muted); display: block; margin-bottom: 5px; }

/* ── Flash ─────────────────────────────────────────────────────────── */

.flash {
  border: 1px solid var(--rule-strong); background: var(--accent-soft);
  padding: 11px 15px; border-radius: var(--radius); margin-bottom: 20px; font-size: 13.5px;
  display: flex; align-items: flex-start; gap: 10px;
}
.flash.warn { background: var(--amber-bg); border-color: var(--amber-br); color: var(--amber); }
.flash button { background: none; border: 0; cursor: pointer; color: inherit; font-size: 16px; line-height: 1; opacity: .5; }

/* ── Tabs ──────────────────────────────────────────────────────────── */

.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--rule); margin-bottom: 24px; flex-wrap: wrap; }
.tabs a {
  padding: 9px 15px; font-size: 11.5px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.tabs a.on { color: var(--ink); border-bottom-color: var(--ink); }

/* ── Dialog ────────────────────────────────────────────────────────── */

/* `margin: auto` is what centres a modal <dialog>, and the `*` reset above
   wipes it — without this every dialog pins to the top-left corner.
   --dx/--dy are the drag offset, so dragging never disturbs the centring. */
dialog {
  border: 1px solid var(--rule-strong); border-radius: var(--radius); padding: 0;
  background: var(--surface); color: var(--ink); box-shadow: var(--shadow);
  max-width: 620px; width: calc(100% - 40px);
  margin: auto;
  --dx: 0px; --dy: 0px;
  transform: translate(var(--dx), var(--dy));
}
/* (With its own fallback: Safari before 17.4 gives ::backdrop none of the page's custom properties, 7 Oct 2026.) And
   the copy of it a closed dialog leaves fading, .gs-backdrop, right after the dialog's own copy (app.js, copyOf): each
   dialog's tint is said once, for both (History came up with none on Safari before 17.4, the page bright and
   live behind it). */
dialog::backdrop, dialog + .gs-backdrop { background: var(--backdrop, rgb(0 0 0 / .3)); }
dialog .dlg-head { padding: 18px 22px 12px; border-bottom: 1px solid var(--rule); display: flex; justify-content: space-between; align-items: center; cursor: move; }
/* Buttons in the header stay clickable rather than becoming drag handles. */
dialog .dlg-head button, dialog .dlg-head a, dialog .dlg-head input, dialog .dlg-head select { cursor: pointer; }
/* (-webkit-user-select beside every user-select: Safari, an iPad's too, knows only that one - a dialog dragged by its
   head selected the words it passed over, and a cropped picture or a video's poster could be selected as a picture.
   tests/test_safari.py holds every rule to it: 7 Oct 2026, the owner: "make sure it all works on safari too".) */
dialog.dragging { -webkit-user-select: none; user-select: none; }
dialog.dragging .dlg-head { cursor: var(--cur-grabbing); }
dialog .dlg-body { padding: 20px 22px; max-height: 68vh; overflow-y: auto; }
dialog .dlg-foot { padding: 14px 22px; border-top: 1px solid var(--rule); display: flex; justify-content: flex-end; gap: 10px; background: var(--paper-2); }


/* Held for one frame while the palette changes: matches everything, which
   forces the restyle, and kills transitions so the page doesn't animate
   through an intermediate colour on the way. Transitions only, since 7 Oct
   2026: `animation: none` as well stopped every animation for those two
   frames and started it again from nothing after - each bubble jumped back to
   the foot of its rise, and a menu blooming as the look changed bloomed twice.
   No keyframes on the page move a colour, so there is nothing for them to
   animate through. */
:root.theme-swap, :root.theme-swap * {
  transition: none !important;
}

/* Replaces the browser's confirm(). Narrower than a form dialog — it holds one
   question — and with no header bar, since there is nothing to title or drag. */
dialog.confirm { max-width: 420px; }
dialog.confirm .dlg-body { padding: 22px 22px 18px; }
.confirm-q {
  font-family: var(--serif); font-size: 18px; line-height: 1.35;
  font-weight: 400; margin: 0; color: var(--ink);
}
.confirm-note {
  margin: 10px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--muted);
  white-space: pre-line;
}


/* ═══ utility (GuideStar app.css 1872–1890) ═══ */
/* ── Utility ───────────────────────────────────────────────────────── */

.row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.row.tight { gap: 6px; }
.right { margin-left: auto; }
.muted { color: var(--muted); }
.faint { color: var(--faint); }
.red { color: var(--red); }
.small { font-size: 12px; }
.tiny { font-size: 11px; }
.serif { font-family: var(--serif); }
.nowrap { white-space: nowrap; }
.mt { margin-top: 16px; }
.mt-lg { margin-top: 28px; }
.mb { margin-bottom: 16px; }
.pre { white-space: pre-wrap; }
.code { font-family: 'DM Mono', ui-monospace, Consolas, monospace; font-size: 12px; background: var(--paper-2); padding: 2px 6px; border-radius: 2px; }
.hide { display: none !important; }


/* ═══ the right-click menu (GuideStar app.css 2455–2511) ═══ */
/* The right-click menu, and the editor's menus (⋯ among them). Fixed-position so nothing it opens over clips it. */
/* A fixed width, not a min-width: the menu is measured before it is moved into
   place, and a width that depends on where it sits would rewrap the labels and
   change its height, leaving the clamp that keeps it on screen working from a
   stale number. max-height covers the case where it is simply taller than the
   window — better a scrollable menu than a "Delete" you cannot reach. */
.ctxmenu {
  position: fixed; z-index: 200; width: 244px; padding: 5px;
  max-height: calc(100vh - 16px); overflow-y: auto; overscroll-behavior: contain;
  background: var(--surface); border: 1px solid var(--rule-strong);
  border-radius: var(--radius); box-shadow: var(--shadow-3);
}
.ctxmenu[hidden] { display: none; }
/* A heading's words: --muted with a touch of ink, as .brand-sub, not --faint - at 11.5px --faint measured 3.99-4.35:1
   on the menu's own pane in the light theme, in every palette but Aero and Harbour (1 Oct 2026, measured for ⋯'s new
   "Full screen": tests/test_fullscreen.py). */
.menu-head {
  padding: 6px 9px 7px; font-size: 11.5px; color: color-mix(in srgb, var(--ink) 30%, var(--muted));
  border-bottom: 1px solid var(--rule); margin-bottom: 4px;
  overflow: hidden;
}
/* Two lines now — who and when, then what. Each truncates on its own rather
   than the pair sharing one nowrap, which would have hidden the subjects
   entirely the moment the first line filled the width. */
.menu-head, .menu-head i { display: block; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; }
.menu-head i { margin-top: 1px; }
.menu-item {
  display: block; width: 100%; text-align: left;
  padding: 6px 9px; border: 0; border-radius: 5px; background: transparent;
  font: inherit; font-size: 13px; color: var(--ink);
  text-decoration: none; cursor: pointer;
}
.menu-item:hover { background: var(--paper-2); }
/* The shortcut, shown so the keyboard route is discoverable from the menu
   rather than being something you had to be told about. */
.menu-key {
  float: right; margin-left: 12px;
  font-size: 10.5px; font-style: normal; color: var(--faint);
  font-variant-numeric: tabular-nums;
}
/* A line that explains rather than does — no hover, no pointer. */
.menu-item.is-note { color: var(--faint); font-size: 12px; cursor: default; }
.menu-item.is-note:hover { background: transparent; }
.menu-head i { font-style: normal; color: var(--muted); }
.menu-item.danger { color: var(--red); }
.menu-rule { height: 1px; background: var(--rule); margin: 4px 0; }
.menu-label {
  padding: 4px 9px 2px; font-size: 10px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--faint);
}
/* Subjects in the menu carry their own colour, so picking one is picking the
   colour too — the tag and the tint can't drift apart. */
.subject-item { display: flex; align-items: center; gap: 9px; }
.subject-dot {
  flex: none; width: 10px; height: 10px; border-radius: 50%;
  border: 1px solid rgb(0 0 0 / .18);
}
.menu-item.on { background: var(--accent-soft); font-weight: 500; }
.menu-item.on::after { content: "✓"; margin-left: auto; color: var(--accent); }

/* ═══ the theme toggle (GuideStar app.css 2690–2714) ═══ */
/* ── Theme toggle ──────────────────────────────────────────────────── */

/* Sign out (templates/base.html, signed in) is the same small button beside it, and so are the Settings panel's
   gear before it and the panel's own close and "Play a sample" buttons. */
.theme-toggle, .signout-btn, .settings-btn, .st-close, .st-play {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; padding: 0; cursor: pointer;
  background: transparent; border: 1px solid var(--rule-strong);
  border-radius: var(--radius); color: var(--muted); line-height: 1;
  transition: background .12s, color .12s, border-color .12s;
}
.theme-toggle:hover, .signout-btn:hover, .settings-btn:hover, .st-close:hover, .st-play:hover:not(:disabled) { background: var(--paper-2); color: var(--ink); border-color: var(--muted); }
.theme-toggle svg, .signout-btn svg, .settings-btn svg, .st-close svg, .st-play svg { width: 15px; height: 15px; }
.settings-btn svg { width: 17px; height: 17px; }
/* Its form takes no room of its own: the button sits in the nav's row like the rest. */
nav.main form.signout { display: contents; }
.theme-toggle .sun { display: none; }
.theme-toggle .moon { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .sun { display: block; }
  :root:not([data-theme="light"]) .theme-toggle .moon { display: none; }
}
:root[data-theme="dark"] .theme-toggle .sun { display: block; }
:root[data-theme="dark"] .theme-toggle .moon { display: none; }
:root[data-theme="light"] .theme-toggle .sun { display: none; }
:root[data-theme="light"] .theme-toggle .moon { display: block; }

/* No per-theme component overrides live here any more — the variables above
   already carry both sides. */

/* ═══ the Settings panel (1 Oct 2026: templates/settings.html, js/settings.js; lane L19's Appearance panel before) ═══ */
/* A dialog, so on Glass it is already a pane like the cards (the glass rules above): these only lay it out. It
   opens under the masthead at the right, where its gear is, and dims nothing - the page behind is what a change of
   the look is judged by, so it stays in full view (the backdrop is clear, and a click on it closes the panel).

   Its sections are listed down its left side and one is shown at a time, so the panel keeps one height whichever
   is open (nothing jumps as you go from one to the next). Each setting is a row: its name and a line saying what it
   does on the left, its control in a column of its own on the right - every control starts at the same place - or,
   for the palettes and the type, across the whole row under its words. On a phone the sections run across the top
   and every control goes under its words.

   Every word in it is --ink, --muted with 40% of --ink (--st-quiet), or --btn-fg on an opaque --btn-bg (the
   section shown, the choice made): each measured as drawn at 4.5:1 or more in every palette, light and dark, glass
   and plain (tests/test_settings.py, tests/test_appearance.py). Bare --muted is not enough here: the pane's own
   sheen (--gloss-soft) lightens its top half, and in the dark theme --muted there measured 3.4-4.4:1 (lane L19).
   The masthead's 30% of ink was not enough either: Ember's dark Glass put the descriptions at 4.39:1. No --faint
   at all: on the glass over the aurora it falls short. */
dialog.settings {
  --st-quiet: color-mix(in srgb, var(--ink) 40%, var(--muted));
  width: min(700px, calc(100% - 24px)); max-width: none;
  height: min(620px, calc(100vh - 78px)); height: min(620px, calc(100dvh - 78px));
  margin: 62px 16px auto auto;
  overflow: hidden;
  /* (Its column on the closed dialog too, since 7 Oct 2026. Under [open] alone, a dialog going - drawn on for its
     fade after [open] came off - was laid out again as a block for the whole of it, its foot jumping up the pane;
     [open] now only gives it its flex, and GSMotion keeps [open] on to the end of the fade besides.) */
  flex-direction: column;
}
dialog.settings[open] { display: flex; }
dialog.settings::backdrop, dialog.settings + .gs-backdrop { background: transparent; }
/* Over a whiteboard, in the dark, the Glass pane is nearly solid (96%) and --st-quiet has more ink (75%, not 40%).
   What the pane lets through there is the page's paper - near white whatever the theme - not the dark aurora of the
   list of whiteboards, and 14% of near white under the sheen lifted it enough to put --st-quiet at 3.3-4.4:1 in
   every section, in every palette (measured as drawn, 1 Oct 2026; over the list it passed). More ink alone could not
   mend it - the descriptions would have had to be nearly --ink - and the pane alone left the lines highest in a
   section, under the sheen, at 4.2-4.5 (Ember's the lowest). 55% ink measured 4.6, until the names' lines and a
   phone's longer panel (main, later the same day) put some at 4.35-4.49; 65% measures 4.62 on a phone, 4.9 wider.
   9 Oct 2026: a row more in Writing (the ring where the pen writes) brought a description to the top of a phone's
   screenful, under the sheen, at 4.35-4.47 (Ember's the lowest) - 75% measures 4.61 there, 5.26 wider.
   In the light both stay as they are (Glass's 86%, 40%): the paper behind is only lighter glass. Set as --pane, so that with
   less transparency asked for (prefers-reduced-transparency, the Glass rules) the pane is still the solid surface. */
:root[data-style="glass"] body:has(.bd) dialog.settings {
  --pane: light-dark(color-mix(in srgb, var(--surface) 86%, transparent), color-mix(in srgb, var(--surface) 96%, transparent));
  --st-quiet: light-dark(color-mix(in srgb, var(--ink) 40%, var(--muted)), color-mix(in srgb, var(--ink) 75%, var(--muted)));
}
@supports not (color: light-dark(#000, #fff)) {
  :root[data-style="glass"] body:has(.bd) dialog.settings {
    --pane: var(--not-light, color-mix(in srgb, var(--surface) 86%, transparent)) var(--not-dark, color-mix(in srgb, var(--surface) 96%, transparent));
    --st-quiet: var(--not-light, color-mix(in srgb, var(--ink) 40%, var(--muted))) var(--not-dark, color-mix(in srgb, var(--ink) 75%, var(--muted)));
  }
}
/* New whiteboard (and Save as a template's sheet) over a whiteboard, in the dark: nearly solid too, as Settings
   (6 Oct 2026). Under the masthead it had the masthead's dark behind its top half; with one slim bar over the editor
   it is the page all the way up, its light paper through Glass's 86% - a template's sentence, --ns-soft, read 3.9:1
   (GuideStar's) and the stronger ink alone still 4.4 (Ember's). */
:root[data-style="glass"] body:has(.bd) dialog:is(.bi-sheet, .tp-sheet) {
  --pane: light-dark(color-mix(in srgb, var(--surface) 86%, transparent), color-mix(in srgb, var(--surface) 96%, transparent));
}
@supports not (color: light-dark(#000, #fff)) {
  :root[data-style="glass"] body:has(.bd) dialog:is(.bi-sheet, .tp-sheet) {
    --pane: var(--not-light, color-mix(in srgb, var(--surface) 86%, transparent)) var(--not-dark, color-mix(in srgb, var(--surface) 96%, transparent));
  }
}
/* Over the list of whiteboards, in the dark, --st-quiet with 65% ink, as over a whiteboard (2 Oct 2026): its New whiteboard is opaque now
   (app.css, "New whiteboard"), and a palette's button in the dark theme is pale - right under the panel's top right
   at a desktop's width, under its lines on a phone's. Through the pane it lifted the descriptions over it to
   4.1-4.5:1 (Citrus the lowest), measured as drawn. A more solid pane (92%) was tried first: it mended the desktop and
   not the phone, where the dark aurora behind other lines is darker than the pane, so more pane is less contrast
   there; 55% left Citrus's type line at 4.48 on a phone. The light theme's button is dark, and leaves them be. */
:root[data-style="glass"] body:has(.bi-newbtn) dialog.settings {
  --st-quiet: light-dark(color-mix(in srgb, var(--ink) 40%, var(--muted)), color-mix(in srgb, var(--ink) 65%, var(--muted)));
}
@supports not (color: light-dark(#000, #fff)) {
  :root[data-style="glass"] body:has(.bi-newbtn) dialog.settings {
    --st-quiet: var(--not-light, color-mix(in srgb, var(--ink) 40%, var(--muted))) var(--not-dark, color-mix(in srgb, var(--ink) 65%, var(--muted)));
  }
}
/* A popped-out whiteboard has no masthead to sit under. */
body.bare dialog.settings { margin-top: 12px; height: min(620px, calc(100dvh - 24px)); }
/* Nor has a whiteboard since 5 Oct 2026: the panel opens just under the editor's bar, whose gear is at its right end -
   43 px high with a mouse (app.css, "The editor's bar"); a finger's 55 px bar leaves the 62 px above as it was. */
@media (pointer: fine) and (min-width: 601px) {
  body:not(.bare):has(.bd) dialog.settings { margin-top: 51px; height: min(620px, calc(100vh - 63px)); height: min(620px, calc(100dvh - 63px)); }
}

.st-head { display: flex; align-items: center; justify-content: space-between; gap: 12px;
           padding: 14px 20px 12px; border-bottom: 1px solid var(--rule); }
.st-head h2 { flex: 1; min-width: 0; font-size: 22px; line-height: 1.2; }
/* A phone's way back from a section to the list (settings.js, back). */
.st-back {
  display: inline-flex; align-items: center; justify-content: center; flex: none; width: 34px; height: 34px;
  margin: -4px 0 -4px -8px; padding: 0; border: 0; border-radius: 999px; background: transparent; color: var(--ink);
  cursor: pointer;
}
.st-back[hidden] { display: none; }
.st-back svg { width: 20px; height: 20px; }
.st-back:hover { background: var(--paper-2); }
.st-back:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.st-main { flex: 1; min-height: 0; display: grid; grid-template-columns: 150px minmax(0, 1fr); }

/* The sections: a column of names, the one shown in the primary button's colours (as a chosen choice is). */
.st-tabs { display: flex; flex-direction: column; gap: 4px; padding: 14px 10px; border-right: 1px solid var(--rule);
           overflow-y: auto; }
.st-tab {
  display: block; width: 100%; padding: 8px 14px; cursor: pointer; text-align: left;
  border: 0; border-radius: var(--radius); background: transparent;
  font: inherit; font-size: 14px; line-height: 20px; color: var(--ink);
  transition: background .12s, color .12s;
}
.st-tab:hover { background: var(--paper-2); }
.st-tabname { display: block; }
/* What is in each, said only in the phone's list of them (below). */
.st-tabnote { display: none; }
.st-tab[aria-selected="true"] { background: var(--btn-bg); color: var(--btn-fg); font-weight: 500; }
.st-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
:root[data-style="glass"] .st-tab { border-radius: 999px; }
:root[data-style="glass"] .st-tab[aria-selected="true"] {
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%);
  box-shadow: var(--bevel);
}

.st-panels { min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 0 22px 10px; }
.st-panel:focus-visible { outline: none; }
.st-heading { font-size: 19px; line-height: 1.25; padding: 16px 0 4px; }
.st-intro { font-size: 13px; line-height: 1.45; color: var(--st-quiet); padding-bottom: 4px; }
.st-row {
  display: grid; grid-template-columns: minmax(0, 1fr) 16rem; align-items: center; gap: 8px 20px;
  padding: 14px 0; border-bottom: 1px solid var(--rule);
}
.st-row:last-child { border-bottom: 0; }
.st-wide { grid-template-columns: minmax(0, 1fr); }
.st-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
/* (A <label> for the sliders: the form rule for labels - small capitals in --faint - is not wanted here.) */
.st-name {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin: 0;
  font-size: 14px; line-height: 1.35; letter-spacing: 0; text-transform: none; font-weight: 500; color: var(--ink);
}
.st-now { font-weight: 500; font-size: 13px; color: var(--ink); }
.st-desc { font-size: 12.5px; line-height: 1.45; color: var(--st-quiet); }
/* What a pane of the editor's is set to now (2 Oct 2026: the pen's buttons, "Lower: Eraser and back · Upper: Move the page"). */
.st-says { font-size: 12.5px; line-height: 1.45; font-weight: 500; color: var(--ink); }
.st-note { grid-column: 1 / -1; font-size: 12px; line-height: 1.45; color: var(--st-quiet); }
.st-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px;
           padding: 12px 20px 14px; border-top: 1px solid var(--rule); }
.st-said { font-size: 12.5px; color: var(--st-quiet); min-width: 0; }
.st-foot .btn { flex: none; }

/* About: a short list of facts, a name and its words. */
.st-facts { display: grid; grid-template-columns: 6.5rem minmax(0, 1fr); gap: 12px 16px; padding: 14px 0;
            font-size: 13.5px; line-height: 1.5; }
.st-facts dt { font-weight: 500; color: var(--ink); }
.st-facts dd { color: var(--st-quiet); }

/* A row of choices, one of which is on: a sunken track (a field is a hole, as above) with the chosen one raised
   out of it in the primary button's colours. */
.st-seg {
  display: flex; gap: 3px; padding: 3px; min-width: 0;
  background: var(--field-bg); border: 1px solid var(--rule); border-radius: calc(var(--radius) + 3px);
  box-shadow: var(--field-inset);
}
.st-seg button {
  flex: 1 1 auto; min-width: 0; padding: 6px 10px; cursor: pointer;
  border: 0; border-radius: var(--radius); background: transparent;
  font: inherit; font-size: 13px; line-height: 18px; color: var(--ink); white-space: nowrap;
  transition: background .12s, color .12s;
}
.st-seg button:hover { background: var(--paper-2); }
.st-seg button[aria-checked="true"] { background: var(--btn-bg); color: var(--btn-fg); font-weight: 500; }
.st-seg button:disabled { opacity: .45; cursor: not-allowed; background: transparent; }
.st-seg button[aria-checked="true"]:disabled { background: var(--btn-bg); }
.st-seg button:focus-visible, .st-swatch:focus-visible, .st-play:focus-visible, .st-close:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
/* Glass: lozenges, and the chosen one lacquered like a button - but opaque, and with a lighter gloss than .btn's.
   Measured: .btn's own (68% fill, 42% sheen) puts the light words of the light theme's dark button at about
   3:1, too little for 13px. */
:root[data-style="glass"] .st-seg,
:root[data-style="glass"] .st-seg button { border-radius: 999px; }
:root[data-style="glass"] .st-seg button[aria-checked="true"] {
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%);
  box-shadow: var(--bevel);
}
/* Each type pack's button in its own pack (the stacks are app.css's "Type packs"; the head script fetches the
   few letters each needs as the panel opens). */
.st-type [data-value=""] { font-family: 'Jost', 'Segoe UI', system-ui, sans-serif; }
.st-type [data-value="aero"] { font-family: 'Segoe UI', 'Source Sans 3', system-ui, sans-serif; }
.st-type [data-value="techno"] { font-family: 'Chakra Petch', 'Rajdhani', 'Segoe UI', system-ui, sans-serif; }
.st-type [data-value="terminal"] { font-family: 'Share Tech Mono', ui-monospace, Consolas, monospace; }

/* The palettes, each painted in its own colours rather than named in words: its page, a card on it with a line
   of its ink, and its two accents. The colours come from app.css itself (app/look.py), light and dark, and
   light-dark() picks the side the page is on. */
.st-swatches { display: grid; grid-template-columns: repeat(auto-fill, minmax(34px, 1fr)); gap: 8px; padding: 2px; }
.st-swatch {
  position: relative; aspect-ratio: 1; min-width: 0; max-width: 52px; padding: 0; cursor: pointer; overflow: hidden;
  border: 1px solid var(--rule-strong); border-radius: 12px;
  background: light-dark(var(--sw-lp), var(--sw-dp));
  transition: transform .12s, box-shadow .12s;
}
@supports not (color: light-dark(#000, #fff)) {
  .st-swatch {
    background: var(--not-light, var(--sw-lp)) var(--not-dark, var(--sw-dp));
  }
}
.st-swatch:hover { transform: translateY(-1px); }
.st-swatch i { position: absolute; display: block; }
.st-sw-card { left: 14%; top: 42%; width: 56%; height: 42%; border-radius: 3px;
              background: light-dark(var(--sw-ls), var(--sw-ds)); box-shadow: 0 1px 2px rgb(0 0 0 / .18); }
@supports not (color: light-dark(#000, #fff)) {
  .st-sw-card {
    background: var(--not-light, var(--sw-ls)) var(--not-dark, var(--sw-ds));
  }
}
.st-sw-card::after { content: ''; position: absolute; left: 18%; right: 26%; top: 40%; height: 2px; border-radius: 1px;
                     background: light-dark(var(--sw-li), var(--sw-di)); }
@supports not (color: light-dark(#000, #fff)) {
  .st-sw-card::after {
    background: var(--not-light, var(--sw-li)) var(--not-dark, var(--sw-di));
  }
}
.st-sw-a { right: 13%; top: 13%; width: 34%; height: 34%; border-radius: 50%; background: light-dark(var(--sw-la), var(--sw-da)); }
@supports not (color: light-dark(#000, #fff)) {
  .st-sw-a {
    background: var(--not-light, var(--sw-la)) var(--not-dark, var(--sw-da));
  }
}
.st-sw-b { right: 15%; bottom: 14%; width: 20%; height: 20%; border-radius: 50%; background: light-dark(var(--sw-lb), var(--sw-db)); }
@supports not (color: light-dark(#000, #fff)) {
  .st-sw-b {
    background: var(--not-light, var(--sw-lb)) var(--not-dark, var(--sw-db));
  }
}
.st-swatch[aria-checked="true"] { border-color: var(--accent); box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent); }
/* Glass: little bubbles of colour, lit from above. */
:root[data-style="glass"] .st-swatch { border-radius: 50%; }
:root[data-style="glass"] .st-swatch::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background-image: var(--gloss); opacity: .55; box-shadow: var(--bevel);
}

/* Colour packs (15 Oct 2026; templates/settings.html): a card a pack, made to feel like its mood since the owner's
   second look ("nahhh these are all too similar, just looks like you've changed the saturation"): the pens one strip
   of big swatches side by side, like the owner's reference palettes, the highlighters a thinner strip under them, a
   squiggle drawn in the pens, and the name in a small tag - light paper's pens in the light, slate's in the dark (only
   Classic's differ). The name is the pane's ink on the pane (the tag only ringed, nothing filled behind it, lit or
   not: the chosen card is ringed in the accent), so it reads as .st-name does in every look (tests/test_colour_packs.py)
   - only the inks drop the readability rule, by the owner's choice, never the words. The strip is ringed in the faint
   rule, so the palest still shows against the pane. */
.st-packs { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 15rem), 1fr)); gap: 10px; padding: 3px; }
.st-pack {
  display: flex; flex-direction: column; align-items: stretch; gap: 7px; min-width: 0; padding: 8px 10px 6px;
  cursor: pointer; border: 1px solid var(--rule-strong); border-radius: 12px; background: transparent;
  font: inherit; font-size: 13px; line-height: 18px; color: var(--ink); text-align: left;
  transition: transform .12s, box-shadow .12s;
}
.st-pack:hover { transform: translateY(-1px); }
.st-pack:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.st-pack[aria-checked="true"] { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); font-weight: 500; }
:root[data-style="glass"] .st-pack { border-radius: 16px; }
.st-packname { align-self: flex-start; padding: 0 8px; border: 1px solid var(--rule-strong); border-radius: 999px;
               font-size: 12px; line-height: 18px; letter-spacing: .01em; }
.st-packsw { display: grid; grid-template-columns: repeat(var(--n, 8), 1fr); row-gap: 3px; overflow: hidden;
             border-radius: 8px; box-shadow: 0 0 0 1px var(--rule-strong); }
.st-packsw i { height: 28px; min-width: 0; background: light-dark(var(--sw-l), var(--sw-d)); }
.st-packhls { grid-column: 1 / -1; display: flex; }   /* (a row of its own under the pens, never split) */
.st-packsw i.st-packhl { flex: 1 1 0; height: 9px; opacity: .85; }
.st-packstroke { display: block; width: 100%; height: 20px; overflow: visible; }
.st-packstroke path { fill: none; stroke-width: 3.2; stroke-linecap: round; stroke: light-dark(var(--sw-l), var(--sw-d)); }
@supports not (color: light-dark(#000, #fff)) {
  .st-packsw i { background: var(--not-light, var(--sw-l)) var(--not-dark, var(--sw-d)); }
  .st-packstroke path { stroke: var(--not-light, var(--sw-l)) var(--not-dark, var(--sw-d)); }
}
:root[data-style="glass"] .st-packsw i { background-image: var(--gloss); }
:root[data-motion="reduce"] .st-pack { transition: none; }

/* A slider: the track, its value, and for the volume a button to hear it. */
.st-slide { display: flex; align-items: center; gap: 10px; min-width: 0; }
.st-slide input[type="range"] { flex: 1; width: auto; min-width: 0; height: 22px; padding: 0; border: 0;
                                background: transparent; box-shadow: none; accent-color: var(--accent); }
.st-slide input[type="range"]:disabled { opacity: .45; }
.st-value { flex: none; width: 38px; text-align: right; font-size: 12.5px; color: var(--st-quiet);
            font-variant-numeric: tabular-nums; }
.st-play { flex: none; }
.st-play:disabled { opacity: .45; cursor: not-allowed; }
:root[data-style="glass"] .st-play { border-radius: 999px; }

/* Sharing (static/js/sharing.js; the People page until 1 Oct 2026): a link a row - who, and under it what it opens
   and when it was made and used; what they may do in a lozenge; a ⋯ with the rest. A new link is a form of the
   panel's own rows. Its words are the panel's: --ink, --st-quiet, and --red only in the ⋯ (on its opaque pane). */
.st-subhead { font-family: var(--serif); font-size: 16px; font-weight: 600; line-height: 1.3; color: var(--ink); padding: 18px 0 4px; }
.sh-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 0 6px; }
.sh-bar .btn { flex: none; }
/* The panel's own primary buttons (New link, Make the link, Copy) are opaque under the lighter gloss its chosen
   choices have: Glass's .btn (68% fill, 42% sheen) measured 2.6-3.0:1 for "New link" in the light theme, as drawn. */
:root[data-style="glass"] dialog.settings .btn:not(.ghost):not(.danger) {
  background-color: var(--btn-bg); border-color: var(--btn-bg);
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%);
}
/* A warning, so --ink: in --st-quiet, high in the section on a phone, it measured 4.36:1 on Ember's dark Glass. */
.sh-warn { padding: 6px 0; color: var(--ink); }
.sh-list { list-style: none; }
.sh-row { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center; gap: 6px 12px;
          padding: 12px 0; border-bottom: 1px solid var(--rule); }
.sh-who { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sh-who .st-name { justify-content: flex-start; }
.sh-who b { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sh-badge { flex: none; padding: 0 8px; border-radius: 999px; border: 1px solid var(--rule-strong); font-size: 11.5px; font-weight: 400;
            color: var(--ink); }
/* What they may do, in words ("Can edit"), not in a lozenge: a lozenge beside the ⋯ read as a button to press, and
   said "Edit" where the Share dialog said "Can edit" (1 Oct 2026). */
.sh-role { flex: none; font-size: 12.5px; font-weight: 500; color: var(--ink); white-space: nowrap; }
.sh-more { flex: none; }
/* A link's ⋯: a little menu of its own, on an opaque pane under the button. */
.sh-menu {
  position: absolute; right: 0; top: calc(100% - 8px); z-index: 5; display: flex; flex-direction: column; min-width: 230px;
  padding: 5px; background: var(--surface); border: 1px solid var(--rule-strong); border-radius: 12px; box-shadow: var(--shadow-3);
}
.sh-menu button {
  min-height: 40px; padding: 9px 12px; border: 0; border-radius: 8px; background: transparent; cursor: pointer; text-align: left;
  font: inherit; font-size: 13.5px; color: var(--ink);
}
.sh-menu button:hover { background: var(--paper-2); }
.sh-menu button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.sh-menu .sh-danger { color: var(--red); border-top: 1px solid var(--rule); border-radius: 0 0 8px 8px; }
/* What the link lets them do, one of three, in the same menu (2 Oct 2026): set apart by rules, the one it has ticked. */
.sh-menu .sh-roleitem { position: relative; padding-left: 30px; }
.sh-menu .sh-roleitem[aria-checked="true"] { font-weight: 500; cursor: default; }
.sh-menu .sh-roleitem[aria-checked="true"]::before { content: '✓'; position: absolute; left: 11px; }
.sh-menu .sh-rolefirst { border-top: 1px solid var(--rule); border-radius: 0; }
.sh-menu .sh-afterroles { border-top: 1px solid var(--rule); border-radius: 0; }
/* You (1 Oct 2026): the owner's name, a field and its Save. */
.st-nameset { display: flex; gap: 8px; min-width: 0; }
.st-nameset input { flex: 1; min-width: 0; }
.st-nameset .btn { flex: none; }
/* A new link. */
.sh-form { padding-bottom: 6px; border-bottom: 1px solid var(--rule); }
.sh-form .st-row:last-of-type { border-bottom: 0; }
.sh-pick { grid-column: 1 / -1; display: flex; flex-direction: column; max-height: 220px; overflow-y: auto; padding: 6px 12px;
           border: 1px solid var(--rule); border-radius: 12px; background: var(--field-bg); }
.sh-pick[hidden] { display: none; }
.sh-pickhead { margin: 6px 0 2px; font-size: 12.5px; font-weight: 500; color: var(--ink); }
.sh-check { display: flex; align-items: center; gap: 9px; margin: 0; min-height: 34px; padding: 4px 2px; cursor: pointer;
            font-size: 13.5px; font-weight: 400; letter-spacing: 0; text-transform: none; color: var(--ink); }
.sh-check input { flex: none; width: auto; box-shadow: none; accent-color: var(--accent); }
.sh-check span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sh-check em { font-style: normal; color: var(--st-quiet); }
.sh-error { display: flex; gap: 8px; padding: 8px 0 0; font-size: 13.5px; font-weight: 500; color: var(--ink); }
.sh-error[hidden] { display: none; }
.sh-error::before { content: '!'; flex: none; display: grid; place-items: center; width: 16px; height: 16px; margin-top: 2px;
                    border-radius: 50%; background: var(--red); color: var(--surface); font-size: 11px; font-weight: 700; }
.sh-acts { display: flex; justify-content: flex-end; gap: 8px; padding: 10px 0 4px; }
.sh-made { padding: 4px 0 14px; border-bottom: 1px solid var(--rule); }
.sh-copyrow { display: flex; gap: 8px; margin: 10px 0; }
.sh-copyrow input { flex: 1; min-width: 0; font-size: 13px; font-family: ui-monospace, 'Cascadia Mono', Consolas, monospace; }
/* Apps (9 Oct 2026, static/js/apps.js): the Helper's keys, a row each as Sharing's links are, with Revoke in place of
   the ⋯ - and the new key's name typed in the bar itself, the one thing a key is asked. Ghost buttons: their words are
   --ink on the pane, as Done's and Cancel's are, measured with the rest of the panel. */
#ap-form { flex-wrap: wrap; }
#ap-form input { flex: 1 1 160px; min-width: 0; }
.ap-row { grid-template-columns: minmax(0, 1fr) auto; }
.sh-copyrow .btn { flex: none; }
/* The AI, and its log folded away. */
.sh-ai { padding-top: 6px; }
.sh-log summary { padding: 8px 0; cursor: pointer; font-size: 13.5px; font-weight: 500; color: var(--ink); }
.sh-logrows { list-style: none; }
.sh-logrows li { display: flex; flex-direction: column; gap: 2px; padding: 8px 0; border-bottom: 1px solid var(--rule); }
@media (max-width: 600px) {
  .sh-row { grid-template-columns: minmax(0, 1fr) auto; }
  .sh-row .sh-role { grid-row: 2; justify-self: start; }
  .sh-row .sh-more { grid-row: 1 / span 2; grid-column: 2; }
}
/* Safety (4 Oct 2026, static/js/safety.js): the panel's own rows and words, as Sharing's are - the blocklist a field
   across the row, what waits a row each (its poster, what it is and who added it, Approve lit and the rest ghosts). */
.sf-block textarea { grid-column: 1 / -1; width: 100%; min-height: 72px; resize: vertical; box-sizing: border-box; font: inherit;
  font-size: 13.5px; }
.sf-blockacts { grid-column: 1 / -1; display: flex; justify-content: flex-end; }
.sf-how { padding: 8px 0 2px; }
.sf-list { list-style: none; }
.sf-row { display: grid; grid-template-columns: 96px minmax(0, 1fr) auto; align-items: center; gap: 6px 12px; padding: 12px 0;
  border-bottom: 1px solid var(--rule); }
.sf-poster { width: 96px; height: 54px; border-radius: 8px; object-fit: cover; background: #10161d; display: block; }
.sf-file { background: linear-gradient(180deg, #1d2733, #10161d); }
.sf-what { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sf-what .st-name { justify-content: flex-start; }
.sf-what b { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sf-acts { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
@media (max-width: 600px) {
  .sf-row { grid-template-columns: 72px minmax(0, 1fr); }
  .sf-poster { width: 72px; height: 40px; }
  .sf-acts { grid-column: 1 / -1; justify-content: flex-start; }
}
/* The masthead's gear, for the owner, when videos wait for their OK (js/safety.js): a count in a red bubble on its
   corner - white on the Board's own red, the same in every look (5.9:1). */
.settings-btn { position: relative; }
.gs-vbadge { position: absolute; top: -6px; right: -7px; min-width: 17px; height: 17px; box-sizing: border-box; padding: 0 4px;
  border-radius: 999px; background: #c0262d; color: #fff; font: 700 11px/17px system-ui, "Segoe UI", sans-serif; text-align: center;
  box-shadow: 0 0 0 1.5px #fff, 0 1px 3px rgb(0 0 0 / .3); pointer-events: none; }

/* A phone: one column. The panel opens on the list of sections, a row each with what is in it; a section opened
   from it fills the panel, with the way back in the head (settings.js: data-view). Every control goes under its
   words, and the panel is the width of the screen under the masthead. */
@media (max-width: 600px) {
  dialog.settings { width: calc(100% - 16px); margin: 62px auto auto; height: calc(100dvh - 70px); }
  body.bare dialog.settings { margin-top: 8px; height: calc(100dvh - 16px); }
  .st-main { grid-template-columns: minmax(0, 1fr); }
  dialog.settings[data-view="list"] .st-panels, dialog.settings[data-view="section"] .st-tabs { display: none; }
  dialog.settings[data-view="list"] .st-foot { display: none; }
  .st-tabs { gap: 2px; padding: 10px 10px 14px; border-right: 0; overflow-y: auto; }
  .st-tab { display: flex; flex-direction: column; gap: 2px; min-height: 56px; justify-content: center; padding: 9px 16px;
            border-radius: 14px; }
  :root[data-style="glass"] .st-tab { border-radius: 16px; }
  .st-tab .st-tabname { font-size: 15px; font-weight: 500; }
  .st-tabnote { display: block; font-size: 12.5px; line-height: 1.35; color: var(--st-quiet); }
  .st-tab[aria-selected="true"] .st-tabnote { color: inherit; }
  .st-panels { padding: 0 16px 10px; }
  .st-row { grid-template-columns: minmax(0, 1fr); gap: 9px; }
  .st-seg button { padding: 7px 8px; }
  .st-facts { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .st-facts dd { margin-bottom: 10px; }
  .st-head { padding: 12px 16px 10px; }
  .st-foot { padding: 10px 16px 12px; }
}


/* ═══ the editor's ⋯ menu (1 Oct 2026: board.js moreHtml - "the stuff in the ⋯ menu needs cleaning up - nice
   toggleable options and sliders") ═══ */
/* The same .ctxmenu pane, wider, with rows of one height: a switch (its words on the left, a lozenge on the right),
   a few choices under their words, a slider under its words with its value at the end of them, and the plain
   actions. Each section under its heading, with a little air above it. Last, apart and in red, what takes things
   away. Taller than the window, it scrolls within itself (.ctxmenu's max-height). On a touch screen every row and
   every choice is 44px tall, as a finger needs (Apple's own measure); with a mouse, 36px.

   The words are --ink, the headings and a slider's value --muted with ink in it, a chosen choice --btn-fg on
   --btn-bg, and the red ones --red: each measured as drawn in every palette, light and dark, glass and plain
   (tests/test_settings.py). The switch is drawn, not written: its track's edge is --muted (3:1 and more, as a line
   must be), on it is --btn-bg. */
.ctxmenu.bd-more { width: min(320px, calc(100vw - 16px)); padding: 6px; }
/* Its sheen goes with its first rows as it scrolls, rather than staying at the top over whatever is scrolled under
   it: on Glass in the dark theme the sheen took a slider's words to 4.05-4.45:1 there, in most palettes, once the
   menu had a row more (Share's branch's Teaching, 1 Oct 2026 - tests/test_settings.py). */
:root[data-style="glass"] .ctxmenu.bd-more { background-attachment: local; }
/* Its headings in --ink: the first sits under the pane's sheen (--gloss-soft), where .menu-head's own measured
   3.1-3.6:1 in every palette, as drawn (1 Oct 2026). */
.bd-more .menu-head { margin-top: 8px; color: var(--ink); font-weight: 500; }
.bd-more .menu-head:first-child { margin-top: 0; }
/* (32 px rows for a mouse, 44 for a finger, below: with its rows cut to what a lesson reaches for, 1 Oct 2026, the menu
   fits under the bar in a window 800 px tall without scrolling - tests/test_settings.py.) */
.bd-more .menu-item, .bd-mrow {
  display: flex; align-items: center; gap: 12px; min-height: 32px; padding: 6px 10px;
  border-radius: 7px; font-size: 13px; line-height: 1.3; color: var(--ink);
}
/* A shortcut's keys: not --faint here - at 10.5px it measured 4.24:1 on Aero's light Glass, 4.36 on Harbour's. */
.bd-more .menu-item .menu-key { float: none; margin-left: auto; color: color-mix(in srgb, var(--ink) 40%, var(--muted)); }
.bd-mlabel { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 10px; }
.bd-mlabel output { margin-left: auto; font-size: 12.5px; font-variant-numeric: tabular-nums;
                    color: color-mix(in srgb, var(--ink) 60%, var(--muted)); }
.bd-more [data-pick]:focus-visible, .bd-more input[data-slide]:focus-visible {
  outline: 2px solid var(--accent); outline-offset: -2px;
}

/* A switch: a lozenge with a bead in it, the bead at the right and the track filled when on. */
.bd-switch {
  position: relative; flex: none; width: 38px; height: 22px; border-radius: 999px;
  background: var(--field-bg); border: 1px solid var(--muted); box-shadow: var(--field-inset);
  transition: background .15s, border-color .15s;
}
.bd-switch::after {
  content: ''; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%;
  background: var(--surface); box-shadow: 0 1px 2px rgb(0 0 0 / .3), inset 0 0 0 1px var(--rule-strong);
  transition: transform .15s;
}
/* (The New whiteboard sheet's own window, .ns-own, is one too: 2 Oct 2026.) */
:is(.bd-mtoggle, .ns-own)[aria-checked="true"] .bd-switch { background: var(--btn-bg); border-color: var(--btn-bg); }
:is(.bd-mtoggle, .ns-own)[aria-checked="true"] .bd-switch::after { transform: translateX(16px); background: var(--btn-fg); }
/* Glass: lacquered, as a button is - a hard-stop gloss on the track, the bead a little bubble lit from above. */
:root[data-style="glass"] .bd-switch {
  background-image: linear-gradient(180deg, rgb(255 255 255 / .30) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%);
}
:root[data-style="glass"] .bd-switch::after {
  background-image: radial-gradient(circle at 35% 30%, rgb(255 255 255 / .9) 0, rgb(255 255 255 / 0) 60%);
}
:root[data-motion="reduce"] .bd-switch, :root[data-motion="reduce"] .bd-switch::after { transition: none; }

/* A few choices: the words, then the choices in a sunken track under them, the chosen one raised in the primary
   button's colours (as Settings' are). */
.bd-mchoice { flex-direction: column; align-items: stretch; gap: 6px; }
.bd-mchoice.bd-minline { flex-direction: row; align-items: center; gap: 12px; }
.bd-minline .bd-mseg { flex: none; }
.bd-minline .bd-mseg button { padding-inline: 10px; }
.bd-mseg {
  display: flex; gap: 3px; padding: 3px;
  background: var(--field-bg); border: 1px solid var(--rule); border-radius: 9px; box-shadow: var(--field-inset);
}
.bd-mseg button {
  flex: 1 1 auto; min-width: 0; min-height: 30px; padding: 4px 8px; cursor: pointer;
  border: 0; border-radius: 6px; background: transparent;
  font: inherit; font-size: 12.5px; line-height: 1.2; color: var(--ink);
}
.bd-mseg button:hover { background: var(--paper-2); }
.bd-mseg button[aria-checked="true"] { background: var(--btn-bg); color: var(--btn-fg); font-weight: 500; }
:root[data-style="glass"] .bd-mseg, :root[data-style="glass"] .bd-mseg button { border-radius: 999px; }
:root[data-style="glass"] .bd-mseg button[aria-checked="true"] {
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%);
  box-shadow: var(--bevel);
}

/* A slider: its words and value, the track under them, the width of the menu. */
.bd-mslider { flex-direction: column; align-items: stretch; gap: 2px; margin: 0; cursor: default;
              letter-spacing: 0; text-transform: none; font-weight: 400; }
.bd-mslider .bd-mlabel { font-size: 13px; color: var(--ink); }
.bd-mslider input[type="range"] { width: 100%; height: 24px; padding: 0; border: 0; background: transparent;
                                  box-shadow: none; accent-color: var(--accent); }

/* "More settings…", under a line of its own; then what takes things away, last, under a stronger one, in red, each
   asking first (board.js). */
.bd-mfoot { margin-top: 6px; padding-top: 4px; border-top: 1px solid var(--rule); }
.bd-mdanger { margin-top: 6px; padding-top: 4px; border-top: 1px solid var(--rule-strong); }
.bd-more .menu-item.danger { color: var(--red); }
.bd-more .menu-item.danger:hover { background: var(--red-bg); }

/* A finger: 44px, every row and every choice. */
@media (pointer: coarse) {
  .bd-more .menu-item, .bd-mrow { min-height: 44px; }
  .bd-mseg button { min-height: 44px; }
  .bd-mslider input[type="range"] { height: 44px; }
  .bd-mchoice, .bd-mslider { min-height: auto; }
}

/* ═══ the History panel (lane L14, 1 Oct 2026: static/js/board/history.js) ═══ */
/* A page's history: a picture of the page as it was, when it was so, and a slider across the versions kept - the
   oldest at the left, the page as it is now at the right. A dialog, so on Glass a pane like the cards, with the
   Settings panel's head and foot (.st-head, .st-close, .st-foot - the Appearance panel's until 1 Oct 2026); centred,
   and wider, for the picture.
   Its words are --ink on the pane, but for the foot's note and the slider's two ends (--muted, low in the pane,
   below the sheen that took --muted under 4.5:1 at the Appearance panel's top): measured as drawn in every palette,
   light and dark, glass and plain (tests/test_history.py). */
dialog.history {
  width: min(760px, calc(100% - 24px)); max-width: none;
  max-height: calc(100vh - 24px); max-height: calc(100dvh - 24px);
  overflow: hidden; flex-direction: column;            /* (on the closed one too: dialog.settings says why) */
}
dialog.history[open] { display: flex; }
.hs-body { overflow-y: auto; overscroll-behavior: contain; padding: 14px 20px 8px; }
/* The picture, on the page's own paper (drawn on the canvas as the page's thumbnail is: board.js thumbnail). */
.hs-pic { position: relative; height: clamp(170px, 50vh, 430px); border-radius: 10px; overflow: hidden;
  background: #fbfaf6; box-shadow: 0 0 0 1px var(--rule-strong), 0 1px 3px rgb(0 0 0 / .12); }
.hs-pic.hs-dark { background: #1d272b; }
.hs-pic canvas { position: absolute; inset: 0; display: block; width: 100%; height: 100%; transition: opacity .12s; }
.hs-pic[data-loading] canvas { opacity: .55; }
/* A page with nothing on it then: dark letters on paper, light on slate (as .bd-pgthumb em). */
.hs-blank { position: absolute; inset: 0; display: grid; place-items: center; font-style: normal; font-size: 13px; color: #4f5963; }
.hs-pic.hs-dark .hs-blank { color: #c3ced3; }
/* Glass: a lit edge round the picture, over it (an inset shadow on the box itself would lie under the canvas). */
:root[data-style="glass"] .hs-pic { border-radius: 14px; }
:root[data-style="glass"] .hs-pic::after { content: ''; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none; box-shadow: var(--bevel); }
.hs-when { min-height: 1.45em; margin: 12px 0 2px; font-size: 14px; line-height: 1.45; color: var(--ink); }
.hs-when b { font-weight: 600; }
.hs-what { white-space: nowrap; }
.hs-scrub { display: flex; }
.hs-ends { display: flex; justify-content: space-between; gap: 12px; font-size: 12px; color: var(--muted);
  font-variant-numeric: tabular-nums; }
dialog.history .st-foot .btn { flex: none; }
.hs-note { margin: 0; font-size: 12px; line-height: 1.45; color: var(--muted); text-align: left; }
/* Restore, on Glass: solid, under a lighter gloss than .btn's - as the masthead's current page and the Appearance
   panel's chosen choice are, for the same reason. Measured as drawn (1 Oct 2026): .btn's own, 68% fill under a 42%
   sheen, put its light words at 2.7-3.0:1 in the light theme in every palette, and 4.3-4.5 in the dark in two. */
:root[data-style="glass"] dialog.history .st-foot .btn {
  background-color: var(--btn-bg); border-color: var(--btn-bg);
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%);
}
:root[data-motion="reduce"] .hs-pic canvas { transition: none; }


/* ═══ the progress tube (Redraw's progress) (GuideStar app.css 3456–3550) ═══ */

/* The tube. Nine pixels rather than six: below about eight a bubble is
   smaller than the rounding on the ends and the whole thing reads as noise
   on a line. */
.genprog-track {
  position: relative;
  height: 9px; border-radius: 999px; overflow: hidden;
  background-color: color-mix(in srgb, var(--ink) 9%, transparent);
  background-image: linear-gradient(180deg,
    color-mix(in srgb, var(--ink) 10%, transparent) 0,
    transparent 55%);
  box-shadow:
    inset 0 1px 2px light-dark(rgb(10 60 80 / .13), rgb(0 0 0 / .36)),
    inset 0 -1px 0 var(--sheen-soft, rgb(255 255 255 / .30));
}
@supports not (color: light-dark(#000, #fff)) {
  .genprog-track {
    box-shadow:
      inset 0 1px 2px var(--not-light, rgb(10 60 80 / .13)) var(--not-dark, rgb(0 0 0 / .36)),
      inset 0 -1px 0 var(--sheen-soft, rgb(255 255 255 / .30));
  }
}

/* The lit water. background-color as its own longhand, never folded into the
   shorthand, so a mistake in the image layer can never take the colour with
   it. */
.genprog-fill {
  position: relative;
  width: 0; height: 100%; border-radius: 999px;
  transition: width .18s linear;
  background-color: var(--gf-glow, var(--accent-2));
  background-image: linear-gradient(180deg,
    var(--sheen, rgb(255 255 255 / .70)) 0 16%,
    var(--sheen-soft, rgb(255 255 255 / .28)) 16% 30%,
    transparent 30% 74%,
    color-mix(in srgb, var(--ink) 14%, transparent) 100%);
}

/* Clips the bubbles to the water. Its own element so the fill keeps its
   rounded ends and the meniscus can still overhang them. */
.genprog-body {
  position: absolute; inset: 0; z-index: 1;
  border-radius: inherit; overflow: hidden; pointer-events: none;
}

/* The bubbles. Two fields on different clocks and different tile widths, so
   the pattern never visibly repeats and the near ones outrun the far ones —
   which is most of what makes it read as depth rather than wallpaper.

   Each bubble is a bright core with a faint dark rim, because a core alone
   disappears on a pale palette and a rim alone disappears on a dark one. */
.genprog-body::before,
.genprog-body::after {
  content: ''; position: absolute;
  top: -6px; bottom: -6px; left: -140px; width: 900px;
  will-change: transform;
}
.genprog-body::before {
  background-image:
    radial-gradient(circle 2.6px at 6px 62%,
      var(--sheen, rgb(255 255 255 / .82)) 0 42%,
      color-mix(in srgb, var(--ink) 16%, transparent) 62% 82%, transparent 86%),
    radial-gradient(circle 1.8px at 30px 34%,
      var(--sheen, rgb(255 255 255 / .82)) 0 46%,
      color-mix(in srgb, var(--ink) 14%, transparent) 66% 84%, transparent 88%),
    radial-gradient(circle 2.2px at 52px 74%,
      var(--sheen, rgb(255 255 255 / .82)) 0 44%,
      color-mix(in srgb, var(--ink) 15%, transparent) 64% 84%, transparent 88%);
  background-size: 71px 100%;          /* coprime with the finer field */
  animation: gp-stream 7.5s linear infinite;
}
.genprog-body::after {
  background-image:
    radial-gradient(circle 1.4px at 10px 40%,
      var(--sheen-soft, rgb(255 255 255 / .45)) 0 52%, transparent 60%),
    radial-gradient(circle 1.1px at 34px 70%,
      var(--sheen-soft, rgb(255 255 255 / .45)) 0 54%, transparent 62%);
  background-size: 47px 100%;
  opacity: .8;
  animation: gp-stream 11.5s linear infinite;
}

/* The meniscus: the brightest thing on the bar, sitting exactly on the true
   width. Whatever the bubbles are doing, the eye lands on this edge, and
   this edge never lies. */
.genprog-fill::after {
  content: ''; position: absolute; z-index: 2;
  top: -1px; bottom: -1px; right: -1px; width: 5px;
  border-radius: 999px; pointer-events: none;
  background: linear-gradient(90deg, transparent,
    var(--sheen, rgb(255 255 255 / .78)));
  opacity: .95;
}

.genprog-note { margin: 7px 0 0; }

/* Bubbles stream along the tube. One tile width per cycle, so it loops with
   no seam and never accumulates an offset over a long run. */
@keyframes gp-stream {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(71px, 0, 0); }
}

/* ═══ the Board (GuideStar app.css 5423–6622) ═══ */
/* ══ The Board ══════════════════════════════════════════════════════════
   The index (boards.html) and the editor (board.html, js/board.js). The
   editor's controls float over the page as small panes — the page is the
   thing, and it runs edge to edge under them. */

/* Notebook colours: a dot on the rail, the cards and the editor's chip. */
.nb-sky    { --nb: #3a8fd9; }
.nb-leaf   { --nb: #3aa05a; }
.nb-sun    { --nb: #e0a21b; }
.nb-rose   { --nb: #d9537a; }
.nb-violet { --nb: #7e5bd6; }
.nb-slate  { --nb: #5d7483; }
.nb-none   { --nb: color-mix(in srgb, var(--ink) 30%, transparent); }
.bi-dot { display: inline-block; flex: none; width: 9px; height: 9px; border-radius: 50%;
  background: var(--nb, var(--accent)); box-shadow: inset 0 1px 1px rgb(255 255 255 / .45); }

/* ── The index ── */
.board-index { display: grid; grid-template-columns: 236px 1fr; gap: 20px; align-items: start; }
.bi-rail { position: sticky; top: 96px; padding: 12px 10px; display: flex; flex-direction: column; gap: 4px; }
.bi-link { display: flex; align-items: center; gap: 9px; padding: 7px 10px; border-radius: 10px;
  color: var(--ink); font-size: 13.5px; transition: background .15s; }
.bi-link b { font-weight: 500; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bi-link span { margin-left: auto; font-size: 11.5px; color: color-mix(in srgb, var(--ink) 15%, var(--muted));
  font-variant-numeric: tabular-nums; }
.bi-link:hover { background: color-mix(in srgb, var(--accent) 9%, transparent); color: var(--ink); }
.bi-link.on { background: color-mix(in srgb, var(--accent) 16%, transparent); font-weight: 500; }
.bi-head { display: flex; align-items: center; justify-content: space-between; margin: 12px 10px 4px;
  font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase; color: color-mix(in srgb, var(--ink) 15%, var(--muted)); }
.bi-add { width: 24px; height: 24px; border-radius: 50%; border: 1px solid var(--rule-strong); background: transparent;
  color: var(--ink); font-size: 16px; line-height: 1; cursor: pointer; }
.bi-add:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.bi-none { margin: 2px 10px 6px; }
.bi-tags { display: flex; flex-wrap: wrap; gap: 5px; padding: 2px 8px 4px; }
.bi-tag, .bi-cardtags a { font-size: 11.5px; padding: 2px 8px; border-radius: 999px; color: var(--ink);
  background: color-mix(in srgb, var(--accent) 8%, transparent); border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent); }
.bi-tag.on, .bi-tag:hover, .bi-cardtags a:hover { background: color-mix(in srgb, var(--accent) 20%, transparent); color: var(--ink); }
.bi-bottom { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--rule); }
.bi-main { min-width: 0; }
/* The disk nearly full (11 Oct 2026, services/storage.py): one quiet line over the list, the owner's alone - glass
   tinted with the yellow made for tinting, written in the plain ink. Settings → Storage has the same words (.sg-warn). */
.bi-storage, .sg-warn { margin: 0 0 14px; padding: 9px 14px; border-radius: 14px; font-size: 13.5px; line-height: 1.45;
  color: var(--ink); background: color-mix(in srgb, var(--amber-glass) 16%, var(--surface));
  border: 1px solid var(--amber-br); }
.bi-storage a { color: var(--ink); font-weight: 600; margin-left: 6px; white-space: nowrap; }
.sg-warn[hidden] { display: none; }
.bi-search { display: flex; gap: 10px; margin-bottom: 14px; }
.bi-search input { flex: 1; }
.bi-search select { width: auto; }
.bi-nbhead { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0 0 14px; }
.bi-nbhead h2 { margin: 0; font-size: 22px; }
.bi-nbhead .row { margin-left: auto; }
.bi-nbhead .bi-dot { width: 13px; height: 13px; }
.bi-colours { display: flex; gap: 5px; }
.bi-colour { width: 20px; height: 20px; border-radius: 50%; border: 2px solid transparent; background: var(--nb); cursor: pointer;
  box-shadow: inset 0 1px 2px rgb(255 255 255 / .5); }
.bi-colour.on { border-color: var(--ink); }
.bi-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(214px, 1fr)); gap: 16px; }
.bi-card { position: relative; display: flex; flex-direction: column; border-radius: 14px; overflow: hidden;
  background: var(--surface); border: 1px solid var(--rule); box-shadow: var(--shadow-1);
  transition: box-shadow .18s, transform .18s; }
/* (Lifted under a pointer that can hover alone - 7 Oct 2026: on an iPad a tapped card kept the lift after the finger
   had gone, the "hover" a touch screen leaves behind.) */
@media (hover: hover) { .bi-card:hover { box-shadow: var(--shadow-2); transform: translateY(-2px); } }
.bi-card.bi-binned { opacity: .72; }
.bi-thumb { position: relative; display: block; aspect-ratio: 3 / 2; background: #fbfaf6; border-bottom: 1px solid var(--rule); }
.bi-thumb.paper-slate { background: #1d272b; }
.bi-thumb.paper-glass { background: color-mix(in srgb, var(--paper) 60%, #dfeaf2); }
.bi-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top center; }
.bi-empty { position: absolute; inset: 0; display: grid; place-items: center; font-size: 12px; color: rgb(60 70 80 / .55); }
.bi-thumb.paper-slate .bi-empty { color: rgb(255 255 255 / .5); }
.bi-pin { position: absolute; top: 8px; left: 10px; color: #e0a21b; font-size: 15px; text-shadow: 0 1px 2px rgb(0 0 0 / .25); }
.bi-meta { padding: 10px 40px 12px 12px; display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.bi-title { font-weight: 500; font-size: 14.5px; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bi-when { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.bi-chip { display: inline-flex; align-items: center; gap: 5px; }
.bi-snippet { color: var(--ink); background: color-mix(in srgb, #ffd21f 22%, transparent); border-radius: 6px; padding: 3px 6px; }
.bi-cardtags { display: flex; flex-wrap: wrap; gap: 4px; }
.bi-more { position: absolute; right: 8px; bottom: 10px; width: 28px; height: 26px; border: 0; border-radius: 8px; background: transparent;
  color: var(--muted); font-size: 16px; cursor: pointer; }
.bi-more:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--ink); }
.bi-emptystate { grid-column: 1 / -1; padding: 40px 0; text-align: center; color: color-mix(in srgb, var(--ink) 15%, var(--muted)); }
:root[data-style="glass"] .bi-card { background-color: var(--pane); background-image: var(--gloss-soft);
  box-shadow: var(--rim), var(--shadow-1); border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); }
:root[data-style="glass"] .bi-card:hover { box-shadow: var(--rim), var(--shadow-2); }
@media (max-width: 760px) {
  .board-index { grid-template-columns: 1fr; }
  .bi-rail { position: static; }
}

/* ── New whiteboard (2 Oct 2026: templates/boards.html, the owner: "improve the experience of making a new
   whiteboard") ── The list's one main action, a sheet it opens, the first card in the grid, and a welcome where there
   are no whiteboards yet. Lane L17's menu of templates here before went into the sheet.

   The button, and every lit one in the sheet, opaque under the lighter gloss, as Share's and Settings' are: Glass's
   own .btn - its colour at 68% under a 42% sheen - read as a dull grey-blue lozenge no weightier than the ghosts
   beside it (the owner's screenshot), and measured 2.6-3.0:1 for its words in the light theme. Every word in the
   sheet is --ink on the pane, or --ns-soft (--ink 45% into --muted, as the templates' menu measured with cards
   showing through Glass behind it), lifted to 70% where a tile is chosen; measured as drawn, in every palette,
   light and dark, glass and plain, at a desktop's width and a phone's (tests/test_new_whiteboard.py). */
.bi-acts { align-items: center; }
.bi-openfile svg { width: 14px; height: 14px; flex: none; }
.bi-newbtn { gap: 8px; padding: 10px 20px 10px 16px; font-size: 12.5px; }
.bi-newbtn svg { width: 15px; height: 15px; flex: none; }
:root[data-style="glass"] .bi-newbtn:not(:active),
:root[data-style="glass"] dialog.bi-sheet .btn:not(.ghost):not(.danger):not(:active) {
  background-color: var(--btn-bg); border-color: var(--btn-bg);
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%);
}
/* The main action glows a little more than any other button does. */
:root[data-style="glass"] .bi-newbtn {
  box-shadow: var(--bevel), var(--shadow-1), 0 0 22px -5px color-mix(in srgb, var(--accent) 80%, transparent);
}
@media (pointer: coarse) { .bi-newbtn, .bi-openfile { min-height: 44px; } }

/* The grid's first card: the same size as a whiteboard's, its picture a bubble with a +, its edge dashed - a place
   for one rather than one. A <button>, so the card's own look is set back where a button has its own. */
.bi-newcard { padding: 0; font: inherit; text-align: left; color: var(--ink); cursor: pointer;
  border: 1.5px dashed color-mix(in srgb, var(--accent) 50%, var(--rule-strong));
  background: color-mix(in srgb, var(--accent) 5%, var(--surface)); }
:root[data-style="glass"] .bi-newcard { border-color: color-mix(in srgb, var(--accent) 50%, var(--rule-strong)); }
.bi-newcard:hover { border-color: color-mix(in srgb, var(--accent) 75%, var(--rule-strong)); }
.bi-newcard:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.bi-newthumb { display: grid; place-items: center; aspect-ratio: 3 / 2;
  border-bottom: 1px dashed color-mix(in srgb, var(--accent) 35%, var(--rule)); }
.bi-newplus { display: grid; place-items: center; width: 54px; height: 54px; border-radius: 50%;
  background: var(--btn-bg); color: var(--btn-fg); box-shadow: var(--shadow-1); transition: transform .18s; }
.bi-newplus svg { width: 24px; height: 24px; }
.bi-newcard:hover .bi-newplus { transform: scale(1.06); }
/* Glass: a bubble - lit from above, a highlight at its top left, the accent's glow round it. */
:root[data-style="glass"] .bi-newplus {
  background-image: radial-gradient(circle at 34% 26%, rgb(255 255 255 / .55) 0, rgb(255 255 255 / 0) 42%),
                    linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%);
  box-shadow: var(--bevel), var(--shadow-1), 0 0 22px -4px color-mix(in srgb, var(--accent) 75%, transparent);
}
.bi-newcard .bi-meta { padding-right: 12px; }
.bi-newcard .bi-title { display: flex; align-items: center; gap: 8px; }
.bi-newsays { font-size: 12px; line-height: 1.35; color: color-mix(in srgb, var(--ink) 45%, var(--muted)); }
/* N, as a key: only where there is a keyboard to press it on. */
.bi-key { margin-left: auto; padding: 1px 6px; border-radius: 5px; border: 1px solid var(--rule-strong); border-bottom-width: 2px;
  font: 500 11px/16px var(--sans); color: var(--ink); background: color-mix(in srgb, var(--surface) 60%, transparent); }
@media (hover: none), (pointer: coarse) { .bi-key { display: none; } }

/* No whiteboards where one could be: a welcome, with the way to one. */
.bi-welcome { display: flex; flex-direction: column; align-items: center; gap: 8px; max-width: 520px; margin: 6px auto 0;
  padding: 30px 24px 28px; color: var(--ink); }
.bi-welcome h2 { margin: 4px 0 0; font-size: 22px; line-height: 1.25; }
.bi-welcome p { margin: 0 0 8px; max-width: 42ch; font-size: 13.5px; line-height: 1.5; color: color-mix(in srgb, var(--ink) 45%, var(--muted)); }
.bi-welcomeart { width: 132px; height: auto; }
.bw-board { fill: #fbfaf6; stroke: color-mix(in srgb, var(--accent) 45%, var(--rule-strong)); stroke-width: 1.5; }
.bw-dots { fill: none; stroke: rgb(38 62 96 / .32); stroke-width: 2.4; stroke-linecap: round; }
.bw-ink { fill: none; stroke: #1f5fc4; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.bw-pen { fill: var(--btn-bg); stroke: var(--btn-bg); stroke-width: 1; stroke-linejoin: round; }
.bw-pentip { fill: #1c1f24; }

/* The sheet. A dialog, so on Glass already a pane (the glass rules above): these only lay it out - its name, what it
   starts from (four plain papers, then the templates, each a little picture of itself), the paper and the notebook
   side by side, its own window, then Create. On a phone one column, the templates a row each. */
dialog.bi-sheet { --ns-soft: color-mix(in srgb, var(--ink) 45%, var(--muted));
  width: min(700px, calc(100% - 24px)); max-width: none; margin: auto;
  max-height: calc(100vh - 24px); max-height: calc(100dvh - 24px); overflow: hidden;
  flex-direction: column; }                            /* (on the closed one too: dialog.settings says why) */
dialog.bi-sheet[open] { display: flex; }
dialog.bi-sheet::backdrop, dialog.bi-sheet + .gs-backdrop { background: color-mix(in srgb, var(--backdrop, rgb(0 0 0 / .3)) 70%, transparent); }
.ns-form { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.ns-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 20px 8px; }
.ns-head h2 { font-size: 22px; line-height: 1.2; }
.ns-body { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 4px 20px 16px; }
.ns-label { display: block; margin: 0 0 7px; padding: 0; font-family: var(--sans); font-size: 11px; font-weight: 500;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink); }
.ns-opt { text-transform: none; letter-spacing: 0; font-weight: 400; color: var(--ns-soft); }
/* 16px: Safari zooms the page to a field set in less. */
input.ns-name { margin: 0 0 16px; padding: 10px 12px; font-size: 16px; }
.ns-tiles { display: flex; flex-direction: column; gap: 7px; margin: 0 0 16px; }
.ns-sub { font-size: 12px; color: var(--ns-soft); }
.ns-set { display: grid; gap: 8px; }
.ns-set + .ns-sub { margin-top: 4px; }
.ns-papers { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.ns-templates { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.ns-tile { position: relative; display: flex; flex-direction: column; gap: 7px; min-width: 0; padding: 6px 6px 9px;
  border-radius: 14px; border: 1px solid var(--rule-strong); background: transparent; color: var(--ink);
  font: inherit; text-align: left; cursor: pointer; transition: background .15s, border-color .15s, box-shadow .15s; }
.ns-tile:hover { background: color-mix(in srgb, var(--accent) 7%, transparent); }
.ns-tile[aria-checked="true"] { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, transparent); }
:root[data-style="glass"] .ns-tile[aria-checked="true"] { background-image: var(--gloss-soft); box-shadow: inset 0 0 0 1px var(--accent), var(--bevel); }
.ns-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Chosen: a tick in the corner of its picture, in the primary button's colours (the circle, and the tick a mask
   filled with --btn-fg - in the dark the button is light and its words dark), ringed in white to stand off any paper. */
.ns-tile[aria-checked="true"] .ns-prev::before,
.ns-tile[aria-checked="true"] .ns-prev::after { content: ''; position: absolute; z-index: 1; top: 5px; right: 5px; width: 18px; height: 18px;
  border-radius: 50%; }
.ns-tile[aria-checked="true"] .ns-prev::before { background: var(--btn-bg);
  box-shadow: 0 0 0 1.5px rgb(255 255 255 / .9), 0 1px 3px rgb(0 0 0 / .3); }
.ns-tile[aria-checked="true"] .ns-prev::after { background: var(--btn-fg);
  -webkit-mask: no-repeat center / 12px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 6.3 5 8.6l4.6-5' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  mask: no-repeat center / 12px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 6.3 5 8.6l4.6-5' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
.ns-tname { padding: 0 4px; font-size: 13px; font-weight: 500; line-height: 1.3; color: var(--ink); }
.ns-twords { display: flex; flex-direction: column; gap: 2px; min-width: 0; padding: 0 4px; }
.ns-twords .ns-tname { padding: 0; }
/* A template's sentence: --ink 70% into --muted, chosen or not (6 Oct 2026: --ns-soft, under a sheet nearly solid
   over a whiteboard - see "New whiteboard … over a whiteboard" - still read 4.4:1 in GuideStar's and Blossom's dark
   Glass, the board's light paper and ink a little through the pane under the cards). */
.ns-tsays { font-size: 12px; line-height: 1.35; color: color-mix(in srgb, var(--ink) 70%, var(--muted)); }

/* A tile's picture: the paper chosen (data-paper on the tiles), its lines as the editor draws them (board.js,
   drawPaper: their colours, at a smaller step), and a template's lines and boxes over them in the editor's inks.
   Its own colours, not the look's: a whiteboard's paper is the same in every palette. */
.ns-prev { position: relative; display: block; aspect-ratio: 2 / 1; border-radius: 9px; overflow: hidden;
  background-color: var(--np-bg); border: 1px solid var(--np-edge);
  --np-bg: #fbfaf6; --np-edge: rgb(38 62 96 / .16); --np-line: rgb(38 62 96 / .2); --np-heavy: rgb(38 62 96 / .34);
  --np-dot: rgb(38 62 96 / .45); --np-ink: #1c1f24; --np-blue: #1f5fc4; --np-red: #c8352b; --np-tint: rgb(82 220 112 / .3); }
.ns-tiles[data-paper="slate"] .ns-prev {
  --np-bg: #1d272b; --np-edge: rgb(255 255 255 / .12); --np-line: rgb(255 255 255 / .14); --np-heavy: rgb(255 255 255 / .26);
  --np-dot: rgb(255 255 255 / .34); --np-ink: #eef2f3; --np-blue: #8cbcff; --np-red: #ff8f84; --np-tint: rgb(127 220 161 / .24); }
/* Glass: the page behind through a veil of the page's colour, as the editor's (.bd-stage.paper-glass) - and so the
   inks for the dark in the dark theme, as the editor's are (board.js, onDark). */
.ns-tiles[data-paper="glass"] .ns-prev {
  --np-bg: color-mix(in srgb, var(--paper) var(--glass-veil), transparent);
  --np-edge: light-dark(rgb(38 62 96 / .16), rgb(255 255 255 / .12));
  --np-line: light-dark(rgb(38 62 96 / .2), rgb(255 255 255 / .14));
  --np-heavy: light-dark(rgb(38 62 96 / .34), rgb(255 255 255 / .26));
  --np-dot: light-dark(rgb(38 62 96 / .45), rgb(255 255 255 / .34));
  --np-ink: light-dark(#1c1f24, #eef2f3); --np-blue: light-dark(#1f5fc4, #8cbcff); --np-red: light-dark(#c8352b, #ff8f84);
  --np-tint: light-dark(rgb(82 220 112 / .3), rgb(127 220 161 / .24)); }
@supports not (color: light-dark(#000, #fff)) {
  .ns-tiles[data-paper="glass"] .ns-prev {
    --np-edge: var(--not-light, rgb(38 62 96 / .16)) var(--not-dark, rgb(255 255 255 / .12));
    --np-line: var(--not-light, rgb(38 62 96 / .2)) var(--not-dark, rgb(255 255 255 / .14));
    --np-heavy: var(--not-light, rgb(38 62 96 / .34)) var(--not-dark, rgb(255 255 255 / .26));
    --np-dot: var(--not-light, rgb(38 62 96 / .45)) var(--not-dark, rgb(255 255 255 / .34));
    --np-ink: var(--not-light, #1c1f24) var(--not-dark, #eef2f3);
    --np-blue: var(--not-light, #1f5fc4) var(--not-dark, #8cbcff);
    --np-red: var(--not-light, #c8352b) var(--not-dark, #ff8f84);
    --np-tint: var(--not-light, rgb(82 220 112 / .3)) var(--not-dark, rgb(127 220 161 / .24));
  }
}
.ns-l-dots { background-image: radial-gradient(circle, var(--np-dot) 0 1.1px, transparent 1.5px);
  background-size: 10px 10px; background-position: 5px 5px; }
.ns-l-lines { background-image: linear-gradient(var(--np-line) 1px, transparent 1px); background-size: 100% 10px; background-position: 0 9px; }
.ns-l-grid { background-image: linear-gradient(var(--np-line) 1px, transparent 1px), linear-gradient(90deg, var(--np-line) 1px, transparent 1px);
  background-size: 10px 10px; }
/* Graph paper: fine squares, a heavier line every fifth, one of them each way through the middle, where the axes are. */
.ns-l-graph { background-image: linear-gradient(var(--np-heavy) 1px, transparent 1px), linear-gradient(90deg, var(--np-heavy) 1px, transparent 1px),
  linear-gradient(var(--np-line) 1px, transparent 1px), linear-gradient(90deg, var(--np-line) 1px, transparent 1px);
  background-size: 20px 20px, 20px 20px, 4px 4px, 4px 4px; background-position: calc(50% + 10px) calc(50% + 10px); }
/* A template's a little flatter, so the sheet fits a laptop's window with each one's words under it. */
.ns-tpl .ns-prev { aspect-ratio: 5 / 2; }
.ns-prev svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.ns-prev svg * { fill: none; stroke-linecap: round; }
.ns-prev .np-ink { stroke: var(--np-ink); }
.ns-prev .np-blue { stroke: var(--np-blue); stroke-width: 1.2; }
.ns-prev .np-red { stroke: var(--np-red); }
.ns-prev .np-tint { fill: var(--np-tint); }

.ns-two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14px; margin: 0 0 14px; }
.ns-paper button { padding: 7px 10px; }
select.ns-nb { padding: 8px 10px; }
.ns-own { display: flex; align-items: center; gap: 14px; width: 100%; padding: 10px 14px; border-radius: 14px;
  border: 1px solid var(--rule-strong); background: transparent; color: var(--ink); font: inherit; text-align: left; cursor: pointer; }
.ns-own:hover { background: color-mix(in srgb, var(--accent) 7%, transparent); }
.ns-ownwords { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.ns-ownname { font-size: 14px; font-weight: 500; color: var(--ink); }
.ns-ownsays { font-size: 12.5px; line-height: 1.4; color: var(--ns-soft); }
.ns-foot { display: flex; align-items: center; gap: 8px; padding: 12px 20px 14px; border-top: 1px solid var(--rule); }
.ns-file { margin-right: auto; padding: 6px 2px; border: 0; background: none; font: inherit; font-size: 13px; color: var(--ink);
  text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
.ns-file:hover { color: var(--ink); text-decoration-color: var(--accent); }
dialog.bi-sheet .btn { justify-content: center; min-height: 40px; }
.ns-make { min-width: 116px; }
dialog.bi-sheet :is(button, input, select):focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (pointer: coarse) {
  .ns-paper button, dialog.bi-sheet .btn, select.ns-nb, .ns-file { min-height: 44px; }
}
/* A phone: one column, every template a row - its picture, then its name and what is on it - and no own window
   (another tab, there). Cancel and Create share a row; opening a file goes under them. */
@media (max-width: 600px) {
  dialog.bi-sheet { width: calc(100% - 16px); max-height: calc(100vh - 16px); max-height: calc(100dvh - 16px); }
  .ns-head { padding: 14px 16px 6px; }
  .ns-body { padding: 4px 16px 14px; }
  .ns-papers { gap: 6px; }
  .ns-papers .ns-tile { padding: 4px 4px 7px; }
  .ns-papers .ns-tname { padding: 0 2px; font-size: 12.5px; }
  .ns-templates { grid-template-columns: minmax(0, 1fr); }
  .ns-tpl { flex-direction: row; align-items: center; gap: 12px; padding: 6px 10px 6px 6px; }
  .ns-tpl .ns-prev { flex: none; width: 100px; }
  .ns-two { grid-template-columns: minmax(0, 1fr); gap: 12px; }
  .ns-own { display: none; }
  .ns-foot { flex-wrap: wrap; padding: 10px 16px 12px; }
  .ns-foot .btn { flex: 1 1 0; }
  .ns-file { order: 1; flex-basis: 100%; margin: 0; text-align: center; }
}
:root[data-motion="reduce"] .ns-tile, :root[data-motion="reduce"] .bi-newplus { transition: none; }
@media (prefers-reduced-transparency: reduce) {
  :root[data-style="glass"] dialog.bi-sheet { background-color: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* ── Templates of one's own (4 Oct 2026: templates/newsheet.html, static/js/newsheet.js) ── After the Board's own in the
   sheet, under "Your templates": a tile as theirs is, its picture the page as it was kept (a thumbnail: 3:2, the top of
   the page across its width, so shown from the top), and a round ⋯ of its own in the tile's corner, beside the words -
   a button beside the tile's, not in it. Its menu (Rename…, Delete…) lies inside the sheet, as a dialog's top layer
   leaves nothing outside it over it. The same sheet over the editor since that day: its words measured there as well
   as over the list (tests/test_own_templates.py). */
.ns-mine { position: relative; display: flex; min-width: 0; }
.ns-mine .ns-tile { flex: 1; }
.ns-mine .ns-twords { padding-right: 30px; }
.ns-prev img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center top; }
.ns-tmore { position: absolute; right: 6px; bottom: 6px; display: grid; place-items: center; width: 28px; height: 28px; padding: 0;
  border-radius: 50%; border: 1px solid var(--rule-strong); background: var(--surface); color: var(--ink); cursor: pointer; }
.ns-tmore svg { width: 15px; height: 15px; }
.ns-tmore:hover, .ns-tmore[aria-expanded="true"] { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, var(--surface)); }
.ns-tmore:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
:root[data-style="glass"] .ns-tmore { background-image: var(--gloss-soft); box-shadow: var(--bevel); }
@media (pointer: coarse) { .ns-tmore { width: 36px; height: 36px; } .ns-mine .ns-twords { padding-right: 38px; } }
/* Positioned within the sheet (a dialog is fixed: the containing block), not the window. */
dialog.bi-sheet .ns-tmenu { position: absolute; width: 168px; }
@media (pointer: coarse) { .ns-tmenu .menu-item { min-height: 44px; } }

/* The small sheet a template is named in: the New whiteboard sheet's parts, narrower. Its lock is a switch as the
   sheet's own window is - shown on a phone too, where that one is not. A refusal in ink beside a red rule (red words
   are under 4.5:1 on Glass in the light theme: tests/test_toolbox.py found as much for "Time's up"). */
dialog.tp-sheet { width: min(480px, calc(100% - 24px)); }
.tp-lede { margin: 0 0 14px; font-size: 13.5px; line-height: 1.5; color: var(--ink); }
dialog.tp-sheet input.ns-name { margin-bottom: 14px; }
@media (max-width: 600px) { dialog.tp-sheet .tp-lock { display: flex; } }
.tp-lock[hidden] { display: none !important; }
.tp-error { margin: 12px 0 0; padding: 6px 10px; border-left: 3px solid var(--red); border-radius: 4px; font-size: 13px;
  line-height: 1.45; color: var(--ink); background: color-mix(in srgb, var(--red-bg) 60%, transparent); }
.tp-error[hidden], .ns-own[hidden] { display: none; }

/* ── A PDF put in (9 Oct 2026: static/js/board/pdf.js) ── The New whiteboard sheet's parts, narrower: the file - a
   picture of its first page as a sheet of paper, its name, how many pages - then how it is to go in, two tiles as the
   templates are (their pictures drawn here: pages down the board's dotted paper, and pages each a page of their own,
   in the strip's frames), then which pages (All, or Some: from, to), a bar while its pages are drawn, and Put in.
   Every word is --ink, or --ink 70% into --muted as a template's sentence is (that sheet's measure over a whiteboard,
   in the dark on Glass: --ns-soft read 4.4:1); the bar's fill is the main button's colour on a field's sunken track,
   3:1 and more either way in every look (tests/test_pdf_import.py measures both as drawn). */
dialog.pdf-sheet { width: min(520px, calc(100% - 24px)); --pdf-soft: color-mix(in srgb, var(--ink) 70%, var(--muted)); }
.pdf-file { display: flex; align-items: center; gap: 14px; margin: 0 0 16px; min-width: 0; }
.pdf-thumb { flex: none; display: grid; place-items: center; width: 64px; height: 84px; border-radius: 6px;
  background: #fbfaf6; border: 1px solid rgb(38 62 96 / .22); box-shadow: 0 1px 3px rgb(0 0 0 / .18); overflow: hidden; }
.pdf-thumb img { display: block; max-width: 100%; max-height: 100%; object-fit: contain; }
.pdf-thumb img[hidden] { display: none; }
.pdf-words { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.pdf-name { font-size: 15px; font-weight: 500; line-height: 1.3; color: var(--ink); overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.pdf-count { font-size: 13px; line-height: 1.35; color: var(--pdf-soft); }
.pdf-hows { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin: 0 0 16px; }
.pdf-hows .ns-tile { padding-bottom: 10px; }
.pdf-hows .ns-tsays { color: var(--pdf-soft); }
/* Their pictures: little pages, white with grey lines of type - on the board's dotted paper going on down, or each in
   a page's frame of its own. Their own colours, as a template's picture has: a page is the same in every palette. */
.pdf-pic { aspect-ratio: 5 / 2; }
.pdf-pic i { position: absolute; display: block; width: 22%; aspect-ratio: 3 / 4; border-radius: 2px; background-color: #fff;
  border: 1px solid rgb(38 62 96 / .3); box-shadow: 0 1px 2px rgb(0 0 0 / .14);
  background-image: repeating-linear-gradient(transparent 0 4px, rgb(38 62 96 / .24) 4px 5px);
  background-size: 70% 100%; background-repeat: no-repeat; background-position: 50% 4px; }
.pdf-pichere i:nth-child(1) { left: 39%; top: 9%; }
.pdf-pichere i:nth-child(2) { left: 39%; top: 87%; }
.pdf-picpages { display: flex; align-items: center; justify-content: center; gap: 7%; }
.pdf-picpages b { position: relative; display: grid; place-items: center; width: 24%; height: 78%; border-radius: 5px;
  background: var(--np-bg); border: 1.5px solid var(--np-heavy); }
.pdf-picpages b i { position: relative; width: 56%; }
.pdf-range { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; }
.pdf-range[hidden], #pdf-rangelabel[hidden], .pdf-fromto[hidden], .pdf-note[hidden], .pdf-progress[hidden] { display: none; }
.pdf-which button { padding: 6px 16px; }
.pdf-fromto { display: inline-flex; align-items: center; gap: 8px; font-size: 14px; color: var(--ink); }
.pdf-fromto label { margin: 0; font: inherit; letter-spacing: 0; text-transform: none; color: var(--ink); }
/* 16px: Safari zooms the page to a field set in less. */
.pdf-fromto input { width: 5.2em; padding: 7px 8px; font-size: 16px; text-align: center; }
.pdf-note { margin: 10px 0 0; font-size: 12.5px; line-height: 1.45; color: var(--pdf-soft); }
.pdf-progress { display: flex; flex-direction: column; gap: 7px; margin: 16px 0 0; }
.pdf-bar { position: relative; height: 10px; overflow: hidden; border-radius: 999px; background: var(--field-bg);
  border: 1px solid var(--rule-strong); box-shadow: var(--field-inset); }
.pdf-bar i { position: absolute; inset: 0; display: block; border-radius: inherit; background-color: var(--btn-bg);
  transform-origin: 0 50%; scale: var(--done, 0) 1; transition: scale .25s var(--ease-out, ease-out); }
:root[data-style="glass"] .pdf-bar i {
  background-image: linear-gradient(180deg, rgb(255 255 255 / .34) 0, rgb(255 255 255 / .1) 50%, transparent 50.5%); }
/* While the server reads it: a lozenge of light going along, as long as it takes (not with motion turned down: a
   still, fainter bar says the same). */
.pdf-bar.pdf-busy i { right: auto; width: 34%; scale: 1 1; animation: pdf-busy 1.2s cubic-bezier(.45, 0, .55, 1) infinite; }
@keyframes pdf-busy { from { translate: -110% 0; } to { translate: 310% 0; } }
:root[data-motion="reduce"] .pdf-bar.pdf-busy i { width: 100%; opacity: .5; animation: none; translate: none; }
.pdf-said { font-size: 13px; line-height: 1.4; color: var(--pdf-soft); }
@media (max-width: 600px) {
  .pdf-hows { grid-template-columns: minmax(0, 1fr); }
  .pdf-hows .ns-tile { flex-direction: row; align-items: center; gap: 12px; padding: 6px 10px 6px 6px; }
  .pdf-hows .ns-prev { flex: none; width: 100px; }
}

/* ── The editor ── */
main.board-main { max-width: none; padding: 0; }
/* The page is a column: the board taking the whole window (no masthead over it since 5 Oct 2026: the editor's bar
   below), with nothing to scroll. */
html:has(.bd), body:has(.bd) { overflow: hidden; }
html:has(.bd) { scrollbar-gutter: auto; }
body:has(.bd) { display: flex; flex-direction: column; height: 100vh; height: 100dvh; }
body:has(.bd) > main.board-main { flex: 1; min-height: 0; width: 100%; display: flex; flex-direction: column; }
.bd { position: relative; display: flex; flex-direction: column; flex: 1; min-height: 0; }
/* ── The editor's bar (1 Oct 2026) ──
   "Top bar isn't spaced properly": at 1920 px the masthead's middle 1240 px put the brand 360 px in, over nothing,
   while the editor's bar under it ran edge to edge; the title took 420 px whatever it said, so the notebook and
   "Saved" floated mid-bar; and the tools were one crowded run at the right. Then the masthead over the editor ran
   edge to edge too, with the bar's own gutters (--bar-gutter); the title is as wide as its words (board.js
   fitTitle), its notebook and save state just after it; and the tools are in groups (templates/board.html,
   data-hgroup) - people, finding and going, the page, out of this window - with more room between groups than
   within them.

   And narrower (later the same day): an iPad upright (768-834 px) and up to 1024 took two rows of 87 px, a phone
   four of 167 px - "Saved" and the notebook each stranded on a row of their own, ⋯ alone on the fourth - because
   the bar wrapped wherever it ran out. Now it never wraps by chance: the title gives way first, ending in "…";
   under 1180 px the notebook is an icon, "Saved" a dot (its words its tooltip; a save that failed still says so)
   and Share its icon alone; under 900 px what is set up once rather than pressed in a lesson - Export and Pop out,
   Paper and lines, auto convert - folds into ⋯ (board.js moreHtml offers each there while it is folded, as it
   always did Export); and a phone (600 px and under) has two rows on purpose: the way back, the title, the
   notebook, the save state and ⋯, then the tools. On a touch screen every button is 44 px, a finger's size.

   And one bar, not two (5 Oct 2026, the owner: "try and make the top bar a bit more low profile, more space to see
   the board"): the masthead (62 px) over this bar (49 px) took 111 px before the page began. A whiteboard has no
   masthead now (templates/board.html leaves base.html's block empty, so nothing is hidden or moved as the page
   opens), and what it held that a whiteboard needs - their name, Settings, light or dark, signing out - is the last
   group here, after ⋯ (.bd-account, the masthead's own markup: templates/_account.html). The brand is a small mark
   in the way back, where there is room (.bd-mark). And slimmer: with a mouse every button 32 px
   (--hbtn) and the bar 5 px above and below them, 43 px in all; a finger's 44 px buttons as before (55 px).
   Measured at 360-2560 px, mouse and touch: tests/test_topbar.py. */
:root { --bar-gutter: clamp(14px, 1.5vw, 28px); --hbtn: 32px; --hgap: 6px; --hgroup-gap: 18px; }
/* (Narrower than a laptop - an iPad upright - a little less between groups.) */
@media (max-width: 900px) { :root { --hgroup-gap: 12px; } }
@media (pointer: coarse) { :root { --hbtn: 44px; } }
.bd-head { position: relative; z-index: 5; display: flex; flex-wrap: nowrap; align-items: center; gap: 10px;
  padding: 5px var(--bar-gutter); border-bottom: 1px solid var(--rule); background: var(--surface); min-width: 0; }
:root[data-style="glass"] .bd-head { background-color: var(--pane); background-image: var(--gloss-soft);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur); }
.bd-back { display: grid; place-items: center; width: 30px; height: 30px; border-radius: 50%; color: var(--ink); flex: none; }
.bd-back svg { width: 18px; height: 18px; }
/* The mark beside it, from 1180 px (5 Oct 2026): the masthead's brand went with the masthead, and the way back to the
   whiteboards is where the Board's own sign belongs - one control, a lozenge round both. Narrower, the room is the
   title's. (width: auto with the size attributes' aspect ratio, as the masthead's .brand img.) */
.bd-mark { display: none; }
@media (min-width: 1180px) {
  .bd-back:has(.bd-mark) { display: inline-flex; align-items: center; gap: 2px; width: auto; padding: 0 9px 0 4px; border-radius: 999px; }
  .bd-mark { display: block; height: 20px; width: auto; aspect-ratio: 731 / 1185; filter: var(--logo-filter); }
}
.bd-back:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
/* (A finger's size on a touch screen, its arrow where it was: the extra reaches out into the gutter.) */
@media (pointer: coarse) { .bd-back { width: 44px; height: 44px; margin-left: -7px; margin-right: -4px; } }
/* Its width is its words', set by board.js; it gives way, ending in "…", before anything else on a narrow bar (its
   flex-shrink outweighs the tools' many times over, so they keep their row while it has any room to give). */
input.bd-title { flex: 0 1000 auto; width: 12em; min-width: 5em; max-width: min(420px, 100%); border: 1px solid transparent;
  background: transparent; box-shadow: none; padding: 3px 8px; font-family: var(--serif, var(--sans)); font-size: 18px;
  color: var(--ink); border-radius: 8px; text-overflow: ellipsis; }
@media (pointer: coarse) { input.bd-title { min-height: 44px; } }
input.bd-title:hover { border-color: var(--rule); }
input.bd-title:focus { border-color: color-mix(in srgb, var(--accent) 50%, var(--rule)); background: color-mix(in srgb, var(--surface) 80%, transparent); }
.bd-chip { position: relative; display: inline-flex; align-items: center; gap: 6px; flex: none; max-width: 180px; padding: 4px 10px;
  border-radius: 999px; border: 1px solid var(--rule); background: transparent; color: var(--ink); font: inherit; font-size: 12.5px; cursor: pointer; }
@media (pointer: coarse) { .bd-chip { min-height: var(--hbtn); } }
.bd-chip span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-chip:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); color: var(--ink); }
.bd-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.bd-nbicon { display: none; width: 18px; height: 18px; flex: none; }
.bd-save { flex: none; font-size: 11.5px; color: color-mix(in srgb, var(--ink) 15%, var(--muted)); min-width: 5.5em; }
.bd-save[data-state="error"], .bd-save[data-state="conflict"] { color: var(--red); max-width: 22em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* (And it gives way after the title, ending in "…", the whole of it its tooltip: with the account group at the bar's
   end, 5 Oct 2026, "Not saved — the server did not answer" ran it past a 1024 px window. Before the tools do, with
   search's field open in them (7 Oct 2026): sharing it alike, the field went under its least while the words kept
   twice theirs, and ran into the buttons beside it.) */
.bd-save:is([data-state="error"], [data-state="conflict"]) { flex: 0 100 auto; min-width: 6em; }
/* (The tools never give way themselves - a shave of a pixel wrapped ⋯ onto a row of its own: the title does. Only an
   open search field lets them, below.) */
.bd-headacts { margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end;
  column-gap: var(--hgroup-gap); row-gap: 6px; flex: 0 0 auto; min-width: 0; }
.bd-headacts > * { flex: none; }
.bd-hgroup { display: flex; align-items: center; gap: var(--hgap); }
/* A group with nothing showing takes no room (nor a gap): the people when nobody else is here and this is not the
   owner, the page's own settings for View only. */
.bd-hgroup:not(:has(> :not([hidden]))),
.bd[data-view="1"] .bd-hgroup[data-hgroup="page"] { display: none; }
@media (max-width: 820px) { .bd-hbtn { padding: 4px 9px; font-size: 12px; } }
.bd-hbtn { display: inline-flex; align-items: center; gap: 6px; padding: 5px 12px; border-radius: 999px;
  border: 1px solid var(--rule-strong); background: transparent; color: var(--ink); font: inherit; font-size: 12.5px; cursor: pointer;
  transition: background .15s, border-color .15s; }
.bd-hbtn svg { width: 14px; height: 14px; }
.bd-hbtn:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); border-color: color-mix(in srgb, var(--accent) 40%, var(--rule-strong)); }
.bd-hbtn[aria-pressed="true"] { background: color-mix(in srgb, var(--accent) 20%, transparent); border-color: var(--accent); }
/* The header's buttons as icons ("instead of text these could be icons, clear and nice", 29 Sep 2026): one round
   size, an 18 px icon, and each one's name (its aria-label, what a screen reader says) shown just under it on
   hover or keyboard focus - at once, in the app's own look, where a browser tooltip waits and looks foreign. The
   notebook as an icon, and the save state as a dot, have theirs the same way (aria-label, and the dot's words). */
.bd-headacts .bd-hbtn { position: relative; width: var(--hbtn); height: var(--hbtn); padding: 0; justify-content: center; gap: 0; }
.bd-headacts .bd-hbtn svg { width: 18px; height: 18px; flex: none; }
.bd-headacts .bd-hbtn[aria-label]:hover::after,
.bd-headacts .bd-hbtn[aria-label]:focus-visible::after,
.bd-chip.bd-chip[aria-label]:is(:hover, :focus-visible)::after {
  content: attr(aria-label); position: absolute; top: calc(100% + 7px); left: 50%; transform: translateX(-50%);
  width: max-content; max-width: 260px; white-space: normal; text-align: center;
  padding: 5px 10px; border-radius: 9px; font-size: 12px; font-weight: 500; line-height: 1.35; letter-spacing: 0;
  text-transform: none; color: var(--ink); background: var(--surface); border: 1px solid var(--rule-strong);
  box-shadow: var(--shadow-2); pointer-events: none; z-index: 30; }
:root[data-style="glass"] .bd-headacts .bd-hbtn[aria-label]:hover::after,
:root[data-style="glass"] .bd-headacts .bd-hbtn[aria-label]:focus-visible::after,
:root[data-style="glass"] .bd-chip.bd-chip[aria-label]:is(:hover, :focus-visible)::after {
  background-color: var(--pane); background-image: var(--gloss-soft);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur); box-shadow: var(--rim), var(--shadow-2); }
/* (The last two - the last of the group before ⋯, and ⋯ - open their names towards the page, not off the window's
   edge.) */
.bd-headacts > .bd-hbtn:last-child[aria-label]:is(:hover, :focus-visible)::after,
.bd-headacts > .bd-hgroup:nth-last-child(2) > .bd-hbtn:last-child[aria-label]:is(:hover, :focus-visible)::after { left: auto; right: 0; transform: none; }
/* (None while a menu or a dialog is still going - #bd-menu.gs-leave, or the copy of a closed dialog, dialog.gs-copy:
   GSMotion, app.js. ⋯'s menu closed by Esc gives ⋯ the keys back at once, and its name came up under the menu's
   glass and showed through it for the whole of its going, 7 Oct 2026; it comes once the menu has gone.) */
:root:has(#bd-menu.gs-leave, dialog.gs-copy) :is(.bd-headacts .bd-hbtn[aria-label], .bd-chip.bd-chip[aria-label])::after { display: none; }
/* Share, the owner's (board/share.js): a lozenge like the bar's others, its name beside its icon - an icon alone
   under 1180 px. Not lit: in the bar, lit means on (Follow me, Present - presence.js), and a Share lit all the time
   made a lit Present hard to tell from one at rest. Measured: tests/test_topbar.py. */
.bd-headacts .bd-sharebtn { width: auto; padding: 0 15px 0 12px; gap: 7px; font-size: 13px; font-weight: 500; letter-spacing: .02em; }
.bd-sharebtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.bd-morebtn { padding: 5px 10px; font-size: 15px; line-height: 1; }
/* The account group (5 Oct 2026: templates/_account.html, the masthead's controls): at the right end, after ⋯ with the
   room between groups. Its round buttons are the bar's own (.bd-hbtn's size, border and --ink - not the masthead's
   squarer --muted ones), and the name chip one of them high, its words smaller: "Jo Tutor" at 12 px, the role at 10.
   It gives way only after the title has (its flex-shrink a thousandth of the title's), and then only the words of an
   owner's "seeing it as", ending in "…" - never past its own buttons (7 Oct 2026, the bug hunt): its min-width was 0,
   and with a save that failed, or search open, the buttons went over the window's edge, where nothing scrolls to
   them. Its least is its content's least now (min-width: auto), the buttons whole - but for "seeing it as", whose
   words are its content's least too, their whole width (a browser counts words whole there, though they would end in
   "…"): there it gives way from them, and they from their end (the bar's 601 px ran 1 px past the window with them
   whole). What there is room for is said by the window's width, and a step less while the bar has more to say
   (below). */
.bd-account { display: flex; align-items: center; gap: var(--hgap); flex: 0 1 auto; min-width: auto;
  margin-left: calc(var(--hgroup-gap) - 10px); }
.bd-account:has(.who-as) { min-width: 0; }
.bd-account form.signout { display: contents; }
.bd-account :is(.settings-btn, .theme-toggle, .signout-btn) { position: relative; width: var(--hbtn); height: var(--hbtn);
  border-radius: 999px; border-color: var(--rule-strong); color: var(--ink); flex: none;
  transition: background .15s, border-color .15s; }
.bd-account :is(.settings-btn, .theme-toggle, .signout-btn):hover { background: color-mix(in srgb, var(--accent) 12%, transparent);
  border-color: color-mix(in srgb, var(--accent) 40%, var(--rule-strong)); color: var(--ink); }
.bd-account :is(.settings-btn, .theme-toggle, .signout-btn):focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.bd-account :is(.theme-toggle, .signout-btn) svg { width: 17px; height: 17px; }
.bd-account .settings-btn svg { width: 18px; height: 18px; }
.bd-account .who-chip { box-sizing: border-box; height: var(--hbtn); flex: 0 1 auto; min-width: 0; padding: 0 4px 0 11px; gap: 7px;
  font-size: 12px; }
.bd-account .who-chip b { max-width: 11em; }
.bd-account .who-chip span { padding: 1px 7px; font-size: 10px; }
/* (The gear's count of videos waiting, js/safety.js: tucked a little further in than the masthead's, so its top is not
   cut off by the window's.) */
.bd-account .gs-vbadge { top: -4px; right: -5px; }
.bd-account .who-as { box-sizing: border-box; height: var(--hbtn); flex: 0 1 auto; padding: 0 3px 0 11px; gap: 7px; font-size: 12px; }
.bd-account .who-leave { padding: 3px 10px; font-size: 12px; }

/* Under 1180 px (an iPad on its side, a laptop's small window, and narrower): Share an icon, the notebook an icon - a
   notebook, with its colour's dot on its corner - and "Saved" a dot of its state's colour (saved green, saving
   amber: each 3:1 or more on the bar, as a mark must be), its words its tooltip and still what a screen reader
   hears. A save that failed keeps its words: that is to be read, not hovered for. */
@media (max-width: 1179.98px) {
  .bd-headacts .bd-sharebtn { width: var(--hbtn); padding: 0; gap: 0; }
  .bd-sharelabel { display: none; }
  .bd-chip { width: var(--hbtn); height: var(--hbtn); min-height: 0; padding: 0; justify-content: center; }
  .bd-chip span { display: none; }
  .bd-chip .bd-nbicon { display: block; }
  /* (An owner looking through a guest's eyes: "Sam [Leave this link]" in the account group, the words round the name
     gone, as the masthead's were on a phone.) */
  .bd-account :is(.who-as-pre, .who-as-role) { display: none; }
  .bd-chip .bi-dot { position: absolute; right: 4px; bottom: 5px; width: 9px; height: 9px;
    box-shadow: 0 0 0 2px var(--surface); }
  .bd-save:not([data-state="error"], [data-state="conflict"]) { position: relative; display: inline-block;
    width: 22px; min-width: 0; height: 22px; font-size: 0; color: transparent; cursor: default; }
  /* (Placed, not laid out: the words, at no size, are still a line of their own beside it.) */
  .bd-save:not([data-state="error"], [data-state="conflict"])::before { content: ''; position: absolute; left: 50%; top: 50%;
    width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px; border-radius: 50%; background: var(--green); }
  .bd-save[data-state="saving"]::before { background: var(--amber); }
  .bd-save[title]:not([title=""], [data-state="error"], [data-state="conflict"]):hover::after {
    content: attr(title); position: absolute; top: calc(100% + 7px); left: 50%; transform: translateX(-50%);
    width: max-content; max-width: 260px; padding: 5px 10px; border-radius: 9px; font-size: 12px; font-weight: 500;
    line-height: 1.35; color: var(--ink); background: var(--surface); border: 1px solid var(--rule-strong);
    box-shadow: var(--shadow-2); pointer-events: none; z-index: 30; }
}

/* Under 900 px - an iPad upright, a phone, a narrow window: what is set up once rather than pressed mid-lesson folds
   into ⋯, which offers it while it is folded (board.js moreHtml; auto convert as its switch, convert.js). A popped-out
   window keeps its way back. Not for View only, whose bar has no ⋯ - and room for what it has. With a finger's
   buttons under 1024 px too (5 Oct 2026): at 900 the owner's bar already took the gutter, and the gear had no room. */
@media (max-width: 899.98px), (pointer: coarse) and (max-width: 1023.98px) {
  .bd:not([data-view="1"]) :is(#bd-exportbtn, #bd-popbtn, #bd-paperbtn, .bd-autobtn),
  .bd:not([data-view="1"]) .bd-hgroup[data-hgroup="page"],
  .bd:not([data-view="1"]):not(.bd-pop) .bd-hgroup[data-hgroup="out"] { display: none; }
}
/* How much of the account group there is room for (5 Oct 2026), measured on the owner's busiest bar - their name,
   someone on the page, Follow me, Present and Share - against a title of 16 letters left whole where it can be:
   - with a mouse, all of it from 1280 px; under that no owner's name chip (it is Settings → You), and a guest's - whose
     bar has no Follow me, Present or Share - their name alone, the role in its tooltip; under 900 px the gear alone:
     Settings has light or dark (Look) and the name (You, and "Your name…" in ⋯), and the list of whiteboards, one
     press back, signing out or leaving the link;
   - with a finger's 44 px buttons, all of it from 1600 px, the gear, light or dark and the way out from 1366, the
     gear alone from 768 (an iPad upright ran 85 px past the window with all of it, on its side 73), and under 768
     none (below);
   - an owner looking through a guest's eyes keeps "Sam [Leave this link]" wherever the group shows: it says whose
     eyes these are (⋯ has Leave this link too). */
@media (max-width: 1279.98px) {
  .bd-account .who-me, .bd-account .who-guest span { display: none; }
  .bd-account .who-guest { padding-right: 11px; }
}
@media (pointer: coarse) and (max-width: 1599.98px) {
  .bd-account .who-chip { display: none; }
}
@media (max-width: 899.98px), (pointer: coarse) and (max-width: 1365.98px) {
  .bd-account :is(.who-chip, .theme-toggle, form.signout) { display: none; }
}
/* A finger's 44 px buttons between a phone's width and an iPad's - a phone on its side (667-740 px), a narrow window
   on a touch screen: the owner's bar with someone on the page ran 21-37 px past the window there already (measured
   5 Oct 2026), and the gear would have made it 70-90. So, as on a phone, no account group (⋯ has Settings and Leave
   this link), and a little less room between things - the groups still further apart than the buttons in them, and
   the title giving way a little further (4em). */
@media (pointer: coarse) and (min-width: 601px) and (max-width: 767.98px) {
  :root { --hgap: 4px; --hgroup-gap: 8px; }
  .bd-head { gap: 6px; }
  input.bd-title { min-width: 4em; }
  .bd-account { display: none; }
}
/* A bar with more to say (7 Oct 2026, the bug hunt): a save that failed says so in words, and search opens its field in
   the "view" group - each far wider than what it stands in for. The account group gave way to them below its own
   buttons, and Sign out, light or dark, even the gear, went past the window's edge, where nothing scrolls to them
   (the owner's busiest bar at 900-1100 px with a mouse, an iPad's 768-1180); with the field open, the tools wrapped
   onto a row of their own. The account group never gives way past its buttons now (.bd-account), and while either is there the bar
   steps down as it does a step narrower - measured on that same bar with both at once, the title and the failed
   save's words at their least and the field at its 64 px: the owner's name chip goes under 1920 px, and with a
   finger (Settings → You has it - under 1600 until the review: a name as long as "Christopher Montgomery-Smith", its
   chip at its 11em, ran the find field's arrows over Pages at 1600-1650); the gear alone, as under 900, under 1280 with a mouse, 1600 with a finger; what is
   set up once rather than pressed mid-lesson folded into ⋯, as under 900, under 1280 with a mouse, 1366 with a
   finger; and between a phone's width and 900 with a mouse, 1024 with a finger, as a phone on its side has it: no
   account group (⋯ has Settings), a little less room between things, and the field taking the save state's room and
   the notebook's, as under 760 (a save that failed says so again once search closes), and full screen's (⋯ has it,
   as under 640). But an owner looking through a guest's eyes keeps "Sam [Leave this link]" there, the gear alone
   gone (⋯ has More settings…): with a save failed or search open, nothing else said whose eyes these were (7 Oct
   2026, the review). Measured: tests/test_topbar.py. */
@media (max-width: 1919.98px), (pointer: coarse) {
  .bd-head:has(.bd-save:is([data-state="error"], [data-state="conflict"]), .bd-findbox:not([hidden])) .bd-account .who-me { display: none; }
}
@media (max-width: 1279.98px), (pointer: coarse) and (max-width: 1599.98px) {
  .bd-head:has(.bd-save:is([data-state="error"], [data-state="conflict"]), .bd-findbox:not([hidden])) .bd-account :is(.who-chip, .theme-toggle, form.signout) { display: none; }
}
@media (max-width: 1279.98px), (pointer: coarse) and (max-width: 1365.98px) {
  .bd:not([data-view="1"]) .bd-head:has(.bd-save:is([data-state="error"], [data-state="conflict"]), .bd-findbox:not([hidden])) :is(#bd-exportbtn, #bd-popbtn, #bd-paperbtn, .bd-autobtn),
  .bd:not([data-view="1"]) .bd-head:has(.bd-save:is([data-state="error"], [data-state="conflict"]), .bd-findbox:not([hidden])) .bd-hgroup[data-hgroup="page"],
  .bd:not([data-view="1"]):not(.bd-pop) .bd-head:has(.bd-save:is([data-state="error"], [data-state="conflict"]), .bd-findbox:not([hidden])) .bd-hgroup[data-hgroup="out"] { display: none; }
}
@media (min-width: 601px) and (max-width: 899.98px), (pointer: coarse) and (min-width: 601px) and (max-width: 1023.98px) {
  .bd-head:has(.bd-save:is([data-state="error"], [data-state="conflict"]), .bd-findbox:not([hidden])) { --hgap: 4px; --hgroup-gap: 8px; gap: 6px; }
  .bd-head:has(.bd-save:is([data-state="error"], [data-state="conflict"]), .bd-findbox:not([hidden])) .bd-account:not(:has(.who-as)) { display: none; }
  .bd-head:has(.bd-save:is([data-state="error"], [data-state="conflict"]), .bd-findbox:not([hidden])) .bd-account .settings-btn { display: none; }
  /* (Its group as far from the tools as any group from the next, the bar's gap a little less here: its margin is
     what the gap leaves of --hgroup-gap.) */
  .bd-head:has(.bd-save:is([data-state="error"], [data-state="conflict"]), .bd-findbox:not([hidden])) .bd-account { margin-left: calc(var(--hgroup-gap) - 6px); }
  .bd-head:has(.bd-findbox:not([hidden])) :is(.bd-save, #bd-where) { display: none; }
  .bd:not([data-view="1"]) .bd-head:has(.bd-findbox:not([hidden])) #bd-fullbtn { display: none; }
}
/* (Under 700 px with search open the field and "Year 9 [Leave this link]" had not room both: at 601 px, with a save
   failed too, the field's arrows ran over the page's button. There the group goes while searching, as everyone's
   does, and ⋯ has Leave this link - back once search closes. 7 Oct 2026.) */
@media (min-width: 601px) and (max-width: 699.98px) {
  .bd-head:has(.bd-findbox:not([hidden])) .bd-account { display: none; }
}
/* (And a finger's 44 px buttons narrower than 700 px - a phone on its side, an iPad's Split View or Slide Over - had not
   room for the owner's Follow me and Present beside either: the field's arrows ran over Pages at 601-650 px, and a
   save that failed pushed ⋯ itself past the window's edge, where nothing scrolls to it, main window and popped out
   alike (7 Oct 2026, the review). There the two fold into ⋯ while the bar has more to say, under Teaching, as Export
   does under 900 (presence.js) - back in the bar once it has said it.) */
@media (pointer: coarse) and (min-width: 601px) and (max-width: 699.98px) {
  .bd-head:has(.bd-save:is([data-state="error"], [data-state="conflict"]), .bd-findbox:not([hidden])) :is(#bd-followme, #bd-present) { display: none; }
}

/* A phone (600 px and under), or a narrow popped-out window: two rows, on purpose. The way back, the title (taking
   what is left), the notebook, the save state and ⋯; then the tools, from the left. The tools' box steps aside
   (display: contents) so ⋯ can stand on the first row and the groups on the second, an empty line between them. */
@media (max-width: 600px) {
  .bd-head { flex-wrap: wrap; row-gap: 0; padding-block: 6px; }
  .bd-headacts { display: contents; }
  /* (No account group on a phone, as there was no masthead over its editor: ⋯ has Settings, "Your name…" and Leave
     this link, and the list of whiteboards the rest.) */
  .bd-account { display: none; }
  input.bd-title { flex: 1 1 0; min-width: 3em; font-size: 18px; }
  /* (A save that failed: its words, cut short with "…" - the whole of them its tooltip - so ⋯ keeps its row.) */
  .bd-save:is([data-state="error"], [data-state="conflict"]) { flex: 0 1 auto; min-width: 0; max-width: 9em; }
  .bd-morebtn { order: 1; margin-left: auto; }
  .bd-head::after { content: ''; order: 2; flex: 0 0 100%; }
  .bd-hgroup { order: 3; margin-top: 6px; }
}
.menu-item.bd-mon { font-weight: 500; }
.bd-pageitem { display: flex !important; align-items: center; gap: 9px; }
.bd-pageitem img, .bd-pageitem i { flex: none; width: 42px; height: 28px; border-radius: 4px; object-fit: cover; object-position: top;
  background: #fbfaf6; border: 1px solid var(--rule); }
.bd-pageitem span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.bd-stage { position: relative; flex: 1; min-height: 0; overflow: hidden; touch-action: none; user-select: none;
  -webkit-user-select: none; background: #fbfaf6; }
.bd-stage.paper-slate { background: #1d272b; }
/* See-through paper shows the page behind it - and on Glass the aurora, which at its strongest took the coloured
   inks for light paper down to 1.6-3.5:1, in every palette, the default too, and Aero's dark ink to 3.7:1
   (measured 30 Sep 2026, lane L19: tests/test_appearance.py). So a veil of the page's own colour lies behind the
   writing: the aurora still tints it, but can no longer wash an ink out. 85% on light paper, where the coloured
   inks are darkest and have least to spare (at 70% Aero's and Ember's orange still fell to 2.9); 60% on dark,
   whose inks kept over 5:1 even so. */
:root { --glass-veil: 85%; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --glass-veil: 60%; } }
:root[data-theme="dark"]  { --glass-veil: 60%; }
:root[data-theme="light"] { --glass-veil: 85%; }
.bd-stage.paper-glass { background: color-mix(in srgb, var(--paper) var(--glass-veil), transparent); }
.bd-layer { position: absolute; left: 0; top: 0; pointer-events: none; }
#bd-over { pointer-events: auto; touch-action: none; }
/* The pen, highlighter and eraser: the ring where the pen writes, as the computer's own pointer (9 Oct 2026, the owner:
   "im getting lag with the pointer showing where im writing") - a picture of it board.js makes for the nib in hand and
   puts in --nib (showCursor, nibPicture), the system moving it with the pen at the screen's own moment. None where the
   page draws the ring itself (.bd-cursor, below). */
.bd-stage[data-tool="pen"] #bd-over, .bd-stage[data-tool="hl"] #bd-over, .bd-stage[data-tool="eraser"] #bd-over { cursor: var(--nib, none); }
/* Shapes and the Cursor: a crosshair of the Board's own (7 Oct 2026, the owner: "when drawing shapes the colour of
   the pointer is white so doesn't show up on a white background"). The system's crosshair is drawn by inverting
   what is under it, and Windows shows an inverting cursor as plain white in places - with HDR on, for one - so on
   white paper it was not there. Dark lines with a white edge, as the Fill tool's pot: seen on paper and on slate.
   Kept in --bd-cross for the Cursor snipping too (below). */
.bd-stage { --bd-cross: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M12 2v7M12 15v7M2 12h7M15 12h7' fill='none' stroke='%23fff' stroke-width='3.6' stroke-linecap='round'/%3E%3Ccircle cx='12' cy='12' r='1.9' fill='%23fff'/%3E%3Cpath d='M12 2v7M12 15v7M2 12h7M15 12h7' fill='none' stroke='%231c1f24' stroke-width='1.6' stroke-linecap='round'/%3E%3Ccircle cx='12' cy='12' r='0.9' fill='%231c1f24'/%3E%3C/svg%3E") 12 12, crosshair; }
.bd-stage[data-tool="lasso"] #bd-over, .bd-stage[data-tool="shape"] #bd-over { cursor: var(--bd-cross); }
.bd-stage[data-tool="text"] #bd-over { cursor: text; }
/* The Fill tool: a paint pot, pouring where it points (a white edge keeps it seen on slate). */
.bd-stage[data-tool="fill"] #bd-over { cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke='%23fff' stroke-width='3.6' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M10.9 3.8l7.6 7.6-6.3 6.3a1.8 1.8 0 0 1-2.6 0l-4.9-4.9a1.8 1.8 0 0 1 0-2.6z'/%3E%3Cpath d='M6.6 2.6l4.3 4.3'/%3E%3Cpath d='M21.3 19.2a1.75 1.75 0 1 1-3.5 0c0-1.3 1.3-2.1 1.75-3.7.45 1.6 1.75 2.4 1.75 3.7z'/%3E%3C/g%3E%3Cg fill='none' stroke='%231c1f24' stroke-width='1.6' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M10.9 3.8l7.6 7.6-6.3 6.3a1.8 1.8 0 0 1-2.6 0l-4.9-4.9a1.8 1.8 0 0 1 0-2.6z'/%3E%3Cpath d='M6.6 2.6l4.3 4.3M3.7 12.3h13.9'/%3E%3C/g%3E%3Cpath d='M21.3 19.2a1.75 1.75 0 1 1-3.5 0c0-1.3 1.3-2.1 1.75-3.7.45 1.6 1.75 2.4 1.75 3.7z' fill='%231c1f24'/%3E%3C/svg%3E") 19 20, crosshair; }
.bd-stage.bd-picking #bd-over { cursor: default; }       /* the mouse where it is a cursor: a pointer, not a nib */
/* The nib down writing or rubbing: no pointer, over every other (11 Oct 2026, the owner: "the pointer is flashing
   when writing which is distracting" - board.js nibWriting); the page's ring, drawn as the pointer was, in its place. */
#bd-stage.bd-writing #bd-over { cursor: none; }
.bd-stage.bd-grab #bd-over { cursor: var(--cur-grab); }
.bd-stage.bd-panning #bd-over { cursor: var(--cur-grabbing); }
/* Selecting: over a thing or inside the selection it moves; on a corner it
   resizes (board.js sets data-hover). Grabbing the page still wins. */
.bd-stage[data-hover="move"]:not(.bd-grab, .bd-panning) #bd-over { cursor: move; }
.bd-stage[data-hover="nwse"]:not(.bd-grab, .bd-panning) #bd-over { cursor: nwse-resize; }
.bd-stage[data-hover="nesw"]:not(.bd-grab, .bd-panning) #bd-over { cursor: nesw-resize; }
.bd-stage[data-hover="ew"]:not(.bd-grab, .bd-panning) #bd-over { cursor: ew-resize; }
.bd-stage[data-hover="ns"]:not(.bd-grab, .bd-panning) #bd-over { cursor: ns-resize; }
/* The Cursor snipping (16 Oct 2026, board/snip.js: Snip in its "Select by"): every drag draws the snip's rectangle, so
   the crosshair - the mouse's arrow, a selection's four-way arrow and a handle's hand are not what a press does then. */
#bd-stage[data-snip]:not(.bd-grab, .bd-panning) #bd-over { cursor: var(--bd-cross); }
/* The page's own ring, where the pointer cannot be it, and in the pointer's place while the nib is down (board.js
   showCursor, nibWriting): placed by its transform alone, a layer of its own while shown so a move is only moved, not
   painted again, and never eased into place (9 Oct 2026). It is the pointer's own picture (board.js nibArt: --u, and
   its side --s), its middle where the pen is, so the two look the same to the pixel (11 Oct 2026, the owner: "I'd
   prefer no white pixels around the pointer" - and the ring no longer to change as the nib comes down). */
.bd-cursor { position: absolute; left: 0; top: 0; z-index: 3; pointer-events: none; width: var(--s); height: var(--s);
  margin: calc(var(--s) / -2) 0 0 calc(var(--s) / -2); background: var(--u) 0 0 / 100% 100% no-repeat;
  will-change: transform; transition: none; }
.bd-cursor[hidden] { display: none; }
/* The eraser pressed, rubbing out (12 Oct 2026, the owner: "can't see eraser while rubbing out"; board.js rubLook,
   rubArt): in the ring's own box, its picture put by meanwhile - a firmer edge round a disc of the paper's colour that
   dims what it is over - squashed a little along the way it moves, by its transform alone (board.js rubSquash). */
.bd-cursor .bd-rub { position: absolute; inset: 0; display: none; background: 0 0 / 100% 100% no-repeat; }
#bd-stage.bd-rubbing .bd-cursor[data-kind="eraser"] { background-image: none; }
#bd-stage.bd-rubbing .bd-cursor[data-kind="eraser"] .bd-rub { display: block; will-change: transform; }
/* The ring that fills round a pen held still, until the radial menu opens
   (board.js: showHoldCue) — seen coming, a pause before writing can write on.
   Its dashes in the circle's own length, 2π × 15 (7 Oct 2026): not pathLength="100", which an iPad's Safari has
   been known to leave out of a dash's sums - a ring that stopped short of full, or filled unevenly. Let go of before
   the menu opens (the pen moved on, or lifted), it fades where it stopped (board.js: hideHoldCue holds the fill there)
   and is gone; the menu opening on it takes its place at once, with nothing to fade. */
.bd-holdcue { position: absolute; z-index: 8; width: 40px; height: 40px; margin: -20px 0 0 -20px; pointer-events: none; }
.bd-holdcue svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); overflow: visible;
  filter: drop-shadow(0 0 1.5px rgb(0 0 0 / .45)); }
/* (Its shadow on the drawing, not the ring in it - the same, but that Safari draws no CSS filter on a shape inside an
   SVG, and on light paper the ring had no dark edge to it there. 7 Oct 2026, the owner: "make sure it all works on
   safari too".) */
.bd-holdcue circle { fill: none; stroke: color-mix(in srgb, var(--accent) 80%, #fff); stroke-width: 3.5; stroke-linecap: round;
  stroke-dasharray: 94.25; stroke-dashoffset: 94.25;
  animation: bd-holdfill var(--fill, 400ms) linear forwards; }
@keyframes bd-holdfill { to { stroke-dashoffset: 0; } }
.bd-holdcue.gs-leave { animation: gs-fadeout var(--t-quick-out) var(--ease-away) forwards; }
:root[data-motion="reduce"] .bd-holdcue circle { animation: none; stroke-dashoffset: 0; opacity: .75; }
.bd-edit { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
/* The page's own layers of elements (board.js: makeLayer), placed in page
   units and moved and zoomed with the page by one transform. They take no
   presses themselves; an element in one that wants them says so with
   pointer-events: auto. Under the page: beneath the page's canvas, seen
   where the page is cut away. Over it: above the ink, under the tools. */
.bd-world { position: absolute; left: 0; top: 0; width: 0; height: 0; transform-origin: 0 0; pointer-events: none; }
.bd-world > * { position: absolute; }
.bd-overlay { z-index: 1; }
.bd-textedit { position: absolute; left: 0; top: 0; pointer-events: auto; resize: none; overflow: hidden; margin: 0;
  padding: 0 2px; border: 1.5px dashed color-mix(in srgb, var(--accent) 70%, transparent) !important; border-radius: 4px;
  background: color-mix(in srgb, #fff 55%, transparent) !important; box-shadow: none !important; transform-origin: 0 0; min-height: 1.4em; }
/* Light words on dark paper (slate, or see-through paper in the dark theme — board.js
   sets bd-ondark by the rule its ink colours follow): a darkened box, not a white one. */
.bd-textedit.bd-ondark { background: rgb(0 0 0 / .35) !important; }

/* The floating panes: rail, options, selection bar, zoom, the reading. */
.bd-rail, .bd-props, .bd-selbar, .bd-zoom, .bd-hint, .bd-conflict, .bd-diagprog, .bd-followbar, .bd-fullhelp, .bd-snipchip {
  position: absolute; z-index: 4; background: var(--surface); border: 1px solid var(--rule-strong);
  box-shadow: var(--shadow-2); }
:root[data-style="glass"] :is(.bd-rail, .bd-props, .bd-selbar, .bd-zoom, .bd-hint, .bd-conflict, .bd-diagprog, .bd-followbar, .bd-fullhelp, .bd-snipchip) {
  background-color: var(--pane); background-image: var(--gloss-soft);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); box-shadow: var(--rim), var(--shadow-2); }
/* The tools sit halfway down the left edge, where the hand finds them — or
   down the right, when chosen (data-side) — centred by their margins, and
   scrolling within themselves when the window is short. */
.bd-rail { left: 12px; top: 12px; bottom: 12px; margin-block: auto; height: fit-content; display: flex; flex-direction: column;
  gap: 3px; padding: 6px; border-radius: 22px; max-height: calc(100% - 24px); overflow-y: auto; scrollbar-width: none; }
.bd-stage[data-side="right"] .bd-rail { left: auto; right: 12px; }
.bd-rail::-webkit-scrollbar { display: none; }
.bd-railgap { height: 1px; margin: 5px 6px; background: var(--rule); flex: none; }
.bd-tool { position: relative; display: grid; place-items: center; width: 38px; height: 38px; flex: none; border-radius: 50%;
  border: 1px solid transparent; background: transparent; color: var(--ink); cursor: pointer; transition: background .15s, border-color .15s; }
.bd-tool svg { width: 21px; height: 21px; }
.bd-tletter { font-size: 15px; font-weight: 600; line-height: 1; }   /* a tool that came without an icon: its first letter */
.bd-tool:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.bd-tool.on { background: color-mix(in srgb, var(--accent) 22%, transparent); border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .5); }
.bd-tool:disabled { opacity: .35; cursor: default; background: transparent; }
.bd-nib { display: block; width: var(--nibw, 5px); height: 18px; border-radius: 999px; background: var(--nib, var(--ink));
  box-shadow: inset 0 1px 1px rgb(255 255 255 / .45), 0 0 0 1px rgb(0 0 0 / .08); }
.bd-pen[data-style="fountain"] .bd-nib { border-radius: 999px 999px 40% 40%; }
.bd-pen[data-style="marker"] .bd-nib { border-radius: 3px; }
.bd-hlswatch { opacity: .75; }
/* The pen's recent settings, in its options: a short line in each one's
   colour, size and nib — lit from within when it is the one in hand. */
.bd-recent { display: flex; align-items: center; gap: 3px; flex-wrap: wrap; }
.bd-recent .bd-plabel { padding: 0 4px 0 2px; }
.bd-recentpen { display: grid; place-items: center; width: 38px; height: 28px; padding: 0; border: 1px solid transparent;
  border-radius: 999px; background: transparent; cursor: pointer; transition: background .15s, border-color .15s; }
.bd-recentpen svg { width: 30px; height: 20px; overflow: visible; }
.bd-recentpen path { fill: none; stroke: var(--nib); stroke-width: var(--nibw); stroke-linecap: round; stroke-linejoin: round; }
.bd-recentpen[data-style="marker"] path { stroke-linecap: square; stroke-linejoin: miter; }
.bd-recentpen[data-style="fine"] path { stroke-width: calc(var(--nibw) * .8); }
.bd-recentpen:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.bd-recentpen.on { background: color-mix(in srgb, var(--accent) 20%, transparent); border-color: color-mix(in srgb, var(--accent) 50%, transparent);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .5); }

/* The chosen tool's options: a pane that opens beside the rail, level with
   the tool's button (board.js places it), its groups one above another. */
.bd-props { left: 64px; top: 12px; display: flex; flex-direction: column; align-items: flex-start; gap: 9px; padding: 9px 10px;
  border-radius: 20px; width: max-content; max-width: min(236px, calc(100% - 84px)); max-height: calc(100% - 16px);
  overflow-y: auto; scrollbar-width: none; }
.bd-props::-webkit-scrollbar { display: none; }
.bd-props[hidden] { display: none; }
.bd-props .bd-swatches, .bd-props .bd-sizes { flex-wrap: wrap; }
.bd-props .bd-kinds { border-radius: 16px; }
.bd-props .bd-plabel { line-height: 1.4; }
/* More ink in the dark (4 Oct 2026): the options' pane over a whiteboard has the page's near-white paper behind it,
   which lifts the dark Glass pane - "Yours" over your own colours measured 4.0-4.35:1 there with 15% (Ember the
   lowest), as "Recent" beside it would have. 50% measures 5.0 or more in every palette (tests/test_colours.py). */
.bd-plabel { font-size: 12.5px; color: light-dark(color-mix(in srgb, var(--ink) 15%, var(--muted)), color-mix(in srgb, var(--ink) 50%, var(--muted)));
  padding: 4px 4px; }
@supports not (color: light-dark(#000, #fff)) {
  .bd-plabel {
    color: var(--not-light, color-mix(in srgb, var(--ink) 15%, var(--muted))) var(--not-dark, color-mix(in srgb, var(--ink) 50%, var(--muted)));
  }
}
.bd-seg { display: flex; padding: 2px; border-radius: 999px; background: color-mix(in srgb, var(--ink) 6%, transparent); }
.bd-seg button { border: 0; background: transparent; color: var(--ink); font: inherit; font-size: 12px; padding: 4px 10px;
  border-radius: 999px; cursor: pointer; white-space: nowrap; }
.bd-seg button:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.bd-seg button.on { background: var(--surface); box-shadow: 0 1px 2px rgb(0 0 0 / .16); font-weight: 500; }
.bd-kinds { flex-wrap: wrap; }
.bd-swatches { display: flex; gap: 5px; align-items: center; }
.bd-swatch { position: relative; width: 22px; height: 22px; border-radius: 50%; padding: 0; cursor: pointer; flex: none;
  border: 2px solid var(--surface); background: var(--sw); box-shadow: 0 0 0 1px var(--rule-strong), inset 0 1px 2px rgb(255 255 255 / .35); }
.bd-swatch:hover { transform: scale(1.1); }
.bd-swatch.on { box-shadow: 0 0 0 2px var(--accent), inset 0 1px 2px rgb(255 255 255 / .35); }
/* A very pale swatch ringed in a thin dark line, a very dark one in a light line (board.js swTone, 15 Oct 2026): a
   colour pack's are no longer made to read on the paper (the owner: "Let's kinda ignore readability"), so Lemon cream
   on a light pane and Bloom's Night on a dark one would be lost as swatches without it. Lit, the accent ring instead. */
.bd-swatch.bd-pale:not(.on) { box-shadow: 0 0 0 1px rgb(0 0 0 / .55), inset 0 1px 2px rgb(255 255 255 / .35); }
.bd-swatch.bd-deep:not(.on) { box-shadow: 0 0 0 1px rgb(255 255 255 / .7), inset 0 1px 2px rgb(255 255 255 / .35); }
/* The any-colour swatch is a <label> round the colour picker: the app-wide
   label rule's bottom margin lifted it above the rest of its row. */
.bd-custom { overflow: hidden; margin: 0; background: conic-gradient(#f44, #fd3, #4d4, #3bf, #a4f, #f44); }
.bd-custom input { position: absolute; inset: -6px; width: 40px; height: 40px; opacity: 0; cursor: pointer; }
/* Your own colours (board.js, mineRow; 4 Oct 2026): a row under the tool's own, the same everywhere colours are
   offered. Each is ringed in the pane's ink, not the faint rule the named inks have, so the palest on a light pane
   and the darkest on a dark one still show as swatches - 3:1 as drawn in all 44 looks (tests/test_colours.py: 3.4 at
   the least, Ember's dark Glass; 66% ink was 3.1 there) - and lit, the accent goes round that. "+ Save" is a small lozenge after a colour picked and not kept yet. */
.bd-swatch.bd-mine { --mine-ring: color-mix(in srgb, var(--ink) 72%, transparent);
  box-shadow: 0 0 0 1.5px var(--mine-ring), inset 0 1px 2px rgb(255 255 255 / .35); -webkit-touch-callout: none; touch-action: manipulation; }
.bd-swatch.bd-mine.on { box-shadow: 0 0 0 1.5px var(--mine-ring), 0 0 0 3.5px var(--accent), inset 0 1px 2px rgb(255 255 255 / .35); }
.bd-props .bd-mines { margin-top: -2px; row-gap: 7px; padding-left: 1px; }
.bd-mines .bd-plabel { padding: 0 3px 0 2px; }    /* "Yours", as the pen's "Recent" */
.bd-props .bd-mines .bd-swatch.on { margin-inline: 1px; }
.bd-savecol { display: inline-flex; align-items: center; gap: 3px; height: 24px; padding: 0 10px 0 8px; margin-left: 2px; flex: none;
  border-radius: 999px; border: 1px dashed color-mix(in srgb, var(--ink) 50%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, var(--surface)); color: var(--ink); font: inherit; font-size: 12px; font-weight: 500;
  line-height: 1; white-space: nowrap; cursor: pointer; transition: background .15s, border-color .15s; }
.bd-savecol span { font-size: 15px; font-weight: 600; }
:root[data-style="glass"] .bd-savecol { background-image: var(--gloss-soft); }
.bd-savecol:hover { background-color: color-mix(in srgb, var(--accent) 20%, var(--surface)); border-style: solid; }
.bd-mines .bd-swatch:focus-visible, .bd-savecol:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.bd-sizes { display: flex; gap: 2px; align-items: center; }
.bd-size { display: grid; place-items: center; width: 28px; height: 28px; border: 0; border-radius: 50%; background: transparent; cursor: pointer; }
.bd-size i { display: block; width: var(--d); height: var(--d); border-radius: 50%; background: var(--ink); opacity: .75; }
.bd-size:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.bd-size.on { background: color-mix(in srgb, var(--accent) 22%, transparent); }
.bd-size.on i { opacity: 1; }
/* Fill: the paint in the Fill tool's pot takes the colour chosen (board.js
   sets it); "Fill" leads the Shapes tool's None / Tint / Solid; and the
   selection bar's Fill menu ends with a row of colours. */
.bd-fillswatch { opacity: .8; }
.bd-seglabel { align-self: center; padding: 0 6px 0 8px; font-size: 11.5px; color: color-mix(in srgb, var(--ink) 15%, var(--muted)); }
.bd-fillrow { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 9px 5px; margin-top: 4px; border-top: 1px solid var(--rule); }
.bd-fillrow .bd-swatch { width: 20px; height: 20px; }
.bd-fillrow + .bd-fillrow.bd-mines { border-top: 0; margin-top: 0; padding-top: 4px; }   /* your own colours, under the rest */

.bd-selbar { left: 0; top: 0; display: flex; align-items: center; gap: 4px; padding: 4px 6px; border-radius: 999px; white-space: nowrap; }
.bd-selbar[hidden] { display: none; }
/* Wider than the page beside the tools (a narrow, popped-out window): it
   wraps onto more rows rather than cover them (board.js sets its width). */
.bd-selbar.bd-wrap { flex-wrap: wrap; justify-content: center; border-radius: 16px; }
.bd-sbtn { border: 0; background: transparent; color: var(--ink); font: inherit; font-size: 12.5px; padding: 5px 10px; border-radius: 999px; cursor: pointer; }
.bd-sbtn:hover { background: color-mix(in srgb, var(--accent) 14%, transparent); }
.bd-sbtn[hidden] { display: none; }
.bd-sdanger { color: var(--red); }
.bd-ssep { width: 1px; height: 18px; background: var(--rule); margin: 0 2px; }
.bd-scolours { display: flex; gap: 4px; }
.bd-scolours .bd-swatch { width: 18px; height: 18px; }
.bd-scolours .bd-ssep { align-self: center; margin: 0 1px; }

.bd-zoom { right: 12px; bottom: 12px; display: flex; align-items: center; padding: 3px; border-radius: 999px; }
.bd-stage[data-side="right"] .bd-zoom { right: auto; left: 12px; }      /* out of the way of tools on the right */
.bd-zoom button { border: 0; background: transparent; color: var(--ink); font: inherit; font-size: 13px; min-width: 30px; height: 28px;
  padding: 0 8px; border-radius: 999px; cursor: pointer; font-variant-numeric: tabular-nums; }
.bd-zoom button:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }

.bd-read { position: absolute; left: 0; top: 0; z-index: 6; width: min(380px, calc(100% - 24px)); padding: 14px 16px 12px; }
.bd-read[hidden] { display: none; }
.bd-readx { position: absolute; top: 6px; right: 8px; width: 26px; height: 26px; border: 0; border-radius: 50%; background: transparent;
  color: var(--muted); font-size: 18px; cursor: pointer; }
.bd-readx:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--ink); }
.bd-readx + p { padding-right: 24px; }   /* a message's first line clears the × */
.bd-readtext { width: 100%; font-size: 15px; resize: vertical; margin: 6px 0 8px; }
.bd-readlatex { width: 100%; font-family: ui-monospace, Consolas, monospace; font-size: 13px; margin: 6px 0 10px; }
.bd-mathpreview svg { max-width: 100%; height: auto; color: var(--ink); }
.bd-mathpreview { min-height: 44px; padding: 10px; border-radius: 10px; margin-top: 6px; overflow-x: auto;
  background: color-mix(in srgb, var(--paper) 70%, transparent); border: 1px solid var(--rule); text-align: center; }
.bd-alts { display: flex; flex-wrap: wrap; gap: 5px; align-items: center; margin-bottom: 8px; }
.bd-alt { border: 1px solid var(--rule-strong); background: transparent; color: var(--ink); font: inherit; font-size: 12.5px;
  padding: 2px 9px; border-radius: 999px; cursor: pointer; }
.bd-alt:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.bd-readacts { flex-wrap: wrap; }

.bd-conflict { left: 50%; top: 14px; transform: translateX(-50%); display: flex; align-items: center; gap: 10px;
  padding: 8px 10px 8px 16px; border-radius: 999px; font-size: 13px; }
.bd-conflict[hidden] { display: none; }
.bd-hint { left: 50%; bottom: 16px; transform: translateX(-50%); padding: 7px 16px; border-radius: 999px; font-size: 13px;
  max-width: calc(100% - 200px); text-align: center; pointer-events: none; }
.bd-hint[hidden] { display: none; }
/* A hint that can be undone (one of your own colours taken away): it takes presses, Undo a lozenge at its end. */
.bd-hint.bd-hintact:not([hidden]) { display: flex; align-items: center; justify-content: center; gap: 10px; padding-right: 6px;
  pointer-events: auto; }
.bd-hintbtn { flex: none; padding: 3px 12px; border-radius: 999px; border: 1px solid color-mix(in srgb, var(--accent) 55%, var(--rule));
  background: color-mix(in srgb, var(--accent) 14%, var(--surface)); color: var(--ink); font: inherit; font-size: 12.5px; font-weight: 600;
  cursor: pointer; }
:root[data-style="glass"] .bd-hintbtn { background-image: var(--gloss-soft); }
.bd-hintbtn:hover { background-color: color-mix(in srgb, var(--accent) 24%, var(--surface)); }
.bd-hintbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A picture being redrawn as a diagram (board/diagram.js): where it has got to, over the middle of the picture -
   the step in words, the app's bubble tube (.genprog-track) under it. Gone a moment after it ends. */
.bd-diagprog { width: 180px; padding: 7px 12px 9px; border-radius: 12px; transform: translateX(-50%);
  pointer-events: none; transition: opacity .5s ease .7s; }
.bd-diagprog-note { margin: 0 0 6px; font-size: 12.5px; line-height: 1.3; text-align: center; }
/* A snip being dragged (board/snip.js, 12 Oct 2026): its size in pixels, a small lozenge of the hint's glass under the
   rectangle - in the hint's own colours, so it reads as the hint does in every look. Placed by its translate. */
.bd-snipchip { left: 0; top: 0; padding: 2px 10px; border-radius: 999px; font-size: 12px; font-variant-numeric: tabular-nums;
  white-space: nowrap; pointer-events: none; }
.bd-snipchip[hidden] { display: none; }
/* Cropping a picture (board/crop.js): the whole picture dimmed, the part kept bright inside a frame with eight
   handles, and Reset / Cancel / Done under it. The layer over the page takes every press while it is open. */
.bd-crop { position: absolute; inset: 0; z-index: 6; cursor: default; background: rgb(18 22 28 / .55); }
/* (The page still draws the picture under the veil; this dim copy on top keeps the hidden part just visible.) */
.bd-crop-full { position: absolute; opacity: .3; pointer-events: auto; -webkit-user-select: none; user-select: none; -webkit-user-drag: none; }
.bd-crop-frame { position: absolute; overflow: hidden; cursor: move; touch-action: none;
  outline: 2px solid #fff; box-shadow: 0 0 0 1px rgb(0 0 0 / .45), 0 6px 22px rgb(0 0 0 / .25); }
.bd-crop-shown { position: absolute; max-width: none; pointer-events: none; -webkit-user-select: none; user-select: none; -webkit-user-drag: none; }
.bd-crop-h { position: absolute; width: 14px; height: 14px; border-radius: 3px; background: #fff;
  box-shadow: 0 0 0 1px rgb(0 0 0 / .5); z-index: 1; }
.bd-crop-h[data-h="n"], .bd-crop-h[data-h="s"] { left: calc(50% - 7px); cursor: ns-resize; }
.bd-crop-h[data-h="e"], .bd-crop-h[data-h="w"] { top: calc(50% - 7px); cursor: ew-resize; }
.bd-crop-h[data-h="n"], .bd-crop-h[data-h="ne"], .bd-crop-h[data-h="nw"] { top: -1px; }
.bd-crop-h[data-h="s"], .bd-crop-h[data-h="se"], .bd-crop-h[data-h="sw"] { bottom: -1px; }
.bd-crop-h[data-h="w"], .bd-crop-h[data-h="nw"], .bd-crop-h[data-h="sw"] { left: -1px; }
.bd-crop-h[data-h="e"], .bd-crop-h[data-h="ne"], .bd-crop-h[data-h="se"] { right: -1px; }
.bd-crop-h[data-h="ne"], .bd-crop-h[data-h="sw"] { cursor: nesw-resize; }
.bd-crop-h[data-h="nw"], .bd-crop-h[data-h="se"] { cursor: nwse-resize; }
.bd-crop-bar { position: absolute; transform: translateX(-50%); display: flex; gap: 6px; padding: 5px;
  border-radius: 999px; background: var(--surface); border: 1px solid var(--rule-strong); box-shadow: var(--shadow-2); }
:root[data-style="glass"] .bd-crop-bar { background-color: var(--pane); background-image: var(--gloss-soft);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur); box-shadow: var(--rim), var(--shadow-2); }
.bd-crop-bar button { font: inherit; font-size: 13px; padding: 5px 12px; border-radius: 999px; border: 1px solid var(--rule);
  background: transparent; color: var(--ink); cursor: pointer; }
.bd-crop-bar button.primary { background: var(--accent); color: #fff; border-color: transparent; }

/* …and a four-pointed star bouncing about over the picture meanwhile (board/diagram.js bubbleOver), like the old
   screensavers: the star's shape filled with the colour of the moment (--mark, new at each bounce - a snap, not a
   fade), flashing when it lands square in a corner, faded out at the end. */
.bd-diagmark { position: absolute; left: 0; top: 0; z-index: 5; pointer-events: none; will-change: transform;
  background-color: var(--mark, #2f7bff);
  --mark-shape: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath d='M50 2C53 36 64 47 98 50C64 53 53 64 50 98C47 64 36 53 2 50C36 47 47 36 50 2Z'/%3E%3C/svg%3E");
  -webkit-mask: var(--mark-shape) center / contain no-repeat;
  mask: var(--mark-shape) center / contain no-repeat;
  transition: opacity .5s; }
/* (A glow round it would be cut off by its own shape, so a corner makes it flash instead.) */
.bd-diagmark-corner { animation: mark-corner .2s steps(1) 8; }
@keyframes mark-corner { 0% { filter: brightness(1.7) saturate(1.3); } 50% { filter: none; } }
.bd-diagmark-gone { opacity: 0; }
.bd-diagprog-done { opacity: 0; }
.bd-diagprog-failed { transition-delay: 4s; opacity: 0; }
.bd-diagprog-failed .bd-diagprog-note { color: var(--red); }

/* The radial menu (board.js, showRadial): a glass disc round the pointer
   holding a ring of raised glass orbs — the tools, or what can be done
   with the selection — and, when there is one, a glass band beyond it
   with the tool's colours and sizes (or Colour's and Fill's choices). The
   middle is a sunken well naming what is pointed at. The choice pointed
   at is lit: full lacquer and a glow, a little larger. The box itself
   takes no presses, so its empty corners are the page's. It opens outward
   from the pointer — at once where motion is turned down. */
.bd-radial { position: absolute; z-index: 8; width: 340px; height: 340px; pointer-events: none; }
.bd-rdisc, .bd-rtrack, .bd-rhub, .bd-ritem { position: absolute; left: 50%; top: 50%; border-radius: 50%; pointer-events: auto; }
.bd-rdisc { width: 216px; height: 216px; margin: -108px 0 0 -108px; background: var(--surface);
  border: 1px solid var(--rule-strong); box-shadow: var(--shadow-3); }
.bd-rtrack { width: 330px; height: 330px; margin: -165px 0 0 -165px; background: var(--surface);
  border: 1px solid var(--rule-strong); box-shadow: var(--shadow-2);
  -webkit-mask: radial-gradient(circle, transparent 121px, #000 121.5px); mask: radial-gradient(circle, transparent 121px, #000 121.5px);
  opacity: 0; visibility: hidden; transition: opacity var(--t-quick) var(--ease-out), visibility var(--t-quick); }
.bd-radial.bd-rhasouter .bd-rtrack { opacity: 1; visibility: visible; }
/* A tool's own arc open (board.js: showSub): the colours and sizes of the tool in hand step aside for it - drawn in
   towards the middle as they fade (4 Oct 2026), and back out as it closes - on --ease-out (7 Oct 2026: the pop's
   overshoot on an orb read as a wobble). */
.bd-ritem.bd-rout { transition: scale .16s var(--ease-out), opacity .14s, visibility 0s, box-shadow .12s, border-color .12s; }
.bd-radial.bd-rsubopen .bd-rout { visibility: hidden; opacity: 0; scale: .6;
  transition: scale .12s var(--ease-away), opacity .1s, visibility 0s .12s; }
/* On glass, a breath of the palette's accent in the pane and at its edge:
   untinted, a light pane over light paper all but vanished. */
:root[data-style="glass"] :is(.bd-rdisc, .bd-rtrack) {
  background-color: color-mix(in srgb, var(--accent) 7%, var(--pane)); background-image: var(--gloss-soft);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--accent) 30%, var(--rule-strong)); box-shadow: var(--rim), var(--shadow-3); }
/* The band beyond is glass without the blur (6 Oct 2026, the owner: "when moving around the radial menu it gets stuck
   sometimes", a picture of Text's sizes left over the colours and Undo; and before it the band blank, every orb gone
   from it). A blur behind a pane cut by a mask - the band's hole - is one the browser can fail to paint again on a
   real screen (Chrome on Windows, its GPU): what was drawn there stayed, or what should be did not. Its pane, 86 % of
   the surface, hides the page under it all but as well without one. The disc, uncut, keeps its blur. */
:root[data-style="glass"] .bd-rtrack { backdrop-filter: none; -webkit-backdrop-filter: none; }
.bd-rhub { width: 72px; height: 72px; margin: -36px 0 0 -36px; display: grid; place-items: center; padding: 5px;
  text-align: center; font-size: 11.5px; line-height: 1.15; font-weight: 500; color: var(--ink); overflow-wrap: normal; word-break: normal; hyphens: manual;
  cursor: pointer; background: color-mix(in srgb, var(--ink) 5%, transparent);
  box-shadow: inset 0 1px 2px rgb(10 60 80 / .13), inset 0 -1px 0 var(--sheen-soft, rgb(255 255 255 / .28)); }
/* Its name: as wide as the well and no wider, a word too long for it hidden past the edge rather than spilling, so
   board.js (nameHub) can see it does not fit and take a smaller size (4 Oct 2026: never broken inside a word). */
.bd-rhub span { display: block; width: 100%; min-width: 0; overflow: hidden; }
.bd-ritem { width: var(--s); height: var(--s); margin: calc(var(--s) / -2) 0 0 calc(var(--s) / -2); translate: var(--x) var(--y);
  display: grid; place-items: center; padding: 0; font: inherit; color: var(--ink); cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--ink) 16%, transparent); background: color-mix(in srgb, var(--surface) 84%, transparent);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .55), 0 1px 3px rgb(0 0 0 / .14);
  transition: scale .12s, box-shadow .12s, border-color .12s; }
:root[data-style="glass"] .bd-ritem { background-image: var(--gloss-soft); box-shadow: var(--bevel), 0 1px 3px rgb(0 0 0 / .14); }
.bd-ritem svg { width: 21px; height: 21px; }
/* In an arc or the band, on the smaller orbs (32 px), a smaller picture and glyph (4 Oct 2026): 21 px reached the
   orb's own rim at its corners, which the dark theme draws pale. */
:is(.bd-rsub, .bd-rsubgone) .bd-ritem svg, .bd-ritem.bd-rout svg { width: 18px; height: 18px; }
:is(.bd-rsub, .bd-rsubgone) .bd-rglyph, .bd-ritem.bd-rout .bd-rglyph { font-size: 13.5px; }
.bd-ritem.bd-ron { border-color: color-mix(in srgb, var(--accent) 70%, transparent);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .55), 0 0 0 2px color-mix(in srgb, var(--accent) 45%, transparent); }
/* Pointed at: a little larger - a bead shrunk to fit (board.js layBeads, --lift) only as far as the gap to the next. */
.bd-ritem.bd-raim { scale: var(--lift, 1.14); border-color: color-mix(in srgb, var(--accent) 75%, transparent);
  background-color: color-mix(in srgb, var(--accent) 16%, var(--surface));
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .75), 0 0 0 3px color-mix(in srgb, var(--accent) 32%, transparent), 0 5px 14px rgb(0 0 0 / .2); }
/* On glass the lit orb keeps its fill: the light changes, not the colour. */
:root[data-style="glass"] .bd-ritem.bd-raim { background-color: color-mix(in srgb, var(--surface) 84%, transparent); background-image: var(--gloss);
  box-shadow: var(--bevel), 0 0 0 3px color-mix(in srgb, var(--accent) 34%, transparent),
    0 0 18px 2px color-mix(in srgb, var(--accent) 45%, transparent), 0 5px 14px rgb(0 0 0 / .18); }
.bd-ritem[aria-disabled="true"] { opacity: .38; cursor: default; }
.bd-ritem[aria-disabled="true"].bd-raim { scale: 1; }
.bd-rsw { display: block; width: 20px; height: 20px; border-radius: 50%; background: var(--sw);
  box-shadow: inset 0 1px 2px rgb(255 255 255 / .45), 0 0 0 1px rgb(0 0 0 / .16); }
.bd-rsw.bd-rtint { background: color-mix(in srgb, var(--sw) 32%, transparent); }
.bd-rsw.bd-pale { box-shadow: inset 0 1px 2px rgb(255 255 255 / .45), 0 0 0 1px rgb(0 0 0 / .55); }   /* (swTone, as the swatches') */
.bd-rsw.bd-deep { box-shadow: inset 0 1px 2px rgb(255 255 255 / .45), 0 0 0 1px rgb(255 255 255 / .7); }
/* A swatch on a smaller bead (7 Oct 2026, board.js BEAD_MIN: every one of your colours on the ring, the pen's eighteen
   on 22 px beads) keeps 5 px of its orb round it, as the 20 px one on a 32 px orb keeps 6: at 3 px your colour's ring
   lay over the orb's lit top edge, and a pale ring there measured 1.4:1 against it in the dark. */
.bd-ritem > .bd-rsw { width: min(20px, calc(var(--s) - 10px)); height: min(20px, calc(var(--s) - 10px)); }
/* Your own colour: ringed in the ink, 2 px of 86 % of it (7 Oct 2026; 86 % as the tool arcs' .bd-mine), so a
   near-white one on a light orb and a near-black on a dark still show as swatches - 3:1 as drawn in all 44 looks
   (tests/test_colours_wheels.py: 3.6 at the least, Ember's dark Glass). The 1.5 px of 72 % before, drawn at a
   fraction of a pixel as the ring's places are, had hardly a pixel of its own and blurred into the orb: 2.0 at the
   least in the light, on the pen's smaller beads. */
.bd-rsw.bd-rmine { box-shadow: inset 0 1px 2px rgb(255 255 255 / .45), 0 0 0 2px color-mix(in srgb, var(--ink) 86%, transparent); }
.bd-rsw.bd-rnone { background: linear-gradient(135deg, transparent 44%, var(--red) 44% 56%, transparent 56%), var(--surface); }
.bd-rsw.bd-rany { background: conic-gradient(#1f5fc4, #6a45bd, #c8352b, #c26508, #1b7f45, #1c1f24, #1f5fc4); }
.bd-rdot { display: block; width: var(--d); height: var(--d); border-radius: 50%; background: var(--sw, var(--ink)); }
.bd-rdot.bd-rhollow { background: transparent; border: 1.5px solid var(--ink); }
.bd-rword { font-weight: 600; line-height: 1; }
.bd-rglyph { font-size: 16px; font-weight: 600; line-height: 1; }
/* Opening (4 Oct 2026, "a bloom-open"): the disc swells up under the pointer, and each choice flies out of the middle
   to its place, a few ms after the one before it (--d, board.js: bloom) - from the top round both ways, the band
   beyond a moment later - all of it in under 0.3 s. An arc opens out of its own choice (--fx, --fy: where that is),
   those nearest it first, and folds back into it as another opens (.bd-rsubgone); the menu, closed, fades as it
   shrinks a little (.bd-rgoing, board.js: fadeAway), taking no presses, while one opened again meanwhile blooms over
   it.
   Made clean (7 Oct 2026, the owner: "can we make sure the animations of opening menus etc are super clean"):
   - Out of the pointer itself (--ox, --oy: from the menu's middle to where it was pressed, board.js showRadial):
     near an edge the menu is moved in, and it bloomed from its own middle, away from the ring that filled round the
     pen (the hold cue) - now the disc and the band grow out of that point and the orbs fly out of it.
   - The band beyond swells up with the disc, 30 ms after it (bd-rpop, once, as it is made): it came up whole at once
     - a flash of a ring before anything else - as its transition has nothing to start from on a thing just made. Its
     animation never changes after that, so the band shown and hidden later (an arc's colours) is its transition's.
     It stays without a blur (#68: a blur under its mask is what left it blank or stuck).
   - One curve for the orbs, --ease-out, as the tools' arcs have (5 Oct: the pop's overshoot read as a wobble).
   - Going, each part fades and draws in towards the middle itself - the disc, the band, the well, every orb - never
     the menu as a whole: opacity moving on what holds the disc's glass switches its blur off for as long as it
     moves (the pane snapped from frosted to clear as it went). Closed while still blooming, each part goes from
     where it had got to (board.js pins it there), not from whole. */
.bd-radial > :is(.bd-rdisc, .bd-rhub, .bd-rtrack) { transform-origin: calc(50% + var(--ox, 0px)) calc(50% + var(--oy, 0px)); }
@keyframes bd-rpop { from { opacity: 0; scale: .8; } }
@keyframes bd-rfly { from { opacity: 0; translate: var(--ox, 0px) var(--oy, 0px); scale: .5; } }
@keyframes bd-rarc { from { opacity: 0; translate: var(--fx, 0) var(--fy, 0); scale: .5; } }
@keyframes bd-rfold { to { opacity: 0; translate: var(--fx, 0) var(--fy, 0); scale: .5; } }
@keyframes bd-rclose { to { opacity: 0; scale: .92; } }
@keyframes bd-rcloseorb { to { opacity: 0; translate: calc(var(--x) * .92) calc(var(--y) * .92); scale: .92; } }
.bd-rdisc, .bd-rhub { animation: bd-rpop .16s var(--ease-out); }
.bd-radial > .bd-rtrack { animation: bd-rpop var(--bloom-time) var(--ease-out) 30ms backwards; }
.bd-ritem { animation: bd-rfly var(--bloom-time) var(--ease-out) var(--d, 0ms) backwards; }
.bd-rsub .bd-ritem { animation-name: bd-rarc; animation-duration: var(--arc-time); }
.bd-rsubgone .bd-ritem { animation: bd-rfold .13s var(--ease-away) forwards; pointer-events: none; }
.bd-radial.bd-rgoing > :is(.bd-rdisc, .bd-rtrack, .bd-rhub) { animation: bd-rclose var(--close-time) var(--ease-away) forwards; }
.bd-radial.bd-rgoing > .bd-ritem, .bd-radial.bd-rgoing > .bd-rsub:not(.bd-rsubgone) > .bd-ritem {
  animation: bd-rcloseorb var(--close-time) var(--ease-away) forwards; }
.bd-radial.bd-rgoing .bd-rlens { opacity: 0; transition: opacity var(--close-time) var(--ease-away); }
.bd-radial.bd-rgoing * { pointer-events: none !important; }
/* An iPad on Glass (data-glass-motion="fade", base.html): the disc's blurred glass comes and goes by its opacity
   alone - WebKit there redraws a blurred pane badly while the pane itself is scaled. The rest still swell and fly. */
:root[data-glass-motion="fade"][data-style="glass"] .bd-radial > .bd-rdisc { animation-name: gs-fadein; }
:root[data-glass-motion="fade"][data-style="glass"] .bd-radial.bd-rgoing > .bd-rdisc { animation-name: gs-fadeout; }
/* The lens (board.js: light): a soft light of the palette's accent under the choice pointed at, which glides from
   one choice to the next rather than jumping - a little ahead of the choice's own lighting up. Under the choices, so
   their icons and the words in the middle are read against the orbs as ever. */
.bd-rlens { position: absolute; left: 50%; top: 50%; width: 60px; height: 60px; margin: -30px 0 0 -30px; border-radius: 50%;
  pointer-events: none; opacity: 0;
  background: radial-gradient(circle, color-mix(in srgb, var(--accent) 42%, transparent) 0 40%, transparent 70%);
  transition: translate .15s var(--ease-out), scale .15s var(--ease-out), opacity .14s; }
.bd-rlens.bd-rlit { opacity: 1; }
.bd-rlens.bd-rjump { transition: opacity .14s; }
:root[data-motion="reduce"] .bd-radial :is(.bd-rdisc, .bd-rtrack, .bd-rhub, .bd-ritem) { animation: none; transition: none; }
@media (prefers-reduced-transparency: reduce) {
  :root[data-style="glass"] :is(.bd-rdisc, .bd-rtrack) { background-color: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* The right-click list (the app's .ctxmenu): what cannot be used now is
   greyed; the keys light the entry they are on; the colours sit in a row.
   (And its copy left fading as another menu comes, .gs-ghost - which has no
   id, so that everything finding #bd-menu finds the menu itself - looks the
   same as it goes: 7 Oct 2026, GSMotion.ghost.) */
:is(#bd-menu, .ctxmenu.gs-ghost):focus { outline: none; }
:is(#bd-menu, .ctxmenu.gs-ghost) .menu-item:disabled { color: var(--faint); cursor: default; background: transparent; }
:is(#bd-menu, .ctxmenu.gs-ghost) .menu-item:focus-visible { outline: none; background: var(--paper-2);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 60%, transparent); }
.bd-ctxrow { display: flex; align-items: center; gap: 6px; padding: 5px 9px; }
.bd-ctxrow > span { margin-right: auto; font-size: 13px; color: var(--ink); }
.bd-ctxrow .bd-swatch { width: 18px; height: 18px; }
.bd-ctxrow .bd-swatch:disabled { opacity: .35; cursor: default; transform: none; }
.bd-ctxrow .bd-swatch:focus-visible, .bd-fillrow .bd-swatch:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Your own colours in the list, under Colour: as many rows as they need, from the right as Colour's are. */
.bd-ctxmine { flex-wrap: wrap; justify-content: flex-end; row-gap: 7px; padding-top: 2px; }
.bd-ctxmine .bd-swatch:focus-visible { outline-offset: 3px; }

/* Popped out: the page and nothing else, and a lighter header. */
body.bare main { padding: 0; }
.bd-pop .bd-head { padding: 5px 10px; gap: 8px; }
.bd-pop input.bd-title { font-size: 16px; }
@media (max-width: 640px) {
  .bd-hbtn { padding: 5px 9px; }
  .bd-props { max-width: min(236px, calc(100% - 76px)); }
}
:root[data-motion="reduce"] .bi-card, :root[data-motion="reduce"] .bi-card:hover { transition: none; transform: none; }
:root[data-motion="reduce"] .bd-tool, :root[data-motion="reduce"] .bd-hbtn, :root[data-motion="reduce"] .bd-swatch:hover, :root[data-motion="reduce"] .bd-recentpen { transition: none; transform: none; }

/* ── Search (round 4) ────────────────────────────────────────────────────
   js/board/search.js. Search in the top bar (or Ctrl+F) opens a sunken
   field there: the words, "3 of 12", the one before and the next. Under
   it a glass pane lists what was found, page by page, the words marked;
   on the page each match glows softly in the page's top layer, the one
   gone to brighter. The app-wide input rule is undone for the field. */
.bd-find { position: relative; display: flex; align-items: center; flex: none; }
.bd-findbtn[hidden], .bd-findbox[hidden], .bd-findlist[hidden], .bd-findnote[hidden], .bd-findedge[hidden] { display: none; }
.bd-findbox { display: flex; align-items: center; gap: 1px; height: 30px; padding: 0 3px 0 10px; border-radius: 999px;
  background: var(--field-bg); border: 1px solid var(--rule-strong); box-shadow: var(--field-inset); color: var(--ink); }
.bd-findbox:focus-within { border-color: color-mix(in srgb, var(--accent) 70%, var(--rule-strong));
  box-shadow: var(--field-inset), 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent); }
.bd-findicon { display: grid; place-items: center; flex: none; color: color-mix(in srgb, var(--ink) 15%, var(--muted)); }
.bd-findicon svg { display: block; width: 14px; height: 14px; }
input.bd-findq { flex: 1 1 auto; width: 176px; min-width: 64px; height: 28px; margin: 0; padding: 0 4px 0 7px; border: 0; border-radius: 0;
  background: transparent; box-shadow: none; font-size: 13px; color: var(--ink); }
input.bd-findq:focus { border: 0; }
input.bd-findq::placeholder { color: color-mix(in srgb, var(--ink) 15%, var(--muted)); opacity: 1; }
input.bd-findq::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; display: none; }
.bd-findcount { flex: none; min-width: 3.4em; height: 24px; padding: 0 7px; border: 0; border-radius: 999px; background: transparent;
  font: inherit; font-size: 11.5px; font-variant-numeric: tabular-nums; white-space: nowrap; text-align: right; cursor: pointer;
  color: color-mix(in srgb, var(--ink) 15%, var(--muted)); }
.bd-findcount:empty { min-width: 0; padding: 0; }
.bd-findnav { display: grid; place-items: center; flex: none; width: 24px; height: 24px; padding: 0; border: 0; border-radius: 50%;
  background: transparent; color: var(--ink); font: inherit; cursor: pointer; transition: background .15s, box-shadow .15s; }
.bd-findnav svg { display: block; width: 14px; height: 14px; }
.bd-findx { font-size: 17px; line-height: 1; }
.bd-findnav:disabled { opacity: .38; cursor: default; }
.bd-findcount:hover, .bd-findnav:not(:disabled):hover { background: color-mix(in srgb, var(--accent) 14%, transparent); }
:root[data-style="glass"] :is(.bd-findcount, .bd-findnav:not(:disabled)):hover { background-image: var(--gloss); box-shadow: var(--bevel); }
:is(.bd-findcount, .bd-findnav, .bd-findall, .bd-findhit):focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* The list: a pane of the Board's glass under the field (search.js places
   it). Its words lie over the page, which shows through a glass pane —
   a dark pane over white paper, a light one over slate — and its first
   line sits where the gloss is brightest. Measured, the usual pane (86 %)
   and gloss left the quieter words at 2.7–4.4 : 1 in dark themes over
   paper; so this pane is thicker (93 %), its gloss the soft sheen only,
   and its quieter words (--bd-quiet) lean further to the ink. */
.bd-findlist { --bd-quiet: color-mix(in srgb, var(--ink) 55%, var(--muted));
  position: absolute; z-index: 7; left: 0; top: 0; width: min(420px, calc(100vw - 16px)); max-height: min(60vh, 470px);
  display: flex; flex-direction: column; overflow: hidden; border-radius: 16px;
  background: var(--surface); border: 1px solid var(--rule-strong); box-shadow: var(--shadow-3); }
:root[data-style="glass"] .bd-findlist { background-color: color-mix(in srgb, var(--surface) 93%, transparent);
  background-image: linear-gradient(180deg, var(--sheen-soft) 0, transparent 44%);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); box-shadow: var(--rim), var(--shadow-3); }
/* Coming and going (7 Oct 2026, the owner: "can we make sure the animations of opening menus etc are super clean"; by
   GSMotion, search.js): the field swells out of the button it takes the place of (its transform-origin under the
   button: GSMotion.originAt) and back into it, the button fading out and in again under it; the list comes down out of
   the field as a menu does (.ctxmenu, "Motion"), and goes back up. The list is the glass itself, so its own opacity
   moving switches no blur off; on an iPad on Glass it only fades. (The button keeps its own colours' transitions.) */
.bd-findbtn { transition: background .15s, border-color .15s, opacity var(--t-quick) var(--ease-out); }
.bd-findbtn.gs-enter { opacity: 0; }
/* (The button and the field cross: while both are there, the button fading out under the field as it comes or in
   under it as it goes, it is held at the field's end - where it sits once the field has gone - out of the bar's row,
   which the field keeps as it was. After the review, 7 Oct 2026: the button came in only once the field had gone,
   and the bar blinked empty there. Going, it goes as the field comes - the same time, the same curve - so the two
   together are always one whole: neither held on over the other, nor both half gone.) */
.bd-find:has(.bd-findbox:not([hidden])) .bd-findbtn:not([hidden]) { position: absolute; right: 0; top: 0; bottom: 0; margin-block: auto; }
.bd-findbtn.gs-leave { opacity: 0; pointer-events: none; }
.bd-findbox { transition: opacity var(--t-quick) var(--ease-out), scale var(--t-quick) var(--ease-out); }
.bd-findbox:is(.gs-enter, .gs-leave) { opacity: 0; scale: .96; }
.bd-findlist { transition: opacity var(--t-quick) var(--ease-out), translate var(--t-quick) var(--ease-out); }
.bd-findlist.gs-enter { opacity: 0; translate: 0 -6px; }
.bd-findlist.gs-leave { opacity: 0; translate: 0 -4px; }
:is(.bd-findbox, .bd-findlist).gs-leave { pointer-events: none; transition-duration: var(--t-quick-out); transition-timing-function: var(--ease-away); }
:root[data-glass-motion="fade"][data-style="glass"] .bd-findbox:is(.gs-enter, .gs-leave) { scale: none; }
:root[data-glass-motion="fade"][data-style="glass"] .bd-findlist:is(.gs-enter, .gs-leave) { translate: none; }
.bd-findtop { display: flex; align-items: center; gap: 8px; padding: 7px 8px 7px 14px; border-bottom: 1px solid var(--rule); flex: none; }
.bd-findsay { flex: 1; min-width: 0; font-size: 12.5px; color: var(--bd-quiet); }
.bd-findsay.bd-findbad { color: var(--red); }
.bd-findnote { margin: 0; padding: 6px 14px; font-size: 12px; line-height: 1.35; border-bottom: 1px solid var(--rule); flex: none;
  color: var(--bd-quiet); background: color-mix(in srgb, var(--accent) 6%, transparent); }
/* All whiteboards: a switch, its knob lit when on. */
.bd-findall { display: inline-flex; align-items: center; gap: 7px; flex: none; padding: 4px 11px 4px 5px; border-radius: 999px;
  border: 1px solid var(--rule-strong); background: transparent; color: var(--ink); font: inherit; font-size: 12px; cursor: pointer;
  transition: background .15s, border-color .15s; }
.bd-findall:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.bd-findall i { position: relative; flex: none; width: 26px; height: 15px; border-radius: 999px;
  background: color-mix(in srgb, var(--ink) 22%, transparent); box-shadow: inset 0 1px 2px rgb(0 0 0 / .22); transition: background .15s; }
.bd-findall i::after { content: ""; position: absolute; top: 2px; left: 2px; width: 11px; height: 11px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 2px rgb(0 0 0 / .35); transition: translate .15s; }
.bd-findall[aria-checked="true"] { border-color: color-mix(in srgb, var(--accent) 55%, var(--rule-strong)); }
.bd-findall[aria-checked="true"] i { background: var(--accent); }
.bd-findall[aria-checked="true"] i::after { translate: 11px 0; }
.bd-findhits { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 4px 5px 6px; touch-action: pan-y; }
.bd-findhits:empty { display: none; }
.bd-findgroup { padding: 9px 9px 3px; font-size: 11.5px; font-weight: 600; color: var(--bd-quiet);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* One match: its page and kind, then the words round it. */
.bd-findhit { display: grid; grid-template-columns: auto 1fr; align-items: baseline; column-gap: 8px; row-gap: 2px; width: 100%;
  margin: 1px 0; padding: 6px 9px 7px; border: 1px solid transparent; border-radius: 11px; background: transparent;
  font: inherit; text-align: left; color: var(--ink); cursor: pointer; transition: background .12s; }
.bd-findhit:hover { background: color-mix(in srgb, var(--accent) 9%, transparent); }
.bd-findhit.bd-findon { background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent); box-shadow: inset 0 1px 0 var(--sheen-soft, rgb(255 255 255 / .3)); }
.bd-findhit.bd-findon .bd-findkind { color: var(--ink); }
.bd-findwhere { font-size: 11.5px; font-weight: 600; white-space: nowrap; max-width: 14em; overflow: hidden; text-overflow: ellipsis; }
.bd-findwhere.bd-findhere { color: var(--ink); }
.bd-findwhere.bd-findhere::before { content: ""; display: inline-block; width: 6px; height: 6px; margin: 0 5px 1px 0; border-radius: 50%;
  background: var(--accent); vertical-align: middle; }
.bd-findkind { font-size: 11.5px; color: var(--bd-quiet); }
.bd-findsnip { grid-column: 1 / -1; font-size: 13px; line-height: 1.4; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
/* The words that matched: a highlighter's yellow, dark letters on it in either theme. */
.bd-findsnip mark { padding: 0 1px; border-radius: 3px; background: #ffe066; color: #1c1f24; }
.bd-findtex { font-family: ui-monospace, Consolas, monospace; font-size: 12px; }
.bd-findwhy { color: var(--bd-quiet); font-size: 12px; }

/* On the page: a soft glow round each match, the same size on the screen
   at any zoom (--bdk is 1 ÷ the zoom; the layer is scaled with the page);
   the one gone to glows up once as it is reached. */
.bd-findmarks { left: 0; top: 0; width: 0; height: 0; --bdk: 1; }
.bd-findmark { position: absolute; box-sizing: content-box; pointer-events: none;
  margin: calc(-3px * var(--bdk)) 0 0 calc(-3px * var(--bdk)); padding: calc(3px * var(--bdk)); border-radius: calc(6px * var(--bdk));
  background: color-mix(in srgb, #ffd21f 20%, transparent);
  box-shadow: 0 0 0 calc(1.5px * var(--bdk)) color-mix(in srgb, #f0b000 70%, transparent),
              0 0 calc(12px * var(--bdk)) calc(2px * var(--bdk)) color-mix(in srgb, #ffd21f 45%, transparent); }
.bd-findmark.bd-findcur { background: color-mix(in srgb, #ffd21f 30%, transparent);
  box-shadow: 0 0 0 calc(2.5px * var(--bdk)) #f0a500,
              0 0 calc(22px * var(--bdk)) calc(5px * var(--bdk)) color-mix(in srgb, #ffc400 65%, transparent);
  animation: bd-findglow .9s ease-in-out 2; }
@keyframes bd-findglow {
  50% { box-shadow: 0 0 0 calc(3.5px * var(--bdk)) #f0a500,
                    0 0 calc(34px * var(--bdk)) calc(10px * var(--bdk)) color-mix(in srgb, #ffc400 75%, transparent); }
}
/* A match somewhere in the page's handwriting, not yet placed: the whole page glows at its edge. */
.bd-findedge { position: absolute; inset: 0; z-index: 3; pointer-events: none;
  box-shadow: inset 0 0 0 3px color-mix(in srgb, #f0b000 75%, transparent), inset 0 0 40px 8px color-mix(in srgb, #ffd21f 40%, transparent); }
/* Narrow windows (a popped-out board): the field takes the room of the
   save note and the notebook's name while open, and gives way itself (its
   words box narrows) rather than push the top bar past the window.
   On one row (7 Oct 2026, the bug hunt): the tools wrap where they must not (.bd-headacts), and with the field open
   they had wrapped onto a row of their own at a laptop's 1280-1700 px, the whole page dropping 38 px as it opened
   and jumping back as it closed - laid out into rows before anything gave way, the field never narrowed. */
.bd-head:has(.bd-findbox:not([hidden])) .bd-headacts { flex: 0 1 auto; min-width: 0; flex-wrap: nowrap; }
.bd-find:has(.bd-findbox:not([hidden])) { flex: 0 1 auto; min-width: 0; }
/* (The field is in the bar's "view" group now, 1 Oct 2026: the group gives way with it - and only it: the rest of the
   tools never do (.bd-headacts > *), nor the group's own buttons. A rule that held every child of the tools but the
   field to its size held the group too, once the field was in it - and outweighed this one: the field never gave way,
   and on one row the tools ran left over the title, 7 Oct 2026.) */
.bd-headacts > .bd-hgroup:has(.bd-findbox:not([hidden])) { flex: 0 1 auto; min-width: 0; }
.bd-headacts > .bd-hgroup:has(.bd-findbox:not([hidden])) > :not(.bd-find) { flex: none; }
.bd-findbox { min-width: 0; }
@media (max-width: 760px) {
  .bd-head:has(.bd-findbox:not([hidden])) :is(.bd-save, #bd-where) { display: none; }
  input.bd-findq { width: 130px; min-width: 44px; }
}
@media (max-width: 640px) {
  .bd-findbtn span { display: none; }
  .bd-findbtn { padding-inline: 8px; }
  .bd-findicon { display: none; }
  .bd-findbox { padding-left: 4px; }
  .bd-findcount { min-width: 0; padding: 0 4px; }
}
:root[data-motion="reduce"] .bd-findmark.bd-findcur { animation: none; }
:root[data-motion="reduce"] .bd-findnav, :root[data-motion="reduce"] .bd-findall, :root[data-motion="reduce"] .bd-findall i, :root[data-motion="reduce"] .bd-findall i::after, :root[data-motion="reduce"] .bd-findhit { transition: none; }
@media (prefers-reduced-transparency: reduce) {
  :root[data-style="glass"] .bd-findlist { background-color: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* ── Pages (round 4) ─────────────────────────────────────────────────────
   js/board/pages.js. A whiteboard's pages: a slim glass column on the side
   away from the tools, above the zoom — each page a small picture with its
   number and name, the one shown lit — or, folded, a small pill halfway
   down that side: the page before, "2/5", the page after, and +. Also a
   popped-out window's "Back in the main window" in its top bar. */
.bd-pages, .bd-pagepill, .bd-pgtoast {
  position: absolute; z-index: 4; color: var(--ink); background: var(--surface);
  border: 1px solid var(--rule-strong); box-shadow: var(--shadow-2); }
:root[data-style="glass"] :is(.bd-pages, .bd-pagepill, .bd-pgtoast) {
  background-color: var(--pane); background-image: var(--gloss-soft);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); box-shadow: var(--rim), var(--shadow-2); }
.bd-pages[hidden], .bd-pagepill[hidden], .bd-pgtoast[hidden] { display: none; }
/* Folded away or out (7 Oct 2026, the owner: "can we make sure the animations of opening menus etc are super clean"),
   by GSMotion (pages.js, render): the strip slides in from its own edge as the pill shrinks into its place, and back.
   Each pane is the glass itself, so its own opacity moving switches no blur off; on an iPad on Glass they only fade. */
:is(.bd-pages, .bd-pagepill) { transition: opacity var(--t-quick) var(--ease-out), translate var(--t-mid) var(--ease-out),
  scale var(--t-mid) var(--ease-out); }
.bd-pages:is(.gs-enter, .gs-leave) { opacity: 0; translate: 12px 0; }
.bd-stage[data-side="right"] .bd-pages:is(.gs-enter, .gs-leave) { translate: -12px 0; }
.bd-pagepill:is(.gs-enter, .gs-leave) { opacity: 0; scale: .9; }
:is(.bd-pages, .bd-pagepill).gs-leave { pointer-events: none; transition-duration: var(--t-mid-out); transition-timing-function: var(--ease-away); }
:root[data-glass-motion="fade"][data-style="glass"] :is(.bd-pages, .bd-pagepill):is(.gs-enter, .gs-leave) { translate: none; scale: none; }
:is(.bd-pages, .bd-pagepill) svg { display: block; width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round; }
.bd-pages { right: 12px; top: 12px; bottom: 58px; width: 136px; display: flex; flex-direction: column; border-radius: 18px; overflow: hidden; }
.bd-stage[data-side="right"] .bd-pages { right: auto; left: 12px; }
.bd-pghead { display: flex; align-items: center; justify-content: space-between; flex: none; padding: 6px 6px 4px 12px; }
.bd-pgtitle { font-size: 12px; font-weight: 600; }
.bd-pgtitle b { margin-left: 3px; font-weight: 500; color: color-mix(in srgb, var(--ink) 15%, var(--muted)); font-variant-numeric: tabular-nums; }
.bd-pgfold { display: grid; place-items: center; width: 26px; height: 26px; padding: 0; border: 0; border-radius: 50%;
  background: transparent; color: var(--ink); cursor: pointer; transition: background .15s; }
.bd-stage[data-side="right"] .bd-pgfold svg { transform: scaleX(-1); }   /* folds towards its own edge */
.bd-pglist { position: relative; flex: 1 1 auto; min-height: 0; margin: 0; padding: 3px 9px 4px; list-style: none;
  overflow-y: auto; overscroll-behavior: contain; touch-action: pan-y; scrollbar-width: thin; }
.bd-pg { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; margin: 0 0 9px; }
.bd-pgopen { grid-column: 1 / -1; position: relative; display: block; width: 100%; padding: 0; border: 0; border-radius: 9px;
  background: transparent; cursor: pointer; }
.bd-pgthumb { display: block; aspect-ratio: 3 / 2; overflow: hidden; border-radius: 9px; background: #fbfaf6;
  box-shadow: 0 0 0 1px var(--rule-strong), 0 1px 3px rgb(0 0 0 / .12); transition: box-shadow .15s; }
.bd-pgthumb.paper-slate { background: #1d272b; }
.bd-pgthumb.paper-glass { background: color-mix(in srgb, var(--paper) 60%, #dfeaf2); }
.bd-pgthumb img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top center; pointer-events: none; }
/* A page with nothing on it yet: dark letters on paper, light on slate. */
.bd-pgthumb em { display: grid; place-items: center; height: 100%; font-style: normal; font-size: 11px; color: #4f5963; }
.bd-pgthumb.paper-slate em { color: #c3ced3; }
.bd-pgnum { position: absolute; left: 5px; top: 5px; min-width: 19px; height: 19px; padding: 0 5px; border-radius: 999px;
  font-size: 11px; font-weight: 600; line-height: 19px; text-align: center; font-variant-numeric: tabular-nums;
  color: var(--ink); background: color-mix(in srgb, var(--surface) 92%, transparent); box-shadow: 0 0 0 1px var(--rule-strong); }
.bd-pgopen:hover .bd-pgthumb { box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 60%, var(--rule-strong)),
  0 0 0 4px color-mix(in srgb, var(--accent) 16%, transparent); }
/* The page shown: lit — a ring of the accent and a glow — its number too. */
.bd-pgon .bd-pgthumb { box-shadow: 0 0 0 2px var(--accent), 0 0 12px 1px color-mix(in srgb, var(--accent) 42%, transparent); }
.bd-pgon .bd-pgnum { background: color-mix(in srgb, var(--accent) 32%, var(--surface)); box-shadow: 0 0 0 1px var(--accent); }
.bd-pgname { min-width: 0; padding: 3px 2px 0 3px; font-size: 11.5px; line-height: 1.35; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-pgon .bd-pgname { font-weight: 600; }
.bd-pgmore { width: 26px; height: 20px; margin-top: 3px; padding: 0; border: 0; border-radius: 999px; background: transparent;
  color: var(--ink); font: inherit; font-size: 15px; line-height: 1; cursor: pointer; transition: background .15s; }
input.bd-pgrename { grid-column: 1; width: 100%; min-width: 0; height: 22px; margin: 3px 0 0; padding: 1px 5px; border-radius: 6px;
  font-size: 11.5px; color: var(--ink); background: var(--field-bg, var(--surface)); border: 1px solid var(--accent); box-shadow: none; }
/* Dragged: lifted and following the pointer; a line where it will go. */
.bd-pg.bd-pglift { z-index: 2; opacity: .94; }
.bd-pglift .bd-pgthumb { box-shadow: 0 0 0 2px var(--accent), 0 10px 22px rgb(0 0 0 / .28); }
.bd-pgmark { position: absolute; left: 9px; right: 9px; height: 3px; margin-top: -1px; border-radius: 2px; background: var(--accent);
  box-shadow: 0 0 6px color-mix(in srgb, var(--accent) 60%, transparent); pointer-events: none; }
.bd-pgmark[hidden] { display: none; }
.bd-pgadd { display: flex; align-items: center; justify-content: center; gap: 5px; flex: none; margin: 2px 9px 9px; padding: 5px 8px;
  border: 1px dashed color-mix(in srgb, var(--accent) 60%, var(--rule-strong)); border-radius: 999px; background: transparent;
  color: var(--ink); font: inherit; font-size: 12px; cursor: pointer; transition: background .15s, border-color .15s; }
.bd-pgadd svg { width: 14px; height: 14px; }
/* Folded: the pill. */
/* Bottom left (the owner: "move this to the left"), below the tools' rail
   and clear of the zoom, bottom right. */
.bd-pagepill { left: 12px; bottom: 16px; display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 5px; border-radius: 22px; }
.bd-pagepill button { display: grid; place-items: center; width: 32px; height: 30px; padding: 0; border: 0; border-radius: 999px;
  background: transparent; color: var(--ink); font: inherit; cursor: pointer; transition: background .15s; }
.bd-pagepill button:disabled { opacity: .35; cursor: default; }
.bd-pagepill .bd-pgnow { display: flex; flex-direction: column; align-items: center; height: auto; padding: 4px 0; line-height: 1.1; }
.bd-pgnow b { font-size: 14px; font-weight: 600; font-variant-numeric: tabular-nums; }
/* (The count's "/5" with 45 % ink: with 15 % it measured 3.7-4.4:1 on Glass in the dark, as drawn - 4 Oct 2026,
   tests/test_panels.py, which measures the panels while they are arranged.) */
.bd-pgnow span { font-size: 10.5px; color: color-mix(in srgb, var(--ink) 45%, var(--muted)); font-variant-numeric: tabular-nums; }
.bd-pgsep { width: 18px; height: 1px; margin: 2px 0; background: var(--rule); }
/* Lit by the light, not a new colour: the hover of every control here. */
:is(.bd-pgfold, .bd-pgmore, .bd-pgadd, .bd-pagepill button:not(:disabled), .bd-pgtoast button):hover {
  background: color-mix(in srgb, var(--accent) 14%, transparent); }
:root[data-style="glass"] :is(.bd-pgfold, .bd-pgmore, .bd-pgadd, .bd-pagepill button:not(:disabled), .bd-pgtoast button):hover {
  background-image: var(--gloss); box-shadow: var(--bevel); }
.bd-pgadd:hover { border-style: solid; }
:is(.bd-pgopen, .bd-pgfold, .bd-pgmore, .bd-pgadd, .bd-pagepill button, .bd-pgtoast button):focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A word with a button, for a moment ("Page 3 is in the bin. Put it back"). */
.bd-pgtoast { left: 50%; bottom: 58px; transform: translateX(-50%); display: flex; align-items: center; gap: 10px;
  padding: 5px 6px 5px 15px; border-radius: 999px; font-size: 13px; white-space: nowrap; }
.bd-pgtoast button { padding: 4px 12px; border: 1px solid color-mix(in srgb, var(--accent) 55%, var(--rule-strong)); border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--ink); font: inherit; font-size: 12.5px; font-weight: 500; cursor: pointer; }
/* A popped-out window's way back: the one lit button in its top bar, the
   label shortened as the window narrows (its name stays for the ear). */
.bd-hbtn.bd-popback { font-weight: 500; border-color: color-mix(in srgb, var(--accent) 60%, var(--rule-strong));
  background: color-mix(in srgb, var(--accent) 15%, transparent); }
.bd-hbtn.bd-popback:hover { background: color-mix(in srgb, var(--accent) 26%, transparent); }
:root[data-style="glass"] .bd-hbtn.bd-popback { background-image: var(--gloss-soft); box-shadow: var(--bevel); }
:root[data-style="glass"] .bd-hbtn.bd-popback:hover { background-image: var(--gloss); }
.bd-hbtn.bd-popback:disabled { opacity: .6; cursor: default; }
.bd-popback .bd-short { display: none; }
@media (max-width: 860px) {
  .bd-popback .bd-long { display: none; }
  .bd-popback .bd-short { display: inline; }
}
@media (max-width: 640px) {
  .bd-pages { width: 116px; }
  .bd-pglist { padding-inline: 7px; }
}
@media (max-width: 520px) {
  .bd-popback .bd-short { display: none; }
  .bd-hbtn.bd-popback { padding-inline: 8px; }
}
:root[data-motion="reduce"] .bd-pgthumb, :root[data-motion="reduce"] .bd-pgfold, :root[data-motion="reduce"] .bd-pgmore, :root[data-motion="reduce"] .bd-pgadd, :root[data-motion="reduce"] .bd-pagepill button { transition: none; }
@media (prefers-reduced-transparency: reduce) {
  :root[data-style="glass"] :is(.bd-pages, .bd-pagepill, .bd-pgtoast) { background-color: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* ── Drawing aids (round 4) ──────────────────────────────────────────────
   board.js (Shapes, snapping) and js/board/shapes-edit.js. The Shapes
   options: each kind a small drawing of itself in the ink chosen (--kc),
   in its group on an even grid of four columns — the group's name, then
   up to three — the one in hand lit like a chosen tool; the fill for new
   shapes on the same grid. ⋯ → Snap to the grid: Off / Gentle / Firm as
   one row. The radial menu's kinds: the one in hand lit, not only ringed.
   A selected shape's own handles show "grab" as the pointer. */
.bd-kindgrid { display: grid; grid-template-columns: auto repeat(3, 32px); gap: 3px 4px; align-items: center; }
.bd-kindgrid > i { display: block; }
.bd-kindlabel { padding-right: 6px; font-size: 11.5px; color: color-mix(in srgb, var(--ink) 30%, var(--muted)); }
.bd-kindbtn { display: grid; place-items: center; width: 32px; height: 30px; padding: 0; border: 1px solid transparent;
  border-radius: 10px; background: transparent; color: var(--ink); cursor: pointer; transition: background .15s, border-color .15s; }
.bd-kindbtn svg { width: 20px; height: 20px; overflow: visible; }
.bd-kindbtn path { fill: none; stroke: var(--kc, currentColor); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.bd-kindbtn path.bd-kx { stroke: var(--red); stroke-width: 1.6; }
.bd-kindbtn path.bd-ktint { fill: color-mix(in srgb, var(--kc, currentColor) 26%, transparent); }
.bd-kindbtn path.bd-ksolid { fill: var(--kc, currentColor); }
.bd-kindbtn:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.bd-kindbtn.on { background: color-mix(in srgb, var(--accent) 22%, transparent); border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .5); }
.bd-kindbtn.bd-dim:not(.on) { opacity: .55; }
/* Dark, over light paper the glass pane turns mid-grey: the names in full
   ink, lifted, and each drawing a shade lighter and edged in shadow, so
   they keep 4.5:1 and 3:1 there too. */
:root[data-theme="dark"] .bd-kindlabel { color: var(--ink); text-shadow: 0 1px 2px rgb(0 0 0 / .5); }
:root[data-theme="dark"] .bd-kindgrid { --kc2: color-mix(in srgb, var(--kc, currentColor) 76%, #fff); }
:root[data-theme="dark"] .bd-kindbtn svg { filter: drop-shadow(0 0 1.2px rgb(0 0 0 / .75)); }
:root[data-theme="dark"] .bd-kindbtn path { stroke: var(--kc2); }
:root[data-theme="dark"] .bd-kindbtn path.bd-ksolid { fill: var(--kc2); }
:root[data-theme="dark"] .bd-kindbtn path.bd-kx { stroke: var(--red); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .bd-kindlabel { color: var(--ink); text-shadow: 0 1px 2px rgb(0 0 0 / .5); }
  :root:not([data-theme]) .bd-kindgrid { --kc2: color-mix(in srgb, var(--kc, currentColor) 76%, #fff); }
  :root:not([data-theme]) .bd-kindbtn svg { filter: drop-shadow(0 0 1.2px rgb(0 0 0 / .75)); }
  :root:not([data-theme]) .bd-kindbtn path { stroke: var(--kc2); }
  :root:not([data-theme]) .bd-kindbtn path.bd-ksolid { fill: var(--kc2); }
  :root:not([data-theme]) .bd-kindbtn path.bd-kx { stroke: var(--red); }
}
.bd-kindbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
:root[data-style="glass"] .bd-kindbtn.on { background-image: var(--gloss-soft); }
/* (Tight enough that Firm, lit and bold, stays inside the menu's row — it ran 3 px past it.) */
.bd-snaprow { gap: 2px; }
.bd-snaprow > span { white-space: nowrap; margin-right: 2px; }
.bd-snapopt { border: 1px solid transparent; background: transparent; color: var(--ink); font: inherit; font-size: 12px;
  padding: 2px 6px; border-radius: 999px; cursor: pointer; flex: none; }
.bd-snapopt:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.bd-snapopt.on { background: color-mix(in srgb, var(--accent) 20%, transparent); border-color: color-mix(in srgb, var(--accent) 50%, transparent);
  font-weight: 600; }
.bd-snapopt:focus-visible { outline: none; box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 60%, transparent); }
.bd-ritem.bd-rkind.bd-ron { background-color: color-mix(in srgb, var(--accent) 24%, var(--surface)); }
.bd-changeitem { display: flex; align-items: center; gap: 9px; }
.bd-changeitem svg { width: 18px; height: 18px; flex: none; }
.bd-changeitem .menu-key { margin-left: auto; float: none; }
.bd-stage.bd-onhandle:not(.bd-grab, .bd-panning) #bd-over { cursor: var(--cur-grab); }
.bd-stage.bd-handling:not(.bd-grab, .bd-panning) #bd-over { cursor: var(--cur-grabbing); }
:root[data-motion="reduce"] .bd-kindbtn { transition: none; }
/* The 3D solids (4 Oct 2026, board.js solidTile and solidMenu; "should think how to implement the 3D shapes to not
   clutter menus"): one tile in the Graphs row's spare room - the solid last taken up, "3D", a small arrow that opens
   the eight - and their menu, a grid of four by two, each solid's picture over its short name. The words in --ink, as
   the kinds' names are in the dark (measured in all 44 looks: tests/test_shapes3d.py). */
.bd-kindgrid .bd-solidtile { grid-column: span 2; width: auto; display: flex; align-items: center; justify-content: center;
  gap: 3px; padding: 0 4px 0 6px; }
.bd-solidtile .bd-tile3d { font-size: 11.5px; font-weight: 600; letter-spacing: .02em; color: var(--ink); line-height: 1; }
.bd-kindbtn.bd-solidtile svg.bd-tilechev { width: 10px; height: 10px; margin-left: 1px; }
:root .bd-kindgrid .bd-solidtile .bd-tilechev path { stroke: var(--ink); stroke-width: 1.6; }
.ctxmenu.bd-solidmenu { width: 252px; }
.bd-solidgrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 3px; padding: 2px; }
.bd-kindbtn.bd-solidpick { width: auto; height: auto; min-height: 54px; padding: 6px 2px 5px; gap: 3px; align-content: center; }
.bd-kindbtn.bd-solidpick svg { width: 24px; height: 24px; }
.bd-solidpick span { font-size: 11px; line-height: 1.15; color: var(--ink); }
.bd-solidpick:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
:root[data-theme="dark"] .bd-solidgrid { --kc2: color-mix(in srgb, var(--kc, currentColor) 76%, #fff); }
:root[data-theme="dark"] .bd-solidtile .bd-tile3d { text-shadow: 0 1px 2px rgb(0 0 0 / .5); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .bd-solidgrid { --kc2: color-mix(in srgb, var(--kc, currentColor) 76%, #fff); }
  :root:not([data-theme]) .bd-solidtile .bd-tile3d { text-shadow: 0 1px 2px rgb(0 0 0 / .5); }
}
@media (pointer: coarse) { .bd-kindbtn.bd-solidpick { min-height: 58px; } }
/* A selected solid's 3D menu (board/shapes-edit.js): oblique or isometric, the 3D axes' three letters, and a word on
   the depth handle. */
.bd-axisnames { gap: 10px; }
.bd-axisfields { display: flex; gap: 4px; }
.bd-axisfields input { width: 38px; padding: 4px 6px; font: inherit; font-size: 13px; font-style: italic; text-align: center;
  color: var(--ink); background: var(--field-bg); border: 1px solid var(--rule-strong); border-radius: 7px; box-shadow: var(--field-inset); }
.bd-axisfields input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Their headings in --ink, as ⋯'s are: under the pane's sheen on Glass in the dark, .menu-head's own measured 2.9-4.0:1
   (4 Oct 2026); the note at 60% ink (30% measured 4.3-4.5:1 there). */
.menu-head.bd-headink { color: var(--ink); font-weight: 500; }
/* On Glass in the dark, their pane nearly solid (96%), as Settings' is over a whiteboard (see dialog.settings): what it
   lets through is the page's near-white paper, and under the sheen the first row's words ("View") measured 4.45-4.6:1. */
:root[data-style="glass"] :is(#bd-menu, .ctxmenu.gs-ghost):is(.bd-solidmenu, .bd-solid3d) {
  --pane: light-dark(color-mix(in srgb, var(--surface) 86%, transparent), color-mix(in srgb, var(--surface) 96%, transparent));
}
@supports not (color: light-dark(#000, #fff)) {
  :root[data-style="glass"] :is(#bd-menu, .ctxmenu.gs-ghost):is(.bd-solidmenu, .bd-solid3d) {
    --pane: var(--not-light, color-mix(in srgb, var(--surface) 86%, transparent)) var(--not-dark, color-mix(in srgb, var(--surface) 96%, transparent));
  }
}
.bd-mnote { margin: 4px 10px 6px; font-size: 12px; line-height: 1.35; color: color-mix(in srgb, var(--ink) 60%, var(--muted)); }
/* The tile lit, in the dark: its light word over a deeper lit ground (the kinds' own lit ground, mid-grey pane and all,
   left "3D" at 4.1:1 on Glass). */
:root[data-theme="dark"] .bd-kindgrid .bd-solidtile.on { background-color: color-mix(in srgb, var(--accent) 22%, rgb(0 0 0 / .42)); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .bd-kindgrid .bd-solidtile.on { background-color: color-mix(in srgb, var(--accent) 22%, rgb(0 0 0 / .42)); }
}

/* ── Auto convert (round 4) ──────────────────────────────────────────────
   Writing turned into typed text and equations as it is written
   (js/board/convert.js): the switch in the header (lit while on), the fade
   from ink to type, the "Converted · Undo" chip, and the soft dotted marks
   under words whose spelling the reading put right — in the read box and
   on the page — with the choices for each. */
/* On: lit — a deeper tint of the accent and a soft glow round it, so it reads as on at a glance. */
.bd-autobtn[aria-pressed="true"] { background-color: color-mix(in srgb, var(--accent) 30%, var(--surface));
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .45), 0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent), 0 0 9px color-mix(in srgb, var(--accent) 35%, transparent); }
:root[data-style="glass"] .bd-autobtn[aria-pressed="true"] { background-image: var(--gloss-soft);
  box-shadow: var(--bevel), 0 0 12px color-mix(in srgb, var(--accent) 45%, transparent); }
.bd-autobtn[aria-pressed="true"]:hover { background-color: color-mix(in srgb, var(--accent) 40%, var(--surface)); }
@media (max-width: 760px) {
  .bd-autolabel { display: none; }
  .bd-autobtn { padding-inline: 8px; }
}
/* The page as it was, fading off the new one (laid in the page's own layer, so it pans and zooms with it). */
.bd-morph { pointer-events: none; opacity: 1; transition: opacity .42s ease-in-out, filter .42s ease-in-out; }
.bd-morph.bd-morphgo { opacity: 0; filter: blur(1.2px); }
.bd-convchip { position: absolute; left: 50%; bottom: 58px; z-index: 5; transform: translateX(-50%); display: flex; align-items: center;
  gap: 4px; padding: 4px 5px 4px 14px; border-radius: 999px; font-size: 13px; color: var(--ink); white-space: nowrap;
  background: var(--surface); border: 1px solid var(--rule-strong); box-shadow: var(--shadow-2); animation: bd-convin var(--t-mid) var(--ease-out); }
:root[data-style="glass"] .bd-convchip { background-color: var(--pane); background-image: var(--gloss-soft);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); box-shadow: var(--rim), var(--shadow-2); }
.bd-convsay { padding-right: 4px; }
.bd-convbtn { border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--rule-strong)); background: transparent; color: var(--ink);
  font: inherit; font-size: 12.5px; padding: 3px 11px; border-radius: 999px; cursor: pointer; transition: background .15s, box-shadow .15s; }
.bd-convbtn:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
:root[data-style="glass"] .bd-convbtn { background-image: var(--gloss-soft); box-shadow: var(--bevel); }
:root[data-style="glass"] .bd-convbtn:hover { box-shadow: var(--bevel), 0 0 9px color-mix(in srgb, var(--accent) 38%, transparent); }
.bd-convbtn:active, :root[data-style="glass"] .bd-convbtn:active { background-image: none; box-shadow: inset 0 1px 2px rgb(10 60 80 / .22); }
@keyframes bd-convin { from { opacity: 0; translate: 0 6px; } }
/* Going (7 Oct 2026, the owner: "can we make sure the animations of opening menus etc are super clean"): back down the
   way it came, quicker, and only then taken off the page (convert.js and pastelinks.js: GSMotion.away) - it was simply
   gone. The chips are the glass themselves; on an iPad on Glass they come and go by fading alone. (Moved up out of the
   way of the pages' word, it glides there: convert.js, placeChip.) */
@keyframes bd-chipout { to { opacity: 0; translate: 0 6px; } }
:is(.bd-convchip, .bd-plchip).gs-leave { pointer-events: none; animation: bd-chipout var(--t-quick-out) var(--ease-away) forwards; }
:root[data-glass-motion="fade"][data-style="glass"] :is(.bd-convchip, .bd-plchip, .bd-plask) { animation-name: gs-fadein; }
:root[data-glass-motion="fade"][data-style="glass"] :is(.bd-convchip, .bd-plchip, .bd-plask).gs-leave { animation-name: gs-fadeout; }
/* In the read box: a layer laid exactly over the text box (convert.js copies its
   font and padding) that draws only the marks — its own letters are clear. */
.bd-readmarks { position: absolute; z-index: 1; margin: 0; box-sizing: border-box; overflow: hidden; pointer-events: none;
  color: transparent; background: transparent; border-style: solid; border-color: transparent;
  white-space: pre-wrap; overflow-wrap: break-word; word-break: normal; }
.bd-mu { text-decoration: underline 1.5px color-mix(in srgb, var(--ink) 75%, transparent); text-underline-offset: 3px; }
.bd-mfix { text-decoration: underline dotted 2px var(--accent); text-underline-offset: 4px; text-decoration-skip-ink: none; }
.bd-mu .bd-mfix { text-underline-offset: 6px; }
.bd-fixes { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; margin: -2px 0 8px; }
.bd-fixword, .bd-fixopt { border: 1px solid var(--rule-strong); background: transparent; color: var(--ink); font: inherit;
  font-size: 12.5px; padding: 2px 9px; border-radius: 999px; cursor: pointer; transition: background .15s, box-shadow .15s; }
.bd-fixword { text-decoration: underline dotted 1.5px var(--accent); text-underline-offset: 3px; }
.bd-fixopt small { font-size: 11px; color: color-mix(in srgb, var(--ink) 40%, var(--muted)); }
.bd-fixword:hover, .bd-fixopt:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.bd-fixopt.bd-fixon { font-weight: 500; background: color-mix(in srgb, var(--accent) 18%, transparent);
  border-color: color-mix(in srgb, var(--accent) 55%, transparent); box-shadow: inset 0 1px 0 rgb(255 255 255 / .5); }
.bd-fixlink { border: 0; background: transparent; color: var(--ink); font: inherit; font-size: 12.5px; padding: 3px 7px;
  border-radius: 999px; cursor: pointer; text-decoration: underline 1px color-mix(in srgb, var(--accent) 65%, transparent); text-underline-offset: 3px; }
.bd-fixlink:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.bd-fixrow { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; margin-bottom: 7px; }
.bd-fixwas { font-size: 12.5px; color: color-mix(in srgb, var(--ink) 40%, var(--muted)); padding-right: 2px; }
.bd-fixwas b { color: var(--ink); font-weight: 600; }
.bd-fixopts { display: flex; flex-wrap: wrap; gap: 5px; }
/* On the page: a button over each marked word, while its chip shows or the text is selected — for the
   keyboard only. It takes no presses, so a drag, a double-click, a right-click or the pen on the word
   does what it does anywhere on the page; a tap on the word shows its choices (convert.js). */
.bd-fixhit { pointer-events: none; padding: 0; margin: 0; border: 0; border-radius: 3px; background: transparent; }
.bd-fixhit:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* The choices for words on the page: a small glass pane under the word. */
.bd-fixpop { position: absolute; z-index: 7; width: max-content; max-width: min(360px, calc(100% - 20px)); padding: 10px 12px 8px;
  border-radius: 16px; color: var(--ink); background: var(--surface); border: 1px solid var(--rule-strong); box-shadow: var(--shadow-2); }
:root[data-style="glass"] .bd-fixpop { background-color: var(--pane); background-image: var(--gloss-soft);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); box-shadow: var(--rim), var(--shadow-2); }
.bd-fixhead { font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: color-mix(in srgb, var(--ink) 40%, var(--muted)); margin-bottom: 7px; }
.bd-fixfoot { display: flex; justify-content: flex-end; gap: 4px; border-top: 1px solid var(--rule); padding-top: 6px; }
:root[data-motion="reduce"] .bd-morph { display: none; }
:root[data-motion="reduce"] .bd-convchip { animation: none; }
:root[data-motion="reduce"] .bd-convbtn, :root[data-motion="reduce"] .bd-fixword, :root[data-motion="reduce"] .bd-fixopt { transition: none; }
@media (prefers-reduced-transparency: reduce) {
  :root[data-style="glass"] :is(.bd-convchip, .bd-fixpop) { background-color: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* ── A pasted link, asked about (board/pastelinks.js, 4 Oct 2026) ──────────
   "Protect against accidental pasting of links": a small pane at the paste point, "Insert video…"'s glass (.bd-vask),
   with the address shown short and the choices as lozenges, the first lit. Then "Pasted as a link card · Undo", the
   pages' word-with-a-button (.bd-pgtoast) in the same place. Measured as drawn (tests/test_paste_links.py): the
   heading in small capitals at 60% ink was 3.4-4.1:1 on Glass, the path at 70% 4.35:1 (Ember, the dark), and the lit
   choice, Glass's translucent lozenge over the pane, 4.41:1 (Blossom) - so the heading has the ink itself, the path
   85%, and the lit choice is solid under a lighter gloss, as the nav's current page is. */
.bd-plask { position: absolute; z-index: 8; width: max-content; min-width: 250px; max-width: min(400px, calc(100% - 16px));
  box-sizing: border-box; padding: 11px 12px 11px 14px; border-radius: 18px; color: var(--ink); background: var(--surface);
  border: 1px solid var(--rule-strong); box-shadow: var(--shadow-2); animation: bd-plin var(--t-quick) var(--ease-out); }
:root[data-style="glass"] .bd-plask { background-color: var(--pane); background-image: var(--gloss-soft);
  /* The gloss as tall as one link's pane, whatever this one's height: stretched over several links' its bright top
     came down under the heading, and in the dark that was 4.3:1 there (4 Oct 2026). */
  background-size: 100% 132px; background-repeat: no-repeat;
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); box-shadow: var(--rim), var(--shadow-2); }
.bd-plhead { margin: 0 0 7px; font-size: 10.5px; font-weight: 600; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink); }
.bd-pltitle { margin: 0 0 3px; font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-pllist { display: grid; gap: 3px; }
.bd-pladdr { display: flex; align-items: center; gap: 7px; min-width: 0; font-size: 13.5px; line-height: 1.4; }
.bd-pladdr svg { flex: none; width: 15px; height: 15px; fill: none; stroke: var(--accent); stroke-width: 1.7; stroke-linecap: round;
  stroke-linejoin: round; }
.bd-pladdr svg .bd-plfill { fill: var(--accent); stroke: none; }
.bd-plurl { display: flex; min-width: 0; }
/* The site gives way last: the path shrinks long before it does. */
.bd-plhost { flex: 0 1 auto; min-width: 2.5em; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-plpath { flex: 0 1000 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: color-mix(in srgb, var(--ink) 85%, var(--muted)); }
.bd-plmore, .bd-plwhy { font-size: 12px; color: color-mix(in srgb, var(--ink) 85%, var(--muted)); }
.bd-plmore { padding-left: 22px; }
.bd-plwhy { margin-top: 6px; }
/* (The page's label is small capitals for a field's name; this one is a sentence beside its box.) */
label.bd-pltrack { display: flex; align-items: center; gap: 7px; margin: 9px 0 0; font-size: 12.5px; font-weight: 400;
  letter-spacing: normal; text-transform: none; color: var(--ink); cursor: pointer; }
.bd-pltrack input { flex: none; width: 15px; height: 15px; margin: 0; accent-color: var(--accent); }
.bd-pltrack small { font-size: 11.5px; color: color-mix(in srgb, var(--ink) 85%, var(--muted)); }
.bd-placts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 11px; }
.bd-placts [data-pl="cancel"] { margin-left: auto; }
:root[data-style="glass"] .bd-placts .btn:not(.ghost) { background-color: var(--btn-bg); border-color: var(--btn-bg);
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%); }
.bd-placts .btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (pointer: coarse) { .bd-placts .btn { min-height: 40px; } }
@keyframes bd-plin { from { opacity: 0; translate: 0 -4px; } }
/* Going: back up the way it came (7 Oct 2026; pastelinks.js, close: GSMotion.away). */
@keyframes bd-plout { to { opacity: 0; translate: 0 -4px; } }
.bd-plask.gs-leave { pointer-events: none; animation: bd-plout var(--t-quick-out) var(--ease-away) forwards; }
.bd-plchip { position: absolute; left: 50%; bottom: 58px; z-index: 5; transform: translateX(-50%); display: flex; align-items: center;
  gap: 10px; padding: 5px 6px 5px 15px; border-radius: 999px; font-size: 13px; color: var(--ink); white-space: nowrap;
  background: var(--surface); border: 1px solid var(--rule-strong); box-shadow: var(--shadow-2); animation: bd-convin var(--t-mid) var(--ease-out); }
:root[data-style="glass"] .bd-plchip { background-color: var(--pane); background-image: var(--gloss-soft);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); box-shadow: var(--rim), var(--shadow-2); }
.bd-plchip button { padding: 4px 12px; border: 1px solid color-mix(in srgb, var(--accent) 55%, var(--rule-strong)); border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--ink); font: inherit; font-size: 12.5px; font-weight: 500;
  cursor: pointer; }
.bd-plchip button:hover { background: color-mix(in srgb, var(--accent) 22%, transparent); }
:root[data-style="glass"] .bd-plchip button:hover { background-image: var(--gloss); box-shadow: var(--bevel); }
.bd-plchip button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (pointer: coarse) { .bd-plchip button { min-height: 40px; } }
:root[data-motion="reduce"] :is(.bd-plask, .bd-plchip) { animation: none; }
@media (prefers-reduced-transparency: reduce) {
  :root[data-style="glass"] :is(.bd-plask, .bd-plchip) { background-color: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* ── The equation editor (round 4) ───────────────────────────────────────
   js/board/mathedit.js. A glass pane by the equation being edited: the
   equation as it looks in a sunken well (the visual field, or a picture of
   it offline), five tabs of symbols as raised keys big enough for a pen's
   tap, its LaTeX beneath, then the actions. In a short window the keys
   scroll rather than the pane run off the board. A reading as maths in the
   read box gets an Edit button on its picture. */
.bd-eqed { position: absolute; left: 0; top: 0; z-index: 7; display: flex; flex-direction: column; gap: 8px;
  width: min(560px, calc(100% - 16px)); max-height: calc(100% - 16px); padding: 10px 12px 12px; border-radius: 18px;
  background: var(--surface); border: 1px solid var(--rule-strong); box-shadow: var(--shadow-3); color: var(--ink);
  font-size: 13px; container-type: inline-size;
  /* Its quieter words: nearer the ink than the app's usual lift — measured on
     the sheen of dark glass that one read 3.8:1 (2.6:1 over white paper). */
  --bd-eqquiet: light-dark(color-mix(in srgb, var(--l-ink) 30%, var(--l-muted)), color-mix(in srgb, var(--d-ink) 80%, var(--d-muted))); }
@supports not (color: light-dark(#000, #fff)) {
  .bd-eqed {
    --bd-eqquiet: var(--not-light, color-mix(in srgb, var(--l-ink) 30%, var(--l-muted))) var(--not-dark, color-mix(in srgb, var(--d-ink) 80%, var(--d-muted)));
  }
}
.bd-eqed[hidden] { display: none; }
/* Fuller than the other panes (95%, not 86%): in the dark theme over white
   paper, 86% let the page wash its words out (title 4.4:1, note 2.6:1). */
:root[data-style="glass"] .bd-eqed { background-color: color-mix(in srgb, var(--surface) 95%, transparent); background-image: var(--gloss-soft);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); box-shadow: var(--rim), var(--shadow-3); }
/* Up into place by its equation, and back down (7 Oct 2026, the owner: "can we make sure the animations of opening
   menus etc are super clean"): GSMotion (mathedit.js), where a keyframe on :not([hidden]) brought it in and nothing
   took it away. Placed by its transform (mathedit.js, place), which `translate` adds to. The pane is the glass itself,
   so its own opacity moving switches no blur off; on an iPad on Glass it only fades. */
.bd-eqed { transition: opacity var(--t-quick) var(--ease-out), translate var(--t-mid) var(--ease-out); }
.bd-eqed:is(.gs-enter, .gs-leave) { opacity: 0; translate: 0 6px; }
.bd-eqed.gs-leave { pointer-events: none; transition-duration: var(--t-mid-out); transition-timing-function: var(--ease-away); }
:root[data-glass-motion="fade"][data-style="glass"] .bd-eqed:is(.gs-enter, .gs-leave) { translate: none; }
.bd-eqhead { display: flex; align-items: center; gap: 10px; min-height: 30px; flex: none; }
.bd-eqtitle { font-weight: 600; font-size: 13.5px; white-space: nowrap; }
.bd-eqnote { flex: 1; min-width: 0; font-size: 12px; color: var(--bd-eqquiet);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-eqnote.bd-eqsays { color: var(--ink); }            /* something to say (a mistake, offline), not just the keys */
.bd-eqx { flex: none; margin-left: auto; width: 32px; height: 32px; padding: 0; border: 0; border-radius: 50%; background: transparent;
  color: var(--ink); font: inherit; font-size: 20px; line-height: 1; cursor: pointer; }
.bd-eqx:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
/* The well: sunken, lit on its lower inner edge. The field's own keyboard
   and menu buttons are hidden — the palette is the Board's keyboard. */
.bd-eqfield { flex: none; }
/* The stage's user-select: none reaches the field too, and MathLive takes that as "no pointer selection": a click
   in it left the caret at the end ("should be able to click around in the top box", 29 Sep 2026). */
.bd-eqfield math-field { -webkit-user-select: text; user-select: text; }
.bd-eqfield math-field, .bd-eqshow { display: block; width: 100%; box-sizing: border-box; min-height: 64px; border-radius: 12px;
  border: 1px solid var(--rule-strong); background: var(--field-bg); color: var(--ink); box-shadow: var(--field-inset); }
.bd-eqfield math-field { padding: 8px 12px; font-size: 26px; max-height: 190px; overflow: auto;
  --caret-color: var(--accent); --selection-color: var(--ink);
  --selection-background-color: color-mix(in srgb, var(--accent) 26%, transparent);
  --contains-highlight-background-color: color-mix(in srgb, var(--accent) 5%, transparent);
  --placeholder-color: color-mix(in srgb, var(--accent) 70%, var(--ink)); --smart-fence-color: var(--muted); }
.bd-eqfield math-field:focus-within { outline: none; border-color: var(--accent);
  box-shadow: var(--field-inset), 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }
.bd-eqfield math-field::part(virtual-keyboard-toggle), .bd-eqfield math-field::part(menu-toggle) { display: none; }
.bd-eqfield.bd-eqvisual .bd-eqshow { display: none; }
.bd-eqshow { display: grid; place-items: center; padding: 10px; max-height: 150px; overflow: auto; text-align: center; font-size: 24px; }
.bd-eqshow svg { max-width: 100%; height: auto; color: var(--ink); }
.bd-eqfaint { font-size: 12.5px; color: var(--bd-eqquiet); }
.bd-eqtabs { display: flex; gap: 2px; padding: 3px; flex: none; border-radius: 999px; background: color-mix(in srgb, var(--ink) 6%, transparent);
  overflow-x: auto; scrollbar-width: none; }
.bd-eqtabs::-webkit-scrollbar { display: none; }
.bd-eqtab { flex: 1 0 auto; min-height: 36px; padding: 6px 12px; border: 1px solid transparent; border-radius: 999px; background: transparent;
  color: var(--ink); font: inherit; font-size: 12.5px; cursor: pointer; white-space: nowrap; }
.bd-eqtab:hover { background: color-mix(in srgb, var(--surface) 60%, transparent); }
.bd-eqtab[aria-selected="true"] { background: var(--surface); font-weight: 600; box-shadow: 0 1px 2px rgb(0 0 0 / .16); }
:root[data-style="glass"] .bd-eqtab[aria-selected="true"] { background-image: var(--gloss); box-shadow: var(--bevel), 0 1px 2px rgb(0 0 0 / .12); }
.bd-eqtab:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* The keys: raised and glossy, as things to press are; lit by the accent
   under the pointer, pushed in while pressed. */
.bd-eqpal { display: grid; grid-template-columns: repeat(auto-fill, minmax(50px, 1fr)); gap: 5px; flex: 0 1 auto;
  min-height: 50px; max-height: 204px; overflow-y: auto; padding: 2px; overscroll-behavior: contain; }
.bd-eqpal[data-tab="matrices"] { grid-template-columns: repeat(auto-fill, minmax(58px, 1fr)); }
.bd-eqkey { display: grid; place-items: center; min-height: 44px; padding: 3px; border-radius: 12px; cursor: pointer; color: var(--ink);
  font: inherit; font-size: 16px; border: 1px solid color-mix(in srgb, var(--ink) 14%, transparent);
  background-color: color-mix(in srgb, var(--surface) 82%, transparent);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .55), 0 1px 2px rgb(0 0 0 / .1); transition: box-shadow .12s, border-color .12s; }
:root[data-style="glass"] .bd-eqkey { background-image: var(--gloss); box-shadow: var(--bevel), 0 1px 2px rgb(0 0 0 / .1); }
.bd-eqkey:hover { border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .75), 0 0 0 2px color-mix(in srgb, var(--accent) 22%, transparent), 0 1px 3px rgb(0 0 0 / .12); }
:root[data-style="glass"] .bd-eqkey:hover { box-shadow: var(--bevel), 0 0 12px 1px color-mix(in srgb, var(--accent) 38%, transparent); }
.bd-eqkey:active, :root[data-style="glass"] .bd-eqkey:active { background-image: none; box-shadow: inset 0 2px 4px rgb(10 60 80 / .22); }
.bd-eqkey:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.bd-eqkey:disabled { opacity: .4; cursor: default; }
.bd-eqkey svg { display: block; max-width: 44px; max-height: 34px; overflow: visible; }
.bd-eqglyph { font-size: 17px; line-height: 1; }
.bd-eqword { font-size: 12px; font-weight: 600; line-height: 1.1; }
.bd-eqsrc { display: flex; align-items: flex-start; gap: 8px; flex: none; }
.bd-eqsrcname { flex: none; padding-top: 9px; font-size: 11.5px; color: var(--bd-eqquiet); }
.bd-eqed textarea.bd-eqtex { flex: 1; min-width: 0; min-height: 36px; height: 36px; margin: 0; padding: 7px 10px; resize: none; overflow-y: auto;
  font: 13px/1.5 ui-monospace, Consolas, monospace; letter-spacing: 0; }
.bd-eqacts { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; flex: none; }
.bd-eqacts .btn { min-height: 38px; padding: 8px 16px; font-size: 11.5px; }
.bd-eqacts .btn[hidden] { display: none; }
/* The glass button's 68% fill was measured over the aurora; over this light
   pane (and a dark one) its label fell to 4.36:1 — so fuller here. */
:root[data-style="glass"] .bd-eqacts .btn:not(.ghost) { background-color: color-mix(in srgb, var(--btn-bg) 90%, transparent);
  border-color: color-mix(in srgb, var(--btn-bg) 90%, transparent); }
@container (max-width: 500px) { .bd-eqacts .btn { padding: 8px 12px; } }
/* ∑ Equation in the Text tool's options. */
.bd-eqnew { border: 1px solid var(--rule-strong); background: transparent; color: var(--ink); font: inherit; font-size: 12.5px;
  min-height: 30px; padding: 5px 12px; border-radius: 999px; cursor: pointer; }
.bd-eqnew:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
:root[data-style="glass"] .bd-eqnew { background-image: var(--gloss); box-shadow: var(--bevel); }
/* The read box: Edit on the picture of a reading as maths (the picture itself opens the editor too). */
.bd-eqwrap { position: relative; }
.bd-read .bd-mathpreview.bd-eqtap { cursor: pointer; }
.bd-read .bd-mathpreview.bd-eqtap:hover { border-color: color-mix(in srgb, var(--accent) 50%, var(--rule)); }
.bd-eqpencil { position: absolute; top: 5px; right: 5px; min-height: 26px; padding: 3px 9px; border-radius: 999px; font: inherit; font-size: 12px;
  cursor: pointer; color: var(--ink); border: 1px solid var(--rule-strong); background: color-mix(in srgb, var(--surface) 88%, transparent);
  box-shadow: 0 1px 2px rgb(0 0 0 / .12); }
:root[data-style="glass"] .bd-eqpencil { background-image: var(--gloss); box-shadow: var(--bevel), 0 1px 2px rgb(0 0 0 / .12); }
.bd-eqpencil:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--rule-strong)); }
.bd-read.bd-eqaway { visibility: hidden; }            /* out of sight while the editor puts its reading right */
:root[data-motion="reduce"] .bd-eqkey { transition: none; }
@media (prefers-reduced-transparency: reduce) {
  :root[data-style="glass"] .bd-eqed { background-color: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* ── Brushes and opacity (round 4) ──────────────────────────────────────
   The pen's options open with its brushes: the three nibs, then Pencil,
   Watercolour, Calligraphy, Chalk and Neon, each a short stroke of itself
   on a chip of the paper it will be written on (board/brushes.js draws
   them). The chosen one is lit from within, and its name heads the grid.
   Then how strongly the pen — or the highlighter — lays its ink on: a
   slider. */
.bd-brushes { display: flex; flex-direction: column; gap: 5px; }
.bd-brushhead { display: flex; align-items: baseline; gap: 4px; }
.bd-brushhead .bd-plabel { padding: 0 2px; }
.bd-brushname { font-size: 12.5px; font-weight: 600; color: var(--ink); }
.bd-brushgrid { display: grid; grid-template-columns: repeat(4, 50px); gap: 4px; }
.bd-brush { display: grid; place-items: center; width: 50px; height: 32px; padding: 0; margin: 0; cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--ink) 16%, transparent); border-radius: 12px; background: transparent;
  transition: background .15s, border-color .15s, box-shadow .15s; }
.bd-brush img { display: block; width: 44px; height: 22px; border-radius: 8px; pointer-events: none; }
.bd-brush:hover { background: color-mix(in srgb, var(--accent) 14%, transparent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.bd-brush.on { background: color-mix(in srgb, var(--accent) 26%, transparent); border-color: color-mix(in srgb, var(--accent) 75%, transparent);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .5), 0 0 0 1px color-mix(in srgb, var(--accent) 50%, transparent); }
:root[data-style="glass"] .bd-brush.on { background-image: var(--gloss-soft); }
.bd-brush:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.bd-opacity { display: grid; grid-template-columns: auto minmax(0, 1fr) 2.9em; align-items: center; gap: 6px; width: 100%; }
.bd-opacity .bd-plabel { padding: 0 2px; }
.bd-opacity output { font-size: 12px; font-variant-numeric: tabular-nums; text-align: right; color: var(--ink); }
/* (The same bubble on a groove is the History panel's slider across a page's versions: board/history.js, lane L14; and
   since 23 Oct 2026 a text's size in the font and size pane, .bd-fszsize.) */
:is(.bd-opacity, .hs-scrub, .bd-fszsize) input[type="range"] {
  -webkit-appearance: none; appearance: none; width: 100%; min-width: 0; height: 22px; margin: 0;
  background: transparent; cursor: pointer; --f: 1; --thumb: 16px;
  /* The app's text-box look (border, padding, inset shadow) is not for this. */
  border: 0; padding: 0; border-radius: 0; box-shadow: none; }
.hs-scrub input[type="range"] { height: 30px; --thumb: 20px; }
:is(.bd-opacity, .hs-scrub, .bd-fszsize) input[type="range"]:focus { border: 0; box-shadow: none; }
:is(.bd-opacity, .hs-scrub, .bd-fszsize) input[type="range"]::-webkit-slider-runnable-track {
  height: 6px; border-radius: 999px;
  background: linear-gradient(90deg, var(--accent) 0 calc(var(--thumb) / 2 + (100% - var(--thumb)) * var(--f)),
                              color-mix(in srgb, var(--rule-strong) 70%, transparent) 0);
  box-shadow: inset 0 1px 2px rgb(0 0 0 / .12); }
:is(.bd-opacity, .hs-scrub, .bd-fszsize) input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; width: var(--thumb); height: var(--thumb); margin-top: calc((6px - var(--thumb)) / 2);
  border-radius: 50%; border: 1px solid color-mix(in srgb, var(--accent) 75%, #000);
  background: radial-gradient(circle at 50% 30%, #fff 0 22%, transparent 60%), var(--accent);
  box-shadow: 0 1px 3px rgb(0 0 0 / .28), inset 0 1px 0 rgb(255 255 255 / .6); transition: transform .12s; }
:is(.bd-opacity, .hs-scrub, .bd-fszsize) input[type="range"]:hover::-webkit-slider-thumb { transform: scale(1.08); }
:is(.bd-opacity, .hs-scrub, .bd-fszsize) input[type="range"]:focus-visible { outline: none; }
:is(.bd-opacity, .hs-scrub, .bd-fszsize) input[type="range"]:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent), 0 1px 3px rgb(0 0 0 / .28); }
.hs-scrub input[type="range"]:disabled { opacity: .45; cursor: default; }
/* The pen's recent settings: a brush's shows its picture; each at its strength. */
.bd-recentpen svg, .bd-recentimg { opacity: var(--op, 1); }
.bd-recentimg { display: block; width: 30px; height: 15px; border-radius: 5px; pointer-events: none; }
:root[data-motion="reduce"] .bd-brush { transition: none; }
:root[data-motion="reduce"] :is(.bd-opacity, .hs-scrub, .bd-fszsize) input[type="range"]::-webkit-slider-thumb { transition: none; }

/* ── Videos (round 4) ────────────────────────────────────────────────────
   A YouTube video on the page (board/video.js). Its player sits in a layer
   of its own beneath the page's canvas and is seen through the hole the
   video cuts in the page, so writing over it stays in sight; the layer is
   lifted above the page (bd-vup) only while the mouse uses YouTube's own
   controls. The bar along its foot (its top until 14 Oct 2026) is smoked
   glass — white on a dark tint reads on any picture, in either theme and on
   every paper — and fades while the video plays until a pointer comes near. The pane that asks for
   a link is the Board's own glass. */
.bd-vidlayer.bd-vup { z-index: 1; }
.bd-video { overflow: hidden; background: #000; }
.bd-video > * { position: absolute; left: 0; top: 0; }
.bd-vposter { width: 100%; height: 100%; object-fit: cover; pointer-events: none; -webkit-user-select: none; user-select: none; }
.bd-vframe { display: block; border: 0; margin: 0; transform-origin: 0 0; background: #000; pointer-events: none; }
.bd-video.bd-vraised .bd-vframe { pointer-events: auto; }
.bd-vveil { width: 100%; height: 100%; pointer-events: none; }
.bd-vveil[hidden] { display: none; }
.bd-verr { width: 100%; height: 100%; box-sizing: border-box; display: grid; place-items: center; padding: 6%; text-align: center;
  font: 500 14px/1.4 system-ui, "Segoe UI", sans-serif; color: #fff; background: rgb(10 14 20 / .84); }
/* The bar (14 Oct 2026, the owner: "controls should be at the bottom ... all the normal media controls"): along the
   video's foot, the scrubber across its top and the controls under it. Smoked glass as before - white words and
   icons on a dark tint of its own, so they read the same over any picture, dark or light, in every palette, theme and
   style (measured as drawn over a black and a white frame: tests/test_video_player.py) - with the Aqua sheen on
   Glass, lozenge buttons, and a glossy bubble for the scrubber's thumb. 44 px to press on a touch screen. */
.bd-vbar { display: flex; flex-direction: column; box-sizing: border-box; padding: 0 6px 3px; transform-origin: 0 0;
  pointer-events: auto; touch-action: none; user-select: none; -webkit-user-select: none; cursor: var(--cur-grab); color: #fff;
  font: 500 12.5px/1 system-ui, "Segoe UI", sans-serif; outline: none;
  background: linear-gradient(180deg, rgb(8 12 18 / .8), rgb(8 12 18 / .9));
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .16); transition: opacity .25s; }
/* On glass, the Aqua hard-stop sheen across its top half. */
:root[data-style="glass"] .bd-vbar {
  background: linear-gradient(180deg, rgb(255 255 255 / .15) 0, rgb(255 255 255 / .04) 48%, transparent 48.5%),
              linear-gradient(180deg, rgb(8 12 18 / .8), rgb(8 12 18 / .9));
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.bd-vbar:focus-visible { box-shadow: inset 0 0 0 2px #fff; }
.bd-vbar[hidden] { display: none; }
.bd-vbar.bd-vquiet { opacity: 0; pointer-events: none; }
.bd-vbar.bd-vdragging { cursor: var(--cur-grabbing); }
.bd-vbar.bd-vlocked { cursor: default; }
.bd-vrow { display: flex; align-items: center; gap: 2px; height: 36px; min-width: 0; }
.bd-vbtn { display: inline-flex; align-items: center; justify-content: center; gap: 5px; flex: none; height: 30px; margin: 0;
  padding: 0 9px; border: 1px solid transparent; border-radius: 999px; background: transparent; color: #fff; font: inherit;
  font-size: 12.5px; font-weight: 500; line-height: 1; white-space: nowrap; cursor: pointer; touch-action: manipulation;
  transition: background .15s, border-color .15s; }
.bd-vbtn svg { width: 18px; height: 18px; flex: none; }
.bd-vbtn:hover { background: rgb(255 255 255 / .16); border-color: rgb(255 255 255 / .24); }
.bd-vbtn:active { background: rgb(255 255 255 / .26); }
.bd-vbtn:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }
.bd-vbtn[hidden] { display: none; }
.bd-vicon { width: 32px; padding: 0; }
.bd-vplay svg { width: 20px; height: 20px; }
.bd-vspeed { min-width: 40px; padding: 0 8px; font-variant-numeric: tabular-nums; font-weight: 600; }
.bd-vbtn[aria-pressed="true"] { background: rgb(255 255 255 / .2); border-color: rgb(255 255 255 / .34); }
.bd-vtitle { flex: 1 1 auto; min-width: 0; padding: 0 6px; font-size: 13px; font-weight: 600; line-height: 36px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-vtime { flex: none; padding: 0 6px; font-size: 12.5px; font-variant-numeric: tabular-nums; color: #fff; white-space: nowrap; }
.bd-vtime:empty { display: none; }
/* The scrubber: a track (what has loaded, lighter; what has played, the aqua), and its thumb - a bubble lit from
   above - which swells while it is held. Taller to press than it looks. */
.bd-vseek { position: relative; flex: none; height: 16px; margin: 0 7px; cursor: pointer; touch-action: none; outline: none; }
.bd-vseek.bd-vnolen { cursor: default; }
.bd-vseek:focus-visible { box-shadow: 0 0 0 2px #fff; border-radius: 6px; }
.bd-vtrack { position: absolute; left: 0; right: 0; top: 50%; height: 4px; margin-top: -2px; border-radius: 999px; overflow: hidden;
  background: rgb(255 255 255 / .3); transition: height .12s, margin-top .12s; }
.bd-vseek:hover .bd-vtrack, .bd-vseek.bd-vgrab .bd-vtrack { height: 6px; margin-top: -3px; }
.bd-vbuf, .bd-vdone { position: absolute; left: 0; top: 0; bottom: 0; width: 0; }
.bd-vbuf { background: rgb(255 255 255 / .42); }
.bd-vdone { background: linear-gradient(180deg, #a8e6ff, #4cc2ff 55%, #2aa3e8); }
.bd-vthumb { position: absolute; top: 50%; left: 0; width: 13px; height: 13px; margin: -6.5px 0 0 -6.5px; border-radius: 50%;
  background: linear-gradient(180deg, rgb(255 255 255 / .95) 0, rgb(255 255 255 / .7) 46%, transparent 47%),
              radial-gradient(circle at 50% 75%, #d6f3ff, #4cc2ff 70%, #2490d6);
  box-shadow: 0 0 0 1px rgb(0 0 0 / .5), 0 1px 3px rgb(0 0 0 / .5); transition: transform .12s; pointer-events: none; }
.bd-vseek.bd-vnolen .bd-vthumb { display: none; }
.bd-vseek:hover .bd-vthumb { transform: scale(1.15); }
.bd-vseek.bd-vgrab .bd-vthumb { transform: scale(1.5); }
.bd-vtip { position: absolute; bottom: 100%; left: 0; transform: translateX(-50%); margin-bottom: 4px; padding: 4px 8px;
  border-radius: 999px; color: #fff; font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap;
  pointer-events: none; background: rgb(8 12 18 / .92); box-shadow: inset 0 1px 0 rgb(255 255 255 / .18), 0 2px 8px rgb(0 0 0 / .35); }
.bd-vtip[hidden] { display: none; }
/* The volume: the speaker, and its slider beside it - on hover, on the keys, or (a finger, the pen) opened by a tap. */
.bd-vvol { display: inline-flex; align-items: center; flex: none; }
.bd-vvolume { width: 0; height: 30px; margin: 0; padding: 0; opacity: 0; cursor: pointer; touch-action: none; background: transparent;
  appearance: none; -webkit-appearance: none; transition: width .15s, opacity .15s, margin .15s; }
.bd-vvol:hover .bd-vvolume, .bd-vvol:focus-within .bd-vvolume, .bd-vvol.bd-vvolopen .bd-vvolume { width: 76px; margin: 0 6px 0 2px; opacity: 1; }
.bd-vvolume::-webkit-slider-runnable-track { height: 4px; border-radius: 999px; background: rgb(255 255 255 / .4); }
.bd-vvolume::-moz-range-track { height: 4px; border-radius: 999px; background: rgb(255 255 255 / .4); }
.bd-vvolume::-moz-range-progress { height: 4px; border-radius: 999px; background: #4cc2ff; }
.bd-vvolume::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 13px; height: 13px; margin-top: -4.5px; border: 0;
  border-radius: 50%; background: linear-gradient(180deg, rgb(255 255 255 / .95) 0, rgb(255 255 255 / .7) 46%, transparent 47%),
  radial-gradient(circle at 50% 75%, #d6f3ff, #4cc2ff 70%, #2490d6); box-shadow: 0 0 0 1px rgb(0 0 0 / .5); }
.bd-vvolume::-moz-range-thumb { width: 13px; height: 13px; border: 0; border-radius: 50%;
  background: radial-gradient(circle at 50% 75%, #d6f3ff, #4cc2ff 70%, #2490d6); box-shadow: 0 0 0 1px rgb(0 0 0 / .5); }
.bd-vvolume:focus-visible { outline: 2px solid #fff; outline-offset: 2px; border-radius: 4px; }
/* Narrower, the words go from the buttons first, then the title and the time's length, then what the keys do too. */
.bd-vmid :is([data-v="pop"], [data-v="open"], [data-v="note"]) span { display: none; }
.bd-vmid :is([data-v="pop"], [data-v="open"], [data-v="note"]) { width: 32px; padding: 0; }
.bd-vnarrow .bd-vbtn span { display: none; }
.bd-vnarrow .bd-vbtn:not(.bd-vspeed) { width: 32px; padding: 0; }
.bd-vnarrow .bd-vtitle { padding: 0; }
.bd-vnarrow .bd-vtitle, .bd-vtiny .bd-vtitle { color: transparent; }
.bd-vtiny :is(.bd-vskip, [data-v="note"], [data-v="cc"], .bd-vtime) { display: none; }
@media (any-pointer: coarse) {
  .bd-vrow { height: 46px; gap: 0; }
  .bd-vbtn { height: 44px; min-width: 44px; }
  .bd-vicon, .bd-vmid :is([data-v="pop"], [data-v="open"], [data-v="note"]), .bd-vnarrow .bd-vbtn:not(.bd-vspeed) { width: 44px; }
  .bd-vseek { height: 26px; }
  .bd-vvolume { height: 44px; }
  .bd-vvol:hover .bd-vvolume, .bd-vvol:focus-within .bd-vvolume, .bd-vvol.bd-vvolopen .bd-vvolume { width: 96px; }
}
/* Locked in place: a padlock before its title, and no handle. */
.bd-vlocked .bd-vtitle::before { content: ''; display: inline-block; width: 11px; height: 12px; margin-right: 6px; vertical-align: -1px;
  background: currentColor; -webkit-mask: var(--bd-vlock) center / contain no-repeat; mask: var(--bd-vlock) center / contain no-repeat; }
.bd-vbar { --bd-vlock: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 24'%3E%3Crect x='2' y='10' width='18' height='13' rx='2.5'/%3E%3Cpath d='M6 10V7a5 5 0 0 1 10 0v3' fill='none' stroke='%23000' stroke-width='3'/%3E%3C/svg%3E"); }
/* Carried while it is dragged (board/video.js carry): its layer above the page, under the bars, its box moved by a
   transform alone. */
.bd-vidlayer.bd-vcarried { z-index: 1; }
.bd-video { transform-origin: 0 0; }
.bd-vveil { z-index: 1; }
/* Full screen, the video alone: its picture filling the screen, its bar along the foot. */
.bd-video.bd-vfull { pointer-events: auto; border-radius: 0 !important; background: #000; }
.bd-video.bd-vfull > .bd-vframe { width: 100% !important; height: 100% !important; transform: none !important; }
.bd-video.bd-vfull > :is(.bd-vposter, .bd-vveil) { display: none; }
.bd-video.bd-vnocursor { cursor: none; }
.bd-vbar.bd-vbarfull { position: absolute; left: 0; right: 0; top: auto; bottom: 0; z-index: 2; width: auto; padding: 0 14px 8px;
  border-radius: 0; cursor: default; }
/* Asking for a link: "Insert video…", or Edit on a video. */
.bd-vask { position: absolute; z-index: 6; width: min(340px, calc(100% - 16px)); box-sizing: border-box; padding: 12px 14px;
  border-radius: 18px; background: var(--surface); border: 1px solid var(--rule-strong); box-shadow: var(--shadow-2); }
:root[data-style="glass"] .bd-vask { background-color: var(--pane); background-image: var(--gloss-soft);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); box-shadow: var(--rim), var(--shadow-2); }
.bd-vaskhead { margin: 0 0 8px; font-size: 13.5px; font-weight: 600; color: var(--ink); }
.bd-vask input.bd-vaskurl { display: block; width: 100%; margin: 0; }
.bd-vaskwhy { margin: 6px 0 0; font-size: 12.5px; color: var(--red); }
.bd-vaskwhy:empty { display: none; }
.bd-vaskacts { display: flex; gap: 6px; margin-top: 10px; }
.bd-vask .bd-vaskfile { display: block; width: 100%; margin-top: 8px; }
/* A video file (1 Oct 2026) plays in the browser's own player, the whole of its picture in the video's frame. */
.bd-vfile { object-fit: contain; }
/* Watching a video together (board/watch.js, 1 Oct 2026). The pane over the video lets every press through to the
   page but for what is in it: "Tap to join the video", when the browser waits for a press before it plays with
   sound, and a moment's word of who did what ("Paused by Sam"). Both are the bar's smoked glass - white words on a
   dark tint, the same in every palette, theme and style, over whatever the picture is (measured as drawn:
   tests/test_watch_together.py). */
.bd-vcover { pointer-events: none; overflow: hidden; transform-origin: 0 0; }
.bd-vcover[hidden] { display: none; }
.bd-vtogether.bd-von { background: rgb(255 255 255 / .2); border-color: rgb(255 255 255 / .34); }
.bd-vmid [data-v="together"] span { display: none; }
.bd-vmid [data-v="together"] { padding: 0 6px; }
.bd-vjoin, .bd-vsaid { color: #fff; background: linear-gradient(180deg, rgb(8 12 18 / .9), rgb(8 12 18 / .8));
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .2), 0 4px 18px rgb(0 0 0 / .35); }
:root[data-style="glass"] :is(.bd-vjoin, .bd-vsaid) {
  background: linear-gradient(180deg, rgb(255 255 255 / .17) 0, rgb(255 255 255 / .05) 48%, transparent 48.5%),
              linear-gradient(180deg, rgb(8 12 18 / .9), rgb(8 12 18 / .8));
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.bd-vjoin { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); pointer-events: auto;
  display: inline-flex; align-items: center; gap: 8px; box-sizing: border-box; max-width: calc(100% - 24px); height: 46px;
  margin: 0; padding: 0 20px 0 15px; border: 1px solid rgb(255 255 255 / .32); border-radius: 999px;
  font: 600 15px/1 system-ui, "Segoe UI", sans-serif; white-space: nowrap; cursor: pointer; touch-action: manipulation; }
.bd-vjoin svg { width: 20px; height: 20px; flex: none; }
.bd-vjoin span { overflow: hidden; text-overflow: ellipsis; }
.bd-vjoin:hover { border-color: rgb(255 255 255 / .6); }
.bd-vjoin:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.bd-vjoin[hidden] { display: none; }
.bd-vsaid { position: absolute; left: 8px; top: 8px; box-sizing: border-box; max-width: calc(100% - 16px); padding: 6px 12px;
  border: 1px solid rgb(255 255 255 / .22); border-radius: 999px; font: 600 13px/1.25 system-ui, "Segoe UI", sans-serif;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bd-vsaid[hidden] { display: none; }
/* A video waiting for the owner's OK (4 Oct 2026, board/video.js "Card"; services/videos.py). Beneath the page, seen
   through its hole as a player is, so writing over it stays in sight: the bar's smoked glass, made solid, white words -
   the same in every palette, theme and style - an hourglass, and nothing of the video for anyone but the owner. The
   owner's has the poster behind a darker pane of words, and three lozenges across its foot over the page: Approve,
   the one lit (a deep green, solid under a soft sheen: white on it 6:1 and more), Remove and Watch it first, ghosts.
   Measured as drawn in all 44 looks (tests/test_video_filter.py). */
.bd-vheld { overflow: hidden; color: #fff; pointer-events: none; user-select: none; -webkit-user-select: none;
  background: linear-gradient(180deg, #1d2733, #10161d); box-shadow: inset 0 0 0 1px rgb(255 255 255 / .14); }
:root[data-style="glass"] .bd-vheld {
  background: linear-gradient(180deg, rgb(255 255 255 / .12) 0, rgb(255 255 255 / .04) 42%, transparent 42.5%),
              radial-gradient(120% 90% at 20% 0%, #24384a 0, transparent 70%), linear-gradient(180deg, #1d2733, #10161d); }
.bd-vheldin { position: absolute; left: 0; top: 0; box-sizing: border-box; transform-origin: 0 0; display: flex;
  flex-direction: column; align-items: center; justify-content: center; gap: 6px; padding: 18px; text-align: center;
  font: 400 13.5px/1.35 system-ui, "Segoe UI", sans-serif; }
.bd-vheldin strong { font-size: 16px; font-weight: 600; line-height: 1.3; }
.bd-vheldicon svg { display: block; width: 34px; height: 34px; opacity: .92; }
.bd-vheldwhy { color: rgb(255 255 255 / .9); }
.bd-vheldwhy:empty { display: none; }
.bd-vheld-own .bd-vheldin { justify-content: flex-end; align-items: stretch; padding: 0 0 46px; text-align: left; }
.bd-vheldposter { position: absolute; left: 0; top: 0; width: 100%; height: 100%; object-fit: cover; opacity: .55; }
.bd-vheldtext { position: relative; display: flex; flex-direction: column; gap: 3px; margin: 0 10px; padding: 10px 14px;
  border-radius: 12px; background: rgb(8 12 18 / .88); box-shadow: inset 0 1px 0 rgb(255 255 255 / .16); }
.bd-vheldtext strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-vheldtext > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-vheldtag { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; letter-spacing: .02em; }
.bd-vheldtag svg { width: 15px; height: 15px; flex: none; }
.bd-vheld-small .bd-vheldin { padding: 8px; gap: 3px; font-size: 12px; }
.bd-vheld-small .bd-vheldin strong { font-size: 13px; }
.bd-vheld-small .bd-vheldwhy, .bd-vheld-small .bd-vheldicon svg { display: none; }
.bd-vheld-own.bd-vheld-small .bd-vheldin { padding-bottom: 40px; }
.bd-vheld-tiny .bd-vheldin > :not(.bd-vheldposter) { display: none; }
.bd-vheldacts { position: absolute; display: flex; justify-content: center; gap: 6px; box-sizing: border-box; height: 40px;
  padding: 0 8px; transform-origin: 0 0; pointer-events: none; }
.bd-vheldacts[hidden] { display: none; }
.bd-vheldacts button { pointer-events: auto; display: inline-flex; align-items: center; gap: 6px; height: 30px; margin: 0;
  padding: 0 13px; border-radius: 999px; font: 600 13px/1 system-ui, "Segoe UI", sans-serif; white-space: nowrap;
  cursor: pointer; color: #fff; transition: background .15s, border-color .15s; }
.bd-vheldacts svg { width: 16px; height: 16px; flex: none; }
.bd-vok { border: 1px solid #0f4a26; background: #17592f; box-shadow: 0 2px 8px rgb(0 0 0 / .35); }
:root[data-style="glass"] .bd-vok {
  background-image: linear-gradient(180deg, rgb(255 255 255 / .12) 0, rgb(255 255 255 / .04) 48%, transparent 48.5%); }
.bd-vok:hover { background-color: #12492a; }
.bd-vno { border: 1px solid rgb(255 255 255 / .38); background: rgb(8 12 18 / .88); }
.bd-vno:hover { border-color: rgb(255 255 255 / .7); }
.bd-vheldacts button:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.bd-vheldacts-narrow button { padding: 0 9px; }
.bd-vheldacts-narrow [data-vh="look"] span { display: none; }
/* "Robin added a video - check it", to the owner, live: at the top of the page as Follow me's bar is, its glass. */
.bd-vnotice { position: absolute; z-index: 5; left: 50%; top: 58px; transform: translateX(-50%); display: flex; align-items: center;
  gap: 10px; max-width: calc(100% - 24px); box-sizing: border-box; padding: 6px 6px 6px 12px; border-radius: 999px;
  font-size: 13.5px; color: var(--ink); background: var(--surface); border: 1px solid var(--rule-strong); box-shadow: var(--shadow-2); }
:root[data-style="glass"] .bd-vnotice { background-color: var(--pane); background-image: var(--gloss-soft);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); box-shadow: var(--rim), var(--shadow-2); }
:root[data-style="glass"] .bd-vnotice .btn:not(.ghost):not(.danger) { background-color: var(--btn-bg); border-color: var(--btn-bg);
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%); }
.bd-vnotice[hidden] { display: none; }
.bd-vnoticeicon svg { display: block; width: 18px; height: 18px; }
.bd-vnoticewords { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
.bd-vnotice .btn { flex: none; }
.bd-vnoticeclose { flex: none; width: 30px; height: 30px; padding: 0; border: 0; border-radius: 50%; background: transparent;
  color: var(--ink); font-size: 18px; line-height: 1; cursor: pointer; }
.bd-vnoticeclose:hover { background: var(--paper-2); }
:root[data-motion="reduce"] :is(.bd-vbar, .bd-vbtn, .bd-vheldacts button, .bd-vtrack, .bd-vthumb, .bd-vvolume) { transition: none; }

/* ── Text: underline, bold and italic, and reading it back (round 4) ──
   board.js (openEditor, placeEditor) and js/board/text-format.js. The text
   box shows the styles as the page draws them: its underline as thick and
   as far below the letters (--bd-ut, --bd-uo, set for the face and zoom),
   broken by letters' tails as the page's is. Its lines wrap only between
   words, as the page's do. */
.bd-textedit.bd-rich { white-space: pre-wrap; overflow-wrap: normal; word-break: normal; overflow: visible; cursor: text; outline: none; }
.bd-textedit .bd-b { font-weight: 700; }
.bd-textedit .bd-i { font-style: italic; }
.bd-textedit .bd-u { text-decoration-line: underline; text-decoration-color: currentColor; text-decoration-thickness: var(--bd-ut, 1px);
  text-underline-offset: var(--bd-uo, 2px); text-underline-position: auto; text-decoration-skip-ink: auto; }
/* Struck through (22 Oct 2026): as the page draws it, as thick as an underline; with an underline, both. */
.bd-textedit .bd-s { text-decoration-line: line-through; text-decoration-color: currentColor; text-decoration-thickness: var(--bd-ut, 1px); }
.bd-textedit .bd-u.bd-s { text-decoration-line: underline line-through; }
/* A list item's lines stay on the left, under their markers, in a text centred or on the right (board.js alignOf). */
.bd-textedit .bd-li { text-align: left; }
/* The row of B, I and U above the text box: a small glass lozenge. The one
   that is on is lit, not only outlined. */
.bd-fmt { position: absolute; left: 0; top: 0; z-index: 3; display: flex; gap: 2px; padding: 3px; border-radius: 999px; pointer-events: auto;
  background: var(--surface); border: 1px solid var(--rule-strong); box-shadow: var(--shadow-2); }
:root[data-style="glass"] .bd-fmt { background-color: var(--pane); background-image: var(--gloss-soft);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); box-shadow: var(--rim), var(--shadow-2); }
.bd-fmtbtn { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 28px; margin: 0; padding: 0;
  border: 1px solid transparent; border-radius: 999px; background: transparent; color: var(--ink); font: 600 15px/1 var(--sans);
  cursor: pointer; transition: background-color .15s, box-shadow .15s; }
.bd-fmtbtn:hover { background-color: color-mix(in srgb, var(--accent) 14%, transparent); }
.bd-fmtbtn.on { background-color: color-mix(in srgb, var(--accent) 26%, var(--surface)); border-color: color-mix(in srgb, var(--accent) 60%, transparent);
  color: var(--ink); box-shadow: 0 0 8px color-mix(in srgb, var(--accent) 35%, transparent); }
:root[data-style="glass"] .bd-fmtbtn.on { background-image: var(--gloss-soft); box-shadow: var(--bevel), 0 0 8px color-mix(in srgb, var(--accent) 35%, transparent); }
.bd-fmtbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.bd-fmt-b { font-weight: 800; }
.bd-fmt-i { font-family: Georgia, 'Times New Roman', serif; font-style: italic; font-weight: 600; }
.bd-fmt-u { text-decoration: underline; text-decoration-thickness: 1.5px; text-underline-offset: 2px; }
.bd-fmt-s { text-decoration: line-through; text-decoration-thickness: 1.5px; }
/* Lists (12 Oct 2026): Bullets and Numbers after B I U, a hairline between - and in the box, each line a block of its
   own; a list item's words start its indent in (padding, set by text-format.js in ems of the box's own size, as the
   page's LIST_STEP), its marker drawn before them by ::before - in the words' colour and face, never bold, italic or
   underlined, ending --bd-mk in (LIST_GAP short of the words), on the first line - so it is no character to type over. */
.bd-fmtsep { align-self: stretch; width: 1px; margin: 4px 3px; background: var(--rule-strong); }
.bd-fmtlist svg { width: 18px; height: 18px; display: block; }
.bd-textedit.bd-rich > .bd-para { position: relative; }
.bd-textedit .bd-li::before { content: attr(data-mark); position: absolute; left: 0; top: 0; width: var(--bd-mk, 1.1em);
  text-align: right; white-space: pre; font-weight: inherit; font-style: normal; text-decoration: none; pointer-events: none;
  -webkit-user-select: none; user-select: none; }
/* What was read, large enough to read at a glance before it goes on the page. */
.bd-read .bd-readtext { font-size: 17px; line-height: 1.45; }
:root[data-motion="reduce"] .bd-fmtbtn { transition: none; }
@media (prefers-reduced-transparency: reduce) {
  :root[data-style="glass"] .bd-fmt { background-color: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* ── A text's font and size, one lozenge (15 Oct 2026, board.js fszHtml) ──
   The owner: "could we also condense the font and size selection on text - it's annoying to switch between the menus".
   In the Text tool's options (on the arcs, orbs since 23 Oct 2026: "Text's font and size on the arcs"), on the selection
   bar and in the text box's row: the face's name in that face (the handwriting a little larger, its letters small for
   their size), a dot, the size, then − and +. Its words are the plain ink on its own pane, and the face's name keeps
   its width, so the lozenge does not jump about (nor a ring of the arcs) as the face changes. */
.bd-fsz { display: inline-flex; align-items: center; flex: none; gap: 1px; padding: 2px; border-radius: 999px; white-space: nowrap;
  background: color-mix(in srgb, var(--ink) 6%, transparent); }
.bd-fsz[hidden] { display: none; }
.bd-fsz button { display: inline-flex; align-items: center; justify-content: center; gap: 5px; min-height: 28px; margin: 0; padding: 0 10px;
  border: 0; border-radius: 999px; background: transparent; color: var(--ink); font: inherit; font-size: 12.5px; line-height: 1; cursor: pointer;
  transition: background-color .15s; }
.bd-fsz button:hover:not(:disabled) { background-color: color-mix(in srgb, var(--accent) 14%, transparent); }
.bd-fsz button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.bd-fsz button:disabled { opacity: .45; cursor: default; }
.bd-fszface { min-width: 7.2em; }
.bd-fszname { min-width: 3.2em; text-align: left; font-size: 13.5px; line-height: 18px; }
.bd-fszname[data-face="hand"] { font-size: 16px; }
.bd-fszdot { font-weight: 700; }
.bd-fsznum { min-width: 2ch; font-variant-numeric: tabular-nums; }
.bd-fszsteps { display: inline-flex; gap: 1px; }
.bd-fsz .bd-fszstep { width: 28px; padding: 0; font-size: 16px; font-weight: 600; }
/* (Aa in the face: the Text tool's arcs' alone - "Text's font and size on the arcs", below.) */
.bd-fszaa { display: none; }
.bd-fmt .bd-fsz, .bd-selbar .bd-fsz { background: transparent; padding: 0; }
:root[data-motion="reduce"] .bd-fsz button { transition: none; }
/* A finger: 44 px to press, every part. */
@media (pointer: coarse) {
  .bd-fsz button { min-height: 44px; }
  .bd-fsz .bd-fszstep { width: 44px; }
}
/* The pane its name opens, in #bd-menu: the faces, a list, each in its own face, the one in use raised in the primary
   button's colours (as a chosen size is, .bd-mseg) - focused or pointed at, still those; the sizes, the named ones as
   chips (.bd-mseg) and a box for any; a selection's bold, italic and underline. Wider than a menu, for the row. */
.ctxmenu.bd-fszpop { width: 304px; }
.bd-fszfaces { display: flex; flex-direction: column; gap: 1px; margin-bottom: 4px; }
.bd-fszopt { line-height: 20px; cursor: pointer; }
.bd-fszopt[data-face="hand"] { font-size: 17px; }
:is(#bd-menu, .ctxmenu.gs-ghost) .bd-fszopt[aria-selected="true"] { background: var(--btn-bg); color: var(--btn-fg); }
:is(#bd-menu, .ctxmenu.gs-ghost) .bd-fszopt:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
:root[data-style="glass"] .bd-fszopt { border-radius: 999px; }
:root[data-style="glass"] :is(#bd-menu, .ctxmenu.gs-ghost) .bd-fszopt[aria-selected="true"],
:root[data-style="glass"] .bd-fszstyle button[aria-pressed="true"] {
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%);
  box-shadow: var(--bevel);
}
/* Its headings in more ink, and in the dark its pane nearly solid, as Settings' and New whiteboard's are over a
   whiteboard (measured: "Font", under the pane's sheen at its top, over the light paper through the dark glass, read
   2.7-2.9:1 at --muted with 30% ink, as a menu's are, and 4.1-4.4 in the ink itself at Glass's 86%), and a chosen size's gloss
   fainter than a button's: its name stands in the top half, where the hard-stop gloss lies. Each size a lozenge with
   corners, not a stadium: two lines high and a word wide, its round ends cut into the word (2.6:1 at their edge). */
.ctxmenu.bd-fszpop .menu-head { color: color-mix(in srgb, var(--ink) 75%, var(--muted)); }
:root[data-style="glass"] .ctxmenu.bd-fszpop {
  --pane: light-dark(color-mix(in srgb, var(--surface) 86%, transparent), color-mix(in srgb, var(--surface) 96%, transparent));
}
@supports not (color: light-dark(#000, #fff)) {
  :root[data-style="glass"] .ctxmenu.bd-fszpop {
    --pane: var(--not-light, color-mix(in srgb, var(--surface) 86%, transparent)) var(--not-dark, color-mix(in srgb, var(--surface) 96%, transparent));
  }
}
.bd-fszstyle button[aria-pressed="true"] { background: var(--btn-bg); color: var(--btn-fg); font-weight: 500; }
.bd-fszstyle button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* More faces and the lines' look (22 Oct 2026, board.js fszPopHtml): thirteen faces in three columns, each named in
   its own face - the pane no taller for them than for six. The pane a little wider for them. (The ladder of sizes
   that came with them, seventeen chips, went on 23 Oct 2026: "Its sizes", below.) */
.ctxmenu.bd-fszpop { width: 336px; }
.bd-fszfaces { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px 3px; }
.bd-fszopt { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding-block: 3px; }
:root[data-style="glass"] .bd-fszlook button[aria-checked="true"], :root[data-style="glass"] .bd-fszpre[aria-checked="true"] {
  background-image: linear-gradient(180deg, rgb(255 255 255 / .1) 0, rgb(255 255 255 / .03) 48%, transparent 48.5%);
}
.bd-fszlook button { white-space: nowrap; }
.bd-fszlook button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Its sizes (23 Oct 2026, board.js fszPopHtml). The owner, with a picture of them: "this sizing isn't very clean" -
   three boxes of three shapes for one number (a pill of named sizes with numbers under them, a box beside it, a block
   of seventeen chips), nothing lined up, and "Lines" for a row that set the alignment. Now one grid under the faces:
   each row's name in a column of its own (Size, Style, Align, Spacing - in the ink a box's pane's names have, measured
   there), its controls in the other, every control one height (--fsz-h) and one corner (--fsz-r: a lozenge on Glass,
   as every button there is; a soft square on Plain, as a field is). The size: − the number + in one sunken track (the
   number large, typed into; the browser's own spinner taken off it - − and + are there), and the slider beside it,
   the bubble on a groove the pen's strength and the History panel have (below, .bd-opacity). Under it the named
   sizes as quiet chips - a hairline, the word in a size of its own, lit in the primary button's colours when the
   text is that size. Align and Spacing small and as wide as their choices, not stretched across the pane. */
.bd-fszgrid { --fsz-h: 32px; --fsz-r: 8px; display: grid; grid-template-columns: 56px minmax(0, 1fr); align-items: center;
  gap: 8px 8px; margin-top: 4px; padding: 10px 2px 4px; border-top: 1px solid var(--rule); }
:root[data-style="glass"] .bd-fszgrid { --fsz-r: 999px; }
.bd-fszlabel { padding-left: 7px; font-size: 12.5px; line-height: 1.2; color: color-mix(in srgb, var(--ink) 75%, var(--muted)); }
.bd-fszgrid .bd-mseg { box-sizing: border-box; height: var(--fsz-h); margin: 0; padding: 3px; gap: 2px; border-radius: var(--fsz-r); }
.bd-fszgrid .bd-mseg button { min-height: 0; height: 100%; padding-block: 0; border-radius: calc(var(--fsz-r) - 3px); }
.bd-fszgrid .bd-fszstyle button { flex: 1 1 auto; padding-inline: 6px; }
.bd-fszgrid .bd-fszlook { justify-self: start; }
.bd-fszgrid .bd-fszlook button { flex: none; min-width: 36px; padding-inline: 8px; font-variant-numeric: tabular-nums; }
.bd-fszalign svg { width: 18px; height: 18px; display: block; }
.bd-fszsize { display: flex; align-items: center; gap: 10px; min-width: 0; height: var(--fsz-h); }
.bd-fszsize .bd-mseg { flex: none; align-items: center; }
.bd-fszsize .bd-mseg .bd-fszpm { flex: none; width: calc(var(--fsz-h) - 8px); padding: 0; font-size: 17px; font-weight: 600; line-height: 1; }
.bd-fszsize .bd-fszpm:disabled { opacity: .45; cursor: default; background: transparent; }
.bd-fszsize .bd-fszpm:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.ctxmenu .bd-fszin { flex: none; box-sizing: border-box; width: 46px; height: 100%; margin: 0; padding: 0; border: 0; border-radius: calc(var(--fsz-r) - 3px);
  background: transparent; box-shadow: none; color: var(--ink); font-size: 18px; font-weight: 600; line-height: 1; text-align: center;
  font-variant-numeric: tabular-nums; -moz-appearance: textfield; appearance: textfield; }
.ctxmenu .bd-fszin::-webkit-inner-spin-button, .ctxmenu .bd-fszin::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.ctxmenu .bd-fszin:focus { outline: 2px solid var(--accent); outline-offset: 0; }
.bd-fszsize input[type="range"].bd-fszslide { flex: 1 1 auto; height: var(--fsz-h); --thumb: 18px; touch-action: none; }
/* (Its groove past the thumb in a touch of the ink, not the rule's colour: on the dark pane that all but vanished, and a
   slider with no groove to see is a dot.) Firefox's own parts the same. */
.bd-fszsize input[type="range"]::-webkit-slider-runnable-track {
  background: linear-gradient(90deg, var(--accent) 0 calc(var(--thumb) / 2 + (100% - var(--thumb)) * var(--f)),
                              color-mix(in srgb, var(--ink) 26%, transparent) 0); }
.bd-fszsize input[type="range"]::-moz-range-track { height: 6px; border-radius: 999px; background: color-mix(in srgb, var(--ink) 26%, transparent); }
.bd-fszsize input[type="range"]::-moz-range-progress { height: 6px; border-radius: 999px; background: var(--accent); }
.bd-fszsize input[type="range"]::-moz-range-thumb { box-sizing: border-box; width: var(--thumb); height: var(--thumb); border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--accent) 75%, #000); background: radial-gradient(circle at 50% 30%, #fff 0 22%, transparent 60%), var(--accent);
  box-shadow: 0 1px 3px rgb(0 0 0 / .28), inset 0 1px 0 rgb(255 255 255 / .6); }
.bd-fszsize input[type="range"]:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent), 0 1px 3px rgb(0 0 0 / .28); }
.bd-fszpres { grid-column: 2; display: flex; gap: 6px; min-width: 0; }
.bd-fszpre { flex: 1 1 0; min-width: 0; height: 26px; margin: 0; padding: 0 4px; border: 1px solid var(--rule); border-radius: var(--fsz-r);
  background: transparent; color: var(--ink); font: inherit; line-height: 1; white-space: nowrap; cursor: pointer; transition: background-color .15s; }
.bd-fszpre[data-name="Title"] { font-size: 15px; font-weight: 600; }
.bd-fszpre[data-name="Subtitle"] { font-size: 13.5px; font-weight: 500; }
.bd-fszpre[data-name="Body"] { font-size: 12.5px; }
.bd-fszpre[data-name="Small"] { font-size: 11.5px; }
.bd-fszpre:hover { background-color: var(--paper-2); }
.bd-fszpre[aria-checked="true"] { border-color: var(--btn-bg); background: var(--btn-bg); color: var(--btn-fg); }
:root[data-style="glass"] .bd-fszpre[aria-checked="true"] { box-shadow: var(--bevel); }
.bd-fszpre:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
:root[data-motion="reduce"] .bd-fszpre { transition: none; }

/* ── Text in a box (22 Oct 2026, board.js boxHtml) ──
   The owner: "I need an option to put text in boxes". Beside the font and size lozenge, a lozenge of its own as that
   one is: Box (a small framed page - since 23 Oct 2026 the box as it is, board.js boxIcon - and the word), lit in the
   primary button's colours while the text has a box, and ▾ for the pane (board.js boxPopHtml, the font pane's look:
   .bd-fszpop). On the arcs an orb and a notch: "Text's font and size on the arcs". */
.bd-tboxc { display: inline-flex; align-items: center; flex: none; gap: 1px; padding: 2px; border-radius: 999px; white-space: nowrap;
  background: color-mix(in srgb, var(--ink) 6%, transparent); }
.bd-tboxc[hidden] { display: none; }
.bd-tboxc button { display: inline-flex; align-items: center; justify-content: center; gap: 5px; min-height: 28px; margin: 0; padding: 0 10px;
  border: 0; border-radius: 999px; background: transparent; color: var(--ink); font: inherit; font-size: 12.5px; line-height: 1; cursor: pointer;
  transition: background-color .15s; }
.bd-tboxc button:hover { background-color: color-mix(in srgb, var(--accent) 14%, transparent); }
.bd-tboxc button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.bd-tboxc .bd-tboxon[aria-pressed="true"] { background: var(--btn-bg); color: var(--btn-fg); }
:root[data-style="glass"] .bd-tboxc .bd-tboxon[aria-pressed="true"] {
  background-image: linear-gradient(180deg, rgb(255 255 255 / .1) 0, rgb(255 255 255 / .03) 48%, transparent 48.5%); box-shadow: var(--bevel);
}
.bd-tboxc svg { width: 16px; height: 16px; flex: none; }
.bd-tboxc .bd-tboxmore { width: 24px; padding: 0; font-size: 11px; }
.bd-fmt .bd-tboxc, .bd-selbar .bd-tboxc { background: transparent; padding: 0; }
:root[data-motion="reduce"] .bd-tboxc button { transition: none; }
/* Its pane: a row a choice, the choice's name on the left in a column of its own, its chips on the right. */
.bd-tboxrow { display: flex; align-items: center; gap: 8px; padding: 2px 2px 4px; }
.bd-tboxname { flex: 0 0 62px; padding-left: 8px; font-size: 12.5px; color: color-mix(in srgb, var(--ink) 75%, var(--muted)); }
.bd-tboxrow .bd-mseg { flex: 1 1 auto; margin: 0; }
.bd-tboxrow .bd-mseg button { flex: 1 1 auto; padding-inline: 6px; }
.bd-tboxcols { margin-bottom: 4px; }
/* The words' own colour, first of the box's colours: a swatch with a small A on it, the A in the pane's ink on a ring. */
.bd-tboxcols { flex-wrap: wrap; }
.bd-swatch.bd-tboxsame { position: relative; }
.bd-swatch.bd-tboxsame::after { content: 'A'; position: absolute; inset: 0; display: grid; place-items: center; font: 700 11px/1 var(--sans);
  color: #fff; text-shadow: 0 0 2px #000, 0 0 1px #000; pointer-events: none; }
@media (pointer: coarse) {
  .bd-tboxc button { min-height: 44px; }
  .bd-tboxc .bd-tboxmore { width: 44px; }
  .bd-fszlook button { min-height: 44px; }
}
@media (pointer: coarse) {
  .bd-fszopt { min-height: 44px; display: flex; align-items: center; }
  .bd-fszstyle button { min-height: 44px; }
  /* (The font pane's grid, 23 Oct 2026: every control 44 px inside its track, a named size 44 px tall.) */
  .bd-fszgrid { --fsz-h: 52px; }
  .ctxmenu .bd-fszin { min-height: 44px; }
  .bd-fszsize .bd-mseg .bd-fszpm { width: 44px; }
  .bd-fszpre { height: 44px; }
}

/* ── Selecting and locking (round 4) ──────────────────────────────────────
   board.js (startCursor, the Cursor's options) and js/board/lock.js. The
   Cursor's "Select by": Box or Lasso, a lozenge with its label inside. The
   selection bar's Lock: a padlock beside the word. The mouse over a thing
   locked down: a small glass bead with a padlock at its top right corner,
   which takes no presses. */
.bd-selby { align-items: center; }
/* (Darker than a plain label: the pane is see-through, and over light paper in the dark theme the muted ink measured 3.75:1.) */
.bd-selby .bd-seglabel { padding: 0 4px 0 8px; color: color-mix(in srgb, var(--ink) 45%, var(--muted)); }
/* Snip beside Box and Lasso (16 Oct 2026): the scissors before the word, the size of a letter. */
.bd-selby [data-selby="snip"] { display: inline-flex; align-items: center; gap: 4px; }
.bd-selby .bd-snipicon { width: 14px; height: 14px; flex: none; }
.bd-sbtn[data-act="x-lock"] { display: inline-flex; align-items: center; gap: 4px; }
.bd-sbtn[data-act="x-lock"][hidden] { display: none; }
.bd-sbtn[data-act="x-lock"] svg { flex: none; margin-left: -2px; }
.bd-lockmark { position: absolute; left: 0; top: 0; z-index: 3; width: 26px; height: 26px; margin: -13px 0 0 -13px;   /* (under the panes and bars, z-index 4) */
  display: grid; place-items: center; border-radius: 50%; pointer-events: none; color: var(--ink);
  background: var(--surface); border: 1px solid var(--rule-strong); box-shadow: var(--shadow-2); }
.bd-lockmark[hidden] { display: none; }
:root[data-style="glass"] .bd-lockmark { background-color: var(--pane); background-image: var(--gloss-soft);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); box-shadow: var(--rim), var(--shadow-2); }
@media (prefers-reduced-transparency: reduce) {
  :root[data-style="glass"] .bd-lockmark { background-color: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}
/* ── The pen's buttons (round 4; a dialog of its own since 2 Oct 2026) ──
   js/board/penbuttons.js: Settings → Writing → Pen buttons. A dialog, so on Glass already a pane (the glass rules
   above); these lay it out. Across the top the four steps, then a drawing of the pen - its button being asked for
   glowing, a badge on each saying what it was found as, then what it does - and under it the step: a button asked
   for (waiting, found, or what to change in the pen's own settings app), cards to choose what each does, or a little
   page to try them on. Every word is --ink on the pane, or --pb-soft (--ink 45% into --muted, as Share's and the New
   whiteboard sheet's are, over the editor as they are), lifted to 70% on a chosen card and tab; a chosen card is lit
   as a New whiteboard tile is, and the main button opaque under the lighter gloss, as Share's is. The badges are
   solid lozenges (the drawing behind them is no pane to read through). Measured as drawn in every look, on a desktop
   and a phone (tests/test_penbuttons.py). */
dialog.bd-pbd { --pb-soft: color-mix(in srgb, var(--ink) 45%, var(--muted));
  width: min(620px, calc(100% - 24px)); max-width: none; margin: auto;
  max-height: calc(100vh - 24px); max-height: calc(100dvh - 24px); overflow: hidden;
  flex-direction: column; }                            /* (on the closed one too: dialog.settings says why) */
dialog.bd-pbd[open] { display: flex; }
dialog.bd-pbd::backdrop, dialog.bd-pbd + .gs-backdrop { background: color-mix(in srgb, var(--backdrop, rgb(0 0 0 / .3)) 70%, transparent); }
.pb-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 20px 6px; }
.pb-head h2 { font-size: 22px; line-height: 1.2; }
/* The steps: a bar each, the one in hand in the accent and its name in ink, those done half lit. Their names are
   --ink 70% into --muted, not --pb-soft: high in the pane, under its sheen, --pb-soft measured 4.0-4.5:1 on Glass in
   the dark theme over a whiteboard (Ember's the lowest), as Settings' descriptions once did there. */
.pb-steps { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; margin: 0; padding: 6px 20px 10px; list-style: none; }
.pb-steps li { display: flex; flex-direction: column; gap: 5px; min-width: 0; font-size: 12px; line-height: 1.3;
  color: color-mix(in srgb, var(--ink) 70%, var(--muted)); }
.pb-sname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pb-dot { display: block; height: 4px; border-radius: 999px; background: var(--rule-strong); }
.pb-steps .pb-past .pb-dot { background: color-mix(in srgb, var(--accent) 50%, var(--rule-strong)); }
.pb-steps .pb-now .pb-dot { background: var(--accent); }
.pb-steps .pb-now { color: var(--ink); font-weight: 500; }
.pb-body { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 2px 20px 16px; }

/* The pen: its own greys, darker on a light pane and lighter on a dark one, ringed so it stands off either; a gloss
   along the barrel, as everything Glass has; its buttons pale until found (the accent, softened), lit (the accent,
   glowing, breathing while it is asked for) while asked for, chosen for, or pressed in Try it. */
.pb-pen { position: relative; margin: 0 0 10px; padding: 30px 6px; }
.pb-pensvg { display: block; width: 100%; height: auto; overflow: visible; }
.pb-cone, .pb-grip, .pb-barrel, .pb-tail { stroke: light-dark(rgb(20 30 45 / .5), rgb(255 255 255 / .42)); stroke-width: 1.2; }
@supports not (color: light-dark(#000, #fff)) {
  .pb-cone, .pb-grip, .pb-barrel, .pb-tail {
    stroke: var(--not-light, rgb(20 30 45 / .5)) var(--not-dark, rgb(255 255 255 / .42));
  }
}
.pb-cone { fill: light-dark(#cfd5dc, #8f98a4); }
@supports not (color: light-dark(#000, #fff)) { .pb-cone { fill: var(--not-light, #cfd5dc) var(--not-dark, #8f98a4); } }
.pb-tip { fill: light-dark(#1d2229, #f1f4f7); }
@supports not (color: light-dark(#000, #fff)) { .pb-tip { fill: var(--not-light, #1d2229) var(--not-dark, #f1f4f7); } }
.pb-grip, .pb-tail { fill: light-dark(#262c34, #5f6874); }
@supports not (color: light-dark(#000, #fff)) {
  .pb-grip, .pb-tail {
    fill: var(--not-light, #262c34) var(--not-dark, #5f6874);
  }
}
.pb-barrel { fill: light-dark(#3d4652, #7d8793); }
@supports not (color: light-dark(#000, #fff)) {
  .pb-barrel {
    fill: var(--not-light, #3d4652) var(--not-dark, #7d8793);
  }
}
.pb-shine { fill: rgb(255 255 255 / .36); }
.pb-lead { stroke: var(--rule-strong); stroke-width: 1.2; opacity: 0; }
.pb-lead.pb-shown { opacity: 1; }
.pb-btn { fill: light-dark(#8b95a2, #c5ccd5); stroke: light-dark(rgb(20 30 45 / .55), rgb(255 255 255 / .5)); stroke-width: 1.2;
  transition: fill .2s, filter .2s; }
@supports not (color: light-dark(#000, #fff)) {
  .pb-btn {
    fill: var(--not-light, #8b95a2) var(--not-dark, #c5ccd5);
    stroke: var(--not-light, rgb(20 30 45 / .55)) var(--not-dark, rgb(255 255 255 / .5));
  }
}
.pb-btn.pb-set { fill: color-mix(in srgb, var(--accent) 65%, light-dark(#fff, #000)); }
@supports not (color: light-dark(#000, #fff)) {
  .pb-btn.pb-set {
    fill: color-mix(in srgb, var(--accent) 65%, var(--not-light, #fff) var(--not-dark, #000));
  }
}
.pb-btn.pb-on, .pb-btn.pb-hit { fill: var(--accent); filter: drop-shadow(0 0 4px var(--accent)) drop-shadow(0 0 2px var(--accent)); }
.pb-btn.pb-on:not(.pb-set) { animation: pb-glow 1.4s ease-in-out infinite; }
@keyframes pb-glow { 50% { filter: drop-shadow(0 0 1px var(--accent)); } }
/* Safari draws no CSS filter on a shape inside an SVG (only on the SVG itself, or an SVG filter by url()): there the
   button lit had no glow and did not breathe. In WebKit - :root[data-engine="webkit"], board.js, from WebKit's own
   GestureEvent - the drawing's own filters (penbuttons.js, PEN) instead, the breath a step between two of them, as a
   filter by url() cannot be eased (7 Oct 2026, the owner: "make sure it all works on safari too"). */
:root[data-engine="webkit"] .pb-btn:is(.pb-on, .pb-hit) { filter: url(#pb-glow); }
:root[data-engine="webkit"] .pb-btn.pb-on:not(.pb-set) { animation-name: pb-glow-webkit; }
@keyframes pb-glow-webkit { 50% { filter: url(#pb-glow-low); } }
:is(#pb-glow, #pb-glow-low) feDropShadow { flood-color: var(--accent); }
/* Over the upper button, under the lower one, under the eraser end - each at its place along the drawing (the upper
   at 125 of its 400, the lower at 97, the end at 377), its line running from 16px into it. */
.pb-badge { position: absolute; z-index: 1; max-width: 44%; box-sizing: border-box; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; padding: 2px 9px; border-radius: 999px; border: 1px solid var(--rule-strong); background: var(--surface);
  font-size: 12px; font-weight: 500; line-height: 18px; color: var(--ink); }
.pb-badge.pb-on { border-color: var(--accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent); }
.pb-badge[data-b="upper"] { top: 4px; left: calc(6px + (100% - 12px) * .3125 - 16px); }
.pb-badge[data-b="lower"] { bottom: 4px; left: calc(6px + (100% - 12px) * .2425 - 16px); }
.pb-badge[data-b="tail"] { bottom: 4px; right: calc(6px + (100% - 12px) * .0575 - 16px); }

.pb-kicker { margin: 0 0 3px; font-size: 12px; line-height: 1.4; color: var(--pb-soft); }
.pb-ask { margin: 0 0 6px; font-size: 20px; line-height: 1.25; }
.pb-ask:focus { outline: none; }
.pb-more { margin: 0 0 12px; font-size: 13.5px; line-height: 1.5; color: var(--ink); }
/* What arrived: waiting (three dots breathing), found (a tick in the main button's colours), or not one the Board can
   use (a mark in red). The marks are drawn, not words: their own colours are not text to be read. */
.pb-status { display: flex; align-items: flex-start; gap: 10px; margin: 0 0 12px; padding: 11px 14px; border-radius: 14px;
  border: 1px solid var(--rule-strong); font-size: 14px; line-height: 1.45; color: var(--ink); }
.pb-status b { font-weight: 600; }
.pb-status.pb-ok { border-color: color-mix(in srgb, var(--accent) 60%, var(--rule-strong)); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.pb-status.pb-warn { border-color: color-mix(in srgb, var(--red) 55%, var(--rule-strong)); }
.pb-mark { flex: none; display: grid; place-items: center; width: 20px; height: 20px; margin-top: 0; border-radius: 50%;
  background: var(--btn-bg); color: var(--btn-fg); font-size: 12px; font-weight: 700; line-height: 1; }
.pb-ok .pb-mark::before { content: '✓'; }
.pb-warn .pb-mark { background: var(--red); color: var(--surface); }
.pb-warn .pb-mark::before { content: '!'; }
.pb-wait { flex: none; display: inline-flex; gap: 4px; padding: 7px 2px 0; }
.pb-wait i { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); animation: pb-wait 1.2s ease-in-out infinite; }
.pb-wait i:nth-child(2) { animation-delay: .15s; }
.pb-wait i:nth-child(3) { animation-delay: .3s; }
@keyframes pb-wait { 0%, 80%, 100% { opacity: .25; } 40% { opacity: 1; } }
.pb-help { margin: 0 0 12px; padding: 11px 14px; border-radius: 14px; background: color-mix(in srgb, var(--ink) 5%, transparent);
  font-size: 13px; line-height: 1.5; color: var(--ink); }
.pb-help p { margin: 0 0 6px; }
.pb-help p:last-child { margin: 0; }
.pb-help ul { margin: 0; padding-left: 18px; }
.pb-help li { margin: 3px 0 0; }
.pb-help b { font-weight: 600; }

/* Which button is being chosen for: two halves of one lozenge, the one in hand lit as Share's roles are, each saying
   what its button does now. */
.pb-tabs { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 3px; margin: 4px 0 12px; padding: 3px;
  border: 1px solid var(--rule-strong); border-radius: 16px; }
.pb-tabs button { display: flex; flex-direction: column; align-items: flex-start; gap: 1px; min-width: 0; margin: 0; padding: 6px 14px;
  border: 0; border-radius: 13px; background: transparent; box-shadow: none; color: var(--ink); font: inherit; text-align: left;
  letter-spacing: 0; cursor: pointer; }
.pb-tname { font-size: 13.5px; font-weight: 500; line-height: 1.3; }
.pb-tnow { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; line-height: 1.35; color: var(--pb-soft); }
.pb-tabs button:hover:not([aria-selected="true"]) { background: color-mix(in srgb, var(--accent) 7%, transparent); }
.pb-tabs button[aria-selected="true"] { background: color-mix(in srgb, var(--accent) 9%, transparent);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 60%, var(--rule-strong)); }
.pb-tabs button[aria-selected="true"] .pb-tnow { color: color-mix(in srgb, var(--ink) 70%, var(--muted)); }
:root[data-style="glass"] .pb-tabs { border-radius: 999px; }
:root[data-style="glass"] .pb-tabs button { border-radius: 999px; }
:root[data-style="glass"] .pb-tabs button[aria-selected="true"] { background-image: var(--gloss-soft);
  box-shadow: var(--bevel), inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 60%, var(--rule-strong)); }

/* What a button does: a card each, its picture, its name and a line - two across, one on a phone - the chosen one
   lit as a New whiteboard tile is, with its tick in the main button's colours. */
.pb-cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin: 0 0 10px; }
.pb-card { position: relative; display: flex; align-items: flex-start; gap: 11px; min-width: 0; margin: 0; padding: 10px 30px 11px 12px;
  border-radius: 14px; border: 1px solid var(--rule-strong); background: transparent; box-shadow: none; color: var(--ink); font: inherit;
  text-align: left; letter-spacing: 0; cursor: pointer; transition: background .15s, border-color .15s, box-shadow .15s; }
.pb-card svg { flex: none; width: 22px; height: 22px; margin-top: 1px; fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round; }
.pb-card[data-act="radial"] svg { fill: currentColor; stroke: none; }
.pb-cwords { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pb-cname { font-size: 13.5px; font-weight: 500; line-height: 1.3; }
.pb-csays { font-size: 12px; line-height: 1.4; color: var(--pb-soft); }
.pb-card:hover { background: color-mix(in srgb, var(--accent) 7%, transparent); }
.pb-card[aria-checked="true"] { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, transparent); }
:root[data-style="glass"] .pb-card[aria-checked="true"] { background-image: var(--gloss-soft); box-shadow: inset 0 0 0 1px var(--accent), var(--bevel); }
.pb-card[aria-checked="true"] .pb-csays { color: color-mix(in srgb, var(--ink) 70%, var(--muted)); }
.pb-card[aria-checked="true"]::before,
.pb-card[aria-checked="true"]::after { content: ''; position: absolute; top: 9px; right: 9px; width: 16px; height: 16px; border-radius: 50%; }
.pb-card[aria-checked="true"]::before { background: var(--btn-bg); }
.pb-card[aria-checked="true"]::after { background: var(--btn-fg);
  -webkit-mask: no-repeat center / 11px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 6.3 5 8.6l4.6-5' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  mask: no-repeat center / 11px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 6.3 5 8.6l4.6-5' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
.pb-note { margin: 0; font-size: 12.5px; line-height: 1.45; color: var(--pb-soft); }

/* Try it: a little page of the editor's own paper and ink (not the look's: a whiteboard's paper is the same in every
   palette), its dots and drawing moved by a button that moves the page, the tool in hand on a solid chip. */
.pb-try { position: relative; height: 150px; margin: 0 0 10px; overflow: hidden; border-radius: 14px; touch-action: none;
  border: 1px solid var(--rule-strong); background-color: #fbfaf6;
  background-image: radial-gradient(circle, rgb(38 62 96 / .45) 0 1.2px, transparent 1.6px); background-size: 18px 18px; }
.pb-try[data-tool="hand"] { cursor: var(--cur-grab); }
.pb-trydraw { position: absolute; top: 30px; left: calc(50% - 110px); width: 220px; height: 90px;
  fill: none; stroke: #1f5fc4; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.pb-trytool { position: absolute; top: 8px; left: 8px; padding: 1px 10px; border-radius: 999px; border: 1px solid var(--rule-strong);
  background: var(--surface); font-size: 12px; font-weight: 500; line-height: 18px; color: var(--ink); }
.pb-said { min-height: 1.45em; margin: 0 0 6px; font-size: 14px; line-height: 1.45; color: var(--ink); }
.pb-said b { font-weight: 600; }
.pb-sum { margin: 0 0 12px; font-size: 12.5px; line-height: 1.45; color: var(--pb-soft); }
.pb-more-acts { display: flex; flex-wrap: wrap; gap: 8px; }

.pb-foot { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; padding: 12px 20px 14px; border-top: 1px solid var(--rule); }
.pb-foot .btn:first-child:not(:last-child) { margin-right: auto; }
dialog.bd-pbd .btn { justify-content: center; min-height: 38px; }
dialog.bd-pbd .btn.sm { min-height: 30px; }
:root[data-style="glass"] dialog.bd-pbd .btn:not(.ghost):not(.danger):not(:active) { background-color: var(--btn-bg); border-color: var(--btn-bg);
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%); }
dialog.bd-pbd button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (pointer: coarse) {
  dialog.bd-pbd .btn, dialog.bd-pbd .st-close { min-height: 44px; }
  dialog.bd-pbd .st-close { min-width: 44px; }
  .pb-tabs button { min-height: 44px; }
}
/* A phone: the dialog the width of the screen, a card a row. */
@media (max-width: 600px) {
  dialog.bd-pbd { width: calc(100% - 16px); max-height: calc(100vh - 16px); max-height: calc(100dvh - 16px); }
  .pb-head { padding: 14px 16px 4px; }
  .pb-steps { gap: 6px; padding: 6px 16px 8px; }
  .pb-steps li { font-size: 11.5px; }
  .pb-body { padding: 2px 16px 14px; }
  .pb-ask { font-size: 18px; }
  .pb-cards { grid-template-columns: minmax(0, 1fr); gap: 6px; }
  .pb-card { padding: 8px 30px 9px 12px; }
  .pb-foot { padding: 10px 16px 12px; }
  .pb-foot .btn { flex: 1 1 auto; }
}
:root[data-motion="reduce"] .pb-btn, :root[data-motion="reduce"] .pb-card { transition: none; }
:root[data-motion="reduce"] .pb-btn.pb-on:not(.pb-set), :root[data-motion="reduce"] .pb-wait i { animation: none; }
:root[data-motion="reduce"] .pb-wait i { opacity: .7; }
@media (prefers-reduced-transparency: reduce) {
  :root[data-style="glass"] dialog.bd-pbd { background-color: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}


/* ═══ tactile presses (GuideStar app.css 6655–6684) ═══ */
/* ══ Tactile ("we want the UI to feel tactile and satisfying") ═══════════
   Pressed, a control sinks a touch and dims its shine; let go, it springs
   back just past where it was, like a real key. The sink and the spring are
   the script's own (static/js/sounds.js, on the `scale` property with the
   Web Animations API, so a thing placed by a transform keeps its place);
   only the dimming is here. Since 7 Oct 2026 no longer a class's transition
   and a class's animation: the spring as an `animation` took the place of
   whatever animation the thing already had - a ring menu's orb lost its
   bd-rfly, and bloomed out again when the spring's class came off 420 ms
   later. On Glass not even the dimming: a filter on a thing that also has a
   backdrop-filter is the case in which WebKit flickers. */
.gs-pressed { filter: brightness(.96); }
:root[data-style="glass"] .gs-pressed { filter: none; }


/* ═══ the writing sound's tuner (GuideStar app.css 6716–6735) ═══ */
/* The writing sound's tuning panel (sounds.js: tuner), over the Board, below its header bar. */
.gs-tuner { position: fixed; top: 120px; right: 16px; z-index: 2000; width: min(300px, calc(100vw - 32px));
  max-height: calc(100vh - 136px); overflow: auto;
  padding: 10px 14px 6px; border-radius: 14px; background: var(--surface); color: var(--ink);
  border: 1px solid var(--rule-strong); box-shadow: var(--shadow-3); }
:root[data-style="glass"] .gs-tuner { background: color-mix(in srgb, var(--surface) 72%, transparent);
  backdrop-filter: blur(18px) saturate(1.3); -webkit-backdrop-filter: blur(18px) saturate(1.3); }
.gs-tuner-head { display: flex; align-items: center; gap: 8px; margin-bottom: 2px; }
.gs-tuner-head b { flex: 1; white-space: nowrap; }
.gs-tuner-head select { font: inherit; font-size: .84rem; width: auto; min-width: 0; padding: 3px 24px 3px 8px; height: auto; }
/* A form label here is a slider's row, not the app's small-caps field label. */
.gs-tuner .gs-tune { display: block; margin: 6px 0; text-transform: none; letter-spacing: normal; font-weight: 400; }
.gs-tune-name { display: flex; justify-content: space-between; font-size: .84rem; color: var(--ink); }
.gs-tune-name output { color: var(--muted); font-variant-numeric: tabular-nums; }
.gs-tune input[type="range"] { width: 100%; margin: 2px 0 0; accent-color: var(--accent); }
.gs-tune-ends { display: flex; justify-content: space-between; font-size: .7rem; color: var(--faint); }
.gs-tuner-foot { display: flex; gap: 8px; margin: 8px 0 4px; }
.gs-tune select { display: block; width: 100%; margin-top: 2px; font: inherit; font-size: .84rem; }
.gs-tune-note { display: block; margin-top: 3px; font-size: .72rem; color: var(--faint); line-height: 1.35; }


/* ═══ signing in (lane L3's first part, 30 Sep 2026: app/auth.py) ═══ */
/* ── The sign-in page (templates/signin.html) ────────────────────────
   One pane in the middle of the aurora, the star over it: the only thing on the page. The pane is the app's own
   .card, so it is glass under the glass style and a raised card under plain, in every palette and both themes. */
main.signin-main { display: flex; justify-content: center; padding-top: 8vh; }
.signin { width: 100%; max-width: 400px; padding: 30px 30px 28px; text-align: center; }
.signin-mark { display: block; height: 58px; width: auto; aspect-ratio: 731 / 1185; margin: 0 auto 14px;
  filter: var(--logo-filter); }
.signin h1 { margin: 2px 0 8px; }
/* Small words on glass: the lifted grey, which clears 4.5:1 over the aurora where --muted alone can fall under. */
.signin-lede { font-size: 13.5px; color: color-mix(in srgb, var(--ink) 15%, var(--muted)); }
.signin-note { margin-top: 14px; font-size: 13.5px; font-weight: 500; }
/* What went wrong: the words in ink, the red a mark beside them. Measured over every palette, theme and style:
   --red as the words fell to 3.4:1 in the dark theme on glass where the aurora is strongest (Aero), and lifting it
   30% toward ink still left 4.35 - while a mark needs only 3:1, which it clears everywhere. */
.signin-warn { display: flex; align-items: flex-start; justify-content: center; gap: 7px; text-align: left; }
.signin-warnmark { flex: none; width: 16px; height: 16px; margin-top: 2px; }
.signin-warnmark circle { fill: var(--red); }
.signin-warnmark path { fill: none; stroke: var(--surface); stroke-width: 1.9; stroke-linecap: round; }
.signin-form { display: flex; flex-direction: column; gap: 7px; margin-top: 22px; text-align: left; }
.signin-form label { margin: 0; color: color-mix(in srgb, var(--ink) 15%, var(--muted)); }
/* 16px: below that a phone zooms the page in to type. */
.signin-form input[type="password"] { font-size: 16px; padding: 10px 12px; }
.signin-go { justify-content: center; width: 100%; margin-top: 12px; padding-block: 10px; }
.signin-lede + .signin-go { margin-top: 22px; }             /* "Signed in": the link straight under its words */
/* The name a password manager files the password under: in the form, out of sight and out of the tab order. */
.signin-user { position: absolute; left: 0; top: 0; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

/* ── Opening a link (templates/join.html, 1 Oct 2026) ─────────────────
   The owner's own link: "Open the whiteboard as yourself" the solid button, seeing it as the guest the ghost one
   under it, which opens a box saying how (a private window) and what opening it here does. Anyone else: their name.
   The words are --ink, or the sign-in page's own lifted grey, on the same pane. */
.join .signin-lede b { font-weight: 600; color: var(--ink); }
.join-owner { display: flex; flex-direction: column; margin-top: 22px; }
.join-owner .signin-go { margin-top: 0; }
.join-owner .signin-go + .signin-go { margin-top: 10px; }
.join-as { margin-top: 12px; padding: 12px 14px; border-radius: 14px; border: 1px solid var(--rule-strong); text-align: left; }
.join-as .btn { justify-content: center; width: 100%; }
.join-asnote { margin: 0 0 8px; font-size: 13px; line-height: 1.5; color: var(--ink); }
.join-asnote b { font-weight: 600; }
.join-warn { margin-top: 14px; }
.join-said { min-height: 1.4em; margin: 6px 0 0; font-size: 12.5px; color: var(--ink); }
.join-form input { font-size: 16px; padding: 10px 12px; }
.join-says { margin: 0; font-size: 12.5px; line-height: 1.45; color: color-mix(in srgb, var(--ink) 15%, var(--muted)); }
.join-form .signin-warn { justify-content: flex-start; margin-top: 4px; }
/* The solid button opaque under a lighter gloss, as the Settings panel's and the presenting bar's are: Glass's own
   .btn, translucent under its full sheen, measured 2.8-3.1:1 for "Open the whiteboard as yourself" and "Join" in
   the light theme, and 4.3-4.4 in the dark (tests/test_names.py). */
:root[data-style="glass"] .join .btn:not(.ghost):not(.danger) { background-color: var(--btn-bg); border-color: var(--btn-bg);
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%); }

/* ── Signed out while a page is open (static/js/app.js) ──────────────
   At the top in the middle, over everything: the same kind of pane as the editor's "changed in another window",
   with its one way on. It stays until this browser is signed in again. */
.signedout { position: fixed; z-index: 300; left: 50%; top: 14px; transform: translateX(-50%);
  display: flex; align-items: center; gap: 12px; width: max-content; max-width: calc(100% - 24px);
  padding: 8px 8px 8px 18px; border-radius: 999px; font-size: 13px; line-height: 1.4; color: var(--ink);
  background: var(--surface); border: 1px solid var(--rule-strong); box-shadow: var(--shadow-3); }
.signedout .btn { flex: none; }
:root[data-style="glass"] .signedout {
  background-color: var(--pane); background-image: var(--gloss-soft);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); box-shadow: var(--rim), var(--shadow-3); }
/* Two lines on a phone: a pill that tall reads as a blob, so it becomes a pane with corners. */
@media (max-width: 640px) { .signedout { border-radius: 18px; padding: 10px 10px 10px 14px; } }
@media (prefers-reduced-transparency: reduce) {
  :root[data-style="glass"] .signedout { background-color: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}



/* ═══ invite links and the AI's limits (lanes L3 and L4, 30 Sep 2026: services/people.py, gateway.py) ═══ */
/* ── Who came in by a link, in the masthead ─────────────────────────
   Their name and what their link lets them do, where the owner's has nothing (their links are Settings → Sharing).
   A lozenge like the nav's, not a link. */
.who-chip { display: inline-flex; align-items: center; gap: 8px; padding: 4px 6px 4px 12px; border-radius: 999px;
  border: 1px solid var(--rule-strong); font-size: 12.5px; color: var(--ink); white-space: nowrap; }
.who-chip b { font-weight: 500; max-width: 14em; overflow: hidden; text-overflow: ellipsis; }
.who-chip span { padding: 1px 8px; border-radius: 999px; font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase;
  background: color-mix(in srgb, var(--accent) 14%, transparent); }
:root[data-style="glass"] .who-chip { background-image: var(--gloss); box-shadow: var(--bevel);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule-strong)); }
/* The owner's own, once they have given a name (1 Oct 2026, Settings → You): the same lozenge, and it opens there. */
button.who-chip { font: inherit; font-size: 12.5px; background-color: transparent; cursor: pointer; }
button.who-chip:hover { background-color: var(--paper-2); }
button.who-chip:focus-visible, .who-leave:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The owner's browser holding a guest's link (auth.previewing, 1 Oct 2026): whose eyes the Board is shown through,
   in words, and Leave this link beside them - the lozenge of the chip, the way out a lozenge inside it. Before this
   the browser simply was the guest, with a small sign-out icon the only way back. The words are --ink, the button's
   too, on the masthead's own surface and the chip's tint (measured as drawn in every look, tests/test_names.py). */
.who-as { display: inline-flex; align-items: center; gap: 8px; min-width: 0; margin: 0; padding: 3px 4px 3px 12px;
  border-radius: 999px; border: 1px solid color-mix(in srgb, var(--accent) 55%, var(--rule-strong));
  background: color-mix(in srgb, var(--accent) 9%, transparent); font-size: 12.5px; color: var(--ink); }
.who-as-words { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.who-as b { font-weight: 600; }
.who-leave { flex: none; padding: 4px 11px; border-radius: 999px; cursor: pointer; border: 1px solid var(--rule-strong);
  background: var(--surface); font: inherit; font-size: 12.5px; font-weight: 500; color: var(--ink); white-space: nowrap; }
.who-leave:hover { background: var(--paper-2); }
:root[data-style="glass"] .who-as { background-image: var(--gloss-soft); box-shadow: var(--bevel); }
@media (max-width: 520px) {
  .who-as-pre, .who-as-role { display: none; }
  .who-me { display: none; }
}
/* ── Your name… (4 Oct 2026: templates/base.html #you-name, static/js/yourname.js) ──
   A guest's own name, changed once in: their chip in the masthead is a button now (button.who-chip above, as the
   owner's is), and opens this. A dialog, so on Glass already a pane (the glass rules above); these only lay it out -
   Share's head, one field, Cancel and Save. Every word --ink on the pane; Save opaque under the lighter gloss, as
   Share's and the New whiteboard sheet's are (Glass's own .btn measured 2.6-3.0:1 for its words in the light theme).
   Measured as drawn in all 44 looks, at a desktop's width and a phone's (tests/test_guest_rename.py). */
dialog.you-name { width: min(400px, calc(100% - 24px)); max-width: none; margin: auto; overflow: hidden; }
dialog.you-name::backdrop, dialog.you-name + .gs-backdrop { background: color-mix(in srgb, var(--backdrop, rgb(0 0 0 / .3)) 70%, transparent); }
.yn-form { display: flex; flex-direction: column; padding: 16px 20px 18px; }
.yn-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 8px; }
.yn-head h2 { font-size: 22px; line-height: 1.2; }
.yn-says { margin: 0 0 14px; font-size: 13.5px; line-height: 1.5; color: var(--ink); }
.yn-label { display: block; margin: 0 0 7px; padding: 0; font-family: var(--sans); font-size: 11px; font-weight: 500;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink); }
/* 16px: Safari zooms the page to a field set in less. */
input#yn-name { margin: 0; padding: 10px 12px; font-size: 16px; }
.yn-error { display: flex; gap: 7px; margin: 10px 0 0; font-size: 13.5px; font-weight: 500; color: var(--ink); }
.yn-error[hidden] { display: none; }
.yn-error::before { content: '!'; flex: none; display: grid; place-items: center; width: 16px; height: 16px; margin-top: 2px;
  border-radius: 50%; background: var(--red); color: var(--surface); font-size: 11px; font-weight: 700; }
.yn-acts { display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px; }
dialog.you-name .btn { justify-content: center; min-width: 96px; min-height: 40px; }
:root[data-style="glass"] dialog.you-name .btn:not(.ghost):not(.danger):not(:active) {
  background-color: var(--btn-bg); border-color: var(--btn-bg);
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%); }
dialog.you-name :is(button, input):focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (pointer: coarse) { dialog.you-name .btn, dialog.you-name .st-close { min-height: 44px; } dialog.you-name .st-close { min-width: 44px; } }
@media (max-width: 520px) {
  .yn-form { padding: 14px 16px 16px; }
  .yn-acts .btn { flex: 1 1 0; }
}
@media (prefers-reduced-transparency: reduce) {
  :root[data-style="glass"] dialog.you-name { background-color: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* ── The editor, view only (board.js VIEW_ONLY) ──────────────────────
   Nothing to draw with, so no selection bar - the page, its zoom and its pages. A label in the head says so, in words
   and with an eye. Of the tools, only the Cursor (which moves the page here), with its options: Snip is there (16 Oct
   2026, board/snip.js: it only reads the page, so a guest who may look may snip, and paste it into another app; a tool
   of its own on the rail that morning, beside the Cursor). */
.bd[data-view="1"] :is(.bd-selbar, .bd-pgmore, .bd-autobtn) { display: none !important; }
.bd[data-view="1"] .bd-rail > :not([data-tool="lasso"], .bd-raillight) { display: none !important; }
.bd[data-view="1"] input.bd-title { cursor: default; }
.bd[data-view="1"] input.bd-title:hover, .bd[data-view="1"] input.bd-title:focus { border-color: transparent; background: transparent; }
.bd[data-view="1"] .bd-stage { cursor: var(--cur-grab); }
.bd-viewonly { display: inline-flex; align-items: center; gap: 6px; flex: none; padding: 3px 11px 3px 9px; border-radius: 999px;
  font-size: 12px; font-weight: 500; color: var(--ink); border: 1px solid var(--rule-strong);
  background: color-mix(in srgb, var(--accent) 12%, transparent); }
.bd-viewonly svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linejoin: round; }
:root[data-style="glass"] .bd-viewonly { background-image: var(--gloss); box-shadow: var(--bevel); }

/* ── The words a guest's index opens with (templates/boards.html) ──────
   Straight on the aurora, with no pane under them: --muted with 55% of ink (it fell to 3.5:1 in Aero light with
   less - measured for the People page, 30 Sep 2026, whose styles these were). */
.bi-guestlede { --on-aurora: color-mix(in srgb, var(--ink) 55%, var(--muted)); }
p.bi-guestlede { color: var(--on-aurora); }


/* ═══ who is here, and where they are looking (lane L6, 30 Sep 2026: static/js/board/presence.js) ═══ */
/* Each other person: a circle of their colour with their initials, in the head - overlapping, as a group of people
   stands. Their colours are dark enough for white initials (6:1 and more), with a ring of the head's own colour so
   each is told from the next; the one being followed has a ring of its own colour, a gap out. */
.bd-people { display: flex; align-items: center; padding-left: 6px; }
.bd-person { width: 28px; height: 28px; margin-left: -6px; border-radius: 50%; border: 2px solid var(--surface); padding: 0;
  display: inline-grid; place-items: center; background: var(--pc); color: #fff; font: 600 11px/1 var(--sans);
  letter-spacing: .02em; cursor: pointer; box-shadow: var(--shadow-1); transition: transform .12s; }
.bd-person:hover, .bd-person:focus-visible { transform: translateY(-2px); z-index: 1; }
.bd-person:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.bd-person.bd-following { outline: 2px solid var(--pc); outline-offset: 2px; z-index: 1; }
/* (No gloss on them under Glass: a white sheen over the top half took the white initials under 4.5:1.) */
/* ── Follow me and Present (1 Oct 2026: presence.js) ──
   "Make the buttons clean, less text": icons, round and the size of the bar's others, their names under them on
   hover and focus (the bar's own tooltip, from aria-label) - and beside them only on a wide screen, where there is
   room for a word. Lit while on (aria-pressed), as the masthead's current page is (lane L19): the primary button's
   colours, opaque, under a lighter gloss than .btn's, whose full sheen took the light theme's light words under
   3:1. Measured in every look: tests/test_present.py. */
.bd-headacts .bd-teachbtn[aria-pressed="true"] { background: var(--btn-bg); color: var(--btn-fg); border-color: var(--btn-bg); }
:root[data-style="glass"] .bd-headacts .bd-teachbtn[aria-pressed="true"] { box-shadow: var(--bevel);
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%); }
.bd-headacts .bd-teachbtn[aria-pressed="true"]:hover { border-color: var(--accent); }
.bd-teachbtn .bd-hlabel { display: none; }
@media (min-width: 1440px) {
  .bd-headacts .bd-teachbtn { width: auto; padding: 0 14px 0 11px; gap: 7px; font-size: 13px; font-weight: 500; }
  .bd-teachbtn .bd-hlabel { display: inline; }
}

/* Their cursors (presence.js "Their cursors"; cleaned up 4 Oct 2026 - "make them a bit less distracting"). Over the
   page, its pictures and videos and the words being typed here, and under everything that is pressed: this window's
   own cursor (.bd-cursor), the text's format bar and the lock marks (3), the panes (4), the menus and dialogs - until
   that day they were at 3, after those in the page, so over the format bar and its own cursor.
   Each is a pixel square at the tip (what they point at, and something a test can find) with what is drawn round it:
   - the pointer, in their colour inside a white edge 1.5 px wide - stroked under the fill (paint-order), so the edge is
     all outside the shape and the shape keeps its size - and the softest of shadows. On paper it is seen by its
     colour (each of presence.js COLOURS is 6:1 on white), over ink and slate by its edge; 3:1 either way as drawn, at
     rest too (tests/test_cursors.py). A mouse is an arrow, its tip at the point; a pen a dot, a finger a ring, centred.
   - their name, for a moment (.bd-named), on a small lozenge of the page's own surface with its own words - --ink on
     --surface, 4.5:1 in every look - a dot of their colour at its start and an edge tinted with it; under Glass the
     buttons' hard-stop gloss and bevel. Until that day it was a pill of their colour with white words and a white
     ring, which was most of what caught the eye. A long name ends in "…" (its title has it whole).
   - while they write, rub out or type, a small mark of it by the tip.
   Fading only ever goes down - resting (.bd-rest: 85 %), the name going, the cursor leaving (.bd-away) - over a fifth
   of a second; coming back is at once (no transition in the state it comes back to). None with reduced motion. */
.bd-peers { position: absolute; inset: 0; z-index: 2; pointer-events: none; overflow: hidden; }
/* Glided between the words of where it is, which come every 33 ms (presence.js CURSOR_EVERY): 60 ms is long enough to
   hide one coming late, short enough not to trail (90 ms, with them every 50, until 4 Oct 2026). */
.bd-peer { position: absolute; left: 0; top: 0; width: 1px; height: 1px; transition: transform .06s linear; will-change: transform; }
.bd-peer[hidden] { display: none; }
.bd-ptr { position: absolute; display: block; overflow: visible; fill: var(--pc); stroke: #fff; stroke-width: 3;
  paint-order: stroke; stroke-linejoin: round; filter: drop-shadow(0 .5px 1px rgb(0 0 0 / .22)); }
.bd-peer[data-kind="mouse"] .bd-ptr { left: -2px; top: -2px; }
.bd-peer[data-kind="pen"] .bd-ptr { left: -6px; top: -6px; }
.bd-peer[data-kind="touch"] .bd-ptr { left: -10px; top: -10px; }
/* A finger's ring: their colour 2 px wide, white 1.5 px either side of it, the middle lightly tinted. */
.bd-ptr .bd-edge { fill: none; stroke-width: 5; }
.bd-ptr .bd-edge + circle { fill: color-mix(in srgb, var(--pc) 22%, transparent); stroke: var(--pc); stroke-width: 2; }
.bd-doing { position: absolute; display: block; width: 14px; height: 14px; }
.bd-doing[hidden] { display: none; }
.bd-doing svg { display: block; overflow: visible; }
.bd-doing circle { fill: var(--pc); stroke: #fff; stroke-width: 1.5; }
/* The name's lozenge, and the typer's (below) the same. Placed from the tip by presence.js (placeTags: below and right,
   or turned to the other side near the page's edges), so only how it looks is here. */
.bd-tag, .bd-typer span { display: block; height: 18px; box-sizing: border-box; padding: 0 8px 0 6px; border-radius: 999px;
  white-space: nowrap; background: var(--surface); color: var(--ink); font: 500 11px/16px var(--sans);
  border: 1px solid color-mix(in srgb, var(--pc) 50%, var(--rule-strong)); box-shadow: 0 1px 3px rgb(0 0 0 / .16); }
/* (Glass's bevel, its lit top line dimmer in the dark: --bevel's, a third white, ran along the top of the words of a
   lozenge this small and measured 2.9-3.0:1 against them in the dark - tests/test_cursors.py, 4 Oct 2026.) */
:root[data-style="glass"] :is(.bd-tag, .bd-typer span) { background-image: var(--gloss);
  box-shadow: inset 0 1px 0 light-dark(rgb(255 255 255 / .95), rgb(255 255 255 / .12)),
              inset 0 -2px 3px light-dark(rgb(10 60 80 / .10), rgb(0 0 0 / .28)), 0 1px 3px rgb(0 0 0 / .16); }
@supports not (color: light-dark(#000, #fff)) {
  :root[data-style="glass"] :is(.bd-tag, .bd-typer span) {
    box-shadow: inset 0 1px 0 var(--not-light, rgb(255 255 255 / .95)) var(--not-dark, rgb(255 255 255 / .12)),
                inset 0 -2px 3px var(--not-light, rgb(10 60 80 / .10)) var(--not-dark, rgb(0 0 0 / .28)), 0 1px 3px rgb(0 0 0 / .16);
  }
}
.bd-tag::before, .bd-typer span::before { content: ''; display: inline-block; width: 7px; height: 7px; margin-right: 5px;
  vertical-align: middle; position: relative; top: -1px; border-radius: 50%; background: var(--pc); box-shadow: 0 0 0 1px rgb(255 255 255 / .8); }
.bd-tag { position: absolute; }
/* Two in one text box (27 Oct 2026, board/cotype.js): another's caret in the box - a bar in their colour with a white
   edge (seen on paper and slate, as their cursor is), their name on the lozenge their cursor's has, just above it; "Typing
   with Sam" under the box; and the box a moment quieter while it waits for their words to join them. */
.bd-cocaret { position: absolute; left: 0; top: 0; width: 2px; background: var(--pc); pointer-events: none; z-index: 3;
  box-shadow: 0 0 0 1px rgb(255 255 255 / .85); border-radius: 1px; }
.bd-cocaret[hidden] { display: none; }
.bd-cocaret span { position: absolute; left: -1px; bottom: 100%; margin-bottom: 2px; display: block; height: 16px; padding: 0 6px;
  border-radius: 999px 999px 999px 2px; background: var(--pc); color: #fff; font: 600 10.5px/16px var(--sans); white-space: nowrap; }
.bd-cobadge { position: absolute; left: 0; top: 0; pointer-events: none; z-index: 3; height: 20px; padding: 0 9px; border-radius: 999px;
  background: var(--surface); color: var(--ink); border: 1px solid var(--rule-strong); font: 500 11.5px/18px var(--sans);
  white-space: nowrap; box-shadow: 0 1px 3px rgb(0 0 0 / .14); }
.bd-cobadge[hidden] { display: none; }
.bd-rich.bd-cojoin { opacity: .6; cursor: progress; }
.bd-rich.bd-cojoin::after { content: attr(data-joining); position: absolute; right: 0; bottom: -22px; font: 500 11.5px/18px var(--sans);
  color: var(--ink); background: var(--surface); padding: 0 8px; border-radius: 999px; border: 1px solid var(--rule-strong); white-space: nowrap; }
.bd-tag b { display: inline-block; vertical-align: top; max-width: 132px; overflow: hidden; text-overflow: ellipsis; font-weight: inherit; }
.bd-tag[data-more]::after { content: attr(data-more); margin-left: 4px; font-weight: 600; }
/* Resting, named or not, leaving: see above. */
.bd-ptr, .bd-doing { transition: none; }
.bd-peer.bd-rest :is(.bd-ptr, .bd-doing) { opacity: .85; transition: opacity .2s ease-out; }
.bd-peer .bd-tag { opacity: 0; transition: opacity .2s ease-out; }
.bd-peer.bd-named .bd-tag { opacity: 1; transition: none; }
.bd-peer.bd-merged .bd-tag { opacity: 0; transition: none; }
.bd-peer.bd-away :is(.bd-ptr, .bd-doing, .bd-tag) { opacity: 0; transition: opacity .2s ease-out; }
:root[data-motion="reduce"] :is(.bd-peer, .bd-peer *) { transition: none !important; }
/* Who is typing a text box (2 Oct 2026, presence.js "Words others are typing"): their name on the cursor's lozenge, sat
   on the top left of the frame round the words, moved with the page as their cursor is. */
.bd-typer { position: absolute; left: 0; top: 0; will-change: transform; }
.bd-typer span { position: absolute; left: 0; bottom: 3px; max-width: 180px; overflow: hidden; text-overflow: ellipsis; }
.bd-typer[hidden] { display: none; }

/* Following someone, or leading: said at the top of the page, as "changed in another window" is, with its way out. */
.bd-followbar { left: 50%; top: 14px; transform: translateX(-50%); display: flex; align-items: center; gap: 10px;
  max-width: calc(100% - 24px); padding: 7px 8px 7px 14px; border-radius: 999px; font-size: 13px; color: var(--ink); }
.bd-followbar[hidden] { display: none; }
.bd-followbar .bd-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--pc); flex: none; box-shadow: 0 0 0 1.5px #fff; }
.bd-followbar b { font-weight: 600; }
/* Its lit button (Stop presenting, Let them write, Stop) solid under a lighter gloss, as L14's Restore and L19's
   masthead lozenge are: the glass .btn's own, translucent under its full sheen, measured 2.6-2.9:1 in the light
   theme, every palette (tests/test_present.py, 1 Oct 2026). */
:root[data-style="glass"] .bd-followbar .btn:not(.ghost):not(.danger) { background-color: var(--btn-bg); border-color: var(--btn-bg);
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%); }

/* The pill (14 Oct 2026, the owner: "could we make the presenting more obvious so I don't forget I'm presenting, and
   remove the wordy bits"; presence.js drawBar). Comes down from the top as it comes, and goes back up (GSMotion);
   opacity alone where glass moves by fading (an iPad on Glass). The owner's while the others watch (.bd-livepill):
   edged in the live red, a dot of it breathing out a ring, an eye and how many, and ■ - a round lit button. The red
   is a mark, not words: 3.8:1 on the paper and on slate, more on the panes; every word is --ink on the pane, as the
   bar's always were (measured as drawn, tests/test_present.py and tests/test_present_obvious.py). */
.bd-followbar, .bd-liveframe { --live: #e64545; }
.bd-followbar { transition: opacity var(--t-quick) var(--ease-out), translate var(--t-mid) var(--ease-out); }
.bd-followbar:is(.gs-enter, .gs-leave) { opacity: 0; translate: 0 -12px; }
.bd-followbar.gs-leave { pointer-events: none; transition-duration: var(--t-quick-out); transition-timing-function: var(--ease-away); }
:root[data-glass-motion="fade"][data-style="glass"] .bd-followbar:is(.gs-enter, .gs-leave) { translate: none; }
.bd-followbar.bd-livepill { gap: 9px; padding: 4px 4px 4px 13px; border-color: var(--live); }
:root[data-style="glass"] .bd-followbar.bd-livepill { border-color: var(--live); }
.bd-liveword { font-weight: 600; }
.bd-livedot { position: relative; width: 10px; height: 10px; border-radius: 50%; background: var(--live); flex: none; }
.bd-livedot::after { content: ''; position: absolute; inset: 0; border-radius: 50%; background: var(--live);
  animation: bd-liveping 2s var(--ease-out) infinite; }
@keyframes bd-liveping { from { transform: scale(1); opacity: .55; } 70%, to { transform: scale(2.6); opacity: 0; } }
.bd-watchers { display: inline-flex; align-items: center; gap: 4px; font-variant-numeric: tabular-nums; }
.bd-watchers svg { width: 17px; height: 17px; flex: none; }
.bd-followbar .bd-pillstop { display: inline-grid; place-items: center; width: 28px; height: 28px; padding: 0; border-radius: 50%; }
.bd-pillstop svg { width: 16px; height: 16px; }

/* The frame (14 Oct 2026; presence.js drawFrame): while everyone else follows the owner, a red line round the whole
   window, 3 px, and inside it a glow breathing slowly in and out - seen at the edge of the eye, over whatever is open,
   pressing nothing. Fades in and out with the pill; with motion turned down the glow holds still, half lit. */
.bd-liveframe { position: fixed; inset: 0; z-index: 500; pointer-events: none; box-shadow: inset 0 0 0 3px var(--live);
  transition: opacity var(--t-mid) var(--ease-out); }
.bd-liveframe[hidden] { display: none; }
.bd-liveframe::after { content: ''; position: absolute; inset: 0; pointer-events: none;
  box-shadow: inset 0 0 24px 5px rgb(230 69 69 / .5); animation: bd-livebreathe 3.2s ease-in-out infinite alternate; }
@keyframes bd-livebreathe { from { opacity: .3; } to { opacity: 1; } }
.bd-liveframe:is(.gs-enter, .gs-leave) { opacity: 0; }
.bd-liveframe.gs-leave { transition-timing-function: var(--ease-away); }
:root[data-motion="reduce"] .bd-liveframe::after { animation: none; opacity: .6; }
:root[data-motion="reduce"] .bd-livedot::after { animation: none; opacity: 0; }

/* ═══ Share, from inside the whiteboard (1 Oct 2026: templates/board.html #bd-share, static/js/board/share.js) ═══ */
/* A dialog, so on Glass it is already a pane (the glass rules above); these only lay it out. One column that fits a
   phone: what they may do (three lozenges, one lit, each saying what it means), who it is for, Create link; then the
   link with a big Copy link; then the links that already open this whiteboard. Every word is --ink on the pane, or
   the People page's soft grey (--ink 45% into --muted: measured there on the panes, and here as drawn in every look
   - tests/test_topbar.py); the lit buttons are opaque under the lighter gloss, as the Appearance panel's are. */
dialog.bd-share { --bs-soft: color-mix(in srgb, var(--ink) 45%, var(--muted));
  width: min(460px, calc(100% - 24px)); max-width: none; margin: auto;
  max-height: calc(100vh - 24px); max-height: calc(100dvh - 24px); overflow: hidden;
  flex-direction: column; }                            /* (on the closed one too: dialog.settings says why) */
dialog.bd-share[open] { display: flex; }
dialog.bd-share::backdrop, dialog.bd-share + .gs-backdrop { background: color-mix(in srgb, var(--backdrop, rgb(0 0 0 / .3)) 70%, transparent); }
.bs-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 20px 8px; }
.bs-head h2 { font-size: 22px; line-height: 1.2; }
.bs-body { overflow-y: auto; overscroll-behavior: contain; padding: 0 20px 16px; }
.bs-lede { margin: 0 0 14px; font-size: 13.5px; line-height: 1.5; color: var(--ink); }
.bs-lede b { font-weight: 600; }
.bs-label { display: block; margin: 0 0 7px; padding: 0; font-family: var(--sans); font-size: 11px; font-weight: 500;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink); }
.bs-opt { text-transform: none; letter-spacing: 0; font-weight: 400; color: var(--bs-soft); }
.bs-roles { border: 0; min-width: 0; display: flex; flex-direction: column; gap: 6px; margin: 0 0 14px; }
.bs-role { display: grid; grid-template-columns: auto 1fr; column-gap: 10px; align-items: baseline; margin: 0;
  padding: 9px 13px; border-radius: 14px; border: 1px solid var(--rule-strong); cursor: pointer;
  font-size: 14px; letter-spacing: 0; text-transform: none; color: var(--ink); transition: background .15s, border-color .15s; }
.bs-role b { font-weight: 500; color: var(--ink); }
.bs-role span { grid-column: 2; font-size: 12.5px; line-height: 1.4; color: var(--bs-soft); }
.bs-role input { width: auto; margin: 0; padding: 0; box-shadow: none; accent-color: var(--accent); }
.bs-role:hover { background: color-mix(in srgb, var(--accent) 7%, transparent); }
.bs-role:has(input:checked) { border-color: color-mix(in srgb, var(--accent) 60%, var(--rule-strong));
  background: color-mix(in srgb, var(--accent) 9%, transparent); }
.bs-role:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Lit under the panes' softer sheen, not --gloss, and a lighter tint than People's 13%: measured on Glass in the
   dark theme, those took the lit role's own name to 4.3:1 (Citrus) and its line to 3.7-4.4:1 in every palette - and
   that line is lifted further toward the ink while lit. */
:root[data-style="glass"] .bs-role:has(input:checked) { background-image: var(--gloss-soft); box-shadow: var(--bevel); }
.bs-role:has(input:checked) span { color: color-mix(in srgb, var(--ink) 70%, var(--muted)); }
.bs-for { margin: 0 0 14px; }
.bs-error { display: flex; gap: 7px; margin: 0 0 12px; font-size: 13.5px; font-weight: 500; color: var(--ink); }
.bs-error::before { content: '!'; flex: none; display: grid; place-items: center; width: 16px; height: 16px; margin-top: 2px;
  border-radius: 50%; background: var(--red); color: var(--surface); font-size: 11px; font-weight: 700; }
dialog.bd-share .btn { justify-content: center; }
.bs-make, .bs-copy { width: 100%; min-height: 44px; font-size: 13px; }
/* Stop sharing, asked: the red words on the pane itself, as Plain has them. Glass's danger lozenge (its pale red
   under the buttons' white sheen) measured 2.8:1 in the dark theme, in every palette, and bare --red on the dark
   pane 4.1-4.4:1 at this size: so a red lifted a little toward the ink. */
:root[data-style="glass"] dialog.bd-share .btn.danger { background-color: transparent; background-image: none;
  color: color-mix(in srgb, var(--red) 72%, var(--ink)); border-color: var(--red); backdrop-filter: none; -webkit-backdrop-filter: none; }
:root[data-style="glass"] dialog.bd-share .btn:not(.ghost):not(.danger) { background-color: var(--btn-bg); border-color: var(--btn-bg);
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%); }
.bs-made { margin: 0 0 4px; padding: 14px; border-radius: 16px; border: 1px solid var(--rule-strong);
  background: color-mix(in srgb, var(--accent) 7%, transparent); }
.bs-says { margin: 0 0 10px; font-size: 13.5px; line-height: 1.5; color: var(--ink); }
.bs-says b { font-weight: 600; }
input.bs-url { margin: 0 0 10px; font-family: ui-monospace, 'Cascadia Mono', Consolas, monospace; font-size: 13px; }
.bs-copyrow { display: flex; gap: 8px; }
.bs-copyrow .bs-copy { flex: 1 1 auto; width: auto; }
.bs-copyrow .bs-send { flex: none; min-height: 44px; }
.bs-said { min-height: 1.5em; margin: 6px 0 0; font-size: 12.5px; font-weight: 500; color: var(--ink); }
.bs-note { margin: 2px 0 10px; font-size: 12px; line-height: 1.45; color: var(--bs-soft); }
.bs-try { margin: 8px 0 0; text-align: center; }
.bs-list { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--rule); }
.bs-links { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.bs-link { padding: 10px 12px; border-radius: 14px; border: 1px solid var(--rule-strong); }
.bs-linkhead { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-width: 0; }
.bs-who { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; font-weight: 500; color: var(--ink); }
.bs-chip { flex: none; padding: 1px 9px; border-radius: 999px; border: 1px solid var(--rule-strong); font-size: 12px; color: var(--ink); white-space: nowrap; }
.bs-chip.bs-r-edit_ai { background: color-mix(in srgb, var(--accent) 13%, transparent);
  border-color: color-mix(in srgb, var(--accent) 45%, var(--rule-strong)); }
/* A link's role, changed in place (2 Oct 2026): where its chip was, three small choices in one lozenge - View, Edit,
   Edit + AI - the one it has lit as the roles above are lit (the same light tint, ringed in the accent, and on Glass
   the panes' softer sheen), every word --ink: measured as drawn in every look with the rest of the dialog
   (tests/test_topbar.py). A finger gets taller ones. */
.bs-rolepick { flex: none; display: inline-flex; gap: 2px; padding: 2px; border: 1px solid var(--rule-strong); border-radius: 999px; }
.bs-rolepick button { min-height: 24px; margin: 0; padding: 2px 10px; border: 0; border-radius: 999px; background: transparent;
  box-shadow: none; cursor: pointer; font: inherit; font-size: 12px; font-weight: 500; letter-spacing: 0; color: var(--ink);
  white-space: nowrap; }
.bs-rolepick button:hover:not([aria-pressed="true"]):not(:disabled) { background: color-mix(in srgb, var(--accent) 7%, transparent); }
.bs-rolepick button[aria-pressed="true"] { cursor: default; background: color-mix(in srgb, var(--accent) 9%, transparent);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 60%, var(--rule-strong)); }
:root[data-style="glass"] .bs-rolepick button[aria-pressed="true"] { background-image: var(--gloss-soft);
  box-shadow: var(--bevel), inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 60%, var(--rule-strong)); }
.bs-rolepick button:disabled { cursor: progress; }
dialog.bd-share .bs-rolepick button:focus-visible { outline-offset: 1px; }
@media (pointer: coarse) { .bs-rolepick button { min-height: 36px; padding: 2px 12px; } }
/* A long name and the choices beside it on a phone: the choices go under the name rather than leave it two letters. */
.bs-linkhead:has(.bs-rolepick) { flex-wrap: wrap; row-gap: 6px; }
.bs-when, .bs-reach { margin: 3px 0 0; font-size: 12px; line-height: 1.4; color: var(--bs-soft); }
.bs-reach a, .bs-offnote a { color: var(--ink); text-decoration: underline; text-underline-offset: 2px; }
.bs-acts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }
.bs-ask p { margin: 9px 0 0; font-size: 13px; line-height: 1.45; color: var(--ink); }
.bs-none, .bs-offnote, .bs-gone { margin: 0; font-size: 13px; color: var(--bs-soft); }
.bs-offnote, .bs-gone { list-style: none; padding: 2px 2px 0; }
.bs-foot { display: flex; justify-content: flex-end; padding: 10px 20px 14px; border-top: 1px solid var(--rule); }
.bs-people { font-size: 13px; color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
.bs-people:hover { color: var(--accent); }
dialog.bd-share :is(button, a, input):focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (max-width: 520px) {
  .bs-head { padding: 14px 16px 6px; }
  .bs-body { padding: 0 16px 14px; }
  .bs-foot { padding: 10px 16px 12px; }
}
@media (prefers-reduced-transparency: reduce) {
  :root[data-style="glass"] dialog.bd-share { background-color: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* The masthead's round buttons (the look, light or dark, signing out) a little apart from the pages' names, as the
   editor's bar keeps its groups apart (1 Oct 2026) - whichever comes first of them. */
nav.main > :is(a, .who-chip, .who-as) + :not(a, .who-chip, .who-as) { margin-left: 10px; }

/* ── Tool arcs (4 Oct 2026; one fan, 5 Oct 2026) ──────────────────────────
   The owner, of the pen's and the highlighter's panes: "could we be more creative with these menus maybe doing some
   more radial adjacent stuff". A tool's options open as arcs out of its own button on the rail (board/toolarcs.js):
   glass bands bent round the button, one ring inside the next, in the radial menu's language - its orbs, its lens of
   light (.bd-rlens), its bloom (--bloom-time, --ease-pop) and its close (bd-rclose, --close-time). board.js draws the
   same options as ever: these rules only lay its own buttons out, so #bd-props itself is a point - the button's
   centre - and every choice is placed from it by --x and --y. Each rule is under .bd-props.bd-arcs (three classes),
   which outweighs the options' own rules wherever they come in the file; under :root as well where those rules have
   a :root of their own (a kind lit on glass).

   5 Oct 2026, the owner: "let's do another iteration of improving this design". Beads, orbs, cards, tags and a card of
   words in one menu, and three ways of showing the one in hand, became one language: every choice an orb of one size
   (--s: 34 px, 40 to press - the ::after round it), a colour an orb of that colour, anything else the radial menu's
   glass orb with a picture - a dot of its size, a kind's drawing, a stroke of a brush, Aa in a face, a T of a size, a
   square of a fill; the one in hand an accent ring round it, whatever it is; no tags (a choice's name is said beside
   it as it is pointed at: .bd-atip). The opacity a thin track outside the outermost ring, its knob saying its value
   only while held or with the keys (.bd-ashow).

   The bands are clipped to their shape (clip-path: path(), from board/toolarcs.js), so their glass blurs only what is
   under the band; a rim is the same path stroked inside it. Their gloss is the hard stop bent round: the outer half of
   the band lit (--r, its middle). Motion turned down: nothing blooms, glides or fades ("Motion" makes it take no
   time, and the script asks GSBoard.still). */
.bd-stage > .bd-props.bd-arcs { width: 0; height: 0; padding: 0; border: 0; border-radius: 0; background: none; box-shadow: none;
  -webkit-backdrop-filter: none; backdrop-filter: none; overflow: visible; max-width: none; max-height: none; display: block;
  z-index: 6; pointer-events: none; }
:root[data-style="glass"] .bd-stage > .bd-props.bd-arcs { background: none; box-shadow: none; -webkit-backdrop-filter: none; backdrop-filter: none; }
.bd-props.bd-arcs :is(.bd-swatches, .bd-sizes, .bd-brushes, .bd-brushgrid, .bd-kindgrid, .bd-seg, .bd-opacity, .bd-asubbox) { display: contents; }
/* What the arcs leave to the sheet: the words to read (they are the buttons' tooltips), the tags, the pen's Recent
   (the setup before, at the start of the colours, and the one lit say it), the 3D tile's "3D" and arrow. */
.bd-props.bd-arcs :is(.bd-brushhead, .bd-kindgrid > i, .bd-kindlabel, .bd-seglabel, .bd-plabel, .bd-recent, .bd-opacity > input,
  .bd-opacity > output, .bd-solidtile .bd-tile3d, .bd-solidtile .bd-tilechev) { display: none; }
/* Every choice: placed by its middle. Only its scale and opacity ever move on their own (pointed at, stepping aside). */
.bd-props.bd-arcs .bd-aitem { position: absolute; left: 0; top: 0; margin: 0; z-index: 2; pointer-events: auto;
  translate: calc(var(--x) - 50%) calc(var(--y) - 50%); animation: none; transition: scale .12s var(--ease-out), opacity .12s var(--ease-out); }
/* Blooming out of the button, and going back into it (5 Oct 2026, the owner: "animation when switching between is a
   nice addition but could be a bit smoother"): one curve both ways (--ease-out, the radial menu's, no overshoot: the
   pop's 12% read as a wobble), --bloom-time to open and --close-time to go; each ring one sweep, end to end within
   50 ms (--d, board/toolarcs.js), the next 20 ms after it - not a choice-by-choice ripple. Taking up another tool with
   them open, the old fade into their button as the new bloom out of theirs, at once (.bd-aghost). Only opacity,
   translate and scale move, by the clock; will-change only while they do. */
/* (The choices from 80% of the way out, not from the button's middle: flying the whole way they ran behind their
   bands, which grow from 80% of their size, and read as a spray - the fan now grows out as one piece.) */
/* Going (7 Oct 2026, the owner: "can we make sure the animations of opening menus etc are super clean"): each part
   of the copy fades and draws in towards the button itself - the bands, the track, the lens, every choice - never the
   copy as a whole: opacity moving on what holds the bands' glass switched their blur off for as long as it moved,
   and they snapped from frosted to clear as they went. On --ease-away, which holds a moment before it goes (a
   switch's copy is the exception, ring by ring with the new: .bd-aswitch, below). The copy
   starts from where each part had got to (board/toolarcs.js ghostOut pins it), so a switch mid-bloom never jumps to
   whole first; one switch over another starts the older copies' going again from where they are, with the new. */
@keyframes bd-afly { from { opacity: 0; translate: calc(var(--x) * .8 - 50%) calc(var(--y) * .8 - 50%); scale: .7; } }
@keyframes bd-aband { from { opacity: 0; scale: .8; } }
@keyframes bd-agone { to { opacity: 0; scale: .84; } }
@keyframes bd-agoneitem { to { opacity: 0; translate: calc(var(--x) * .84 - 50%) calc(var(--y) * .84 - 50%); scale: .84; } }
.bd-props.bd-arcs.bd-abloom :is(.bd-aitem, .bd-anotch) { animation: bd-afly var(--bloom-time) var(--ease-out) var(--d, 0ms) backwards; will-change: opacity, translate, scale; }
.bd-props.bd-arcs.bd-abloom :is(.bd-aband, .bd-atrack) { animation: bd-aband var(--bloom-time) var(--ease-out) var(--d, 0ms) backwards; will-change: opacity, scale; }
.bd-props.bd-arcs.bd-aghost { z-index: 5; }
.bd-props.bd-arcs.bd-agoing > :is(.bd-aband, .bd-atrack, .bd-rlens) { animation: bd-agone var(--close-time) var(--ease-away) forwards; }
.bd-props.bd-arcs.bd-agoing :is(.bd-aitem, .bd-anotch) { animation: bd-agoneitem var(--close-time) var(--ease-away) forwards; }
.bd-props.bd-arcs.bd-agoing > :is(.bd-asel, .bd-atip) { animation: gs-fadeout var(--close-time) var(--ease-away) forwards; }
.bd-props.bd-arcs.bd-agoing * { pointer-events: none !important; transition: none !important; }
/* Another tool taken up with them open (.bd-aswitch, board/toolarcs.js ghostOut): each ring of the old set goes
   quickly, on --ease-out, as the new one blooms in the same ring - from the moment it does (its --d, the bloom's
   own) - and not held first, as a close is: so held, the two sets were both nearly whole together for up to 100 ms
   (7 Oct 2026, after the review). The old is under a third by the time the new is half there, ring by ring. */
.bd-props.bd-arcs.bd-agoing.bd-aswitch :is(.bd-aband, .bd-atrack, .bd-rlens, .bd-aitem, .bd-anotch, .bd-asel, .bd-atip) {
  animation-duration: var(--t-quick-out); animation-timing-function: var(--ease-out); animation-delay: var(--d, 0ms); }
/* An iPad on Glass (data-glass-motion="fade", base.html): the bands' and the words' blurred glass comes and goes by
   its opacity alone - WebKit there redraws a blurred pane badly while it is scaled or moved. The orbs, with no blur,
   still fly. */
:root[data-glass-motion="fade"][data-style="glass"] .bd-props.bd-arcs.bd-abloom :is(.bd-aband, .bd-apill) { animation-name: gs-fadein; }
:root[data-glass-motion="fade"][data-style="glass"] .bd-props.bd-arcs:not(.bd-agoing, .bd-asubfold) > .bd-aband.bd-asub { animation-name: gs-fadein; }
:root[data-glass-motion="fade"][data-style="glass"] .bd-props.bd-arcs.bd-agoing :is(.bd-aband, .bd-apill) { animation-name: gs-fadeout; }
/* The one in hand changed (board/toolarcs.js slideRings): its accent ring slides there from the one before, rather
   than going out on one and coming on on the other. */
.bd-props.bd-arcs .bd-asel { position: absolute; left: 0; top: 0; z-index: 3; width: calc(var(--s) + 5px); height: calc(var(--s) + 5px);
  margin: calc(var(--s) / -2 - 2.5px) 0 0 calc(var(--s) / -2 - 2.5px); border-radius: 50%; box-shadow: inset 0 0 0 2.5px var(--accent);
  pointer-events: none; will-change: translate; }
:root .bd-props.bd-arcs .bd-aorb.bd-aarrive.bd-alit { box-shadow: inset 0 1px 0 rgb(255 255 255 / .55), 0 1px 3px rgb(0 0 0 / .14); }
:root[data-style="glass"] .bd-props.bd-arcs .bd-aorb.bd-aarrive.bd-alit { box-shadow: var(--bevel), 0 1px 3px rgb(0 0 0 / .14); }
.bd-props.bd-arcs .bd-abead.bd-aarrive.bd-alit { box-shadow: inset 0 1px 2px rgb(255 255 255 / .35); }
.bd-props.bd-arcs .bd-swatch.bd-mine.bd-aarrive.bd-alit { box-shadow: 0 0 0 1.5px var(--mine-ring), inset 0 1px 2px rgb(255 255 255 / .35); }
/* Over a whiteboard the page's near-white paper is behind this glass: in the dark the usual 86% pane turned mid-grey
   over it, and the light words and dots on it fell under 4.5:1 and 3:1 (the largest size's dot 2.7-2.9, the sheet's
   words 3.1-4.1, measured as drawn in every palette) - nearly solid there, as Settings is over the editor. */
:root[data-style="glass"] .bd-props:is(.bd-arcs, .bd-sheet) {
  --pane: light-dark(color-mix(in srgb, var(--surface) 86%, transparent), color-mix(in srgb, var(--surface) 96%, transparent)); }
@supports not (color: light-dark(#000, #fff)) {
  :root[data-style="glass"] .bd-props:is(.bd-arcs, .bd-sheet) {
    --pane: var(--not-light, color-mix(in srgb, var(--surface) 86%, transparent)) var(--not-dark, color-mix(in srgb, var(--surface) 96%, transparent));
  }
}
/* The bands: one to a ring, its first choice to its last. */
.bd-props.bd-arcs .bd-aband { position: absolute; z-index: 0; pointer-events: auto; background: var(--surface); }
:root[data-style="glass"] .bd-props.bd-arcs .bd-aband {
  background-color: color-mix(in srgb, var(--accent) 7%, var(--pane));
  background-image: radial-gradient(circle, transparent var(--r), var(--sheen-soft) calc(var(--r) + .5px), transparent calc(var(--r) + var(--t) / 2));
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); }
.bd-props.bd-arcs .bd-aband svg { position: absolute; left: 0; top: 0; overflow: visible; pointer-events: none; }
.bd-props.bd-arcs .bd-aband path { fill: none; stroke: var(--rule-strong); stroke-width: 2.5; }
:root[data-style="glass"] .bd-props.bd-arcs .bd-aband path { stroke: color-mix(in srgb, var(--accent) 30%, var(--rule-strong)); }
.bd-props.bd-arcs .bd-rlens { z-index: 1; }
/* Each choice's 40 px to press, round its 34 px orb (the pitch along a ring is 40: no two share a pixel). */
.bd-props.bd-arcs :is(.bd-aorb, .bd-abead)::after { content: ''; position: absolute; inset: -3px; border-radius: 50%; }
/* A colour: an orb of that colour, glossy as the bubbles are. */
.bd-props.bd-arcs .bd-abead { width: var(--s); height: var(--s); transform: none; }
.bd-props.bd-arcs .bd-abead:not(.bd-custom) { background: radial-gradient(circle at 50% 26%, rgb(255 255 255 / .5) 0 16%, transparent 46%), var(--sw); }
.bd-props.bd-arcs .bd-custom { overflow: visible; }
.bd-props.bd-arcs .bd-custom input { inset: -3px; width: auto; height: auto; }
/* Your own colours' ring (board.js, mineRow) in more of the ink here: on a band, tinted with the accent, Citrus's read
   2.92-2.96:1 with the pane's 72%. */
.bd-props.bd-arcs .bd-swatch.bd-mine { --mine-ring: color-mix(in srgb, var(--ink) 86%, transparent); }
/* Anything else: the radial menu's own glass orb (its look again here, as the options' rules for the kinds, sizes,
   brushes and words come elsewhere in the file and each had a shape of its own). */
:root .bd-props.bd-arcs .bd-aorb { width: var(--s); height: var(--s); min-width: 0; min-height: 0; padding: 0; display: grid; place-items: center;
  border-radius: 50%; border: 1px solid color-mix(in srgb, var(--ink) 16%, transparent); color: var(--ink); font: inherit;
  background: light-dark(color-mix(in srgb, var(--surface) 84%, transparent), var(--surface));
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .55), 0 1px 3px rgb(0 0 0 / .14); }
@supports not (color: light-dark(#000, #fff)) {
  :root .bd-props.bd-arcs .bd-aorb {
    background: var(--not-light, color-mix(in srgb, var(--surface) 84%, transparent)) var(--not-dark, var(--surface));
  }
}
:root[data-style="glass"] .bd-props.bd-arcs .bd-aorb { background-image: var(--gloss-soft); box-shadow: var(--bevel), 0 1px 3px rgb(0 0 0 / .14); }
/* The one in hand, whatever it is: an accent ring round it (and aria-pressed) - not a larger orb, a fill, a lifted card. */
:root .bd-props.bd-arcs .bd-aorb.bd-alit { border-color: color-mix(in srgb, var(--accent) 70%, transparent);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .55), 0 0 0 2.5px var(--accent); }
:root[data-style="glass"] .bd-props.bd-arcs .bd-aorb.bd-alit { box-shadow: var(--bevel), 0 0 0 2.5px var(--accent); }
.bd-props.bd-arcs .bd-abead.bd-alit { box-shadow: 0 0 0 2.5px var(--accent), inset 0 1px 2px rgb(255 255 255 / .35); }
.bd-props.bd-arcs .bd-swatch.bd-mine.bd-alit { box-shadow: 0 0 0 1.5px var(--mine-ring), 0 0 0 4px var(--accent), inset 0 1px 2px rgb(255 255 255 / .35); }
/* Pointed at: the lens under it, and a touch larger. */
.bd-props.bd-arcs :is(.bd-aorb, .bd-abead).bd-raim { scale: 1.08; }
.bd-props.bd-arcs :is(.bd-aorb, .bd-abead, .bd-apill):focus-visible, .bd-props.bd-arcs .bd-custom:focus-within {
  outline: 2px solid var(--accent); outline-offset: 4px; }
.bd-props.bd-arcs .bd-aorb:disabled { opacity: .45; cursor: default; }
.bd-props.bd-arcs .bd-aorb.bd-dim:not(.bd-alit) { opacity: .55; }
/* 18 px, as the radial menu's arcs have them: a picture's corners further out met the lit orb's accent rim, light
   on light in the dark (a solid pointed at read 1.8-2.9:1 at 21 px). */
.bd-props.bd-arcs .bd-aorb svg { width: 18px; height: 18px; margin: 0; }
/* A size's dot, at its size: kept inside its orb (the eraser's largest met the orb's rim at 22 px and read at
   2.5-2.9:1 there) and in more of the pane's ink. */
.bd-props.bd-arcs .bd-size i { max-width: 20px; max-height: 20px; opacity: .88; }
.bd-props.bd-arcs .bd-size.bd-alit i { opacity: 1; }
/* A nib or a brush: a round of the paper with a stroke of it, as it will write (brushes.js's own picture). */
.bd-props.bd-arcs .bd-brush img { width: calc(var(--s) - 6px); height: calc(var(--s) - 6px); border-radius: 50%; object-fit: cover; }
/* The setup before: a short stroke of it (board.js prevButton). */
.bd-props.bd-arcs .bd-aprev svg { width: 24px; height: 16px; }
.bd-props.bd-arcs .bd-aprev .bd-recentimg { width: 26px; height: 13px; border-radius: 6px; }
.bd-props.bd-arcs .bd-aprev:disabled path { stroke: color-mix(in srgb, var(--ink) 40%, transparent); stroke-dasharray: 2 3; }
/* Words as orbs (board/toolarcs.js glyphOf): the button's own words hidden (the sheet has them; the name is said as it
   is pointed at), its picture in their place - Aa in each face, a T of each size, ∑, +, a fill's square, Box and Lasso. */
:root .bd-props.bd-arcs .bd-aorb.bd-aworded { font-size: 0; }
.bd-props.bd-arcs .bd-aworded > :not(.bd-aglyph) { display: none; }
/* (Laid over the whole orb and centred in it: the button's own words, left as a text of no size, are a row of the
   orb's grid of their own, and pushed the picture up into the orb's lit rim - "+" read 2.85:1 there, in the dark.) */
.bd-props.bd-arcs .bd-aglyph { position: absolute; inset: 0; display: grid; place-items: center; font-size: 14.5px; font-weight: 600;
  line-height: 1; color: var(--ink); pointer-events: none; }
.bd-props.bd-arcs .bd-eqnew .bd-aglyph { font-size: 17px; }
.bd-props.bd-arcs .bd-apic svg { display: block; overflow: visible; }
.bd-props.bd-arcs .bd-apic path { fill: none; stroke: var(--ink); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.bd-props.bd-arcs .bd-apic path.bd-ktint { fill: color-mix(in srgb, var(--kc, currentColor) 32%, transparent); }
.bd-props.bd-arcs .bd-apic path.bd-ksolid { fill: var(--kc, currentColor); }
/* The 3D solids (board.js solidTile; "should think how to implement the 3D shapes to not clutter menus"): on the arcs
   the tile is one orb like the other kinds - the solid last used, its picture alone - and a small notch at its outer
   foot that says there is more (5 Oct 2026: not the arrow's badge, which was fiddly). Pressed, the eight open as an
   arc of their own out of it, and the rings beyond the tile's step aside (board/toolarcs.js: solidsArc), as the radial
   menu's do for an arc. */
.bd-props.bd-arcs .bd-solidtile::before { content: ''; position: absolute; right: 5px; bottom: 5px; width: 5px; height: 5px;
  border-right: 1.5px solid color-mix(in srgb, var(--ink) 60%, transparent); border-bottom: 1.5px solid color-mix(in srgb, var(--ink) 60%, transparent);
  border-bottom-right-radius: 2px; pointer-events: none; }
.bd-props.bd-arcs[data-dir="left"] .bd-solidtile::before { right: auto; left: 5px; border-right: 0; border-bottom-right-radius: 0;
  border-left: 1.5px solid color-mix(in srgb, var(--ink) 60%, transparent); border-bottom-left-radius: 2px; }
:root .bd-props.bd-arcs .bd-solidtile[aria-expanded="true"] { border-color: color-mix(in srgb, var(--accent) 75%, transparent); }
@keyframes bd-asubfly { from { opacity: 0; translate: calc(var(--fx) - 50%) calc(var(--fy) - 50%); scale: .5; } }
.bd-props.bd-arcs .bd-aitem.bd-asub { animation: bd-asubfly var(--arc-time) var(--ease-out) var(--d, 0ms) backwards; }
.bd-props.bd-arcs .bd-aband.bd-asub { animation: bd-aband var(--arc-time) var(--ease-out) backwards; }
.bd-props.bd-arcs.bd-asubopen :is(.bd-aitem, .bd-aband, .bd-atrack):not(.bd-akeep, .bd-asub) { opacity: 0; visibility: hidden;
  pointer-events: none; transition: opacity .1s var(--ease-away), visibility 0s .1s; }
/* The rings beyond stepping aside and back (7 Oct 2026, "super clean"): their bands are made afresh as the solids open
   and close (board/toolarcs.js lay), and a thing just made has no transition to start from - the bands went at once
   while their orbs faded over 0.1 s, and came back at once while the orbs took 0.12. Now each band fades with its
   orbs (.bd-aaway, .bd-aback: board/toolarcs.js openSolids, closeSolids), on the same time and curve. Closing, the
   solids themselves - taken away at once by the same new drawing - fold back into their tile from a copy
   (.bd-asubfold), as the radial menu's arcs fold into their choice. */
@keyframes bd-aout { from { opacity: 1; visibility: visible; } to { opacity: 0; visibility: visible; } }
.bd-props.bd-arcs .bd-aband.bd-aaway { animation: bd-aout .1s var(--ease-away); }
.bd-props.bd-arcs .bd-aband.bd-aback { animation: gs-fadein .12s var(--ease-out) backwards; }
@keyframes bd-asubfold { to { opacity: 0; translate: calc(var(--fx) - 50%) calc(var(--fy) - 50%); scale: .5; } }
.bd-props.bd-arcs.bd-aghost.bd-asubfold { z-index: 7; }     /* (over the rings coming back, not under them) */
.bd-props.bd-arcs.bd-asubfold .bd-aitem { animation: bd-asubfold var(--t-quick-out) var(--ease-away) forwards; }
.bd-props.bd-arcs.bd-asubfold .bd-aband { animation: gs-fadeout var(--t-quick-out) var(--ease-away) forwards; }
/* A feature's own words, kept as words (none of the Board's own now): a lozenge of the orbs' height and glass. */
.bd-props.bd-arcs .bd-apill { width: max-content; min-height: var(--s); padding: 5px 14px; border-radius: 999px; border: 1px solid var(--rule-strong);
  background: var(--surface); color: var(--ink); font-size: 12.5px; line-height: 18px; white-space: nowrap; cursor: pointer;
  box-shadow: 0 1px 3px rgb(0 0 0 / .14); }
:root[data-style="glass"] .bd-props.bd-arcs .bd-apill { background-color: var(--pane); background-image: var(--gloss-soft);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); box-shadow: var(--rim), 0 1px 3px rgb(0 0 0 / .14); }
.bd-props.bd-arcs .bd-apill.bd-alit { box-shadow: 0 0 0 2.5px var(--accent); font-weight: 600; }
/* Text's font and size on the arcs, and its Box (23 Oct 2026, the owner: "also this text menu is bad" - two wide
   lozenges across an empty band, past its edge, out of line with ∑): their buttons orbs of the ring as any other
   (board/toolarcs.js model), the lozenges round them no box of their own. − and + a sign each; Aa written in the face
   with the size under it, smaller - the size there at a glance, on the orb's own glass (the pane's ink on it, 4.5:1
   measured in every look: tests/test_toolarcs.py); Box the box as it is (board.js boxIcon), lit by the accent ring
   while on, as every choice in use is (not filled: the lens over a filled one lost its picture, 22 Oct 2026), and its
   ▾ a notch on the orb's rim, out from the button and down, inside the band and clear of the box's picture - solid,
   so its sign reads on it. */
.bd-props.bd-arcs :is(.bd-fsz, .bd-fszsteps, .bd-tboxc) { display: contents; }
.bd-props.bd-arcs .bd-fszface :is(.bd-fszname, .bd-fszdot) { display: none; }
/* (The two lines low in the orb and close together, and no wider than they need: Aa's letters higher up stood in its
   lit rim and the gloss over its top, and read 2.5:1 there in the dark - measured as drawn.) */
:root .bd-props.bd-arcs .bd-aorb.bd-fszface { grid-template-rows: auto auto; align-content: center; gap: 0; padding-top: 2px; }
.bd-props.bd-arcs .bd-fszaa { display: block; font-size: 13px; line-height: 12px; color: var(--ink); white-space: nowrap; }
.bd-props.bd-arcs .bd-fszaa[data-face="hand"] { font-size: 15.5px; }
.bd-props.bd-arcs .bd-fszface .bd-fsznum { min-width: 0; font-size: 10px; font-weight: 700; line-height: 9px; color: var(--ink);
  font-variant-numeric: tabular-nums; }
:root .bd-props.bd-arcs .bd-aorb.bd-fszstep { font-size: 18px; font-weight: 600; line-height: 1; }
.bd-props.bd-arcs .bd-tboxon > span { display: none; }
:root .bd-props.bd-arcs .bd-aorb.bd-tboxon { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
:root .bd-props.bd-arcs .bd-aorb.bd-tboxon[aria-pressed="true"] { border-color: color-mix(in srgb, var(--accent) 70%, transparent);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .55), 0 0 0 2.5px var(--accent); }
:root[data-style="glass"] .bd-props.bd-arcs .bd-aorb.bd-tboxon[aria-pressed="true"] { box-shadow: var(--bevel), 0 0 0 2.5px var(--accent); }
.bd-props.bd-arcs .bd-tboxc .bd-anotch { position: absolute; left: 0; top: 0; z-index: 3; width: 14px; min-width: 0; height: 14px; min-height: 0;
  margin: 0; padding: 0; display: grid; place-items: center; border-radius: 50%; pointer-events: auto;
  translate: calc(var(--x) - 50%) calc(var(--y) - 50%); transition: scale .12s var(--ease-out);
  background: var(--surface); color: var(--ink); border: 1px solid color-mix(in srgb, var(--ink) 34%, transparent);
  box-shadow: 0 .5px 1px rgb(0 0 0 / .2); font-size: 8.5px; line-height: 1; }
.bd-props.bd-arcs .bd-tboxc .bd-anotch::after { content: ''; position: absolute; inset: -5px; border-radius: 50%; }
.bd-props.bd-arcs .bd-tboxc .bd-anotch:hover { background: var(--surface); scale: 1.12; }
.bd-props.bd-arcs .bd-tboxc .bd-anotch[aria-expanded="true"] { border-color: var(--accent); box-shadow: 0 0 0 1.5px var(--accent); }
.bd-props.bd-arcs .bd-tboxc .bd-anotch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (pointer: coarse) {
  .bd-props.bd-arcs .bd-tboxc .bd-anotch { width: 18px; height: 18px; font-size: 10px; }
  .bd-props.bd-arcs .bd-fszaa { font-size: 16px; line-height: 15px; }
  .bd-props.bd-arcs .bd-fszface .bd-fsznum { font-size: 11.5px; line-height: 11px; }
}
/* The opacity: a thin groove outside the outermost ring, lit up to the value, and a knob. The knob is 40 px to press
   round a 16 px bead; held, or with the keys, it says its value on a solid lozenge of its own (the lit groove ran
   under a see-through one and took the value under 4.5:1 in Blossom and Periwinkle, measured as drawn). */
.bd-props.bd-arcs .bd-atrack { position: absolute; left: 0; top: 0; width: 1px; height: 1px; overflow: visible; z-index: 1; pointer-events: none; }
.bd-props.bd-arcs .bd-atrack path { fill: none; stroke-linecap: round; }
.bd-props.bd-arcs .bd-agroove { stroke: color-mix(in srgb, var(--ink) 34%, transparent); stroke-width: 3.5; }
.bd-props.bd-arcs .bd-afill { stroke: var(--accent); stroke-width: 3.5; }
.bd-props.bd-arcs .bd-ahit { stroke: transparent; stroke-width: 26; pointer-events: stroke; cursor: pointer; }
.bd-props.bd-arcs .bd-athumb { width: var(--s); height: var(--s); display: grid; place-items: center; border-radius: 50%; cursor: var(--cur-grab);
  touch-action: none; outline: none; }
.bd-props.bd-arcs .bd-athumb::before { content: ''; width: 16px; height: 16px; border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 0 2.5px var(--surface), 0 1px 4px rgb(0 0 0 / .3); grid-area: 1 / 1; }
.bd-props.bd-arcs .bd-aval { display: none; grid-area: 1 / 1; z-index: 1; min-width: 46px; height: 26px; padding: 0 8px; border-radius: 999px;
  place-items: center; font-size: 11.5px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ink); background: var(--surface);
  border: 1.5px solid var(--accent); box-shadow: 0 2px 8px rgb(0 0 0 / .18); pointer-events: none; }
.bd-props.bd-arcs .bd-athumb.bd-ashow .bd-aval { display: grid; }
.bd-props.bd-arcs .bd-athumb:focus-visible .bd-aval { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A choice's name beside it as it is pointed at, outward and on the page: a pen and a finger have no hover for a title. */
.bd-props.bd-arcs .bd-atip { position: absolute; left: 0; top: 0; width: max-content; z-index: 3; transform: translate(-50%, -50%); padding: 3px 10px;
  border-radius: 999px; font-size: 12px; font-weight: 500; white-space: nowrap; color: var(--ink); background: var(--surface);
  border: 1px solid var(--rule-strong); box-shadow: 0 2px 8px rgb(0 0 0 / .16); pointer-events: none; }
.bd-props.bd-arcs .bd-atip[hidden] { display: none; }
/* (Not under a pane opened from the arcs beside them - Text's font and size, its Box: 23 Oct 2026 - where its end
   showed past the pane's edge.) */
body:has(#bd-menu.bd-fszpop:not([hidden])) .bd-props.bd-arcs .bd-atip { visibility: hidden; }
@media (prefers-reduced-transparency: reduce) {
  :root[data-style="glass"] .bd-props.bd-arcs :is(.bd-aband, .bd-apill) { background-color: var(--surface); -webkit-backdrop-filter: none; backdrop-filter: none; }
}
/* One gesture (5 Oct 2026): a press on a tool's button and a slide onto a choice is the page's, not the browser's to
   scroll with - where there are arcs (as "Tablets and phones" has the sheet instead). */
@media (min-width: 601px) and (min-height: 501px) { .bd-rail .bd-tool[data-tool] { touch-action: none; } }
/* No room for arcs (a phone, a small window): a sheet along the foot of the page beside the rail, every choice 44 px,
   scrolling within itself if it must. */
.bd-stage > .bd-props.bd-sheet { top: auto; width: auto; max-width: none; max-height: min(62%, 520px); z-index: 6; padding: 12px 12px 14px;
  gap: 10px; align-items: stretch; border-radius: 22px; overflow-y: auto; }
.bd-props.bd-sheet :is(.bd-swatches, .bd-sizes, .bd-recent) { flex-wrap: wrap; gap: 6px; }
.bd-props.bd-sheet .bd-sizes { gap: 2px; }          /* (the pen's six on one row at 390 px) */
.bd-props.bd-sheet .bd-swatch { width: 44px; height: 44px; border-width: 3px; }
.bd-props.bd-sheet :is(.bd-size, .bd-kindbtn, .bd-recentpen) { width: 44px; height: 44px; }
.bd-props.bd-sheet .bd-kindgrid { grid-template-columns: auto repeat(3, 44px); }
.bd-props.bd-sheet .bd-seg { flex-wrap: wrap; }
.bd-props.bd-sheet :is(.bd-seg button, .bd-savecol, .bd-eqnew) { min-height: 44px; min-width: 44px; }
.bd-props.bd-sheet .bd-brushgrid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.bd-props.bd-sheet .bd-brush { width: auto; height: 44px; }
.bd-props.bd-sheet .bd-prev { align-self: flex-start; min-width: 44px; min-height: 44px; }      /* the setup before (board.js prevButton), first */
.bd-props.bd-sheet .bd-opacity input[type="range"] { height: 44px; --thumb: 26px; }
/* The sheet lies over the foot of the page, writing and all, seen through its glass, and its first words under the
   pane's sheen: its small labels in more ink ("Brush" read 4.0-4.4:1 there with the pane's, 4.2-4.5 with 60% ink). */
.bd-props.bd-sheet .bd-plabel { color: color-mix(in srgb, var(--ink) 85%, var(--muted)); }
/* The sheet coming and going (7 Oct 2026, the owner: "can we make sure the animations of opening menus etc are super
   clean"): it came and went at once. Now it rises 12 px into place as it fades in (board/toolarcs.js, by the clock),
   and a copy of it sinks back as it fades out (.bd-sheetgoing), so the options themselves are free at once. The glass
   pane itself moves, never what holds it (the blur would switch off). Another tool taken up with it open: no copy,
   the new choices fade in on the same pane. An iPad on Glass: by its opacity alone. */
@keyframes bd-sheetgone { to { opacity: 0; translate: 0 12px; } }
.bd-props.bd-sheet.bd-sheetgoing { z-index: 5; pointer-events: none; animation: bd-sheetgone var(--t-mid-out) var(--ease-away) forwards; }
:root[data-glass-motion="fade"][data-style="glass"] .bd-props.bd-sheet.bd-sheetgoing { animation-name: gs-fadeout; }

/* ── Tablets and phones (lane L13, 30 Sep 2026) ────────────────────────────
   An iPad with a pen or a finger, a phone either way up. The Board already told pen from finger from palm
   (board.js "down"); what the screens needed was room, and the browser's own habits kept out of the way. */

/* A long press on the Board is the Board's (the pen held still straightens a shape): not Safari's menu or magnifier,
   nor the grey flash on whatever a finger tapped. The words one types keep their own. */
.bd { -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent; }
.bd :is(input, textarea, [contenteditable="true"], math-field) { -webkit-touch-callout: default; }
/* No zoom by a double tap anywhere in the editor (the page zooms itself: board.js refuses Safari's pinch too), and
   no bounce of the whole window at its ends. */
body:has(.bd) { touch-action: manipulation; overscroll-behavior: none; }
/* Nothing round the page is selected as words either (6 Oct 2026, the owner: "on apple pencil it seems to be
   selecting while they're writing … it's trying to select the text"): the page itself never was (.bd-stage), but
   a Pencil held still or a hand set down near the bar, the tools, the tip, made Safari on an iPad start selecting the
   words there - its blue and its handles over the writing. The words one types keep their own (and board.js lets a
   selection start only in them: see "selectstart"). */
body:has(.bd) { -webkit-user-select: none; user-select: none; }
body:has(.bd) :is(input, textarea, [contenteditable="true"], math-field) { -webkit-user-select: text; user-select: text; }

/* On a short screen the tools stop above the page's own buttons on the same side (they met on a phone), scrolling
   within themselves as they already do when the window is short. (While the pill is there and not going: since it
   fades before it is hidden, asked of [hidden] alone the rail moved by itself after it had gone - 7 Oct 2026, after
   the review. Now it moves at the press, gliding there: board/pages.js, glideRail.) */
@media (max-height: 900px) {
  .bd-stage:not([data-side="right"]):has(.bd-pagepill:not([hidden], .gs-leave)) .bd-rail { bottom: 176px; max-height: calc(100% - 188px); }
}
/* A narrow screen: the tip across the bottom, clear of the page's buttons on the left and the zoom on the right,
   rather than a column of words three wide. */
@media (max-width: 600px) {
  .bd-hint { left: 60px; right: 12px; bottom: 56px; transform: none; max-width: none; border-radius: 16px; }
}
/* The masthead on a phone: one row. The mark stands for the name. */
@media (max-width: 520px) {
  .masthead-inner { padding: 10px 14px; gap: 10px; }
  .brand-text { display: none; }
  nav.main { flex-wrap: nowrap; align-items: center; }
  nav.main a { padding: 7px 9px; letter-spacing: .06em; }
  main { padding: 22px 16px 70px; }
}

/* ── Full screen and the home screen (1 Oct 2026) ──────────────────────────
   The editor's full screen (board/fullscreen.js) is the whole page's, and the Board added to a home screen opens
   with the whole screen too (static/manifest.webmanifest, base.html). */

/* The header's button: the corners out to go in, in to come out. In a narrow window it goes where Export goes
   (⋯ offers it instead) - but not for View only, whose header has no ⋯ and room to spare. */
.bd-fullbtn .bd-fullout { display: none; }
.bd-fullbtn[aria-pressed="true"] .bd-fullin { display: none; }
.bd-fullbtn[aria-pressed="true"] .bd-fullout { display: inline; }
@media (max-width: 640px) { .bd:not([data-view="1"]) #bd-fullbtn { display: none; } }
/* The calculator's own button beside the timer's (21 Oct 2026, board/calc.js): on a bar wide enough for it. Narrower,
   it is Shift+C, the Tools pane's head and the radial menu's More - the bar keeps to one row (tests/test_topbar.py). */
@media (max-width: 1099px) { #bd-calcbtn { display: none; } }
/* And while the bar has more to say (a save that failed, search open: "A bar with more to say" above), wherever the
   rest of what is set up once folds - the field ran its arrows over Pages at 1280 px with it there. */
.bd-head:has(.bd-save:is([data-state="error"], [data-state="conflict"]), .bd-findbox:not([hidden])) #bd-calcbtn { display: none; }
/* Where a page cannot have the whole screen (Safari on an iPhone): how to add the Board to the home screen. A pane
   at the top of the page, as "changed in another window" is; its words in --ink, measured in every palette, light
   and dark, glass and plain (tests/test_fullscreen.py). */
.bd-fullhelp { left: 50%; top: 14px; transform: translateX(-50%); z-index: 6; width: min(360px, calc(100% - 24px));
  padding: 14px 16px 12px; border-radius: 18px; font-size: 13.5px; line-height: 1.45; color: var(--ink); }
.bd-fullhelp-title { display: block; margin-bottom: 4px; font-size: 14.5px; font-weight: 600; }
.bd-fullhelp ol { margin: 8px 0 0; padding-left: 20px; }
.bd-fullhelp li + li { margin-top: 3px; }
.bd-fullhelp svg { width: 16px; height: 16px; vertical-align: -3px; }
.bd-fullhelp-acts { display: flex; justify-content: flex-end; margin-top: 10px; }
/* On Glass, measured (1 Oct 2026): the panes' bright top sheen took its heading's light words in the dark theme to
   4.28-4.46:1 (Ember the worst), so this pane, which is read rather than pressed, has the softer sheen alone; and
   "Got it", a translucent .btn under its 42 % gloss, measured 2.6-3.0:1 in the light theme in every palette - solid
   under a lighter gloss here, as the masthead's current page is (nav.main a.on, lane L19). */
:root[data-style="glass"] .bd-fullhelp { background-image: linear-gradient(180deg, var(--sheen-soft), transparent 40%); }
:root[data-style="glass"] .bd-fullhelp .btn:not(:active) { background-color: var(--btn-bg); border-color: var(--btn-bg);
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%); }

/* The notch, the rounded corners, the home bar and the status bar. viewport-fit=cover (base.html) hands the page
   the whole glass, and from the home screen the status bar lies over the page (black-translucent): so the page
   keeps its own things inside env(safe-area-inset-*), which is 0 wherever there is nothing to keep clear of. The
   aurora (on Glass) and the page's colour still run to the very edges. */
body { padding-left: env(safe-area-inset-left, 0px); padding-right: env(safe-area-inset-right, 0px); }
body { padding-top: env(safe-area-inset-top, 0px); }
.masthead { top: env(safe-area-inset-top, 0px); }           /* stuck under the status bar, not behind it */
body:has(.bd) { padding-bottom: env(safe-area-inset-bottom, 0px); }   /* the tools and the zoom clear of the home bar */
.signedout { top: calc(14px + env(safe-area-inset-top, 0px)); }
/* The status bar's own words are white over the page: a strip of the palette's dark ink under them (the dark
   page in the dark theme), so the time reads on a light page too. Nothing where there is no status bar. */
:root::before { content: ''; position: fixed; z-index: 400; left: 0; right: 0; top: 0;
  height: env(safe-area-inset-top, 0px); pointer-events: none;
  background: color-mix(in srgb, light-dark(var(--l-ink), var(--d-paper)) 90%, transparent); }
@supports not (color: light-dark(#000, #fff)) {
  :root::before {
    background: color-mix(in srgb, var(--not-light, var(--l-ink)) var(--not-dark, var(--d-paper)) 90%, transparent);
  }
}

/* ═══ Tools: a timer and a stopwatch (2 Oct 2026: static/js/board/toolbox.js) ═══
   "Visual and clean with easy copy of the time it records." A small pane over the page, the floating panes' own
   material: on Glass nearly solid (92 %) under the softer sheen alone, as .bd-fullhelp is - the page behind it is
   near-white paper, or dark, not the aurora, and the descriptions of Settings over the editor fell under 4.5:1 on a
   thinner pane (1 Oct 2026). Big digits in a ring of the palette's accent; red for the last ten seconds and at the
   end (--red: 5.5:1 and more on every pane). The primary button solid under a lighter gloss, as the masthead's current
   page is - a translucent .btn's light words measured 2.6-3.0:1 in the light theme over the editor. The small words
   in --ink, or --muted with ink in it (45 %, as the index's menu has them). Measured as drawn in every look:
   tests/test_toolbox.py. */
.bd-toolsbtn[data-running]::before { content: ''; position: absolute; top: 3px; right: 3px; width: 8px; height: 8px;
  border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 2px var(--surface); }
.bd-toolbox, .bd-toolchip, .bd-toolmini { position: absolute; color: var(--ink); background: var(--surface);
  border: 1px solid var(--rule-strong); box-shadow: var(--shadow-2); }
:root[data-style="glass"] :is(.bd-toolbox, .bd-toolchip, .bd-toolmini) {
  background-color: color-mix(in srgb, var(--surface) 92%, transparent);
  background-image: linear-gradient(180deg, var(--sheen-soft), transparent 40%);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); box-shadow: var(--rim), var(--shadow-2); }
.bd-toolbox { z-index: 6; left: 12px; top: 12px; width: min(320px, calc(100% - 32px)); max-height: calc(100% - 16px);
  display: flex; flex-direction: column; gap: 10px; padding: 0 14px 12px; border-radius: 20px; overflow-y: auto;
  overscroll-behavior: contain; font-size: 13px; line-height: 1.35; }
.bd-toolbox[hidden], .bd-toolchip[hidden], .bd-toolmini[hidden] { display: none; }
/* Its head: moved by (anywhere but its buttons), its name, Make small (11 Oct 2026) and ×. */
.tb-head { display: flex; align-items: center; gap: 8px; margin: 0 -14px; padding: 8px 8px 0 13px; cursor: var(--cur-grab);
  touch-action: none; user-select: none; -webkit-user-select: none; }
.tb-moving .tb-head { cursor: var(--cur-grabbing); }
.tb-grip { display: grid; place-items: center; color: color-mix(in srgb, var(--ink) 40%, var(--muted)); }
.tb-grip svg { width: 8px; height: 12px; }
.tb-title { flex: 1; margin: 0; font: 600 14px/1.2 var(--sans); letter-spacing: .02em; color: var(--ink); text-transform: none; }
.tb-mini, .tb-close, .tb-copy, .tc-open { display: grid; place-items: center; flex: none; width: 32px; height: 32px; padding: 0;
  border: 0; border-radius: 50%; background: transparent; color: var(--ink); cursor: pointer; transition: background .15s; }
.tb-mini svg, .tb-close svg, .tb-copy svg { width: 17px; height: 17px; }
.tb-mini:hover, .tb-close:hover, .tb-copy:hover { background: color-mix(in srgb, var(--accent) 14%, transparent); }
/* The tabs: a sunken track, the one shown raised in the primary button's colours (the ⋯ menu's choices, .bd-mseg). */
.tb-tabs { display: flex; gap: 3px; padding: 3px; border-radius: 999px; background: var(--field-bg);
  border: 1px solid var(--rule); box-shadow: var(--field-inset); }
.tb-tabs [role="tab"] { flex: 1 1 0; min-width: 0; min-height: 30px; padding: 4px 10px; border: 0; border-radius: 999px;
  background: transparent; color: var(--ink); font: inherit; font-size: 13px; cursor: pointer; }
.tb-tabs [role="tab"]:hover { background: var(--paper-2); }
.tb-tabs [role="tab"][aria-selected="true"] { background: var(--btn-bg); color: var(--btn-fg); font-weight: 500; }
:root[data-style="glass"] .tb-tabs [role="tab"][aria-selected="true"], :root[data-style="glass"] .tb-go,
:root[data-style="glass"] .tb-chip[aria-pressed="true"] {
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%);
  box-shadow: var(--bevel); }
.bd-toolbox :is(button, .tb-time):focus-visible, :is(.bd-toolchip, .bd-toolmini) button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tb-body { display: flex; flex-direction: column; gap: 10px; }
.tb-body[hidden] { display: none; }
/* The face: the ring, the time in it, what it is doing under that. (Copy, beside it until 13 Oct 2026, is in the head.)
   The timer's has room round it for its bezel - the ticks, and 0 15 30 45 outside them. */
.tb-face { position: relative; display: grid; place-items: center; padding: 2px 0; }
#tb-timer .tb-face { padding: 18px 0 16px; }
.tb-dial { position: relative; width: 176px; height: 176px; }
#tb-timer .tb-dial { width: 172px; height: 172px; }
.tb-ring { display: block; width: 100%; height: 100%; overflow: visible; }
.tb-track { fill: none; stroke: color-mix(in srgb, var(--ink) 12%, transparent); stroke-width: 7; }
.tb-arc { fill: none; stroke: var(--accent); stroke-width: 7; stroke-linecap: round; stroke-dasharray: 100 100;
  transition: stroke .3s; }
:root[data-style="glass"] .tb-arc { filter: drop-shadow(0 0 4px color-mix(in srgb, var(--accent) 55%, transparent)); }
/* Safari draws no CSS filter on a shape inside an SVG, and the arc had no glow there: in WebKit (data-engine, board.js)
   the ring's drawing glows instead - its faint track a touch too (7 Oct 2026, the owner: "make sure it all works on
   safari too"). */
:root[data-engine="webkit"][data-style="glass"] .tb-ring { filter: drop-shadow(0 0 4px color-mix(in srgb, var(--accent) 55%, transparent)); }
:root[data-engine="webkit"][data-style="glass"] .tb-warn .tb-ring { filter: drop-shadow(0 0 4px color-mix(in srgb, var(--red) 55%, transparent)); }
:root[data-engine="webkit"][data-style="glass"] .tb-up .tb-ring { filter: none; }
.tb-read { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; }
/* (Its size is toolbox.js's, fit: as large as 38 px within the ring - "+1:00:00" and an hour of hundredths are wider.) */
.tb-time { padding: 3px 8px; border-radius: 10px; font: 600 38px/1.05 var(--sans); font-variant-numeric: tabular-nums;
  letter-spacing: .01em; color: var(--ink); white-space: nowrap; }
#tb-timer .tb-time { cursor: text; overflow: hidden; }
#tb-timer .tb-time:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.tb-edit { width: 124px; padding: 3px 6px; text-align: center; font: 600 30px/1.1 var(--sans); font-variant-numeric: tabular-nums;
  color: var(--ink); }
/* The ring turned to set it, as a kitchen timer is (13 Oct 2026, toolbox.js "The timer"): the whole dial takes hold
   (.tb-grab, the slider) but the time in the middle, which is tapped to type. A finger on it turns it and never
   scrolls the pane - only while it can be turned. The knob: a bubble of the accent at the arc's end, lit from above
   on Glass; it and the arc glide to a preset or a key's step, and are held where they are with motion turned down. */
#tb-timer .tb-read { pointer-events: none; }
#tb-timer .tb-read > :is(.tb-time, .tb-edit) { pointer-events: auto; }
.tb-grab { position: absolute; inset: 0; border-radius: 50%; touch-action: manipulation; user-select: none;
  -webkit-user-select: none; -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent; }
.tb-dialing .tb-grab { cursor: var(--cur-grab); touch-action: none; }
.tb-turning .tb-grab { cursor: var(--cur-grabbing); }
.tb-grab:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
#tb-timer .tb-grab:focus-visible { outline-offset: 7px; }     /* (outside the bezel's ticks, inside 0 15 30 45) */
/* Calmer, and better to turn (13 Oct 2026, the owner: "too many buttons, not satisfying enough"; toolbox.js has the
   rest). The knob rides the arc's end, set or running - turned by toolbox.js a frame at a time, its springs its own,
   so no transition on it or on the arc here. Held, it swells a little and glows; let go, it settles back with a
   small spring (an ease past 1 and back: its size only - where it points never goes more than 2° past). */
.tb-knob { transform-box: view-box; transform-origin: 50% 50%; }
.tb-up .tb-knob { display: none; }
.tb-knobbody { transform-box: fill-box; transform-origin: 50% 50%; transition: transform .32s cubic-bezier(.3, 1.6, .5, 1); }
.tb-turning .tb-knobbody { transform: scale(1.22); transition-duration: .16s; transition-timing-function: ease-out; }
.tb-knobdot { fill: var(--accent); stroke: var(--surface); stroke-width: 2.5; }
.tb-knobglow { fill: var(--accent); opacity: 0; transition: opacity .2s; }
.tb-turning .tb-knobglow { opacity: .28; }
.tb-knobshine { display: none; fill: rgb(255 255 255 / .7); }
:root[data-style="glass"] .tb-knobshine { display: inline; }
.tb-running .tb-knobbody { transform: scale(.82); }
:root[data-motion="reduce"] :is(.tb-knobbody, .tb-knobglow) { transition: none; }
/* The arc: the accent lit from above (a gradient down it), and on Glass a line of gloss along it - the hard-stop
   shine of a lozenge, drawn thin. Over an hour the ring is full and the hour past it a brighter arc just outside it. */
.tb-fill0, .tb-fill1 { stop-color: var(--accent); }
:root[data-style="glass"] .tb-fill0 { stop-color: color-mix(in srgb, var(--accent) 78%, white); }
/* (Past an hour the first hour steps back, so the lap - the hour being turned - is the bright one.) */
.tb-over .tb-arc { opacity: .5; }
#tb-timer .tb-arc { stroke: url(#tb-arcfill); transition: none; }
.tb-gloss { display: none; fill: none; stroke: rgb(255 255 255 / .5); stroke-width: 1.4; stroke-linecap: round; stroke-dasharray: 100 100; }
:root[data-style="glass"] .tb-gloss { display: inline; }
.tb-up .tb-gloss { display: none; }
.tb-lap { fill: none; stroke: color-mix(in srgb, var(--accent) 72%, white); stroke-width: 2.6; stroke-linecap: round;
  stroke-dasharray: 100 100; stroke-dashoffset: 100; }
.tb-up .tb-lap { display: none; }
/* The bezel: a tick a minute, longer each five; marks (3:1 and more), 0 15 30 45 small words (4.5:1, as .tb-state). */
.tb-tick { fill: none; stroke: color-mix(in srgb, var(--ink) 30%, transparent); stroke-width: .7; stroke-linecap: round; }
.tb-tick.tb-five { stroke: color-mix(in srgb, var(--ink) 55%, var(--muted)); stroke-width: 1.2; }
.tb-nums { position: absolute; inset: 0; pointer-events: none; }
.tb-num { position: absolute; transform: translate(-50%, -50%); font-size: 10px; line-height: 1; font-variant-numeric: tabular-nums;
  color: color-mix(in srgb, var(--ink) 45%, var(--muted)); }
.tb-num[data-m="0"] { left: 50%; top: -8.6%; }
.tb-num[data-m="15"] { left: 109%; top: 50%; }
.tb-num[data-m="30"] { left: 50%; top: 108.6%; }
.tb-num[data-m="45"] { left: -9%; top: 50%; }
/* The digits roll as they change (toolbox.js rollText): in from below for more, from above for less. */
.tb-dg { display: inline-block; }
.tb-dg[data-roll] { animation: .17s cubic-bezier(.2, .8, .3, 1) both; }
.tb-dg[data-roll="u1"] { animation-name: tb-roll-u1; }
.tb-dg[data-roll="u2"] { animation-name: tb-roll-u2; }
.tb-dg[data-roll="d1"] { animation-name: tb-roll-d1; }
.tb-dg[data-roll="d2"] { animation-name: tb-roll-d2; }
@keyframes tb-roll-u1 { from { transform: translateY(55%); opacity: 0; } }
@keyframes tb-roll-u2 { from { transform: translateY(55%); opacity: 0; } }
@keyframes tb-roll-d1 { from { transform: translateY(-55%); opacity: 0; } }
@keyframes tb-roll-d2 { from { transform: translateY(-55%); opacity: 0; } }
:root[data-motion="reduce"] .tb-dg[data-roll] { animation: none; }
.tb-state { min-height: 16px; font-size: 12px; color: color-mix(in srgb, var(--ink) 45%, var(--muted)); white-space: nowrap; }
.tb-copy { position: relative; }
.tb-copy.tb-copied { color: var(--green); }
/* ("Copied" under it: over it, the pane's own top would cut it off.) */
.tb-copiedsays { position: absolute; z-index: 2; top: calc(100% + 3px); right: -6px; padding: 2px 9px; border-radius: 999px;
  font-size: 12px; font-weight: 500; white-space: nowrap; color: var(--btn-fg); background: var(--btn-bg); pointer-events: none; }
/* The last ten seconds, and the end: red, and at the end the ring's track red too and a pulse or two. */
:is(.tb-warn, .tb-up) .tb-arc { stroke: var(--red); }
:root[data-style="glass"] :is(.tb-warn, .tb-up) .tb-arc { filter: drop-shadow(0 0 4px color-mix(in srgb, var(--red) 55%, transparent)); }
/* (The words in red with ink in it: --red alone measured 3.7-4.4:1 for "Time's up" on Glass in the light theme, the
   page's see-through paper behind the pane, and with 30 % ink the chip's small time 4.3-4.5:1 on Glass in the dark.
   The ring keeps the red itself: a mark, 3:1.) */
.bd-toolbox, .bd-toolchip, .bd-toolmini { --tb-red: color-mix(in srgb, var(--red) 60%, var(--ink)); }
:is(.tb-warn, .tb-up) :is(.tb-time, .tc-time, .tm-time) { color: var(--tb-red); }
.tb-up .tb-arc { opacity: 0; }
.tb-up .tb-track { stroke: color-mix(in srgb, var(--red) 40%, transparent); }
.tb-up .tb-state, .tb-up .tc-what { color: var(--tb-red); font-weight: 600; }
.tb-pulse .tb-ring { animation: tb-pulse 1.15s ease-out 3; }
/* (The pane's timer: once - the chime and one swell are the finish; three was a fuss. 13 Oct 2026.) */
#tb-timer .tb-pulse .tb-ring { animation-iteration-count: 1; }
@keyframes tb-pulse {
  0% { transform: scale(1); }
  35% { transform: scale(1.05); filter: drop-shadow(0 0 10px color-mix(in srgb, var(--red) 60%, transparent)); }
  100% { transform: scale(1); }
}
:root[data-motion="reduce"] .tb-pulse .tb-ring { animation: none; }
:root[data-motion="reduce"] .tb-arc { transition: none; }
/* The quick picks and the quiet buttons: lozenges, as the bar's are - ghosts, the big round button the one lit. */
.tb-row { display: flex; gap: 6px; }
.tb-chip { flex: 1 1 0; min-width: 0; min-height: 30px; padding: 4px 4px; border-radius: 999px; border: 1px solid var(--rule-strong);
  background: transparent; color: var(--ink); font: inherit; font-size: 12.5px; font-variant-numeric: tabular-nums;
  white-space: nowrap; cursor: pointer; transition: background .15s, border-color .15s; }
.tb-chip:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); border-color: color-mix(in srgb, var(--accent) 40%, var(--rule-strong)); }
.tb-chip[aria-pressed="true"] { background: var(--btn-bg); color: var(--btn-fg); border-color: var(--btn-bg); font-weight: 500; }
/* The quick picks (13 Oct 2026): three, quiet, a little narrower than the pane - only while the timer is not started. */
.tb-picks { justify-content: center; padding: 0 18px; }
.tb-picks[hidden] { display: none; }
.tb-pick { max-width: 76px; }
.tb-pick:not([aria-pressed="true"]) { border-color: var(--rule); }
.tb-go, .tb-btn { min-height: 38px; padding: 0 14px; border-radius: 999px; font: 500 13.5px/1 var(--sans); letter-spacing: .02em;
  cursor: pointer; transition: background .15s, border-color .15s, opacity .15s; }
.tb-go { flex: 1.5 1 0; border: 1px solid var(--btn-bg); background: var(--btn-bg); color: var(--btn-fg); font-weight: 600; }
.tb-go:hover { border-color: var(--accent); }
:root[data-style="glass"] .tb-go { box-shadow: var(--bevel), 0 0 18px -6px color-mix(in srgb, var(--accent) 70%, transparent); }
.tb-btn { flex: 1 1 0; border: 1px solid var(--rule-strong); background: transparent; color: var(--ink); }
.tb-btn:hover:not(:disabled) { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.tb-btn:disabled { opacity: .45; cursor: default; }
.tb-btn.tb-small { flex: none; min-height: 28px; padding: 0 12px; font-size: 12px; }
.tb-btn.tb-copied { border-color: var(--green); }      /* (its word stays ink: green words were 3.4-4.0:1) */
/* The big round button (13 Oct 2026): play and pause, the Aero media button - the primary colours, on Glass the
   hard-stop gloss and a halo of the accent; pressed, it sinks a little. Reset and Lap beside it, small and quiet, in
   columns of their own so it stays in the middle whichever shows (and when neither does). */
.tb-acts { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 14px; }
.tb-acts > [hidden] { display: none; }
.tb-acts [data-t="reset"], .tb-acts [data-w="reset"] { grid-column: 1; justify-self: end; }
.tb-acts .tb-big { grid-column: 2; }
.tb-acts [data-w="lap"] { grid-column: 3; justify-self: start; }
.tb-go.tb-big { display: grid; place-items: center; width: 58px; height: 58px; min-height: 0; padding: 0; border-radius: 50%;
  transition: background .15s, border-color .15s, opacity .15s, transform .12s ease-out; }
.tb-big svg { width: 24px; height: 24px; }
.tb-big:active:not(:disabled) { transform: scale(.94); }
.tb-big:disabled { opacity: .45; cursor: default; }
:root[data-style="glass"] .tb-go.tb-big { box-shadow: var(--bevel), 0 0 0 4px color-mix(in srgb, var(--accent) 14%, transparent),
  0 0 22px -4px color-mix(in srgb, var(--accent) 70%, transparent); }
.tb-btn.tb-quiet { flex: none; min-height: 30px; padding: 0 13px; font-size: 12.5px; font-weight: 400; border-color: var(--rule);
  animation: tb-quiet-in .2s ease-out; }
@keyframes tb-quiet-in { from { opacity: 0; } }
:root[data-motion="reduce"] .tb-btn.tb-quiet { animation: none; }
:root[data-motion="reduce"] .tb-big:active { transform: none; }
/* Glass: the ghost button's lacquer (.btn.ghost) on what is not lit, so it reads as a button on the dark too, where
   --rule-strong's edge all but goes. */
:root[data-style="glass"] :is(.tb-btn, .tb-chip:not([aria-pressed="true"])) {
  background-image: var(--gloss); border-color: color-mix(in srgb, var(--edge-light) 60%, var(--rule-strong)); }
/* The laps: newest first, scrolling within the pane. */
.tb-laps { padding-top: 8px; border-top: 1px solid var(--rule); }
.tb-laps[hidden] { display: none; }
.tb-laphead { display: flex; align-items: center; justify-content: space-between; font-size: 12.5px; font-weight: 600; }
.tb-lapcols, .tb-laplist li { display: grid; grid-template-columns: 1fr 1.2fr 1.2fr; gap: 8px; }
.tb-lapcols { padding: 6px 4px 3px; font-size: 11.5px; color: color-mix(in srgb, var(--ink) 45%, var(--muted)); }
:is(.tb-lapcols, .tb-laplist li) > span:not(:first-child) { text-align: right; }
.tb-laplist { max-height: 152px; margin: 0; padding: 0; overflow-y: auto; overscroll-behavior: contain; list-style: none;
  font-variant-numeric: tabular-nums; }
.tb-laplist li { padding: 5px 4px; border-top: 1px solid var(--rule); font-size: 13px; }
.tb-lapn { color: color-mix(in srgb, var(--ink) 45%, var(--muted)); }
/* The owner's: Show to everyone (the ⋯ menu's switch, .bd-mtoggle). */
.tb-foot { margin: 0 -6px; padding-top: 6px; border-top: 1px solid var(--rule); }
.tb-share { width: 100%; border: 0; background: transparent; font: inherit; text-align: left; cursor: pointer; }
.tb-share:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.tb-sharesays { margin: 0 10px 2px; font-size: 11.5px; color: color-mix(in srgb, var(--ink) 45%, var(--muted)); }
/* Said to a screen reader only; and the field the fallback copies from, out of sight. */
.tb-say { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.tb-offscreen { position: fixed; left: -9999px; top: 0; opacity: 0; }

/* The owner's timer as everyone else sees it: a chip at the top of the page, opened bigger by a press anywhere on it
   (its one button, the chevron, reaches over the whole of it). */
.bd-toolchip { z-index: 5; left: 50%; top: 14px; transform: translateX(-50%); display: grid; grid-template-columns: auto auto;
  align-items: center; column-gap: 4px; max-width: calc(100% - 24px); padding: 5px 4px 5px 7px; border-radius: 999px; }
.tc-rows { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; }
.tc-row { display: flex; align-items: center; gap: 8px; }
.tc-row .tb-ring { width: 28px; height: 28px; }
.tc-row :is(.tb-track, .tb-arc) { stroke-width: 13; }
.tc-read { display: flex; flex-direction: column; line-height: 1.1; }
.tc-time { font: 600 16px/1.1 var(--sans); font-variant-numeric: tabular-nums; color: var(--ink); white-space: nowrap; }
.tc-what { font-size: 11px; color: color-mix(in srgb, var(--ink) 45%, var(--muted)); white-space: nowrap; }
.tc-rows { grid-column: 1; grid-row: 1; }
.tc-open { position: static; grid-column: 2; grid-row: 1; width: 28px; height: 28px; }
.tc-open svg { width: 16px; height: 16px; transition: transform .2s; }
.tc-open::after { content: ''; position: absolute; inset: 0; border-radius: inherit; }
.bd-toolchip:hover .tc-open { background: color-mix(in srgb, var(--accent) 14%, transparent); }
.tc-open[aria-expanded="true"] svg { transform: rotate(180deg); }
.tc-laps { position: relative; z-index: 1; grid-column: 1 / -1; grid-row: 2; min-width: 240px; }
.tc-laps[hidden] { display: none; }
.tc-whose { margin: 6px 4px 0; font-size: 12px; color: color-mix(in srgb, var(--ink) 45%, var(--muted)); }
.bd-toolchip.tc-big { padding: 12px 10px 10px 14px; border-radius: 20px; align-items: start; }
.tc-big .tc-rows { gap: 12px 22px; }
.tc-big .tc-row { gap: 12px; }
.tc-big .tc-row .tb-ring { width: 84px; height: 84px; }
.tc-big .tc-row :is(.tb-track, .tb-arc) { stroke-width: 8; }
.tc-big .tc-time { font-size: 28px; }
.tc-big .tc-what { font-size: 12px; }
.tc-big .tb-laplist { max-height: 140px; }

/* Tools made small (11 Oct 2026, the owner: "need a way to minimise and make the timer/stopwatch smaller nice looking
   but out of the way"; toolbox.js, Small): a pill of the pane's own glass - a little ring, the time in figures of one
   width, and start or pause in the primary button's lozenge (lit as .tb-go is on Glass). Moved by itself anywhere but
   its button (touch-action: none - a finger moving it is not the page scrolling), gliding to its place by toolbox.js.
   The time is --ink on the pane's material, as the pane's own big digits, and --tb-red in the last ten seconds and
   after; the ring the accent, or red: marks, 3:1. Measured as drawn in every look: tests/test_tools_mini.py. */
.bd-toolmini { z-index: 6; left: 12px; top: 12px; display: flex; align-items: center; gap: 4px; height: 42px;
  padding: 0 5px 0 8px; border-radius: 999px; touch-action: none; user-select: none; -webkit-user-select: none;
  cursor: var(--cur-grab); }
.bd-toolmini.tm-moving { cursor: var(--cur-grabbing); }
.tm-ring.tb-ring { flex: none; width: 24px; height: 24px; }
.tm-ring :is(.tb-track, .tb-arc) { stroke-width: 14; }
.tm-open { min-width: 0; height: 32px; padding: 0 7px; border: 0; border-radius: 999px; background: transparent;
  color: var(--ink); font: 600 17px/1 var(--sans); font-variant-numeric: tabular-nums; letter-spacing: .01em;
  white-space: nowrap; cursor: pointer; transition: background .15s; }
.tm-open:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.tm-go { display: grid; place-items: center; flex: none; width: 30px; height: 30px; padding: 0; border-radius: 50%;
  border: 1px solid var(--btn-bg); background: var(--btn-bg); color: var(--btn-fg); cursor: pointer; }
.tm-go:hover { border-color: var(--accent); }
.tm-go svg { width: 14px; height: 14px; }
:root[data-style="glass"] .tm-go {
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%);
  box-shadow: var(--bevel); }
/* Never over the pen: a stroke near it fades it to a fifth and lets it through, until the pen lifts. */
.bd-toolmini.tm-near { opacity: .2; pointer-events: none; }
/* The timer's end, made small: a ring of red light that swells and goes, gently, until the pill is tapped - drawn
   round it (::after), so the glass's own rim and shadow stay as they are. Motion turned down: the ring held still. */
.bd-toolmini::after { content: ''; position: absolute; inset: -1px; border-radius: inherit; pointer-events: none; }
.bd-toolmini.tm-alert { border-color: var(--red); }
.bd-toolmini.tm-alert::after { animation: tm-alert 1.8s ease-in-out infinite; }
@keyframes tm-alert {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--red) 45%, transparent); }
  50% { box-shadow: 0 0 0 7px color-mix(in srgb, var(--red) 0%, transparent); }
}
:root[data-motion="reduce"] .bd-toolmini.tm-alert::after { animation: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--red) 45%, transparent); }

/* A finger: 44 px, every button. */
@media (pointer: coarse) {
  :is(.tb-chip, .tb-go, .tb-btn, .tb-tabs [role="tab"], .tb-share) { min-height: 44px; }
  .tb-btn.tb-small { min-height: 40px; }
  :is(.tb-mini, .tb-close, .tb-copy) { width: 44px; height: 44px; }
  .tb-btn.tb-quiet { min-height: 44px; }
  .tb-go.tb-big { width: 64px; height: 64px; min-height: 0; }
  .tc-open { width: 40px; height: 40px; }
  .bd-toolmini { height: 52px; padding: 0 6px 0 10px; }
  .tm-open { height: 44px; }
  .tm-go { width: 40px; height: 40px; }
}
/* A phone: the pane across the page between 16 px gutters (toolbox.js keeps it there), a little smaller dial. */
@media (max-width: 560px) {
  .bd-toolbox { width: calc(100% - 32px); }
  .tb-dial, #tb-timer .tb-dial { width: 152px; height: 152px; }
}

/* ═══ The calculator (21 Oct 2026: static/js/board/calc.js, calc-core.js) ═══
   "A mix between the Desmos calculator and the Casio fx-CG50", for a tutor on the board. The floating panes' own
   material, as Tools is (.bd-toolbox: on Glass nearly solid under the softer sheen alone - the page's paper is behind
   it, not the aurora), and its head, its tabs and its pill are Tools' own (.tb-head, .tb-tabs, .bd-toolmini's look).
   Quiet words (line numbers, hints) --ink 45 % into --muted as the index's menu has them; an error's word in red with
   ink in it (--tb-red's sum: --red alone fell under 4.5:1 on Glass in the light theme). Every word measured as drawn
   in all 44 looks: tests/test_calculator.py.

   Made cleaner and clearer (23 Oct 2026). The owner, of the pane as it was: "not sure about these boxes - text should
   be in a clear font". The sums and answers were set in the page's book serif (Cormorant Garamond, Georgia where it
   had not come) with its old-style figures - a 3 and a 2 hanging under the line, a 1 the height of an x, a fraction's
   figures smaller again - and a calculator on the board is read from across a room. Each line was a box (a field
   ringed and inset), with a row of three pills under the line in use, over 35 lozenges in two tints, each ringed. Now,
   as Desmos's scientific calculator has it:
   - The maths in one clear sans with lining, tabular figures (--cx-math: Inter, kept in static/fonts for the page's
     typed text since 22 Oct 2026 - here under a name of the calculator's own, so the pane and the text tool never
     wait on each other), whatever the type pack: every figure on the line and the same width, a 1 not mistaken for
     an l, italic only for a letter that stands for a number (x, a, e). A fraction's figures .85 of the line's, a
     power's .72; the answer a third larger than its sum and on the right.
   - The list a list: rows between hairlines, the line in use tinted with the accent's bar on its left and its field
     not boxed (the bar and the caret say where the keys go: a text field is :focus-visible whenever it has the keys, so
     the pane's ring round a focused control was a box round every line typed in); S⇔D, Sum and Answer once, small and quiet, beside the
     answer of the line in use alone (calc.js actsEl); a line's × only on the line in use, or under a pointer.
   - The keys flat - no rings, a hairline's gap less between them, the gloss a breath of it on Glass: the digits the
     lightest and largest, in a light weight; × ÷ + − one tint of the accent; the functions a quieter tint of the ink;
     EXE the one strong key (--btn-bg); DEL and AC their words in red, not a red ring.

   Typed as it looks (23 Oct 2026). The owner, of the line in use drawn small as maths over the same line as text:
   "this needs to be better". A line is one field of maths now, MathLive's (.cx-mf, board/calc.js; every other line its
   LaTeX drawn still, .cx-tex), as Desmos's. MathLive themed by its own variables from the Board's tokens - the caret and
   a chosen piece in the accent, the box round the part the caret is in a wash of it, an empty box and a bracket still
   to close in the quiet words' colour at full strength (MathLive draws both at .4 and .5: under 4.5:1 in every look) -
   and its figures, letters and words in the pane's sans (--cx-math, lining), its roots, brackets and bars its own
   (KaTeX's, which stretch). Measured as drawn with the rest. */
@font-face { font-family: 'GSB Calc'; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url('../fonts/inter-latin-wght-normal.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'GSB Calc'; font-style: italic; font-weight: 100 900; font-display: swap;
  src: url('../fonts/inter-latin-wght-italic.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'GSB Calc'; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url('../fonts/inter-greek-wght-normal.woff2') format('woff2');
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; }
.bd-calc, .bd-calcmini, .bd-calcpeer, .bd-plotpane { position: absolute; color: var(--ink); background: var(--surface);
  border: 1px solid var(--rule-strong); box-shadow: var(--shadow-2);
  --cx-quiet: color-mix(in srgb, var(--ink) 45%, var(--muted)); --cx-red: color-mix(in srgb, var(--red) 60%, var(--ink));
  --cx-math: 'GSB Calc', 'Segoe UI', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif;
  /* (The keys' tints, made from colours alone: the accent for the operators, the ink for the functions - each a
     wash on the pane, so the ink on them keeps its contrast in every palette, light and dark.) */
  --cx-k-dig: var(--field-bg);
  --cx-k-op: color-mix(in srgb, var(--accent) 16%, var(--field-bg));
  --cx-k-fn: color-mix(in srgb, var(--ink) 6%, var(--surface));
  --cx-k-hover: color-mix(in srgb, var(--accent) 22%, var(--field-bg));
  --caret-color: var(--accent); --primary-color: var(--accent);
  --selection-background-color: color-mix(in srgb, var(--accent) 14%, transparent); --selection-color: var(--ink);
  --contains-highlight-background-color: color-mix(in srgb, var(--accent) 5%, transparent);
  --placeholder-color: var(--cx-quiet); --placeholder-opacity: 1; --smart-fence-color: var(--cx-quiet); --smart-fence-opacity: 1;
  --text-font-family: var(--cx-math); }
:root[data-style="glass"] :is(.bd-calc, .bd-calcmini, .bd-calcpeer, .bd-plotpane) {
  background-color: color-mix(in srgb, var(--surface) 92%, transparent);
  background-image: linear-gradient(180deg, var(--sheen-soft), transparent 40%);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); box-shadow: var(--rim), var(--shadow-2); }
.bd-calc[hidden], .bd-calcmini[hidden], .bd-calcpeer[hidden], .bd-plotpane[hidden] { display: none; }
.bd-calc { z-index: 6; left: 12px; top: 12px; width: 372px; height: 600px; max-width: calc(100% - 16px); max-height: calc(100% - 16px);
  display: flex; flex-direction: column; gap: 8px; padding: 0 12px 10px; border-radius: 20px; overflow: hidden;
  font-size: 13px; line-height: 1.35; }
.bd-calc .tb-head { margin: 0 -12px; }
/* The calculator in Tools' head (toolbox.js), as its other round buttons there. */
.tb-calc { display: grid; place-items: center; flex: none; width: 32px; height: 32px; padding: 0; border: 0; border-radius: 50%;
  background: transparent; color: var(--ink); cursor: pointer; transition: background .15s; }
.tb-calc svg { width: 18px; height: 18px; }
.tb-calc:hover { background: color-mix(in srgb, var(--accent) 14%, transparent); }
.tb-calc:focus-visible, :is(.bd-calc, .bd-calcmini, .bd-calcpeer) :is(button, input, textarea, canvas):focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.cx-modes [role="tab"] { padding: 4px 4px; font-size: 12.5px; }
/* The bar: the angle, SETUP, what the display is set to, Share, Undo and the keys shown or not. */
.cx-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; flex: none; }
/* (Its chips whole, never squeezed under their words: a second row when the line in use's three are there too.) */
.cx-bar > :not(.cx-says) { flex-shrink: 0; }
.cx-bar .cx-chip:not(.cx-icon) { padding: 0 8px; }
/* (With the line in use's three there, what the display is set to gives them its room - SETUP shows it.) */
.cx-bar:has(.cx-sumbtns:not([hidden])) .cx-says { display: none; }
.cx-sumbtns { margin-left: auto; }
.cx-says { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; color: var(--cx-quiet); }
.cx-chip { display: inline-grid; place-items: center; min-height: 28px; min-width: 28px; padding: 0 10px; border-radius: 999px;
  border: 1px solid var(--rule-strong); background: transparent; color: var(--ink); font: 500 12.5px/1 var(--sans);
  cursor: pointer; white-space: nowrap; transition: background .15s, border-color .15s; }
.cx-chip.cx-icon { padding: 0; }
.cx-chip svg { width: 16px; height: 16px; }
.cx-chip:hover:not(:disabled) { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.cx-chip:disabled { opacity: .45; cursor: default; }
.cx-chip[aria-pressed="true"], .cx-chip[aria-expanded="true"] { background: var(--btn-bg); color: var(--btn-fg); border-color: var(--btn-bg); }
.cx-btn { display: inline-flex; align-items: center; gap: 6px; min-height: 32px; padding: 0 14px; border-radius: 999px;
  border: 1px solid var(--btn-bg); background: var(--btn-bg); color: var(--btn-fg); font: 600 12.5px/1 var(--sans); cursor: pointer; }
.cx-btn svg { width: 16px; height: 16px; }
.cx-btn:disabled { opacity: .45; cursor: default; }
:root[data-style="glass"] :is(.cx-btn, .cx-chip[aria-pressed="true"], .cx-chip[aria-expanded="true"]) {
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%);
  box-shadow: var(--bevel); }
/* SETUP: rows of a name and its choices. */
.cx-setup { display: grid; gap: 6px; flex: none; padding: 8px 10px; border-radius: 14px; background: var(--field-bg);
  border: 1px solid var(--rule); box-shadow: var(--field-inset); }
.cx-setup[hidden] { display: none; }
.cx-srow { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.cx-sname { font-size: 12.5px; color: var(--ink); }
.cx-seg { display: inline-flex; gap: 2px; padding: 2px; border-radius: 999px; border: 1px solid var(--rule); background: var(--surface); }
.cx-seg button { min-height: 26px; padding: 0 9px; border: 0; border-radius: 999px; background: transparent; color: var(--ink);
  font: 500 12px/1 var(--sans); cursor: pointer; white-space: nowrap; }
.cx-seg button[aria-checked="true"] { background: var(--btn-bg); color: var(--btn-fg); }
.cx-step { display: inline-flex; align-items: center; gap: 6px; }
.cx-digits { min-width: 2ch; text-align: center; font-variant-numeric: tabular-nums; }
/* The menus' bodies: what is left of the pane's height, scrolling within. */
.cx-bodies { flex: 1 1 auto; min-height: 60px; overflow-y: auto; overscroll-behavior: contain; margin: 0 -4px; padding: 0 4px; }
.cx-body { display: flex; flex-direction: column; gap: 8px; }
.cx-body[hidden] { display: none; }
/* Everything that is maths - a sum, an answer, a key, a statistic, a table's figures - in the clear sans, its figures
   lining and all one width (23 Oct 2026, above). */
:is(.bd-calc, .bd-calcmini, .bd-calcpeer) :is(.cx-sum, .cx-in, .cx-mf, .cx-res, .cx-why, .cx-key, .cx-field, .cx-form, .cx-stat dt,
  .cx-line2, .cx-table, .cm-open, .cp-out) {
  font-family: var(--cx-math); font-variant-numeric: lining-nums tabular-nums; font-feature-settings: 'lnum', 'tnum'; }
/* The list: a line a sum - its number, the sum (typed in, or drawn as maths when left), = its answer on the right of
   the same row when both fit and under it on the right when not (24 Oct 2026, the owner: "make this box better") - rows
   between hairlines, as Desmos's list; the line in use tinted, the accent's bar at its left. */
.cx-list { list-style: none; margin: 0; padding: 0 0 6px; display: flex; flex-direction: column; }
/* (A line brought into view - ↑, ↓, Enter - comes all of it, a little clear of the list's edge: 24 Oct 2026, the owner's
   picture had the line above's tall answer cut off at the top.) */
.cx-bodies { scroll-padding-block: 6px; }
.cx-line { scroll-margin-block: 6px; }
.cx-line { display: grid; grid-template-columns: 22px 1fr 26px; align-items: center; gap: 2px; padding: 3px 0;
  border-bottom: 1px solid var(--rule); }
.cx-line.cx-on { background: color-mix(in srgb, var(--accent) 8%, transparent); box-shadow: inset 3px 0 0 var(--accent); }
.cx-n { text-align: center; font-size: 11.5px; color: var(--cx-quiet); font-variant-numeric: tabular-nums; }
.cx-main { min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 0 6px; }
/* A line's sum: its maths drawn still, or - the line in use - the room the field of maths is laid over (calc.js
   placeField: a <math-field> moved in the page is made again, so it stays put in the list's body and goes over the
   line). Not boxed: the line's tint and bar, and the caret, say it is the one being typed into - a ring round it too
   was the box the owner asked about. */
#cx-run { position: relative; }
.cx-sum { flex: 0 1 auto; min-width: 0; max-width: 100%; box-sizing: border-box; min-height: 34px; padding: 0 8px; font-size: 19px; font-weight: 400; line-height: 1.55; color: var(--ink);
  overflow-x: auto; overflow-y: hidden; white-space: nowrap; cursor: text; }
.cx-mf { position: absolute; left: 0; top: 0; z-index: 1; display: block; box-sizing: border-box; min-height: 34px; padding: 0 8px;
  border: 0; border-radius: 6px; background: transparent; color: var(--ink); font-size: 19px; font-weight: 400; line-height: 1.55;
  outline: 0; box-shadow: none; }
.cx-mf.cx-mfaway { visibility: hidden; }
.bd-calc .cx-mf:is(:focus, :focus-visible, :focus-within) { outline: 0; }
.cx-mf::part(menu-toggle), .cx-mf::part(virtual-keyboard-toggle) { display: none; }
.cx-mf::part(container) { padding: 0; }
/* (Its maths' own box, which hides what stands out of it, room above and below - 24 Oct 2026, the owner: "cutting off
   the top": the ² of 5² stood above it, and went. The same room as a line drawn still, .cx-tex.) */
.cx-mf::part(content) { padding: .3em 0; }
/* (Should MathLive not come: the line in use a plain field of text, as before 23 Oct 2026.) */
.cx-in { width: 100%; min-height: 34px; padding: 4px 8px; border: 0; border-radius: 6px; background: transparent;
  color: var(--ink); font-size: 19px; font-weight: 400; line-height: 1.3; box-shadow: none; }
.bd-calc .cx-line .cx-in:is(:focus, :focus-visible) { outline: 0; }
.cx-sum:has(> .cx-in) { padding: 0; }
/* MathLive's still drawing (a line not in use, an answer, the pill, a shared copy): the pane's sans for its figures,
   letters and words, as in the field; an empty box quiet but whole. */
.cx-tex { display: inline-block; vertical-align: middle; }
/* (Room above and below for a stacked fraction - 5/6, a fraction over a fraction: MathLive's pieces stand out of its
   own box by a little, and an answer or a line scrolls sideways, so what stands out of its row was cut off, the top of
   the 5 of 5/6. The row's own box now holds all of it.) */
:is(.cx-res, .cx-sum, .cp-out) .cx-tex { padding: .3em 0; }
:is(.cx-res, .cx-sum, .cp-out) .cx-tex > .ML__latex { display: block; }
.cx-tex :is(.ML__cmr, .ML__mathit, .ML__mathrm, .ML__placeholder) { font-family: var(--cx-math); font-variant-numeric: lining-nums tabular-nums; }
.cx-tex .ML__placeholder { color: var(--cx-quiet); opacity: 1; }
/* (A power a breath clear of what it is on - of a bracket's curve, (√52)², and of the last figure - not touching it.) */
.cx-tex .ML__msubsup { padding-left: .05em; }
.cx-out { margin-left: auto; max-width: 100%; min-width: 0; display: flex; align-items: center; justify-content: flex-end; }
.cx-out[hidden] { display: none; }
/* The answer: a third larger than its sum, on the right (1.3 × 19 px). */
.cx-res { max-width: 100%; overflow-x: auto; padding: 0 8px; border: 1px solid transparent; border-radius: 8px; background: transparent;
  color: var(--ink); font-size: 25px; font-weight: 500; line-height: 1.45; text-align: right; white-space: nowrap; cursor: copy; }
.cx-res:hover:not(:disabled) { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.cx-res:disabled { cursor: default; }
.cx-res.cx-copied { border-color: var(--green); }
.cx-why { flex: 1 0 100%; display: flex; flex-direction: column; align-items: flex-end; margin: 0; padding: 0 8px 2px; font-size: 13px; line-height: 1.35; }
.cx-why[hidden], .cx-res[hidden] { display: none; }
.cx-eq { margin-right: .3em; font-weight: 400; color: var(--cx-quiet); }
.cx-err { font-weight: 700; color: var(--cx-red); }
.cx-hint { font: 12.5px/1.35 var(--sans); color: var(--cx-quiet); white-space: normal; }
.cx-err + .cx-hint { display: block; text-align: right; }
/* (Why a line left has no answer: a few quiet words under it, on the right - 24 Oct 2026, no shouted ERROR.) */
.cx-whyword { display: block; text-align: right; font-size: 12.5px; }
/* The line in use's S⇔D, Sum and Answer in the bar, while the keys are put away (calc.js sumBtns). */
.cx-sumbtns { display: inline-flex; align-items: center; gap: 5px; }
.cx-sumbtns[hidden] { display: none; }
.cx-del { display: grid; place-items: center; width: 26px; height: 26px; padding: 0; border: 0; border-radius: 50%;
  background: transparent; color: var(--cx-quiet); cursor: pointer; transition: opacity .12s; }
.cx-del svg { width: 14px; height: 14px; }
.cx-del:hover { background: color-mix(in srgb, var(--red) 12%, transparent); color: var(--ink); }
.cx-line:not(.cx-on):not(:hover) .cx-del:not(:focus-visible) { opacity: 0; }
/* Natural maths, drawn by calc-core.js htmlOf and show: a fraction stacked, a root's bar over its number. (A fraction's
   figures .85 of the line's - .92 of an old-style serif's was what read as tiny; a power's .72.) */
.cx-frac { display: inline-flex; flex-direction: column; vertical-align: middle; text-align: center; margin: 0 .12em; font-size: .85em; line-height: 1.2; }
.cx-num { padding: 0 .2em .05em; border-bottom: max(1px, .06em) solid currentColor; }
.cx-den { padding: .08em .2em 0; }
.cx-sqrt { display: inline-flex; align-items: stretch; }
.cx-rad { padding-right: .02em; font-weight: 300; }
.cx-under { padding: .04em .1em 0; border-top: max(1px, .06em) solid currentColor; }
.cx-index { position: relative; top: -.6em; margin-right: -.35em; font-size: .62em; }
.cx-op { margin: 0 .22em; }
.cx-word { font-size: .92em; }
.cx-thin { display: inline-block; width: .18em; }
.cx-x { margin: 0 .1em; }
:is(.cx-sum, .cx-res, .cp-out, .cm-open) i { font-style: italic; padding-right: .04em; }
.bd-calc sup, .bd-calc sub, .bd-calcmini sup, .bd-calcpeer sup, .bd-calcpeer sub { font-size: .72em; line-height: 0; }
/* The menus' fields and their results. */
/* (Not the page's small capitals for a label: these name sums.) */
.cx-label { display: flex; flex-direction: column; gap: 3px; margin: 0; font-size: 12.5px; font-weight: 400; letter-spacing: normal;
  text-transform: none; color: var(--ink); }
.cx-fx { display: flex; align-items: center; gap: 8px; }
.cx-fx .cx-label { flex-direction: row; align-items: center; gap: 4px; white-space: nowrap; min-width: 52px; }
.cx-field { width: 100%; min-width: 0; min-height: 32px; padding: 4px 8px; border-radius: 8px; border: 1px solid var(--rule-strong);
  background: var(--field-bg); color: var(--ink); font-size: 15px; font-weight: 400; line-height: 1.3; box-shadow: var(--field-inset); }
.cx-area { resize: vertical; min-height: 52px; }
.cx-range, .cx-coefs { display: grid; grid-template-columns: repeat(var(--n, 3), 1fr); gap: 6px; }
.cx-form { margin: 0; text-align: center; font-size: 16px; line-height: 1.4; }
.cx-kinds { display: flex; flex-wrap: wrap; justify-content: center; border-radius: 14px; }
.cx-stats { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 2px 10px; margin: 0; }
.cx-stat { display: flex; align-items: baseline; justify-content: space-between; gap: 6px; border-bottom: 1px solid var(--rule); }
.cx-stat dt { font-size: 14px; line-height: 1.4; font-style: italic; }
.cx-stat dd { margin: 0; min-width: 0; }
.cx-stat .cx-res { font-size: 15px; padding: 1px 4px; }
.cx-line2 { margin: 4px 0 0; text-align: center; font-size: 16px; line-height: 1.4; }
.cx-table { width: 100%; border-collapse: collapse; font-size: 14px; line-height: 1.35; }
.cx-table th, .cx-table td { padding: 3px 6px; border-bottom: 1px solid var(--rule); text-align: right; }
.cx-table th { font-weight: 600; color: var(--ink); }
.cx-foot { display: flex; justify-content: flex-end; align-items: center; gap: 6px; flex-wrap: wrap; }
.cx-gfoot .cx-btn { margin-left: auto; }
.cx-swatch { display: inline-block; width: 10px; height: 10px; margin-right: 4px; border-radius: 50%; background: var(--accent); }
.cx-y2 .cx-swatch { background: var(--red); }
.cx-y3 .cx-swatch { background: var(--green); }
.cx-plotbox { position: relative; height: 220px; flex: none; border-radius: 12px; border: 1px solid var(--rule-strong); background: var(--field-bg); overflow: hidden; }
.cx-plot { display: block; width: 100%; height: 100%; touch-action: none; cursor: var(--cur-grab); }
.cx-ghint:empty { display: none; }
p.cx-hint, p.cx-bad { margin: 0; }
p.cx-bad { display: flex; flex-direction: column; gap: 2px; }
/* The keys: the Casio fx-CG50's (24 Oct 2026, calc.js "The keys": the owner, "this has the buttons I use; they should be
   similar to that"). Six a row above - F1-F6 under the bar of what they do, SHIFT OPTN VARS MENU and the round cursor
   pad, ALPHA x² ^ EXIT, the functions, the fraction S⇔D ( ) , → - and five below, the digits with DEL AC and EXE. A
   key's second functions small above it, SHIFT's on the left in gold, ALPHA's letter on the right in red, each a hue
   mixed with the ink so it is darker on a light pane and lighter on a dark one (4.5:1 in every look, measured); SHIFT or
   ALPHA pressed underlines and strengthens its words. The digits and × ÷ + − the lightest keys, as the Casio's white
   ones; DEL AC EXE the accent's (the CG50's are blue); everything else the darker function keys. Flat, a breath of
   gloss on Glass (23 Oct 2026). */
.cx-pad { flex: none; display: flex; flex-direction: column; gap: 3px; padding-top: 6px; border-top: 1px solid var(--rule);
  --cx-shift: color-mix(in srgb, #b98200 42%, var(--ink)); --cx-alpha: color-mix(in srgb, #d4245a 36%, var(--ink));
  --cx-k-fn: color-mix(in srgb, var(--ink) 13%, var(--surface)); }
.cx-pad[hidden] { display: none; }
/* The bar of what F1-F6 do now: the CG50's screen's foot, a thin row of words in cells. */
.cx-fbar { display: grid; grid-template-columns: repeat(6, 1fr); gap: 3px; }
.cx-flab { min-width: 0; padding: 2px 1px; border-radius: 4px 4px 0 0; border-bottom: 2px solid var(--rule-strong); text-align: center;
  font: 600 11px/1.25 var(--cx-math); color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cx-flab.cx-fon { border-bottom-color: var(--accent); }
.cx-flab.cx-foff { color: var(--cx-quiet); border-bottom-style: dotted; }
.cx-flab i { font-style: italic; }
.cx-flab sup, .cx-flab sub { font-size: .75em; line-height: 0; }
.cx-keys6, .cx-keys5 { display: grid; gap: 2px 3px; }
.cx-keys6 { grid-template-columns: repeat(6, 1fr); }
.cx-keys5 { grid-template-columns: repeat(5, 1fr); margin-top: 2px; }
.cx-kc { display: flex; flex-direction: column; min-width: 0; }
/* A key's second functions: SHIFT's left, ALPHA's right, small, over the key. */
.cx-k2l { display: flex; justify-content: space-between; gap: 2px; height: 13px; flex: none; padding: 0 3px; font: 700 11px/13px var(--cx-math);
  white-space: nowrap; overflow: hidden; }
.cx-ks { color: var(--cx-shift); }
.cx-ka { color: var(--cx-alpha); }
.cx-k2l i { font-style: italic; }
.cx-k2l sup, .cx-k2l sub { font-size: .88em; line-height: 0; }
.cx-k2l .cx-kbox { width: .55em; height: .65em; border-width: 1px; }
.cx-k2l .cx-kfrac { font-size: .62em; vertical-align: top; }
.cx-kc > .cx-key { flex: 1 1 auto; }
.cx-l-shift .cx-ks, .cx-l-alpha .cx-ka { font-weight: 800; text-decoration: underline 1.5px; text-underline-offset: 2px; }
.cx-key { min-width: 0; min-height: 30px; padding: 0 2px; border-radius: 8px; border: 0; background-color: var(--cx-k-fn); color: var(--ink);
  font-size: 13.5px; font-weight: 500; line-height: 1; white-space: nowrap; overflow: hidden;
  cursor: pointer; touch-action: manipulation; user-select: none; -webkit-user-select: none; transition: background .12s, transform .1s ease-out; }
.cx-key.cx-kf { min-height: 24px; font-size: 12px; font-weight: 600; }
.cx-keys5 .cx-key { min-height: 36px; font-size: 15px; }
.cx-key.cx-kdig { background-color: var(--cx-k-dig); }
.cx-keys5 .cx-key.cx-kdig { font-size: 20px; font-weight: 400; }
.cx-key i { font-style: italic; }
.cx-key sup, .cx-key sub { font-size: .72em; line-height: 0; }
/* (The place a number goes: a hollow box, drawn, rather than ■ - a blot, or missing, in a good many faces; the fraction
   key two of them over a bar, as the Casio prints it.) */
.cx-kbox { display: inline-block; width: .62em; height: .72em; margin: 0 .05em; vertical-align: -.04em; border: 1.3px solid currentColor; border-radius: 1.5px; box-sizing: border-box; }
.cx-kfrac { display: inline-flex; flex-direction: column; align-items: center; vertical-align: middle; gap: 1px; font-size: .78em; }
.cx-kfrac .cx-kbox { margin: 0; }
.cx-kbar { display: block; width: .9em; height: 1.3px; background: currentColor; }
.cx-pad .cx-key:hover:not([aria-pressed="true"]):not(.cx-kacc) { background-color: var(--cx-k-hover); }
.cx-key:active { transform: scale(.95); }
.cx-key[aria-pressed="true"], .cx-key.cx-kacc { background-color: var(--btn-bg); color: var(--btn-fg); font-weight: 600; }
.cx-keys5 .cx-kacc { font-size: 15px; letter-spacing: .02em; }
/* The cursor pad: round, at the right of two rows, ▲ ▼ ◀ ▶ round its middle. */
.cx-dpad { grid-column: 5 / 7; grid-row: 2 / 4; align-self: center; justify-self: center; position: relative; width: min(100%, 92px);
  aspect-ratio: 1; border-radius: 50%; background-color: var(--cx-k-fn); display: grid;
  grid-template: "  .  up    .  " 1fr "left .   right" 1fr "  .  down  .  " 1fr / 1fr 1fr 1fr; }
.cx-dpad::after { content: ''; position: absolute; inset: 34%; border-radius: 50%; background: var(--surface); opacity: .55; pointer-events: none; }
.cx-dpad .cx-key { min-height: 0; padding: 0; border-radius: 50%; background: transparent; font-size: 12px; }
.cx-kd-up { grid-area: up; } .cx-kd-down { grid-area: down; } .cx-kd-left { grid-area: left; } .cx-kd-right { grid-area: right; }
.cx-dpad .cx-key:hover { background-color: color-mix(in srgb, var(--accent) 18%, transparent) !important; }
:root[data-style="glass"] .cx-key:not(.cx-kd) { background-image: linear-gradient(180deg, rgb(255 255 255 / .12), rgb(255 255 255 / .03) 50%, transparent 50.5%); }
:root[data-style="glass"] .cx-dpad { background-image: linear-gradient(180deg, rgb(255 255 255 / .12), rgb(255 255 255 / .03) 50%, transparent 50.5%); }
:root[data-style="glass"] :is(.cx-kacc, .cx-key[aria-pressed="true"]) {
  background-image: linear-gradient(180deg, rgb(255 255 255 / .22) 0, rgb(255 255 255 / .06) 48%, transparent 48.5%);
  box-shadow: var(--bevel); }
/* MENU: the menus, numbered, over the top of the pane's body (EXIT, or a pick, puts it away). */
.cx-menu { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 6px; flex: none; padding: 8px;
  border-radius: 14px; background: var(--field-bg); border: 1px solid var(--rule); box-shadow: var(--field-inset); }
.cx-menu[hidden] { display: none; }
.cx-mitem { display: flex; align-items: center; gap: 6px; min-height: 40px; padding: 0 10px; border-radius: 10px; border: 1px solid var(--rule-strong);
  background: var(--surface); color: var(--ink); font: 500 13px/1 var(--sans); cursor: pointer; }
.cx-mitem.cx-mon { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.cx-mnum { display: grid; place-items: center; width: 20px; height: 20px; border-radius: 50%; background: var(--btn-bg); color: var(--btn-fg); font-size: 11.5px; font-weight: 700; }
.cx-grow { position: absolute; right: 2px; bottom: 2px; display: grid; place-items: center; width: 18px; height: 18px;
  color: var(--cx-quiet); cursor: nwse-resize; touch-action: none; }
.cx-grow svg { width: 11px; height: 11px; }
/* Made small: a pill of the same glass, the calculator and the last answer (as Tools' pill: calc.js, Small). */
.bd-calcmini { z-index: 6; left: 12px; top: 12px; display: flex; align-items: center; height: 42px; padding: 0 5px;
  border-radius: 999px; touch-action: none; user-select: none; -webkit-user-select: none; cursor: var(--cur-grab); }
.bd-calcmini.tm-moving { cursor: var(--cur-grabbing); }
.bd-calcmini.tm-near { opacity: .2; pointer-events: none; }
.cm-open { display: inline-flex; align-items: center; gap: 6px; max-width: 220px; height: 32px; padding: 0 9px; border: 0; border-radius: 999px;
  background: transparent; color: var(--ink); font-size: 16px; font-weight: 600; line-height: 1; white-space: nowrap; overflow: hidden; cursor: pointer; }
.cm-open svg { flex: none; width: 19px; height: 19px; }
.cm-open:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.cm-ans { overflow: hidden; text-overflow: ellipsis; }
.cm-ans[hidden] { display: none; }
/* Someone else's, shared (calc.js "Shared with everyone"): the same glass, named for whose it is, to read alone. */
/* (27 Oct 2026: the whole calculator - its menus, its list, its keys - at the sharer's width, as tall as it needs up to
   the stage; the list gives way first.) */
.bd-calcpeer { z-index: 5; left: 12px; top: 12px; width: 340px; max-width: calc(100% - 16px); max-height: calc(100% - 16px);
  display: flex; flex-direction: column; gap: 4px; padding: 0 12px 10px; border-radius: 18px; overflow: hidden; font-size: 13px; }
/* Following the sharer's moves: a glide to where theirs went (calc.js follow), never a jump. */
.bd-calcpeer.cp-glide { transition: left var(--t-mid) var(--ease-out), top var(--t-mid) var(--ease-out), opacity var(--t-quick) var(--ease-out), scale var(--t-mid) var(--ease-pop); }
.cp-follow { margin-left: auto; }
.cp-follow[hidden] { display: none; }
.cp-follow + .tb-close { margin-left: 4px; }
.cp-modes { flex: none; }
.cp-tab { flex: 1 1 0; min-width: 0; padding: 4px 4px; border-radius: 999px; font-size: 12px; text-align: center; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cp-tab.cp-on { background: var(--btn-bg); color: var(--btn-fg); font-weight: 500; }
.cp-body { min-height: 60px; }
.cp-padat { flex: none; }
/* A function's graph's pane (27 Oct 2026, board/plot.js): its lines, each with its colour; its sliders; its window; and
   over the graph itself, while the pane is open, a sheet that takes a drag (the window moved) and the wheel (zoomed). */
.bd-plotpane { z-index: 6; width: 330px; max-width: calc(100% - 16px); max-height: calc(100% - 16px); overflow: auto;
  display: flex; flex-direction: column; gap: 8px; padding: 0 12px 12px; border-radius: 18px; font-size: 13px; line-height: 1.35; }
.bd-plotpane .tb-head { margin: 0 -12px; position: sticky; top: 0; z-index: 1; }
.pl-rows { display: flex; flex-direction: column; gap: 6px; }
.pl-row { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 6px; }
.pl-row .pl-src { font-family: var(--cx-math); }
.pl-row .pl-src[aria-invalid="true"] { border-color: var(--cx-red); }
.pl-row .pl-del { width: 28px; height: 28px; }
.pl-row .pl-del svg { width: 15px; height: 15px; }
.pl-ink { width: 22px; height: 22px; border-radius: 50%; padding: 0; cursor: pointer; background: var(--pl-c, var(--accent));
  border: 2px solid var(--surface); box-shadow: 0 0 0 1px var(--rule-strong); }
.pl-err { grid-column: 2 / 4; margin: -2px 0 0; color: var(--cx-red); font-size: 12px; }
.pl-err[hidden] { display: none; }
.pl-add { align-self: flex-start; }
.pl-vars { display: flex; flex-direction: column; gap: 6px; }
.pl-vars:empty { display: none; }
.pl-var { display: grid; grid-template-columns: auto 74px 1fr; align-items: center; gap: 4px 8px; padding: 6px 8px; border-radius: 12px;
  background: var(--cx-k-fn); border: 1px solid var(--rule); }
.pl-vn { font: italic 500 15px/1 var(--cx-math); white-space: nowrap; }
.pl-vn i { font-style: italic; }
.pl-var .pl-vv { min-height: 28px; font-size: 14px; }
.pl-range { width: 100%; min-width: 0; accent-color: var(--accent); }
.pl-vlim { grid-column: 1 / 4; display: flex; align-items: center; gap: 6px; color: var(--cx-quiet); font-size: 12px; }
.pl-vlim .cx-field { width: 64px; min-height: 24px; padding: 2px 6px; font-size: 12.5px; }
.pl-win { display: flex; flex-direction: column; gap: 4px; }
.pl-lim { display: flex; align-items: center; gap: 6px; margin: 0; color: var(--ink); font: 400 13px/1.35 var(--sans);
  text-transform: none; letter-spacing: normal; }
.pl-lim i { width: 10px; font: italic 500 15px/1 var(--cx-math); }
.pl-lim .cx-field { flex: 1; min-height: 28px; font-size: 13.5px; }
.pl-foot { display: flex; flex-wrap: wrap; gap: 6px; }
.pl-tip { margin: 0; color: var(--cx-quiet); font-size: 12px; }
.bd-plotgrab { position: absolute; z-index: 3; cursor: var(--cur-grab, grab); touch-action: none; border-radius: 4px;
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 55%, transparent); }
.bd-plotgrab.pl-grabbing { cursor: var(--cur-grabbing, grabbing); }
.bd-plotgrab[hidden] { display: none; }
/* Their keys: drawn, not pressed - the same keys as spans, centred as a button centres its words. */
.cp-pad .cx-key { display: flex; align-items: center; justify-content: center; cursor: default; user-select: none; }
.cp-pad .cx-key.cp-held { background-color: var(--btn-bg); color: var(--btn-fg); font-weight: 600; }
/* A key pressed there: lit in the accent a moment, so whoever watches sees which. */
.cp-pad .cx-key.cp-flash { animation: cp-flash .45s var(--ease-out); }
@keyframes cp-flash {
  0%, 35% { background-color: var(--accent); color: var(--btn-fg); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 45%, transparent); }
  100% { box-shadow: 0 0 0 0 transparent; }
}
:root[data-motion="reduce"] .cp-pad .cx-key.cp-flash { animation-duration: .6s; animation-timing-function: steps(1, end); }
:root[data-motion="reduce"] .bd-calcpeer.cp-glide { transition: none; }
/* Theirs, said while the owner sees it (calc.js "Seen by the owner"): a strip under the bar, with Stop. */
.cx-watched { display: flex; align-items: center; gap: 8px; margin: 2px 0 0; padding: 5px 6px 5px 12px; border-radius: 12px;
  background: var(--accent-soft); color: var(--ink); font-size: 12.5px; font-weight: 500; }
.cx-watched[hidden] { display: none; }
.cx-watchedsay { flex: 1 1 auto; min-width: 0; }
/* The owner's See theirs: a little menu of everyone on the page. */
.cx-seemenu { position: absolute; z-index: 3; min-width: 200px; max-width: calc(100% - 12px); padding: 5px; display: grid; gap: 2px;
  background: var(--surface); border: 1px solid var(--rule-strong); border-radius: 12px; box-shadow: var(--shadow-2); }
.cx-seemenu button { display: flex; justify-content: space-between; gap: 14px; padding: 7px 10px; border: 0; border-radius: 8px; background: transparent;
  color: var(--ink); font: inherit; font-size: 13.5px; text-align: left; cursor: pointer; }
.cx-seemenu button:hover, .cx-seemenu button:focus-visible { background: var(--paper-2); outline: none; }
.cx-seemenu button[aria-checked="true"] { background: var(--accent-soft); font-weight: 600; }
.cx-seeword { color: color-mix(in srgb, var(--ink) 35%, var(--muted)); font-weight: 400; }
.cx-see[hidden] { display: none; }
.bd-calcpeer .tb-head { margin: 0 -12px; }
.cp-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cp-says { margin: 0; font-size: 12px; color: var(--cx-quiet); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: none; }
.cp-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
.cp-line { grid-template-columns: 1fr; }
.cp-line .cx-sum { cursor: default; color: var(--ink); }
.cp-out { padding: 0 8px; font-size: 22px; font-weight: 500; line-height: 1.4; }
.cp-name { margin: 4px 0; font-weight: 600; }
.cp-rows td:first-child { text-align: left; font-style: italic; }
@media (pointer: coarse) {
  .cx-key, .cx-key.cx-kf, .cx-keys5 .cx-key { min-height: 44px; }
  .cx-dpad { width: min(100%, 132px); }
  .cx-mitem { min-height: 44px; }
  :is(.cx-chip, .cx-act, .cx-seg button) { min-height: 40px; }
  .cx-act { padding: 0 9px; }
  .cx-modes [role="tab"] { min-height: 44px; }
  .cx-del { width: 40px; height: 40px; }
  .tb-calc { width: 44px; height: 44px; }
  .bd-calcmini { height: 52px; }
  .cm-open { height: 44px; }
  .cx-grow { width: 32px; height: 32px; }
}
@media (max-width: 560px) {
  .bd-calc { width: calc(100% - 32px); }
}

/* ═══ Motion (4 Oct 2026; made clean 7 Oct 2026) ════════════════════════
   The owner: "improving animations", then "can we make sure the animations of
   opening menus etc are super clean", and "make sure it all works on safari
   too" (an iPad's Safari from 16.4, and Chrome or Edge on Windows with a pen).
   Light and quick, as the look is - glass that comes up out of where it was
   asked for, a little small and short of its place and see-through, and
   settles there in 0.13-0.2 s; going, the same way back and a touch quicker
   (about 70 % of the time, on --ease-away). Only opacity and the transform
   properties move (scale, translate: the browser moves those without laying
   the page out again or painting it afresh) - never a size, a filter, a
   shadow or a colour - and nothing takes a press away meanwhile: a menu
   opening takes its first press at once, and one opened again while it is
   still going turns round where it is - a transition (it starts from
   wherever it has got to), not a keyframe (which starts again from nothing).

   How a thing comes and goes: app.js's GSMotion, for every surface. Coming,
   it is shown in its first look - the class .gs-enter, which each surface's
   rules give (opacity 0, a little small or short of its place) - and the
   class is taken off after a style is read, so its own transition carries it
   to rest. Going, it takes .gs-leave and is put away ([hidden], a dialog
   closed, or removed) only once every movement in it has run to its end, or
   a little after the longest should have. Until 7 Oct 2026 the panes came in
   by @starting-style and went by holding `display` and `overlay` over to the
   end (transition-behavior: allow-discrete) - which Safari has only from 17.5
   and 18 (and `overlay` not at all), so on the owner's iPad they simply came
   and went. A class, a style read and a timer work everywhere.

   Two things Safari draws badly, kept away from everywhere:
   - Opacity (or a filter, a mask, a clip-path) moving on something that holds
     a pane of blurred glass (backdrop-filter) makes it the glass's backdrop
     root: the blur switches off for as long as it moves, and the pane snaps
     from frosted to clear. The glass itself, or what is beside it, moves -
     never what holds it. (And .bd-rtrack has no backdrop-filter at all: #68,
     the radial ring that went blank under a mask and a blur together.)
   - An iPad redraws a blurred pane badly while the pane itself is scaled or
     moved. There (data-glass-motion="fade", set by base.html's head script on
     every iPad, all of whose browsers are WebKit) a glass pane comes and goes
     by opacity alone, on the Glass style only - each surface's rules have a
     twin under :root[data-glass-motion="fade"][data-style="glass"] that takes
     its scale and translate away. What has no blur (the ring menus' orbs)
     still flies.

   Turned down - by the system, or Settings → Look → Reduce motion (base.html's
   head script puts data-motion="reduce" on <html> for either: GSLook.still) -
   nothing moves at all: every transition and animation on the page takes no
   time, so each thing is at once where it ends; GSMotion shows and puts away
   at once, and the scripts' own movements ask GSLook.still (GSMotion.still,
   GSBoard.still) and stay still. (The tests turn transitions off while words
   are measured as well - tests/browser.py, STILL.)

   The times and curves, for every surface (and GSMotion.time / .curve for the
   scripts' own movements):
   - --ease-out comes in; --ease-away goes, holding a moment before it does.
   - --ease-pop only for the coming `scale` of a pane that pops (a dialog, the
     selection bar, Tools, the rail's tool picture): never a translate, never
     a going, never the ring menus' orbs ("read as a wobble", 5 Oct).
   - --ease-spring only for the press's spring (sounds.js).
   - quick: menus, hints, toasts, chips, small popups, search. mid: the ring
     menus and arcs, the tool sheet, the selection bar, Tools, Arrange, the
     pages strip, the equation editor, the rail's light. slow: dialogs. Each
     with its -out, the time to go. */
html { --ease-out: cubic-bezier(.2, .8, .3, 1); --ease-away: cubic-bezier(.45, 0, .8, .45);
  --ease-pop: cubic-bezier(.2, .8, .3, 1.12); --ease-spring: cubic-bezier(.34, 1.8, .64, 1);
  --t-quick: .13s; --t-quick-out: .09s;
  --t-mid: .18s; --t-mid-out: .13s;
  --t-slow: .2s; --t-slow-out: .14s;
  /* The radial menu's bloom, for anything laid out in rings of its orbs (.bd-ritem, GSBoard.radial.parts): a choice
     flying out, a choice opening out of another, and a menu going. */
  --bloom-time: var(--t-mid); --arc-time: .16s; --close-time: var(--t-mid-out); }
:root[data-motion="reduce"] *, :root[data-motion="reduce"] *::before, :root[data-motion="reduce"] *::after,
:root[data-motion="reduce"] ::backdrop {
  animation-duration: 0s !important; animation-delay: 0s !important; animation-iteration-count: 1 !important;
  transition-duration: 0s !important; transition-delay: 0s !important; scroll-behavior: auto !important; }
/* Opacity alone, either way: for a surface whose going is a keyframe (a chip, the hold cue), and for what comes and
   goes by fading only on an iPad (data-glass-motion="fade", above). */
@keyframes gs-fadeout { to { opacity: 0; } }
@keyframes gs-fadein { from { opacity: 0; } }

/* Dialogs - Settings, Share, a new whiteboard, a template named, the pen's buttons, History, Your name - come up from
   a little below and smaller, and their backdrop darkens behind them as they do, on the same time; going, the same way
   back, quicker, on --ease-away. Settings grows out of its corner, under the gear; Share out from under its button
   (share.js: GSMotion.originAt); the rest from their middle. `scale` and `translate`, not `transform`, which a dialog
   dragged by its head is moved by (--dx, --dy).
   Since 7 Oct 2026 (the owner: "can we make sure the animations of opening menus etc are super clean", and "make sure
   it all works on safari too") by GSMotion.openDialog / closeDialog (app.js): .gs-enter is the first look, taken off
   after a style read, and Esc goes the same way as a close (GSMotion.esc). Closed, a dialog is closed at once - the
   page under it live, the focus back - and a copy of it (dialog.gs-copy, inert, fixed where it was) and of its
   backdrop (.gs-backdrop, its tint the dialog's own: see dialog::backdrop) take .gs-leave, its last look, and are
   taken away once they have gone; kept open and modal to the end of its going instead, the whole page under it was
   inert all that time (a pen's stroke begun on the board just after ✕ was lost). Until then they came in by
   @starting-style and went by holding `display` and `overlay` over (allow-discrete) - and Safari before 17.4 dropped
   the whole transition shorthand for the words it did not know, and has no `overlay` even in 18, so on the owner's
   iPad a dialog simply came and went. Nothing of those is left here.
   The backdrop's own fade has its times and curves written out as well: Safari before 17.4 gives ::backdrop none of
   the page's custom properties (its tint has a fallback for the same reason), and without them it had no transition
   at all - the page dimmed whole at the first frame while the pane was still nothing.
   The pane is the glass itself (on Glass it has the backdrop-filter), so its own opacity moving switches no blur
   off; on an iPad on Glass it fades alone, unscaled and unmoved (app.css, "Motion"). */
dialog { transition: opacity var(--t-mid) var(--ease-out), scale var(--t-slow) var(--ease-pop), translate var(--t-slow) var(--ease-out); }
dialog:is(.gs-enter, .gs-leave) { opacity: 0; scale: .96; translate: 0 10px; }
dialog.gs-leave { pointer-events: none; transition-duration: var(--t-slow-out); transition-timing-function: var(--ease-away); }
dialog::backdrop { transition: opacity var(--t-mid, .18s) var(--ease-out, cubic-bezier(.2, .8, .3, 1)); }
dialog.gs-enter::backdrop { opacity: 0; }
.gs-backdrop { position: fixed; inset: 0; z-index: 2147483600; pointer-events: none;
  transition: opacity var(--t-slow-out) var(--ease-away); }
.gs-backdrop.gs-leave { opacity: 0; }
dialog.settings { transform-origin: 100% 0; }
:root[data-glass-motion="fade"][data-style="glass"] dialog:is(.gs-enter, .gs-leave) { scale: none; translate: none; }

/* The menus (the right-click list, ⋯, a choice's own menu - #bd-menu, #bi-menu, .ns-tmenu, .ctxmenu): down out of
   their button, fading in; away the same way, quicker. Measured and placed as they open (board.js: openMenu), in their
   first look: only their width and height are measured, which neither the look nor the move changes. One opened above
   where it was asked for (near the foot of the window: data-above) comes up into place instead, and goes back down.
   7 Oct 2026 (the owner: "can we make sure the animations of opening menus etc are super clean", and "make sure it
   all works on safari too"): by GSMotion (app.js) - .gs-enter the first look, .gs-leave the last, [hidden] put on
   only once it has gone - where they came in by @starting-style and went by holding `display` (allow-discrete), which
   the owner's iPad has neither of, so there a menu simply came and went. A second menu while one shows (the notebook's
   then ⋯, or the list's Fill… opening its own): the first left fading where it was, a copy (.gs-ghost, the same look:
   the #bd-menu rules have its twins), and the new one comes in afresh at its own place - never one menu jumping. */
.ctxmenu { transition: opacity var(--t-quick) var(--ease-out), translate var(--t-quick) var(--ease-out); }
/* (Nothing moves within its first look: measured there, it can learn only then that it is turned up - data-above -
   and a menu that came up from below began 6 px above its place, sliding down to the start first.) */
.ctxmenu.gs-enter { opacity: 0; translate: 0 -6px; transition: none; }
.ctxmenu.gs-leave { opacity: 0; translate: 0 -4px; pointer-events: none;
  transition-duration: var(--t-quick-out); transition-timing-function: var(--ease-away); }
.ctxmenu[data-above].gs-enter { translate: 0 6px; }
.ctxmenu[data-above].gs-leave { translate: 0 4px; }
:root[data-glass-motion="fade"][data-style="glass"] .ctxmenu:is(.gs-enter, .gs-leave) { translate: none; }

/* The panes over a whiteboard: Tools (the timer and stopwatch) swells up out of its button - under it, wherever the
   pane is put (toolbox.js: GSMotion.originAt; the top of its middle where the button is folded away); the selection
   bar out of the selection under it, as its frame and handles come up (board.js: selFade); a hint, and a page's
   "Moved to the bin · Undo", slide up from the foot of the page and back down. Each by GSMotion (7 Oct 2026, as the
   menus above), going the same way back a touch quicker on --ease-away; on an iPad on Glass, by fading alone. Tools
   is drawn as it will be before it shows (toolbox.js, open), so nothing in it moves as it comes. */
.bd-toolbox { transform-origin: 50% 0; transition: opacity var(--t-quick) var(--ease-out), scale var(--t-mid) var(--ease-pop); }
.bd-toolbox:is(.gs-enter, .gs-leave) { opacity: 0; scale: .95; }
.bd-toolbox.gs-leave { pointer-events: none; transition-duration: var(--t-mid-out); transition-timing-function: var(--ease-away); }
:root[data-glass-motion="fade"][data-style="glass"] .bd-toolbox:is(.gs-enter, .gs-leave) { scale: none; }
/* Tools made small (11 Oct 2026): the pill swells up where it sits as the pane goes into it, and goes the same way as
   the pane comes back out of it; faded alone on an iPad's Glass. Its fade by a stroke near it in the quick time. */
.bd-toolmini { transition: opacity var(--t-quick) var(--ease-out), scale var(--t-mid) var(--ease-pop); }
.bd-toolmini:is(.gs-enter, .gs-leave) { opacity: 0; scale: .9; }
.bd-toolmini.gs-leave { pointer-events: none; transition-duration: var(--t-mid-out); transition-timing-function: var(--ease-away); }
:root[data-glass-motion="fade"][data-style="glass"] .bd-toolmini:is(.gs-enter, .gs-leave) { scale: none; }
/* The calculator (21 Oct 2026, calc.js) comes and goes as Tools does: the pane swells out of what opened it, the pill
   where it sits, and someone else's shared one where it is put; faded alone on an iPad's Glass. */
.bd-calc, .bd-calcpeer { transform-origin: 50% 0; transition: opacity var(--t-quick) var(--ease-out), scale var(--t-mid) var(--ease-pop); }
.bd-calc:is(.gs-enter, .gs-leave), .bd-calcpeer:is(.gs-enter, .gs-leave) { opacity: 0; scale: .95; }
.bd-calcmini { transition: opacity var(--t-quick) var(--ease-out), scale var(--t-mid) var(--ease-pop); }
.bd-calcmini:is(.gs-enter, .gs-leave) { opacity: 0; scale: .9; }
:is(.bd-calc, .bd-calcmini, .bd-calcpeer).gs-leave { pointer-events: none; transition-duration: var(--t-mid-out); transition-timing-function: var(--ease-away); }
:root[data-glass-motion="fade"][data-style="glass"] :is(.bd-calc, .bd-calcmini, .bd-calcpeer):is(.gs-enter, .gs-leave) { scale: none; }
/* (The selection bar, 7 Oct 2026: placed by its translate in whole px - board.js placeSelbar - so it is never moved
   here, only swelled up out of the side the selection is on: its foot over the selection, its head when it sits
   below it, data-below. GSMotion shows it and puts it away; an iPad on Glass fades it alone.) */
.bd-selbar { transform-origin: 50% 100%; transition: opacity var(--t-quick) var(--ease-out), scale var(--t-mid) var(--ease-pop); }
.bd-selbar[data-below] { transform-origin: 50% 0; }
.bd-selbar:is(.gs-enter, .gs-leave) { opacity: 0; scale: .94; }
.bd-selbar.gs-leave { pointer-events: none; transition-duration: var(--t-quick-out); transition-timing-function: var(--ease-away); }
:root[data-glass-motion="fade"][data-style="glass"] .bd-selbar:is(.gs-enter, .gs-leave) { scale: none; }
/* (A hint over one still showing does not go and come again: its words change under a dip of its light, board.js
   hint. Going, its words stay as they were until it has gone. Its slide on --ease-out, not --ease-pop: a translate
   never pops.) */
:is(.bd-hint, .bd-pgtoast) { transition: opacity var(--t-quick) var(--ease-out), translate var(--t-mid) var(--ease-out); }
:is(.bd-hint, .bd-pgtoast):is(.gs-enter, .gs-leave) { opacity: 0; translate: 0 10px; }
:is(.bd-hint, .bd-pgtoast).gs-leave { pointer-events: none; transition-duration: var(--t-quick-out); transition-timing-function: var(--ease-away); }
:root[data-glass-motion="fade"][data-style="glass"] :is(.bd-hint, .bd-pgtoast):is(.gs-enter, .gs-leave) { translate: none; }

/* The tool in hand (board.js: railLight): a bubble of light behind its lozenge that slides along the rail to a tool
   newly taken up and settles there (the tool's own picture swells back to its size as it lands, popping a hair: a
   scale coming in may). 7 Oct 2026, "super clean": its slide on --ease-out, with no overshoot - a light that went past
   its tool and came back read as a wobble, as the ring menus' orbs did - in the ring menus' time. In
   the palette's accent, as the lozenge is, with a hard-stop shine across its top - fainter in the dark, where the
   pictures are light. (A tool's options come in from the rail's side as it changes: board.js, paintProps.) */
.bd-raillight { position: absolute; left: 0; top: 0; width: 38px; height: 38px; border-radius: 50%; pointer-events: none;
  background: linear-gradient(180deg, light-dark(rgb(255 255 255 / .34), rgb(255 255 255 / .08)) 0 46%, transparent 46.5%),
    radial-gradient(circle at 50% 60%, color-mix(in srgb, var(--accent) 24%, transparent) 0 55%, transparent 72%);
  box-shadow: 0 0 12px 1px color-mix(in srgb, var(--accent) 30%, transparent);
  transition: translate var(--t-mid) var(--ease-out); }
@supports not (color: light-dark(#000, #fff)) {
  .bd-raillight {
    background: linear-gradient(180deg, var(--not-light, rgb(255 255 255 / .34)) var(--not-dark, rgb(255 255 255 / .08)) 0 46%, transparent 46.5%),
      radial-gradient(circle at 50% 60%, color-mix(in srgb, var(--accent) 24%, transparent) 0 55%, transparent 72%);
  }
}
.bd-raillight[hidden] { display: none; }
.bd-raillight.bd-rjump { transition: none; }

/* The list of whiteboards: a card lifts under the pointer (above) and gives under a press, springing back as it is
   let go. */
.bi-card { transition: box-shadow .18s var(--ease-out), transform .2s var(--ease-pop); }
.bi-card:active { transform: translateY(0) scale(.985); transition-duration: .08s; }

/* ═══ The side panels arranged (4 Oct 2026: static/js/board/panels.js) ═══
   The owner: "Allow changing the order of buttons on the side panels and hiding selected ones". The tools, the page
   buttons and the zoom: a button hidden is display: none (there still, for its key) - but while arranging every one
   shows, the hidden faded. The pane beside the tools is the floating panes' own material, as the timer's is
   (.bd-toolbox: on Glass nearly solid under the softer sheen alone - the page's near-white paper is behind it, not the
   aurora); its list is Settings → Side panels' too. Its words are --ink, or --muted with 45 % ink (--ar-quiet;
   Settings' own --st-quiet in Settings), Done --btn-fg on --btn-bg; its marks - the pictures, a switch's edge, the line
   where a button would go, the ring round the panel arranged - 3:1 and more. Measured as drawn in all 44 looks:
   tests/test_panels.py.
   5 Oct 2026, the owner: "this looks a bit too complicated and messy". So: a row is a picture, a name and a switch, the
   whole row taken hold of; one line of words; the tabs a quiet segmented control; Done the button and Reset a word; on
   the rail no dashed ring round each button, no cross through the hidden (faded instead) and only the panel arranged
   ringed. */
:root {
  --ar-quiet: color-mix(in srgb, var(--ink) 45%, var(--muted));
  /* (The line where a dragged button would go: the accent with ink in it, so it is a line - 3:1 - in every palette.) */
  --ar-mark: color-mix(in srgb, var(--accent) 70%, var(--ink));
}
/* Hidden - unless arranging. */
:not(.bd-arranging) > .bd-panelhid { display: none !important; }
/* The way in, at the end of the tools: lower than a tool, quieter than one, and lit - softly - while arranging (5 Oct
   2026: it was the primary button's dark lozenge with a ✓ in it, one more thing on the rail to read). */
.bd-railedit { height: 30px; margin-top: 2px; color: color-mix(in srgb, var(--ink) 75%, var(--muted)); }
.bd-railedit svg { width: 18px; height: 18px; }
.bd-railedit[aria-pressed="true"], .bd-railedit[aria-pressed="true"]:hover {
  background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--ink); }
.bd-tool:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* A finger: every tool 44 px (the rail scrolls within itself where the page is shorter than it). */
@media (pointer: coarse) {
  .bd-tool, .bd-railedit { width: 44px; height: 44px; }
}

/* Arranging: every button picked up by any pointer (the page does not scroll under a finger), the line between groups
   with room to take hold of, the hidden faded; and the one panel whose list is shown ringed (.bd-arrangenow). */
/* (The ring is on the page's paper, not a pane: the paper's ink with a little of the accent - dark on paper, light on
   slate, and on Glass paper as the theme has it - so it is a line, 3:1, on whichever.) */
.bd-stage { --ar-ring: color-mix(in srgb, var(--accent) 35%, #1c1f24); }
.bd-stage.paper-slate { --ar-ring: color-mix(in srgb, var(--accent) 35%, #eef2f3); }
.bd-stage.paper-glass { --ar-ring: light-dark(color-mix(in srgb, var(--accent) 35%, #1c1f24), color-mix(in srgb, var(--accent) 35%, #eef2f3)); }
@supports not (color: light-dark(#000, #fff)) {
  .bd-stage.paper-glass {
    --ar-ring: var(--not-light, color-mix(in srgb, var(--accent) 35%, #1c1f24)) var(--not-dark, color-mix(in srgb, var(--accent) 35%, #eef2f3));
  }
}
.bd-arrangenow { outline: 2px solid var(--ar-ring); outline-offset: 3px; }
.bd-arranging > [data-pitem] { position: relative; cursor: var(--cur-grab); touch-action: none; -webkit-user-select: none; user-select: none; }
.bd-arranging > :is(.bd-railgap, .bd-pgsep) { flex: none; height: 14px; margin: 1px 2px; border-radius: 6px;
  background: linear-gradient(var(--rule-strong), var(--rule-strong)) center / calc(100% - 10px) 2px no-repeat; }
.bd-arranging > .bd-pgsep { width: 30px; }
.bd-arranging > :is(.bd-railgap, .bd-pgsep):focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Hidden, while arranging: faded, as a button that cannot be pressed is - it will not be there once arranging is done.
   (Not 3:1 at .35, as a disabled button is not: what it is and that it is hidden are said beside it in the pane, the
   name at 4.5:1 and the switch off at 3:1, and to a screen reader on the button itself; tests/test_panels.py measures
   it all the same and holds it to that.) */
.bd-arranging > .bd-panelhid { opacity: .35; }
.bd-arranging > .bd-panelhid:hover, .bd-arranging > .bd-panelhid:focus-visible { opacity: .6; }
/* The zoom's words keep their own size; while arranging they get a little room between. */
.bd-arranging.bd-zoom { gap: 3px; }
/* Picked up: lifted a little, a soft shadow under it, and following the pointer (no lift with motion turned down). */
.ar-lift { z-index: 3; background: var(--surface) !important; opacity: 1 !important; scale: 1.04;
  box-shadow: 0 8px 20px rgb(0 0 0 / .22), 0 0 0 1px var(--rule-strong); transition: scale .14s var(--ease-out), box-shadow .14s; }
.ar-dragging, .ar-dragging * { cursor: var(--cur-grabbing) !important; -webkit-user-select: none; user-select: none; }
.ar-mark { position: absolute; z-index: 4; height: 3px; border-radius: 2px; background: var(--ar-mark); pointer-events: none;
  box-shadow: 0 0 6px color-mix(in srgb, var(--accent) 55%, transparent); }
.ar-mark.ar-markx { width: 3px; height: auto; }
.ar-mark[hidden] { display: none; }
/* (On a panel the line is drawn under the button taken up, never across it - 5 Oct 2026.) */
.bd-arranging > .ar-lift { z-index: 5; }
/* While arranging, the tool's options step aside for the pane. */
.bd-stage[data-arranging] .bd-props { display: none !important; }

/* The pane: beside the tools, from the top (panels.js placePane), scrolling its list within itself. */
.bd-arrange { position: absolute; z-index: 6; left: 76px; top: 12px; width: min(300px, calc(100% - 92px)); max-height: calc(100% - 24px);
  display: flex; flex-direction: column; gap: 10px; padding: 0 12px 12px; border-radius: 20px; font-size: 13px; line-height: 1.35;
  color: var(--ink); background: var(--surface); border: 1px solid var(--rule-strong); box-shadow: var(--shadow-2); }
:root[data-style="glass"] .bd-arrange {
  background-color: color-mix(in srgb, var(--surface) 92%, transparent);
  background-image: linear-gradient(180deg, var(--sheen-soft), transparent 40%);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); box-shadow: var(--rim), var(--shadow-2); }
.bd-arrange[hidden] { display: none; }
/* Coming in from the rail's side, and going back to it (7 Oct 2026, "super clean": GSMotion, panels.js begin/end): the
   pane is the glass itself, so its own opacity moving switches no blur off; on an iPad on Glass it only fades. */
.bd-arrange { transition: opacity var(--t-quick) var(--ease-out), translate var(--t-mid) var(--ease-out); }
.bd-arrange:is(.gs-enter, .gs-leave) { opacity: 0; translate: -8px 0; }
.bd-stage[data-side="right"] .bd-arrange:is(.gs-enter, .gs-leave) { translate: 8px 0; }
.bd-arrange.gs-leave { pointer-events: none; transition-duration: var(--t-mid-out); transition-timing-function: var(--ease-away); }
:root[data-glass-motion="fade"][data-style="glass"] .bd-arrange:is(.gs-enter, .gs-leave) { translate: none; }
.ar-head { display: flex; align-items: center; gap: 8px; margin-right: -6px; padding: 8px 0 0 4px; }
.ar-title { flex: 1; margin: 0; font: 600 14px/1.2 var(--sans); letter-spacing: .02em; color: var(--ink); text-transform: none; }
.ar-close { display: grid; place-items: center; flex: none; width: 32px; height: 32px; padding: 0; border: 0; border-radius: 50%;
  background: transparent; color: var(--ink); cursor: pointer; transition: background .15s; }
.ar-close svg { width: 17px; height: 17px; }
/* Which panel's list: a quiet segmented control - a faint track, the one shown a raised tile of the pane's own colour
   (5 Oct 2026: it was the primary button's dark lozenge, as loud as Done). */
.ar-tabs { display: flex; flex: none; gap: 2px; padding: 2px; border-radius: 10px;
  background: color-mix(in srgb, var(--ink) 6%, transparent); }
.ar-tabs [role="tab"] { flex: 1 1 0; min-width: 0; min-height: 28px; padding: 3px 8px; border: 0; border-radius: 8px;
  background: transparent; color: color-mix(in srgb, var(--ink) 80%, var(--muted)); font: inherit; font-size: 12.5px; cursor: pointer; }
.ar-tabs [role="tab"]:hover { color: var(--ink); }
.ar-tabs [role="tab"][aria-selected="true"] { background: var(--surface); color: var(--ink); font-weight: 600;
  box-shadow: 0 1px 2px rgb(0 0 0 / .16), 0 0 0 1px var(--rule); }
:root[data-style="glass"] .ar-tabs [role="tab"][aria-selected="true"] {
  background-image: linear-gradient(180deg, var(--sheen-soft), transparent 60%); box-shadow: var(--rim), 0 1px 2px rgb(0 0 0 / .16); }
.ar-lede { flex: none; margin: 0 4px; font-size: 12px; line-height: 1.4; color: var(--ar-quiet); }
.ar-body { flex: 1 1 auto; min-height: 60px; overflow-y: auto; overscroll-behavior: contain; margin: -4px -6px 0; padding: 0 6px; }
/* Done the button; Reset a quiet word beside it (Undo, just after it). */
.ar-foot { display: flex; flex: none; align-items: center; justify-content: space-between; gap: 8px; padding-top: 2px; }
.ar-reset { min-height: 34px; min-width: 44px; padding: 0 8px; border: 0; border-radius: 8px; background: transparent;
  color: color-mix(in srgb, var(--ink) 80%, var(--muted)); font: 500 13px/1 var(--sans); cursor: pointer; }
.ar-reset:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
.ar-go { flex: 0 1 140px; min-height: 34px; padding: 0 18px; border-radius: 999px; font: 600 13px/1 var(--sans); letter-spacing: .02em;
  border: 1px solid var(--btn-bg); background: var(--btn-bg); color: var(--btn-fg); cursor: pointer; transition: border-color .15s; }
.ar-go:hover { border-color: var(--accent); }
:root[data-style="glass"] .ar-go {
  background-image: linear-gradient(180deg, rgb(255 255 255 / .26) 0, rgb(255 255 255 / .08) 48%, transparent 48.5%); box-shadow: var(--bevel); }
/* Said to a screen reader only. */
.ar-say { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* The list, the pane's and Settings': a row a button - its picture, its name, and whether it shows. The whole row is
   taken hold of (a mouse at once, a finger held a moment: the row lets a finger scroll the list otherwise); the focus
   is the row's, ringed whole. */
/* (By its section, not its id #st-panels: the copy a closed Settings leaves fading has no ids - app.js, copyOf - and
   its list's quieter words came up a shade darker in it, 7 Oct 2026.) */
.settings .st-panel[data-section="panels"] { --ar-quiet: var(--st-quiet); }
.ar-list { position: relative; margin: 0; padding: 0; list-style: none; }
.ar-row { position: relative; display: flex; align-items: center; gap: 4px; min-height: 40px; padding: 0 2px 0 6px; border-radius: 12px;
  cursor: var(--cur-grab); touch-action: pan-y; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
@media (hover: hover) { .ar-row:hover { background: color-mix(in srgb, var(--accent) 9%, transparent); } }
.ar-row:has(.ar-item:focus-visible) { outline: 2px solid var(--accent); outline-offset: -2px; }
/* A row taken up (5 Oct 2026, the owner: the line where it would go was drawn through it, and it lay over the rows it
   passed - "still reads as messy"): it follows the pointer up and down, a little raised and whole, the pane's own colour
   under it so nothing shows through; the rows it passes slide apart (0.12 s) to make it room, and that room - the size
   of a row, faintly ringed in the quiet colour, 3:1 as a mark - is where it goes. Let go, it settles into it
   (panels.js paintBox), as rows moved by the keys slide past each other. */
.ar-list .ar-row { transition: translate .12s var(--ease-out); }
.ar-list .ar-row.ar-lift { z-index: 3; scale: 1.02; box-shadow: 0 10px 24px rgb(0 0 0 / .2), 0 0 0 1px var(--rule-strong);
  transition: scale .14s var(--ease-out), box-shadow .14s; }
.ar-sorting .ar-row:not(.ar-lift) { background: transparent; }
.ar-gap { position: absolute; z-index: 1; left: 2px; right: 2px; box-sizing: border-box; border-radius: 12px; pointer-events: none;
  border: 1.5px dashed var(--ar-quiet); background: color-mix(in srgb, var(--accent) 7%, transparent); }
.ar-item { display: flex; flex: 1 1 auto; align-items: center; gap: 10px; min-width: 0; min-height: 40px; outline: none; }
/* (30 px: the zoom level's "100%" is a picture in words, and wider than a drawing - 5 Oct 2026, it ran into its name.) */
.ar-icon { position: relative; display: grid; place-items: center; flex: none; width: 30px; height: 28px; color: var(--ink); }
.ar-icon svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* (The Pen's nib is the ink's own colour for the paper - dark ink on the dark theme's pane: ringed, so it shows.) */
.ar-icon .bd-nib { height: 17px; box-shadow: inset 0 1px 1px rgb(255 255 255 / .45), 0 0 0 1px color-mix(in srgb, var(--ink) 55%, transparent); }
.ar-glyph { font-size: 11.5px; font-weight: 600; letter-spacing: -.01em; line-height: 1; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ar-label { flex: 1 1 auto; min-width: 0; font-size: 13.5px; color: var(--ink); overflow-wrap: anywhere; }
/* Hidden: quieter - the name and the picture in the quiet colour (4.5:1 and 3:1 still), the switch off. */
.ar-off :is(.ar-label, .ar-icon) { color: var(--ar-quiet); }
.ar-off .ar-icon .bd-nib { opacity: .5; }
.ar-show { display: grid; place-items: center; flex: none; width: 46px; height: 36px; padding: 0; border: 0; border-radius: 999px;
  background: transparent; cursor: pointer; }
.ar-show[aria-disabled="true"] { cursor: not-allowed; }
.ar-show[aria-checked="true"] .bd-switch { background: var(--btn-bg); border-color: var(--btn-bg); }
.ar-show[aria-checked="true"] .bd-switch::after { transform: translateX(16px); background: var(--btn-fg); }
.ar-close:hover { background-color: color-mix(in srgb, var(--accent) 12%, transparent); }
:is(.ar-show, .ar-close, .ar-tabs [role="tab"], .ar-reset, .ar-go):focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Settings → Side panels: each panel's name with a picture of it as it will show, then its list. */
.ar-sets { display: flex; flex-direction: column; gap: 6px; padding-bottom: 6px; }
.ar-sethead { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 6px 14px; padding-bottom: 6px; }
.ar-preview { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 1px; padding: 3px 8px; border-radius: 999px;
  border: 1px solid var(--rule-strong); background: var(--surface); }
.ar-preview .ar-icon { width: auto; min-width: 24px; height: 26px; padding: 0 2px; }
.ar-preview .ar-icon svg { width: 17px; height: 17px; }
.ar-preview .ar-icon .bd-nib { height: 15px; }
.ar-pgap { width: 1px; height: 16px; margin: 0 4px; background: var(--rule-strong); }

/* A finger: 44 px, every control and every row. */
@media (pointer: coarse) {
  .bd-arrange { width: min(330px, calc(100% - 92px)); }
  .ar-row, .ar-item { min-height: 46px; }
  .ar-show { width: 52px; height: 44px; }
  .ar-close { width: 44px; height: 44px; }
  :is(.ar-reset, .ar-go, .ar-tabs [role="tab"]) { min-height: 44px; }
}
/* Motion turned down: nothing lifts or eases; a row taken up is only shadowed. */
:root[data-motion="reduce"] :is(.ar-close, .ar-lift, .ar-go) { transition: none; }
:root[data-motion="reduce"] .ar-lift { scale: none; }
:root[data-motion="reduce"] :is(.ar-list .ar-row, .ar-list .ar-row.ar-lift) { transition: none; }
@media (prefers-reduced-transparency: reduce) {
  :root[data-style="glass"] .bd-arrange { background-color: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* ── Docs, the documents editor (26 Oct 2026, docs/DOCS_PLAN.md; templates/docs.html and doc.html) ──────────────────
   The list is the Board's own cards. The editor's chrome is the Board's look (every style, theme and palette: tokens
   only); the sheet itself stays a sheet - --surface, the page's own colour - and is the width of an A4 page at 96 px to
   the inch until lane D2 draws real pages. */
.dl-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.dl-item { display: flex; align-items: center; gap: 14px; padding: 12px 16px; }
.dl-item + .dl-item { margin-top: 0; }
.dl-main { flex: 1 1 auto; min-width: 0; display: grid; gap: 2px; color: inherit; text-decoration: none; }
.dl-title { font-weight: 650; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dl-peek { color: var(--muted); font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dl-when { color: var(--muted); font-size: 13px; white-space: nowrap; }
.dl-empty { margin: 40px 0; text-align: center; }
/* The empty list's one card (27 Oct 2026): a little sheet with a +, and what it is beside it - a place for the first
   document, dashed as the whiteboards' first card is. */
.dl-start { display: flex; align-items: center; gap: 22px; width: min(560px, 100%); margin: 28px auto; padding: 20px 26px;
  font: inherit; text-align: left; color: var(--ink); cursor: pointer; border-style: dashed; border-width: 1.5px; }
:root[data-style="glass"] .dl-start { border-color: color-mix(in srgb, var(--accent) 50%, var(--rule-strong)); }
.dl-start:hover { border-color: color-mix(in srgb, var(--accent) 75%, var(--rule-strong)); }
.dl-start:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.dl-startsheet { flex: none; display: grid; place-items: center; width: 64px; height: 84px; border-radius: 4px;
  background: var(--surface); border: 1px solid var(--rule-strong); box-shadow: 0 1px 2px rgb(0 0 0 / .08), 0 6px 18px rgb(0 0 0 / .08); }
.dl-startplus { display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%; color: var(--btn-fg); background: var(--btn-bg);
  transition: transform .15s; }
:root[data-style="glass"] .dl-startplus { background-image: var(--gloss); box-shadow: var(--bevel); }
.dl-startplus svg { width: 18px; height: 18px; }
.dl-start:hover .dl-startplus { transform: scale(1.06); }
.dl-starttext { display: grid; gap: 4px; }
.dl-starttext strong { font-size: 17px; font-weight: 650; }
.dl-starttext span { font-size: 14px; color: color-mix(in srgb, var(--ink) 15%, var(--muted)); }
:root[data-motion="reduce"] .dl-startplus { transition: none; }
a.dl-main:hover .dl-title, a.dl-main:focus-visible .dl-title { color: var(--accent); }

main.doc-main { max-width: none; padding: 0; }
.dc { display: flex; flex-direction: column; min-height: calc(100vh - 64px); }
/* The title and the toolbar (lane D1, 27 Oct 2026): one pane that stays at the top as the document scrolls - the page's
   colour on Plain, glass on Glass, as the masthead is. */
.dc-head { position: sticky; top: 0; z-index: 5; border-bottom: 1px solid var(--rule); background: var(--paper); }
:root[data-style="glass"] .dc-head {
  background-color: color-mix(in srgb, var(--surface) 80%, transparent);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-bottom-color: color-mix(in srgb, var(--edge-light) 40%, var(--rule));
}
/* Kept low (27 Oct 2026, the owner: "need to compress the top of the page, this is taking up way too much vertical
   room, name box does not need to be that big"): 105 px of title and toolbar became one row of 40 where the window is
   wide enough, and two of 72 below it - the title's row first, the toolbar under it. And then ("shouldn't the logo be
   in the corner etc, we are still wasting space") the masthead's 60 px above them went, as over a whiteboard: the
   mark is in the way back and the account's buttons at the end, so one row needs 1600 px - the way back, the title,
   the toolbar, the count and Saved, the owner's name and their three buttons side by side. */
.dc-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 10px; padding: 4px 14px; }
/* The way back, with the brand's mark in it (27 Oct 2026: a document has no masthead, as a whiteboard has none - the
   whiteboard's .bd-back and .bd-mark). */
.dc-back { display: inline-flex; align-items: center; gap: 2px; height: 30px; padding: 0 9px 0 4px; border-radius: 999px;
  color: var(--ink); border: 1px solid var(--rule-strong); text-decoration: none; flex: none; box-sizing: border-box; }
.dc-back svg { width: 16px; height: 16px; }
.dc-mark { display: block; height: 20px; width: auto; aspect-ratio: 731 / 1185; filter: var(--logo-filter); }
/* The whiteboard's account group gives way to its own ⋯ on a phone and a touch screen's narrow window (.bd-account,
   "How much of the account group there is room for"); a document has no ⋯, so the gear at least stays - Settings has
   light or dark and the name - and the rest gives way as on a whiteboard. */
.dc-account { margin-left: 4px; }
.dc-account.bd-account { display: flex; }
.dc-back:hover, .dc-back:focus-visible { color: var(--accent); border-color: var(--accent); }
.dc-title { flex: 1 1 200px; min-width: 0; font: inherit; font-size: 15px; font-weight: 600; color: var(--ink);
  background: transparent; border: 1px solid transparent; border-radius: 6px; padding: 3px 8px; }
.dc-title:hover { border-color: var(--rule); }
.dc-title:focus { outline: none; border-color: var(--accent); background: var(--surface); }
.dc-title::placeholder { color: var(--muted); opacity: 1; }
.dc-count, .dc-status { color: var(--muted); font-size: 13px; white-space: nowrap; }
.dc-status[data-state="error"] { color: var(--red); font-weight: 600; }
.dc-wrap { flex: 1 1 auto; padding: 28px 16px 80px; display: flex; justify-content: center; align-items: flex-start; }
.dc-sheet { width: 100%; max-width: 794px; min-height: 1123px; box-sizing: border-box; padding: 94px;
  background: var(--surface); color: var(--ink); border: 1px solid var(--rule); border-radius: 4px;
  box-shadow: 0 1px 2px rgb(0 0 0 / .08), 0 8px 30px rgb(0 0 0 / .08); }
@media (max-width: 860px) { .dc-sheet { padding: 28px 22px; min-height: 70vh; } .dc-wrap { padding: 12px 8px 60px; } }
/* The text pointer of the Board's own (27 Oct 2026, the owner: "cursor when hovering near text is invisible, white on
   white"): the system's I-beam is drawn by inverting what is under it, and Windows shows an inverting cursor as plain
   white in places - with HDR on, for one - so over the white sheet it was not there. Dark lines with a white edge, as
   the whiteboard's crosshair (--bd-cross, 7 Oct 2026): seen on the light sheet and the dark. Everywhere words are
   typed in a document - the sheet (its margins too, which put the cursor in it), the title, the size, a link's
   address. */
.dc { --dc-ibeam: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M8.5 3.5h7M12 3.5v17M8.5 20.5h7' fill='none' stroke='%23fff' stroke-width='3.6' stroke-linecap='round'/%3E%3Cpath d='M8.5 3.5h7M12 3.5v17M8.5 20.5h7' fill='none' stroke='%231c1f24' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E") 12 12, text; }
:is(.dc-sheet, .dc-prose, .dc-title, .dt-size, .dt-linkform input) { cursor: var(--dc-ibeam); }
.dc-prose { outline: none; min-height: 60vh; font-size: 16px; line-height: 1.5; overflow-wrap: anywhere; }
.dc-prose > * + * { margin-top: .75em; }
/* Title, Heading 1, 2 and 3 (toolbar.js, STYLES: h1-h4). */
.dc-prose h1 { font-size: 2.2em; line-height: 1.15; margin-top: 1em; font-weight: 650; }
.dc-prose h2 { font-size: 1.6em; line-height: 1.25; margin-top: 1em; font-weight: 650; }
.dc-prose h3 { font-size: 1.3em; line-height: 1.3; margin-top: 1em; font-weight: 650; }
.dc-prose h4 { font-size: 1.12em; line-height: 1.35; margin-top: 1em; font-weight: 650; }
.dc-prose > :first-child { margin-top: 0; }
.dc-prose ul, .dc-prose ol { padding-left: 1.6em; }
.dc-prose li > p { margin: 0; }
.dc-prose blockquote { margin-left: 0; padding-left: 14px; border-left: 3px solid var(--rule-strong); color: var(--muted); }
.dc-prose code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .92em; padding: 1px 5px;
  border-radius: 4px; background: var(--paper-2); }
.dc-prose pre { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .92em; padding: 12px 14px;
  border-radius: 8px; background: var(--paper-2); overflow-x: auto; }
.dc-prose pre code { background: none; padding: 0; }
.dc-prose hr { border: 0; border-top: 1px solid var(--rule-strong); margin: 1.4em 0; }
.dc-prose a { color: var(--accent); text-decoration: underline; }
.dc-prose p.is-editor-empty:first-child::before { content: attr(data-placeholder); float: left; height: 0; pointer-events: none;
  color: var(--muted); }
.dc-prose ::selection { background: var(--accent-soft); }
.dc-prose h1, .dc-prose h2, .dc-prose h3, .dc-prose h4 { margin-bottom: 0; }
/* A highlight behind letters: dark letters on it in either theme (its colours are all pale - toolbar.js, HIGHLIGHTS),
   unless the letters have a colour of their own. */
.dc-prose mark { color: #1f2328; border-radius: 2px; padding: 0 1px; box-decoration-break: clone; -webkit-box-decoration-break: clone; }
/* A check list (Tiptap's task list): a box before each line, a done line struck through. */
.dc-prose ul[data-type="taskList"] { list-style: none; padding-left: .15em; }
.dc-prose ul[data-type="taskList"] li { display: flex; align-items: flex-start; gap: .55em; }
.dc-prose ul[data-type="taskList"] li > label { flex: none; margin-top: .2em; user-select: none; }
.dc-prose ul[data-type="taskList"] li > label input { width: 1.05em; height: 1.05em; margin: 0; accent-color: var(--accent); cursor: pointer; }
.dc-prose ul[data-type="taskList"] li > div { flex: 1 1 auto; min-width: 0; }
.dc-prose ul[data-type="taskList"] li[data-checked="true"] > div { text-decoration: line-through; color: var(--muted); }
.dc-prose ul[data-type="taskList"] ul[data-type="taskList"] { margin-top: .25em; }

/* ── The formatting toolbar (lane D1, 27 Oct 2026; templates/doc.html #dt, static/js/docs/toolbar.js) ───────────────
   One row of small buttons in groups, under the title. Each a soft square on Plain, a lozenge on Glass; pressed (bold
   on, in a list) it is lit in the accent's soft colour, as a chosen tab is. Wraps on a narrow window; on a phone it is
   one row that scrolls sideways, so the sheet keeps the screen. */
.dt { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 0; order: 5; flex: 1 0 100%; margin-left: -6px; }
@media (min-width: 1600px) {
  /* (From 120 px, so the row is laid out as fitting with the title at its least; it grows to 240 where there is room.) */
  .dc-title { flex: 1 1 120px; max-width: 240px; }
  .dt { order: 0; flex: 0 1 auto; margin-left: 0; border-left: 1px solid var(--rule); padding-left: 4px; }
  .dc-count { margin-left: auto; }
}
.dt-group { display: flex; align-items: center; gap: 2px; padding: 0 6px; }
.dt-group + .dt-group { border-left: 1px solid var(--rule); }
.dt-group:first-child { padding-left: 0; }
.dt-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; gap: 2px;
  min-width: 30px; height: 30px; padding: 0 5px; border: 1px solid transparent; border-radius: 8px; background: transparent;
  color: var(--ink); font: inherit; font-size: 14px; cursor: pointer; flex: none; }
:root[data-style="glass"] .dt-btn { border-radius: 999px; }
.dt-btn svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; flex: none; }
.dt-btn svg .dt-thin { stroke-width: 1.5; }
.dt-btn svg .dt-dot { fill: currentColor; stroke: none; }
.dt-btn:hover:not(:disabled) { background: var(--paper-2); }
.dt-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.dt-btn[aria-pressed="true"], .dt-btn[aria-expanded="true"] { background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 35%, transparent); }
:root[data-style="glass"] .dt-btn[aria-pressed="true"] { background-image: var(--gloss); box-shadow: var(--bevel); }
.dt-btn:disabled { opacity: .38; cursor: default; }
.dt-caret { width: 14px !important; height: 14px !important; margin-left: 1px; opacity: .7; }
.dt-wide { justify-content: space-between; padding: 0 6px 0 10px; }
.dt-stylebtn { width: 124px; }
.dt-fontbtn { width: 140px; }
.dt-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left; }
.dt-glyph { font-size: 16px; line-height: 1; width: 1em; text-align: center; font-family: Georgia, 'Times New Roman', serif; }
.dt-b { font-weight: 800; }
.dt-i { font-style: italic; }
.dt-u { text-decoration: underline; text-underline-offset: 2px; }
.dt-s { text-decoration: line-through; }
/* The colour buttons: the letter or the pen with a bar of the colour under the cursor beneath it. */
[data-menu="colour"], [data-menu="highlight"] { flex-direction: column; gap: 1px; padding-top: 3px; }
[data-menu="colour"] .dt-glyph { font-size: 15px; font-family: inherit; font-weight: 600; }
[data-menu="highlight"] svg { width: 16px; height: 16px; }
.dt-bar { display: block; width: 16px; height: 3px; border-radius: 2px; }
.dt-bar-colour { background: var(--dt-colour, currentColor); }
.dt-bar-highlight { background: var(--dt-highlight, transparent); box-shadow: inset 0 0 0 1px var(--rule-strong); }
/* The size: − the number + in one sunken track. */
.dt-sizes { gap: 0; }
.dt-size { width: 42px; height: 26px; box-sizing: border-box; margin: 0 2px; padding: 0 4px; text-align: center;
  font: inherit; font-size: 14px; font-variant-numeric: tabular-nums; color: var(--ink); background: var(--surface);
  border: 1px solid var(--rule-strong); border-radius: 6px; }
:root[data-style="glass"] .dt-size { border-radius: 999px; }
.dt-size:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }

/* A menu of the toolbar's: a small pane under its button, over everything. */
.dt-pop { position: fixed; z-index: 1000; min-width: 190px; max-width: calc(100vw - 16px); max-height: min(60vh, 460px);
  overflow: auto; box-sizing: border-box; padding: 6px; color: var(--ink); background: var(--surface);
  border: 1px solid var(--rule-strong); border-radius: 12px; box-shadow: var(--shadow-2); }
:root[data-style="glass"] .dt-pop {
  background-color: var(--pane); background-image: var(--gloss-soft);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-color: color-mix(in srgb, var(--edge-light) 55%, var(--rule)); box-shadow: var(--rim), var(--shadow-2); border-radius: 16px;
}
.dt-pop:focus { outline: none; }
.dt-item { display: flex; align-items: center; justify-content: space-between; gap: 20px; width: 100%; box-sizing: border-box;
  padding: 7px 10px; border: 0; border-radius: 8px; background: transparent; color: var(--ink); font: inherit; font-size: 14px;
  text-align: left; cursor: pointer; }
.dt-item:hover, .dt-item:focus-visible { background: var(--paper-2); outline: none; }
.dt-item[aria-checked="true"] { background: var(--accent-soft); font-weight: 650; }
/* The key, quieter than its name - --muted with a third of the ink in it: plain --muted on the chosen row's accent
   was 4.37:1 in GuideStar's (measured, test_docs_toolbar). */
.dt-item kbd { font: inherit; font-size: 12px; font-weight: 400; color: color-mix(in srgb, var(--ink) 35%, var(--muted)); }
.dt-item.dt-style-h1 span { font-size: 22px; font-weight: 650; }
.dt-item.dt-style-h2 span { font-size: 18px; font-weight: 650; }
.dt-item.dt-style-h3 span { font-size: 16px; font-weight: 650; }
.dt-item.dt-style-h4 span { font-size: 14.5px; font-weight: 650; }
.dt-item.dt-auto { margin-bottom: 4px; }
.dt-swatches { display: grid; grid-template-columns: repeat(10, 22px); gap: 5px; padding: 4px 6px 6px; }
.dt-swatch { width: 22px; height: 22px; padding: 0; border-radius: 50%; border: 1px solid rgb(0 0 0 / .18); cursor: pointer; }
.dt-swatch:hover { transform: scale(1.12); }
.dt-swatch[aria-checked="true"], .dt-swatch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* (A <label>, so the page's labels' small capitals set back: it is a choice like the others, not a field's name.) */
.dt-any { display: flex; align-items: center; gap: 8px; padding: 6px 8px 2px; font-size: 13.5px; cursor: pointer;
  text-transform: none; letter-spacing: normal; font-weight: 500; color: var(--ink); margin: 0; }
.dt-any input { width: 30px; height: 24px; padding: 0; border: 1px solid var(--rule-strong); border-radius: 6px; background: none; cursor: pointer; }
.dt-linkpop { min-width: min(360px, calc(100vw - 16px)); padding: 10px; }
.dt-linkform { display: flex; gap: 6px; align-items: center; }
.dt-linkform input { flex: 1 1 auto; min-width: 0; height: 32px; box-sizing: border-box; padding: 0 10px; font: inherit; font-size: 14px;
  color: var(--ink); background: var(--surface); border: 1px solid var(--rule-strong); border-radius: 8px; }
.dt-linkform input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.dt-linksay { margin: 8px 2px 0; font-size: 13px; color: var(--red); max-width: 340px; }

/* A smaller window (a laptop at 1000, an iPad): the toolbar's own row, tightened to stay one row - the style and
   font buttons narrower, the groups closer. */
@media (max-width: 1100px) {
  .dt-group { padding: 0 3px; }
  .dt-stylebtn { width: 108px; }
  .dt-fontbtn { width: 112px; }
  .dt-btn { min-width: 28px; padding: 0 4px; }
}
/* A phone: the title row without its word count. */
@media (max-width: 700px) {
  .dc-bar { padding: 4px 10px; gap: 2px 8px; }
  .dc-count { display: none; }
  /* (The title from 6em, so it shares its row with "Unsaved changes" and the gear rather than taking one of its own.) */
  .dc-title { flex-basis: 6em; }
}
/* Narrower than the whole toolbar (an iPad upright, a phone): one row that scrolls sideways, not a third row. */
@media (max-width: 960px) {
  .dt { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .dt::-webkit-scrollbar { display: none; }
}
/* A finger: 44 px. */
@media (pointer: coarse) {
  .dt-btn { min-width: 44px; height: 44px; }
  .dt-size { height: 38px; }
  .dt-item { min-height: 44px; }
  .dt-swatches { grid-template-columns: repeat(10, 28px); }
  .dt-swatch { width: 28px; height: 28px; }
}
