/* ==========================================================================
   style.css — shared styling for every TeacherPal page.

   Dark, premium dashboard look (see design-ref.png), with the blue swapped
   for a subtle pink: deep plum-navy ground with a soft radial glow, near-black
   cards with 24px corners, faint 1px borders and a pink glow line along the
   top edge, white titles with a square outlined ↗ button, bordered pill
   chips, outlined percentage badges, slim progress bars.

   Fills the viewport on any screen (fluid root font, rem everywhere, app
   shell grid locked to 100dvh). Projector-first: group results and seating
   names are big white text on dark cards — never pink body text.

   Tweak the theme by editing the tokens in the first section only.
   ========================================================================== */

/* ==========================================================================
   Tokens
   --------------------------------------------------------------------------
   Structural tokens (spacing, sizes, transition, touch) live in the shared
   `:root` block below — they don't change per theme.

   Every colour, gradient, shadow, font, radius and typographic track lives
   in a per-theme block: `:root[data-theme="<id>"] { … }`. Add a new theme
   by copying the halloween (dark) or pink (light) block and swapping the
   values — see CLAUDE.md.

   The default (no `data-theme` attribute) is default, so unstyled pages
   still render correctly.
   ========================================================================== */

/* ---------- Tokens: shared (structure) ---------- */

:root {
  --fs-xs: 0.86rem;
  --fs-sm: 0.98rem;
  --fs-md: 1.08rem;
  --fs-lg: 1.25rem;
  --fs-xl: 1.6rem;
  --fs-2xl: 2.4rem;
  --fs-stat: 2rem;                  /* big key numbers (mono) */
  --fs-hero: clamp(3rem, 10vw, 12rem);

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --page-pad: clamp(12px, 2vw, 40px);

  --transition: 160ms ease;
  --touch: 2.5rem;
  --rail-width: 12.5rem;

  --font: var(--font-body);
}

/* ---------- Tokens: default (dark HUD) ---------- */

:root,
:root[data-theme="default"] {
  color-scheme: dark;

  /* ground */
  --bg-0: #151320;                  /* page edge */
  --bg-1: #1E1A2E;                  /* page glow centre */
  --bg: var(--bg-0);
  --bg-glow: rgba(240, 127, 174, 0.10);   /* subtle accent-tinted top wash on body */
  --body-pattern: none;

  /* surfaces */
  --card: #0D0B14;                  /* near-black, slight plum tint */
  --card-2: #15121F;                /* chips, inputs, inset wells */
  --card-3: #1C1828;                /* hover / raised rows */
  --border: rgba(255, 255, 255, 0.06);
  --border-strong: rgba(255, 255, 255, 0.12);
  --border-hover: rgba(255, 255, 255, 0.16);

  /* text */
  --text: #FFFFFF;
  --text-2: #C9C4D4;
  --text-muted: #8B8699;

  /* pink accent — subtle, not bubblegum */
  --accent: #F07FAE;
  --accent-deep: #D9578F;
  --accent-soft: rgba(240, 127, 174, 0.14);
  --accent-glow: rgba(240, 127, 174, 0.35);
  --accent-ink: var(--accent);     /* accent as text / icon colour; a theme with a dark accent points it at a lighter tint */
  --lavender: #B79CF5;              /* secondary accent for contrast */
  --lavender-soft: rgba(183, 156, 245, 0.16);
  --group-tint: 16%;                /* how much a group's colour washes its card */
  --group-ink-dl: 0;                /* label / dot lightness vs the group colour */
  --danger: #FF7A8A;
  --ok: #6FD3A6;

  /* gradients */
  --gradient-accent: linear-gradient(180deg, rgba(240, 127, 174, 0.32), rgba(240, 127, 174, 0.06));
  --gradient-bar: linear-gradient(90deg, var(--accent-deep), var(--accent));
  --gradient-glow-top: linear-gradient(90deg, transparent, var(--accent-glow), transparent);

  /* rings & shadows */
  --ring: rgba(240, 127, 174, 0.35);
  --shadow-card: 0 1px 0 rgba(255, 255, 255, 0.03) inset, 0 10px 30px rgba(0, 0, 0, 0.35);
  --shadow-card-hover: 0 1px 0 rgba(255, 255, 255, 0.04) inset, 0 14px 36px rgba(0, 0, 0, 0.45), 0 0 24px rgba(240, 127, 174, 0.08);
  --shadow-pop: 0 18px 50px rgba(0, 0, 0, 0.6);
  --shadow-pressed: inset 0 0 0 1px var(--accent), inset 0 0 18px rgba(240, 127, 174, 0.25);
  --shadow-text: rgba(0, 0, 0, 0.35);
  /* depth extras — off here; a theme turns them on (halloween and thanksgiving do) */
  --panel-shadow: none;             /* under every .hud-box panel */
  --hover-lift: 0px;                /* clickable things rise this much on hover */
  --hover-glow: none;               /* shadow added under a hovered control */

  /* semantic surfaces (HUD panels/boxes, top bar, form fields) */
  --panel-bg: rgba(13, 11, 20, 0.6);
  --panel-bg-hover: rgba(13, 11, 20, 0.85);
  --field-bg: rgba(13, 11, 20, 0.8);
  --topbar-bg: rgba(13, 11, 20, 0.55);
  --on-accent: #1A0A12;                   /* text/icon that reads on the accent background */

  /* semantic tints (translucent overlays that stack on card surfaces) */
  --surface-tint: rgba(255, 255, 255, 0.05);         /* faint fill (mini map) */
  --surface-tint-strong: rgba(255, 255, 255, 0.22);  /* seat.taken border */
  --surface-tint-present: rgba(255, 255, 255, 0.35); /* full-screen seat.taken border */
  --surface-tint-formula: rgba(255, 255, 255, 0.14); /* hard-rule border in formula */
  --surface-tint-progress: rgba(255, 255, 255, 0.08);/* progress bar bg */
  --canvas-dot: rgba(255, 255, 255, 0.12);           /* seating dot grid */

  /* accent-tinted overlays and glows */
  --accent-fill-soft: rgba(240, 127, 174, 0.08);     /* marquee bg, snap-ghost bg */
  --accent-line: rgba(240, 127, 174, 0.45);          /* rotation ring dashed */
  --accent-line-strong: rgba(240, 127, 174, 0.55);   /* snap-ghost dashed border */
  --accent-ring-soft: rgba(240, 127, 174, 0.15);     /* primary button ring */
  --accent-ring-strong: rgba(240, 127, 174, 0.28);   /* primary button hover ring */
  --accent-inset-soft: rgba(240, 127, 174, 0.07);    /* editing roster inset */
  --accent-inset-chip: rgba(240, 127, 174, 0.25);    /* unseated student chip inset */
  --accent-icon-glow: rgba(240, 127, 174, 0.6);      /* hud-panel hover icon drop-shadow */

  /* the room: seats, floors, and the small accents around them. Defaults
     follow the surfaces above, so a theme only sets these to break away
     (halloween and thanksgiving do — see their blocks). */
  --seat-bg: var(--card-3);                 /* a taken seat */
  --seat-line: var(--surface-tint-strong);
  --seat-empty-bg: var(--card-2);           /* an empty seat */
  --seat-empty-line: var(--border-strong);
  --chart-floor: transparent;               /* Attendance chart canvas (the panel shows through) */
  --room-floor: var(--card);                /* Seating builder canvas */
  --label-ink: var(--text-muted);           /* small HUD / section labels */
  --figure-ink: var(--text);                /* HUD readout figures (clock, counts) */
  --chip-line: var(--hud-line);             /* HUD chips (AUTO, FIRST NAMES…) at rest */
  --chip-ink: var(--text-muted);

  /* type — three faces, HUD flavoured */
  --font-display: "Orbitron", "Rajdhani", "Segoe UI", system-ui, sans-serif;
  --font-mono:    "Share Tech Mono", "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --font-body:    "Rajdhani", "Segoe UI", system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;
  --track-display: 0.14em;          /* Orbitron headings */
  --track-label:   0.18em;          /* small mono / display labels */
  --track-body:    0.01em;          /* Rajdhani reads better slightly opened up */
  --label-transform: uppercase;

  --fw-regular:  500;               /* Rajdhani 400 is too light on a dark ground */
  --fw-medium:   500;
  --fw-semibold: 600;
  --fw-bold:     700;

  --radius-lg:   24px;              /* cards */
  --radius-md:   14px;              /* inner cards, rows, dialogs */
  --radius-sm:   10px;              /* buttons, inputs, chips */
  --radius-pill: 999px;
}

/* ---------- Tokens: pink (true light theme) ----------
   Pink is a light theme. Every colour token default defines is redefined
   here with a light-appropriate value — nothing falls back to a default
   dark value. Structural tokens (fonts, radii, tracks, spacing) inherit
   from :root so layout stays identical to default.

   Palette rules:
   • Page background = soft blush #FFF5F8
   • Cards, surfaces, top bar = solid white #FFFFFF (no transparency
     over dark)
   • Borders = #F4CFDD
   • Primary text = deep plum #3D2433
   • Muted text = #8A6378 (readable, not ghosted)
   • Accent = rose #E8467C, secondary lavender #9F7AEA
   Every text/background pair passes WCAG AA. */
:root[data-theme="pink"] {
  color-scheme: light;

  /* ground */
  --bg-0: #FFF5F8;                  /* soft blush page bg */
  --bg-1: #FFEEF3;                  /* radial glow centre (barely different) */
  --bg: var(--bg-0);
  --bg-glow: rgba(232, 70, 124, 0.05);   /* very subtle accent wash on body */
  /* Page texture: three small hearts per 96px tile, scattered at different
     sizes and angles so it reads as texture rather than a grid. Blush pink
     only a shade off --bg-0 (#FFF5F8) at 32% — noticeable up close, gone from
     the back of the room. Background layer only: never over content, and the
     pop-out projector views drop it (they set their own flat background). */
  --body-pattern:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E%3Cg fill='%23F3BFD4' fill-opacity='.32'%3E%3Cpath transform='translate(10 14) scale(.8) rotate(-10 8 8)' d='M8 14.5C8 14.5 1 10.2 1 5.6 1 3 3 1.2 5.2 1.2 6.6 1.2 7.5 2 8 2.8 8.5 2 9.4 1.2 10.8 1.2 13 1.2 15 3 15 5.6 15 10.2 8 14.5 8 14.5Z'/%3E%3Cpath transform='translate(60 44) scale(.62) rotate(14 8 8)' d='M8 14.5C8 14.5 1 10.2 1 5.6 1 3 3 1.2 5.2 1.2 6.6 1.2 7.5 2 8 2.8 8.5 2 9.4 1.2 10.8 1.2 13 1.2 15 3 15 5.6 15 10.2 8 14.5 8 14.5Z'/%3E%3Cpath transform='translate(30 72) scale(.5) rotate(-6 8 8)' d='M8 14.5C8 14.5 1 10.2 1 5.6 1 3 3 1.2 5.2 1.2 6.6 1.2 7.5 2 8 2.8 8.5 2 9.4 1.2 10.8 1.2 13 1.2 15 3 15 5.6 15 10.2 8 14.5 8 14.5Z'/%3E%3C/g%3E%3C/svg%3E");
  --body-pattern-size: 96px 96px;

  /* surfaces — cards / top bar are pure white; no transparency over dark */
  --card:   #FFFFFF;
  --card-2: #FDF6F9;                /* inputs, chips, wells (barely blush so distinct from cards) */
  --card-3: #FBE9F0;                /* hover rows */
  --border:        #F4CFDD;
  --border-strong: #E9B7CB;
  --border-hover:  #DE9BB6;

  /* text — deep plum on soft blush ≈ 11:1 contrast; muted ≈ 5.4:1 (WCAG AA) */
  --text:       #3D2433;
  --text-2:     #6B4459;
  --text-muted: #8A6378;

  /* accent — rose */
  --accent:      #E8467C;
  --accent-deep: #C93168;
  --accent-soft: rgba(232, 70, 124, 0.10);
  --accent-glow: rgba(232, 70, 124, 0.25);
  --lavender:      #9F7AEA;
  --lavender-soft: rgba(159, 122, 234, 0.14);
  --group-tint: 10%;
  --group-ink-dl: -0.12;            /* darker ink so labels hold on white cards */
  --danger:  #D63757;
  --ok:      #4EB889;

  /* gradients */
  --gradient-accent: linear-gradient(180deg, rgba(232, 70, 124, 0.24), rgba(232, 70, 124, 0.04));
  --gradient-bar: linear-gradient(90deg, var(--accent-deep), var(--accent));
  --gradient-glow-top: linear-gradient(90deg, transparent, var(--accent-glow), transparent);

  /* rings & shadows — no white insets; soft plum-tinted drops so cards lift off the blush ground */
  --ring: rgba(232, 70, 124, 0.35);
  --shadow-card:        0 1px 3px rgba(74, 44, 61, 0.06), 0 6px 20px rgba(74, 44, 61, 0.08);
  --shadow-card-hover:  0 2px 6px rgba(74, 44, 61, 0.10), 0 12px 28px rgba(74, 44, 61, 0.14);
  --shadow-pop:         0 18px 50px rgba(74, 44, 61, 0.22);
  --shadow-pressed:     inset 0 0 0 1px var(--accent), 0 0 12px rgba(232, 70, 124, 0.18);
  --shadow-text:        rgba(74, 44, 61, 0.08);

  /* semantic surfaces — solid whites, not translucent over dark */
  --panel-bg:       #FFFFFF;
  --panel-bg-hover: #FFFFFF;
  --field-bg:       #FDF6F9;
  --topbar-bg:      #FFFFFF;
  --on-accent:      #FFFFFF;               /* white text on pink accent */

  /* semantic tints — dark alpha over light instead of light alpha over dark */
  --surface-tint:          rgba(74, 44, 61, 0.05);
  --surface-tint-strong:   rgba(74, 44, 61, 0.20);
  --surface-tint-present:  rgba(74, 44, 61, 0.35);
  --surface-tint-formula:  rgba(74, 44, 61, 0.14);
  --surface-tint-progress: rgba(74, 44, 61, 0.08);
  --canvas-dot:            rgba(74, 44, 61, 0.14);

  /* accent overlays — a touch stronger to stay visible on a light ground */
  --accent-fill-soft:  rgba(232, 70, 124, 0.10);
  --accent-line:       rgba(232, 70, 124, 0.50);
  --accent-line-strong:rgba(232, 70, 124, 0.65);
  --accent-ring-soft:  rgba(232, 70, 124, 0.14);
  --accent-ring-strong:rgba(232, 70, 124, 0.28);
  --accent-inset-soft: rgba(232, 70, 124, 0.08);
  --accent-inset-chip: rgba(232, 70, 124, 0.28);
  --accent-icon-glow:  rgba(232, 70, 124, 0.5);

  /* HUD tokens — same shapes, calmer alphas on light */
  --hud:              var(--accent);
  --hud-dim:          rgba(232, 70, 124, 0.35);
  --hud-line:         rgba(232, 70, 124, 0.24);
  --hud-line-strong:  rgba(232, 70, 124, 0.55);
  --hud-glow:         0 0 12px rgba(232, 70, 124, 0.14), 0 0 2px rgba(232, 70, 124, 0.35);
  --hud-glow-soft:    0 0 18px rgba(232, 70, 124, 0.08);
  --hud-grid:         rgba(74, 44, 61, 0.05);
  --hud-scan:         rgba(74, 44, 61, 0.025);

  /* attendance overlays — recomputed off pink's danger + amber for equivalent visibility on white */
  --att-absent-soft: rgba(214, 55, 87, 0.10);
  --att-tardy-soft:  rgba(245, 184, 75, 0.22);

  /* wheel slices — candy pastels, light enough for the deep-plum labels */
  --wheel-r0: #FFC2D6;
  --wheel-r1: #D9C2F5;
  --wheel-r2: #A8E6C9;
  --wheel-r3: #FFE0A3;
  --wheel-r4: #FFD6E4;
  --wheel-r5: #E7D8FA;
  --wheel-r6: #C6F0DC;
  --wheel-r7: #FFEDC7;
  --wheel-divider: #FFFFFF;
  --wheel-ink: #3D2433;
  --wheel-ink-shadow: rgba(255, 255, 255, 0.55);
  --wheel-won: #FF8FBA;            /* plum names 6.4:1 */
  /* pink's own slice cycle (wheel.js p0-p4): pink, lavender, mint, peach,
     butter - neighbours clearly apart, nothing loud; plum names ≥ 9:1 */
  --wheel-p0: #FFC4D8;
  --wheel-p1: #DCC8F7;
  --wheel-p2: #BDECD5;
  --wheel-p3: #FFD2BA;
  --wheel-p4: #FFEBA8;
  /* the machine: soft rounded frame in rose porcelain */
  --wheel-metal:    #F6DCE8;
  --wheel-metal-2:  #EFC9DC;
  --wheel-metal-hi: rgba(232, 70, 124, 0.45);
  --wheel-housing:  #F9E4EE;
  --wheel-housing-top: #FFF4F8;    /* the ring's gentle gradient, top → bottom */
  --wheel-housing-bot: #F6B6CF;
  --wheel-housing-shadow: rgba(201, 49, 104, 0.22);
  --wheel-face-hi:  rgba(255, 255, 255, 0.32);
  --wheel-face-lo:  rgba(232, 70, 124, 0.04);
  /* the bunny beside the wheel (same cast palette as the other mascots) */
  --wb-fur:    #FFB6C9;
  --wb-inner:  #FF89A9;
  --wb-belly:  #FFE3EC;
  --wb-line:   #4A2C3D;
  --wb-bow:    #E8467C;
  --wb-blush:  #FF6B9E;
  --wb-shadow: rgba(74, 44, 61, 0.12);
  /* the Name Wheel bubble tea (boba.js; pink's picker) */
  --bt-cup:        rgba(255, 255, 255, 0.45);
  --bt-cup-line:   rgba(74, 44, 61, 0.72);   /* the cup is a mascot: a thick soft plum outline */
  --bt-shine:      rgba(255, 255, 255, 0.85);
  --bt-tea:        #D6B190;     /* warm milk tea: lighter under the foam ... */
  --bt-tea-mid:    #C89F7B;
  --bt-tea-edge:   rgba(98, 60, 32, 0.20);     /* where the tea meets the plastic */
  --bt-milk:       rgba(255, 244, 228, 0.55); /* the cream that hasn't mixed in */
  --bt-pearl-ink:  #FFF2E2;     /* names on the pearls */
  --bt-spot-dim:   rgba(74, 44, 61, 0.62);    /* behind the picked pearl */
  --bt-rim-light:  #FFFFFF;
  --bt-shine-soft: rgba(255, 255, 255, 0.55);
  --bt-lid-line:   rgba(74, 44, 61, 0.62);
  --bt-lid-shadow: rgba(74, 44, 61, 0.09);
  --bt-shadow-core: rgba(74, 44, 61, 0.10);
  --bt-ink:        #4A2C3D;     /* the cup's face: the pink mascots' outline plum */
  --bt-eye-white:  #FFFFFF;
  --bt-blush:      #FF8FB4;
  --bt-limb:       #FFD3DF;     /* arms and feet */
  --bt-pearl-shadow: rgba(36, 14, 6, 0.28);
  --bt-tea-deep:   #A87A55;     /* ... darker down where it meets the boba */
  --bt-foam:       #F2E0C8;
  --bt-drop:       rgba(255, 255, 255, 0.82);
  --bt-drop-line:  rgba(98, 60, 32, 0.22);
  --bt-lid:        rgba(255, 246, 250, 0.72);   /* a faint tint, so it reads as its own piece */
  --bt-band:       #FCE4EC;
  --bt-straw:      rgba(255, 190, 210, 0.55);   /* soft pink, see-through */
  --bt-straw-hover: rgba(255, 165, 195, 0.72);
  --bt-straw-in:   rgba(201, 49, 104, 0.07);
  --bt-straw-rim:  #FFE6EE;
  --bt-straw-line: #EFA3BD;
  --bt-pearl:      #24150E;     /* real boba: near-black brown */
  --bt-pearl-hi:   #5C3B27;
  --bt-pearl-deep: #0D0705;
  --bt-shadow:     rgba(74, 44, 61, 0.16);
  /* the bunny face on the Spin hub */
  --pink-hub-face: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 -1 40 37'><ellipse cx='13.5' cy='9' rx='4' ry='8.5' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2'/><ellipse cx='26.5' cy='9' rx='4' ry='8.5' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2'/><ellipse cx='13.5' cy='10' rx='1.6' ry='5' fill='%23FF89A9'/><ellipse cx='26.5' cy='10' rx='1.6' ry='5' fill='%23FF89A9'/><circle cx='20' cy='23' r='12' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2.2'/><ellipse cx='15.5' cy='22' rx='1.7' ry='2.1' fill='%234A2C3D'/><ellipse cx='24.5' cy='22' rx='1.7' ry='2.1' fill='%234A2C3D'/><circle cx='16.1' cy='21.2' r='0.6' fill='white'/><circle cx='25.1' cy='21.2' r='0.6' fill='white'/><circle cx='11.5' cy='26' r='2' fill='%23FF6B9E' opacity='0.5'/><circle cx='28.5' cy='26' r='2' fill='%23FF6B9E' opacity='0.5'/><ellipse cx='20' cy='25.3' rx='1' ry='0.7' fill='%23E8467C'/><path d='M18 27 Q19 28.3 20 27.2 Q21 28.3 22 27' stroke='%234A2C3D' stroke-width='1.1' fill='none' stroke-linecap='round'/></svg>");

  /* noise zones — the zone word's colour (pink's meter is the bunny, no bar):
     mint-green quiet, the rose accent for OK, danger red for too loud */
  --noise-quiet: #2F9E6E;
  --noise-ok:    var(--accent-deep);
  --noise-loud:  var(--danger);
  --noise-quiet-soft: rgba(47, 158, 110, 0.14);
  --noise-ok-soft:    rgba(217, 148, 18, 0.16);
  --noise-loud-soft:  rgba(214, 55, 87, 0.12);
  /* level bar: lavender (quiet) → pink (OK) → rose-red (too loud) on a pale
     blush track, a glossy top, and a little heart riding the tip */
  --noise-bar-fill:
    linear-gradient(180deg, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0) 55%),
    linear-gradient(90deg, #C3ABF5 0%, #D7A8EC 27%, #F59BC2 33%, #F2729F 67%, #E8467C 73%, #D63757 100%);
  --noise-bar-track: #FBE4EE;
  --noise-bar-line:  #F4CFDD;
  --noise-bar-mark:  rgba(74, 44, 61, 0.45);
  --noise-bar-tick:  transparent;
  --noise-bar-inset: inset 0 2px 4px rgba(201, 49, 104, 0.12);
  --noise-rider: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-2 -2 20 20'><path d='M8 14.5C8 14.5 1 10.2 1 5.6 1 3 3 1.2 5.2 1.2 6.6 1.2 7.5 2 8 2.8 8.5 2 9.4 1.2 10.8 1.2 13 1.2 15 3 15 5.6 15 10.2 8 14.5 8 14.5Z' fill='%23E8467C' stroke='%23FFFFFF' stroke-width='2' stroke-linejoin='round'/><ellipse cx='5' cy='5' rx='1.7' ry='1' fill='%23FFFFFF' fill-opacity='0.65' transform='rotate(-30 5 5)'/></svg>");

  /* the Noise Meter bunny (noise.js builds it; pink only). Same palette as
     the Timer bunny; "hot" is the too-loud colour the fur warms to. */
  --nm-fur:       #FFB6C9;
  --nm-fur-hot:   #FFB08C;
  --nm-ear:       #FF89A9;
  --nm-ear-hot:   #FF7F57;
  --nm-tummy:     #FFE1EC;
  --nm-tummy-hot: #FFE2D2;
  --nm-line:      #4A2C3D;
  --nm-glint:     #FFFFFF;
  --nm-sheen:     rgba(255, 255, 255, 0.38);
  --nm-shadow:    rgba(74, 44, 61, 0.12);
  --nm-blush:     #FF6B9E;
  --nm-blush-hot: #F2552E;
  --nm-bow:       #E8467C;
  --nm-bow-knot:  #F2729F;
  --nm-z:         #3FAE80;
  --nm-star:      #F5B83D;
  --nm-drop:      #7CC5EC;
  --nm-glow-quiet: rgba(110, 216, 170, 0.50);
  --nm-glow-ok:    rgba(255, 137, 169, 0.34);
  --nm-glow-loud:  rgba(255, 112, 62, 0.72);
  /* a heart shape for masks (the checked preset pill) */
  --nm-heart-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M8 14.5C8 14.5 1 10.2 1 5.6 1 3 3 1.2 5.2 1.2 6.6 1.2 7.5 2 8 2.8 8.5 2 9.4 1.2 10.8 1.2 13 1.2 15 3 15 5.6 15 10.2 8 14.5 8 14.5Z' fill='black'/></svg>");
}

/* ==========================================================================
   Halloween — a colour swap of default (tokens only) with two
   opt-in extras: faint corner webs on the page and a bat / pumpkin on empty
   states. Purple-black ground, bone-white text, pumpkin orange for actions
   and icons, purple for selected fills, witch green only for ok.
   ========================================================================== */
:root[data-theme="halloween"] {
  color-scheme: dark;

  /* ground — near-black with a purple cast */
  --bg-0:#120D1A;
  --bg-1:#1A1226;   /* canvas / radial glow centre */
  --bg: var(--bg-0);
  --bg-glow: rgba(139, 79, 191, 0.14);    /* faint purple wash at the top */
  --body-pattern: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='560' height='560' viewBox='0 0 560 560'><defs><g id='b'><path d='M32 10C30.4 10 29.2 11 28.8 12.4L27.6 8.6 26.9 13.1C23 9.6 15 7.6 4 10 8 12.4 9.6 16 8.8 19.6 12.4 18.2 16.4 19 18.4 21.6 19.6 19.6 22 18.8 24.4 19.6 26 22 28.6 25.6 32 28Z'/><path d='M32 10C30.4 10 29.2 11 28.8 12.4L27.6 8.6 26.9 13.1C23 9.6 15 7.6 4 10 8 12.4 9.6 16 8.8 19.6 12.4 18.2 16.4 19 18.4 21.6 19.6 19.6 22 18.8 24.4 19.6 26 22 28.6 25.6 32 28Z' transform='matrix(-1 0 0 1 64 0)'/></g></defs><use href='%23b' fill='%23F5E6D3' fill-opacity='0.04' transform='translate(40 60) rotate(-10 17.6 9.9) scale(0.55)'/><use href='%23b' fill='%23F5E6D3' fill-opacity='0.055' transform='translate(310 24) rotate(8 9.6 5.4) scale(0.3)'/><use href='%23b' fill='%23F5E6D3' fill-opacity='0.065' transform='translate(462 150) rotate(-6 7 4) scale(0.22)'/><use href='%23b' fill='%23F5E6D3' fill-opacity='0.07' transform='translate(150 214) rotate(12 5.8 3.2) scale(0.18)'/><use href='%23b' fill='%23F5E6D3' fill-opacity='0.035' transform='translate(372 290) rotate(-4 24 13.5) scale(0.75)'/><use href='%23b' fill='%23F5E6D3' fill-opacity='0.06' transform='translate(48 372) rotate(6 9 5) scale(0.28)'/><use href='%23b' fill='%23F5E6D3' fill-opacity='0.045' transform='translate(236 432) rotate(-12 13.4 7.6) scale(0.42)'/><use href='%23b' fill='%23F5E6D3' fill-opacity='0.065' transform='translate(486 476) rotate(10 6.4 3.6) scale(0.2)'/><use href='%23b' fill='%23F5E6D3' fill-opacity='0.07' transform='translate(168 532) rotate(0 4.8 2.7) scale(0.15)'/><use href='%23b' fill='%23F5E6D3' fill-opacity='0.07' transform='translate(520 360) rotate(-14 4.2 2.3) scale(0.13)'/></svg>");   /* bats of every size across the whole page; the big ones fainter */
  --body-pattern-size: 560px 560px;

  /* surfaces */
  --card:#241833;   /* cards and panels */
  --card-2:#1C1329;   /* inputs, chips, wells: sunk below the card */
  --card-3:#2F2043;   /* raised / hover */
  --border:#4A3161;
  --border-strong:#7A55A0;   /* buttons, inputs, chips: 2.9:1 on the card */
  --border-hover:#FF7518;    /* hover brightens toward orange */

  /* text */
  --text:#F5E6D3;   /* warm bone white (13.7:1 on the card) */
  --text-2:#DCCBBE;   /* body (10.6:1) */
  --text-muted:#A08BB8;   /* muted purple (5.5:1, 4.9:1 on raised) */

  /* accents — pumpkin primary, purple secondary, green for ok */
  --accent:#FF7518;   /* pumpkin (6.2:1 on the card) */
  --accent-deep:#D9600E;
  --accent-soft:rgba(255, 117, 24, 0.18);
  --accent-glow:rgba(255, 117, 24, 0.42);
  --lavender:#C394EE;   /* purple as text (7.0:1); #8B4FBF is the fill */
  --lavender-soft:rgba(139, 79, 191, 0.30);
  --group-tint: 16%;
  --group-ink-dl: 0;
  --danger:#FF6F61;   /* (6.1:1) */
  --ok:#7FBF3F;       /* witch green (7.5:1) */

  /* the room: a floor darker than the page, purple seats with lilac edges */
  --seat-bg: #3A2852;
  --seat-line: #9C78C4;   /* 5.5:1 on the floor */
  --seat-empty-bg: #1C1329;
  --seat-empty-line: #5A3F78;
  --chart-floor: #0E0A14;
  --room-floor: #0E0A14;
  --label-ink:#A08BB8;
  --figure-ink:#FF9A4D;   /* readouts in light pumpkin (8.0:1) */
  --chip-line:rgba(195, 148, 238, 0.70);
  --chip-ink:#C394EE;

  /* group cards: pumpkin, witch green, purple, candy corn, bone */
  --group-c1: #FFB27A;
  --group-c2: #A9D873;
  --group-c3: #CFA6F2;
  --group-c4: #F5D06B;
  --group-c5: #E8D6C2;

  /* gradients — selected / pressed fills are purple, edged in orange */
  --gradient-accent:linear-gradient(180deg, rgba(139, 79, 191, 0.62), rgba(139, 79, 191, 0.20));
  --gradient-bar: linear-gradient(90deg, var(--accent-deep), var(--accent));
  --gradient-glow-top:linear-gradient(90deg, transparent, rgba(255, 117, 24, 0.65), rgba(139, 79, 191, 0.55), transparent);

  /* rings & shadows */
  --ring:rgba(255, 117, 24, 0.55);
  --shadow-card:0 1px 0 rgba(245, 230, 211, 0.05) inset, 0 2px 4px rgba(5, 2, 9, 0.6), 0 10px 26px rgba(5, 2, 9, 0.55);
  --shadow-card-hover:0 1px 0 rgba(245, 230, 211, 0.07) inset, 0 4px 8px rgba(5, 2, 9, 0.6), 0 16px 34px rgba(5, 2, 9, 0.55), 0 0 0 1px rgba(255, 117, 24, 0.40);
  --shadow-pop:0 20px 54px rgba(4, 2, 8, 0.85);
  --shadow-pressed:inset 0 0 0 1px var(--accent), inset 0 0 18px rgba(139, 79, 191, 0.45), 0 0 14px rgba(255, 117, 24, 0.28);
  --shadow-text: rgba(8, 4, 12, 0.5);

  /* depth: panels cast a shadow, clickable things lift with an orange glow */
  --panel-shadow: 0 1px 0 rgba(245, 230, 211, 0.04) inset, 0 3px 6px rgba(5, 2, 9, 0.5), 0 14px 32px rgba(5, 2, 9, 0.5);
  --hover-lift: -1px;
  --hover-glow: 0 3px 12px rgba(255, 117, 24, 0.22);

  /* semantic surfaces */
  --panel-bg:rgba(36, 24, 51, 0.96);
  --panel-bg-hover:rgba(47, 32, 67, 0.97);
  --field-bg:#1C1329;
  --topbar-bg:rgba(18, 13, 26, 0.92);
  --on-accent:#1A0D05;   /* near-black on pumpkin (7.1:1) */

  /* semantic tints — bone alpha over purple */
  --surface-tint: rgba(245, 230, 211, 0.06);
  --surface-tint-strong:rgba(195, 148, 238, 0.36);
  --surface-tint-present: rgba(255, 154, 77, 0.38);
  --surface-tint-formula: rgba(195, 148, 238, 0.18);
  --surface-tint-progress: rgba(245, 230, 211, 0.10);
  --canvas-dot: rgba(195, 148, 238, 0.16);

  /* accent overlays */
  --accent-fill-soft:rgba(255, 117, 24, 0.12);
  --accent-line:rgba(255, 117, 24, 0.60);
  --accent-line-strong:rgba(255, 117, 24, 0.80);
  --accent-ring-soft:rgba(255, 117, 24, 0.30);
  --accent-ring-strong:rgba(255, 117, 24, 0.50);
  --accent-inset-soft: rgba(139, 79, 191, 0.12);
  --accent-inset-chip:rgba(255, 117, 24, 0.30);
  --accent-icon-glow:rgba(255, 117, 24, 0.70);

  /* HUD tokens — purple panel lines; hover lines and glows go orange */
  --hud:              var(--accent);
  --hud-dim:rgba(139, 79, 191, 0.50);
  --hud-line:rgba(139, 79, 191, 0.55);
  --hud-line-strong:rgba(255, 117, 24, 0.85);
  --hud-glow:0 0 18px rgba(255, 117, 24, 0.28), 0 0 2px rgba(255, 154, 77, 0.6);
  --hud-glow-soft:    0 0 24px rgba(139, 79, 191, 0.18);
  --hud-grid:         rgba(195, 148, 238, 0.035);
  --hud-scan:         rgba(195, 148, 238, 0.025);

  /* attendance */
  --att-absent:      var(--danger);
  --att-absent-soft: rgba(255, 111, 97, 0.16);
  --att-tardy:       #F5C04B;   /* candy-corn yellow, clear of the orange accent (10:1) */
  --att-tardy-soft:  rgba(245, 192, 75, 0.16);

  /* wheel slices — deep pumpkin, purple and green in turn (wheel.js walks
     r0 r4 r1 r5 r2 r6 r3 r7); bone names >= 6.8:1 on every one */
  --wheel-r0: #7A3510;
  --wheel-r4: #4A2470;
  --wheel-r1: #3F5E1E;
  --wheel-r5: #8A3F12;
  --wheel-r2: #5B2A78;
  --wheel-r6: #35522A;
  --wheel-r3: #6E3A14;
  --wheel-r7: #62307F;
  --wheel-divider: rgba(245, 230, 211, 0.45);
  --wheel-ink:        #FFF3E4;
  --wheel-ink-shadow: rgba(10, 5, 14, 0.7);
  --wheel-won:        #A84A16;   /* (5.3:1) */
  --wheel-metal:    #2A1C3B;
  --wheel-metal-2:  #1A1226;
  --wheel-metal-hi: rgba(255, 117, 24, 0.55);
  --wheel-housing:  #2F2043;
  --wheel-face-hi:  rgba(245, 230, 211, 0.08);
  --wheel-face-lo:  rgba(5, 2, 9, 0.45);

  /* noise zones */
  --noise-quiet: var(--ok);
  --noise-ok:    var(--att-tardy);
  --noise-loud:  var(--danger);
  --noise-quiet-soft: rgba(127, 191, 63, 0.16);
  --noise-ok-soft:    rgba(245, 192, 75, 0.16);
  --noise-loud-soft:  rgba(255, 111, 97, 0.16);
  /* level bar: a glowing potion / ember trail (witch green → candle →
     pumpkin) in a dark purple vial with a faint inner glow; a bat rides the
     tip, and the fill flickers like flame once it's too loud */
  --noise-bar-fill:
    linear-gradient(180deg, rgba(255, 255, 255, 0.32), rgba(255, 255, 255, 0) 50%, rgba(0, 0, 0, 0.18)),
    linear-gradient(90deg, #4E9A2A 0%, #7FBF3F 27%, #F5C04B 33%, #FF9A3C 67%, #FF7518 73%, #FF4A12 100%);
  --noise-bar-track: #170E22;
  --noise-bar-line:  #4A3161;
  --noise-bar-mark:  rgba(245, 230, 211, 0.55);
  --noise-bar-tick:  transparent;
  --noise-bar-inset: inset 0 0 0.8rem rgba(139, 79, 191, 0.5);
  --noise-rider: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 4 64 28'><g fill='%23241634' stroke='%23C394EE' stroke-width='1.6' stroke-linejoin='round'><path d='M32 10C30.4 10 29.2 11 28.8 12.4L27.6 8.6 26.9 13.1C23 9.6 15 7.6 4 10 8 12.4 9.6 16 8.8 19.6 12.4 18.2 16.4 19 18.4 21.6 19.6 19.6 22 18.8 24.4 19.6 26 22 28.6 25.6 32 28Z'/><path d='M32 10C30.4 10 29.2 11 28.8 12.4L27.6 8.6 26.9 13.1C23 9.6 15 7.6 4 10 8 12.4 9.6 16 8.8 19.6 12.4 18.2 16.4 19 18.4 21.6 19.6 19.6 22 18.8 24.4 19.6 26 22 28.6 25.6 32 28Z' transform='matrix(-1 0 0 1 64 0)'/></g><circle cx='30.2' cy='15' r='1.4' fill='%23FF7518'/><circle cx='33.8' cy='15' r='1.4' fill='%23FF7518'/></svg>");

  /* the Noise Meter ghost (noise.js builds the grave scene; halloween only).
     The ghost is pale blue-white when quiet, warm bone when OK, hot orange
     when too loud; --nh-ghost / --nh-glow are registered (@property) so the
     shift between them is a smooth colour transition. */
  --nh-ghost-quiet: #D2E4FF;
  --nh-ghost-ok:    #FFF2DA;
  --nh-ghost-loud:  #FFC089;
  --nh-glow-quiet:  rgba(140, 180, 255, 0.30);
  --nh-glow-ok:     rgba(255, 222, 160, 0.28);
  --nh-glow-loud:   rgba(255, 117, 24, 0.78);
  --nh-ghost-line:  rgba(36, 20, 56, 0.55);
  --nh-eye:         #1A1226;
  --nh-glint:       #FFFFFF;
  --nh-blush:       rgba(255, 130, 165, 0.55);
  --nh-stone:       #5E5574;
  --nh-stone-hi:    #8479A0;
  --nh-stone-line:  #2A2238;
  --nh-ground:      #382950;
  --nh-ground-hi:   #5A4580;
  --nh-hole:        #060309;
  --nh-grass:       #4F7A2A;
  --nh-wood:        #6B4A35;
  --nh-bat:         #0B0810;
  --nh-bat-eye:     #FF7518;

  /* the Name Wheel cauldron (cauldron.js; halloween only) */
  --cd-pot-1:      #1A1424;
  --cd-pot-2:      #2C2340;
  --cd-pot-3:      #07050B;
  --cd-pot-line:   #3A2E4A;
  --cd-pot-hi:     rgba(255, 255, 255, 0.1);
  --cd-leg:        #0B0810;
  --cd-log:        #3A2416;
  --cd-log-line:   #160C06;
  --cd-log-end:    #6E4628;
  --cd-log-ring:   #3F2615;
  --cd-crack:      #FF9A3C;
  --cd-crack-glow: rgba(255, 110, 20, 0.95);
  --cd-brew-1:     #A8FF82;
  --cd-brew-2:     #3CCB47;
  --cd-brew-3:     #145C22;
  --cd-sheen:      rgba(255, 255, 255, 0.2);
  --cd-glow:       rgba(120, 255, 120, 0.75);
  --cd-glow-soft:  rgba(120, 255, 120, 0.35);
  --cd-haze:       rgba(110, 255, 120, 0.32);
  --cd-fire-glow:  rgba(255, 120, 30, 0.55);
  --cd-pool:       rgba(255, 150, 50, 0.85);
  --cd-bed:        #FFC24A;
  --cd-bed-mid:    rgba(255, 90, 20, 0.75);
  --cd-under-glow: rgba(255, 117, 24, 0.7);
  --cd-rimlight:   rgba(255, 160, 70, 0.95);
  --cd-flame-out:  #F2560F;          /* the outer layer: base → tip */
  --cd-flame-tip:  #A8200C;
  --cd-flame-mid:  #FFA22A;
  --cd-flame-mid-tip: #FF6F14;
  --cd-flame-core: #FFFBEA;          /* the core: yellow-white → yellow */
  --cd-flame-core-tip: #FFD54F;
  --cd-ember:      #FFA040;
  --cd-ember-hot:  #FFE48A;
  --cd-steam:      rgba(225, 255, 228, 0.2);
  --cd-bubble:     rgba(24, 84, 36, 0.92);
  --cd-bubble-edge: rgba(10, 46, 18, 0.95);
  --cd-bubble-hi:  rgba(255, 255, 255, 0.55);
  --cd-bubble-line: rgba(170, 255, 160, 0.85);
  --cd-ink:        #F5E6D3;          /* names on the bubbles: bone on deep green, >= 9:1 */
  --cd-spark:      #8CFF5A;
  --cd-winner:     #EAFFDC;

  /* extras — every decoration is an inline-SVG data URI, used only by the
     halloween rules next to pink's mascots */
  --halloween-web-tl: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240'><path d='M0 0L230 0M0 0L222.2 59.5M0 0L199.2 115M0 0L162.6 162.6M0 0L115 199.2M0 0L59.5 222.2M0 0L0 230M34 0Q29 3.8 32.8 8.8Q27 11.2 29.4 17Q23.2 17.8 24 24Q17.8 23.2 17 29.4Q11.2 27 8.8 32.8Q3.8 29 0 34M62 0Q52.9 7 59.9 16Q49.3 20.4 53.7 31Q42.3 32.5 43.8 43.8Q32.5 42.3 31 53.7Q20.4 49.3 16 59.9Q7 52.9 0 62M92 0Q78.4 10.3 88.9 23.8Q73.1 30.3 79.7 46Q62.8 48.2 65.1 65.1Q48.2 62.8 46 79.7Q30.3 73.1 23.8 88.9Q10.3 78.4 0 92M124 0Q105.7 13.9 119.8 32.1Q98.5 40.8 107.4 62Q84.6 64.9 87.7 87.7Q64.9 84.6 62 107.4Q40.8 98.5 32.1 119.8Q13.9 105.7 0 124M158 0Q134.7 17.7 152.6 40.9Q125.5 52 136.8 79Q107.8 82.7 111.7 111.7Q82.7 107.8 79 136.8Q52 125.5 40.9 152.6Q17.7 134.7 0 158M194 0Q165.4 21.8 187.4 50.2Q154.1 63.8 168 97Q132.4 101.6 137.2 137.2Q101.6 132.4 97 168Q63.8 154.1 50.2 187.4Q21.8 165.4 0 194' fill='none' stroke='%23F5E6D3' stroke-opacity='0.085' stroke-width='1.1' stroke-linecap='round'/></svg>");
  --halloween-web-tr: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240'><path d='M0 0L230 0M0 0L222.2 59.5M0 0L199.2 115M0 0L162.6 162.6M0 0L115 199.2M0 0L59.5 222.2M0 0L0 230M34 0Q29 3.8 32.8 8.8Q27 11.2 29.4 17Q23.2 17.8 24 24Q17.8 23.2 17 29.4Q11.2 27 8.8 32.8Q3.8 29 0 34M62 0Q52.9 7 59.9 16Q49.3 20.4 53.7 31Q42.3 32.5 43.8 43.8Q32.5 42.3 31 53.7Q20.4 49.3 16 59.9Q7 52.9 0 62M92 0Q78.4 10.3 88.9 23.8Q73.1 30.3 79.7 46Q62.8 48.2 65.1 65.1Q48.2 62.8 46 79.7Q30.3 73.1 23.8 88.9Q10.3 78.4 0 92M124 0Q105.7 13.9 119.8 32.1Q98.5 40.8 107.4 62Q84.6 64.9 87.7 87.7Q64.9 84.6 62 107.4Q40.8 98.5 32.1 119.8Q13.9 105.7 0 124M158 0Q134.7 17.7 152.6 40.9Q125.5 52 136.8 79Q107.8 82.7 111.7 111.7Q82.7 107.8 79 136.8Q52 125.5 40.9 152.6Q17.7 134.7 0 158M194 0Q165.4 21.8 187.4 50.2Q154.1 63.8 168 97Q132.4 101.6 137.2 137.2Q101.6 132.4 97 168Q63.8 154.1 50.2 187.4Q21.8 165.4 0 194' fill='none' stroke='%23F5E6D3' stroke-opacity='0.085' stroke-width='1.1' stroke-linecap='round' transform='matrix(-1 0 0 1 240 0)'/></svg>");
  --halloween-web-bl: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240'><path d='M0 0L230 0M0 0L222.2 59.5M0 0L199.2 115M0 0L162.6 162.6M0 0L115 199.2M0 0L59.5 222.2M0 0L0 230M34 0Q29 3.8 32.8 8.8Q27 11.2 29.4 17Q23.2 17.8 24 24Q17.8 23.2 17 29.4Q11.2 27 8.8 32.8Q3.8 29 0 34M62 0Q52.9 7 59.9 16Q49.3 20.4 53.7 31Q42.3 32.5 43.8 43.8Q32.5 42.3 31 53.7Q20.4 49.3 16 59.9Q7 52.9 0 62M92 0Q78.4 10.3 88.9 23.8Q73.1 30.3 79.7 46Q62.8 48.2 65.1 65.1Q48.2 62.8 46 79.7Q30.3 73.1 23.8 88.9Q10.3 78.4 0 92M124 0Q105.7 13.9 119.8 32.1Q98.5 40.8 107.4 62Q84.6 64.9 87.7 87.7Q64.9 84.6 62 107.4Q40.8 98.5 32.1 119.8Q13.9 105.7 0 124M158 0Q134.7 17.7 152.6 40.9Q125.5 52 136.8 79Q107.8 82.7 111.7 111.7Q82.7 107.8 79 136.8Q52 125.5 40.9 152.6Q17.7 134.7 0 158M194 0Q165.4 21.8 187.4 50.2Q154.1 63.8 168 97Q132.4 101.6 137.2 137.2Q101.6 132.4 97 168Q63.8 154.1 50.2 187.4Q21.8 165.4 0 194' fill='none' stroke='%23F5E6D3' stroke-opacity='0.085' stroke-width='1.1' stroke-linecap='round' transform='matrix(1 0 0 -1 0 240)'/></svg>");
  --halloween-web-br: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240'><path d='M0 0L230 0M0 0L222.2 59.5M0 0L199.2 115M0 0L162.6 162.6M0 0L115 199.2M0 0L59.5 222.2M0 0L0 230M34 0Q29 3.8 32.8 8.8Q27 11.2 29.4 17Q23.2 17.8 24 24Q17.8 23.2 17 29.4Q11.2 27 8.8 32.8Q3.8 29 0 34M62 0Q52.9 7 59.9 16Q49.3 20.4 53.7 31Q42.3 32.5 43.8 43.8Q32.5 42.3 31 53.7Q20.4 49.3 16 59.9Q7 52.9 0 62M92 0Q78.4 10.3 88.9 23.8Q73.1 30.3 79.7 46Q62.8 48.2 65.1 65.1Q48.2 62.8 46 79.7Q30.3 73.1 23.8 88.9Q10.3 78.4 0 92M124 0Q105.7 13.9 119.8 32.1Q98.5 40.8 107.4 62Q84.6 64.9 87.7 87.7Q64.9 84.6 62 107.4Q40.8 98.5 32.1 119.8Q13.9 105.7 0 124M158 0Q134.7 17.7 152.6 40.9Q125.5 52 136.8 79Q107.8 82.7 111.7 111.7Q82.7 107.8 79 136.8Q52 125.5 40.9 152.6Q17.7 134.7 0 158M194 0Q165.4 21.8 187.4 50.2Q154.1 63.8 168 97Q132.4 101.6 137.2 137.2Q101.6 132.4 97 168Q63.8 154.1 50.2 187.4Q21.8 165.4 0 194' fill='none' stroke='%23F5E6D3' stroke-opacity='0.085' stroke-width='1.1' stroke-linecap='round' transform='matrix(-1 0 0 -1 240 240)'/></svg>");
  --halloween-card-web-l: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 60'><path d='M0 0L60 0M0 0L55.4 23M0 0L42.4 42.4M0 0L23 55.4M0 0L0 60M16 0Q13.5 2.7 14.8 6.1Q11.4 7.6 11.3 11.3Q7.6 11.4 6.1 14.8Q2.7 13.5 0 16M30 0Q25.3 5 27.7 11.5Q21.5 14.3 21.2 21.2Q14.3 21.5 11.5 27.7Q5 25.3 0 30M46 0Q38.8 7.7 42.5 17.6Q32.9 22 32.5 32.5Q22 32.9 17.6 42.5Q7.7 38.8 0 46' fill='none' stroke='%23F5E6D3' stroke-opacity='0.16' stroke-width='0.8' stroke-linecap='round'/></svg>");
  --halloween-card-web-r: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 60'><path d='M0 0L60 0M0 0L55.4 23M0 0L42.4 42.4M0 0L23 55.4M0 0L0 60M16 0Q13.5 2.7 14.8 6.1Q11.4 7.6 11.3 11.3Q7.6 11.4 6.1 14.8Q2.7 13.5 0 16M30 0Q25.3 5 27.7 11.5Q21.5 14.3 21.2 21.2Q14.3 21.5 11.5 27.7Q5 25.3 0 30M46 0Q38.8 7.7 42.5 17.6Q32.9 22 32.5 32.5Q22 32.9 17.6 42.5Q7.7 38.8 0 46' fill='none' stroke='%23F5E6D3' stroke-opacity='0.16' stroke-width='0.8' stroke-linecap='round' transform='matrix(-1 0 0 1 60 0)'/></svg>");
  --halloween-flock: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 220 70'><defs><g id='b'><path d='M32 10C30.4 10 29.2 11 28.8 12.4L27.6 8.6 26.9 13.1C23 9.6 15 7.6 4 10 8 12.4 9.6 16 8.8 19.6 12.4 18.2 16.4 19 18.4 21.6 19.6 19.6 22 18.8 24.4 19.6 26 22 28.6 25.6 32 28Z'/><path d='M32 10C30.4 10 29.2 11 28.8 12.4L27.6 8.6 26.9 13.1C23 9.6 15 7.6 4 10 8 12.4 9.6 16 8.8 19.6 12.4 18.2 16.4 19 18.4 21.6 19.6 19.6 22 18.8 24.4 19.6 26 22 28.6 25.6 32 28Z' transform='matrix(-1 0 0 1 64 0)'/></g></defs><g fill='%23523A75' fill-opacity='0.85' stroke='%23FF7518' stroke-opacity='0.22' stroke-width='0.9'><use href='%23b' transform='translate(6 18) rotate(-8 32 16) scale(1)'/><use href='%23b' transform='translate(96 4) rotate(6 21 10.5) scale(0.6599999999999999)'/><use href='%23b' transform='translate(150 34) rotate(-3 26 13) scale(0.8099999999999999)'/></g></svg>");
  --halloween-flock-far: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 220 70'><defs><g id='b'><path d='M32 10C30.4 10 29.2 11 28.8 12.4L27.6 8.6 26.9 13.1C23 9.6 15 7.6 4 10 8 12.4 9.6 16 8.8 19.6 12.4 18.2 16.4 19 18.4 21.6 19.6 19.6 22 18.8 24.4 19.6 26 22 28.6 25.6 32 28Z'/><path d='M32 10C30.4 10 29.2 11 28.8 12.4L27.6 8.6 26.9 13.1C23 9.6 15 7.6 4 10 8 12.4 9.6 16 8.8 19.6 12.4 18.2 16.4 19 18.4 21.6 19.6 19.6 22 18.8 24.4 19.6 26 22 28.6 25.6 32 28Z' transform='matrix(-1 0 0 1 64 0)'/></g></defs><g fill='%23412E5E' fill-opacity='0.75' stroke='%23FF7518' stroke-opacity='0.22' stroke-width='0.9'><use href='%23b' transform='translate(6 18) rotate(-8 32 16) scale(1)'/><use href='%23b' transform='translate(96 4) rotate(6 21 10.5) scale(0.6599999999999999)'/><use href='%23b' transform='translate(150 34) rotate(-3 26 13) scale(0.8099999999999999)'/></g></svg>");
  --halloween-head-bat: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 32'><defs><g id='b'><path d='M32 10C30.4 10 29.2 11 28.8 12.4L27.6 8.6 26.9 13.1C23 9.6 15 7.6 4 10 8 12.4 9.6 16 8.8 19.6 12.4 18.2 16.4 19 18.4 21.6 19.6 19.6 22 18.8 24.4 19.6 26 22 28.6 25.6 32 28Z'/><path d='M32 10C30.4 10 29.2 11 28.8 12.4L27.6 8.6 26.9 13.1C23 9.6 15 7.6 4 10 8 12.4 9.6 16 8.8 19.6 12.4 18.2 16.4 19 18.4 21.6 19.6 19.6 22 18.8 24.4 19.6 26 22 28.6 25.6 32 28Z' transform='matrix(-1 0 0 1 64 0)'/></g></defs><use href='%23b' fill='%23C394EE'/><circle cx='30.4' cy='14.6' r='1' fill='%23FF7518'/><circle cx='33.6' cy='14.6' r='1' fill='%23FF7518'/></svg>");
  --font-spooky: "Pirata One", var(--font-display);   /* timer digits + label (loaded on the timer pages only) */
  --halloween-candle: #FFD25A;   /* timer digits in the carved grin */
  --halloween-carve: #1E0C03;    /* the carving's dark inside (digit outline) */
  /* the timer pumpkin, three layers: spent (dark) under lit (clipped from the
     bottom as time drains) under the carved face (always lit) */
  --halloween-pumpkin-lit: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><path d='M46.5 20C46.6 13 45.2 8 42.4 4L49.6 2.2C52.2 7 53.6 13 53.5 20Z' fill='%23556B26' stroke='%23000' stroke-opacity='0.35' stroke-width='0.8'/><path d='M53.8 11.6C57.6 6.6 65 6.2 68.8 9.6 64 10.2 60.4 12.6 57.4 16Z' fill='%237FBF3F'/><path d='M43.6 10.6C38.6 7.8 33.8 10 34.2 13.8 34.6 17 38.4 17 38.6 14.6' fill='none' stroke='%237FBF3F' stroke-width='1.1' stroke-linecap='round'/><g stroke='%237A3005' stroke-width='0.6'><ellipse cx='28' cy='57' rx='23' ry='36' fill='%23B84A0A'/><ellipse cx='72' cy='57' rx='23' ry='36' fill='%23B84A0A'/><ellipse cx='39' cy='56.5' rx='21' ry='39.5' fill='%23E0600E'/><ellipse cx='61' cy='56.5' rx='21' ry='39.5' fill='%23E0600E'/><ellipse cx='50' cy='56' rx='17' ry='40' fill='%23FF7518'/></g><path d='M35 19Q23 56 35 93M65 19Q77 56 65 93M44.5 16.8Q41 56 44.5 95.4M55.5 16.8Q59 56 55.5 95.4' fill='none' stroke='%239A3C06' stroke-width='0.8' stroke-opacity='0.7'/><ellipse cx='17' cy='36' rx='3.5' ry='8.5' fill='%23FFFFFF' fill-opacity='0.10' transform='rotate(18 17 36)'/></svg>");
  --halloween-pumpkin-spent: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><path d='M46.5 20C46.6 13 45.2 8 42.4 4L49.6 2.2C52.2 7 53.6 13 53.5 20Z' fill='%232E3A1A' stroke='%23000' stroke-opacity='0.35' stroke-width='0.8'/><path d='M53.8 11.6C57.6 6.6 65 6.2 68.8 9.6 64 10.2 60.4 12.6 57.4 16Z' fill='%23334A22'/><path d='M43.6 10.6C38.6 7.8 33.8 10 34.2 13.8 34.6 17 38.4 17 38.6 14.6' fill='none' stroke='%23334A22' stroke-width='1.1' stroke-linecap='round'/><g stroke='%23503570' stroke-width='0.6'><ellipse cx='28' cy='57' rx='23' ry='36' fill='%23241830'/><ellipse cx='72' cy='57' rx='23' ry='36' fill='%23241830'/><ellipse cx='39' cy='56.5' rx='21' ry='39.5' fill='%232C1E3A'/><ellipse cx='61' cy='56.5' rx='21' ry='39.5' fill='%232C1E3A'/><ellipse cx='50' cy='56' rx='17' ry='40' fill='%23332343'/></g><path d='M35 19Q23 56 35 93M65 19Q77 56 65 93M44.5 16.8Q41 56 44.5 95.4M55.5 16.8Q59 56 55.5 95.4' fill='none' stroke='%23170F20' stroke-width='0.8' stroke-opacity='0.7'/></svg>");
  --halloween-pumpkin-face: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><defs><radialGradient id='c' cx='50%' cy='55%' r='65%'><stop offset='0' stop-color='%238A3A0A'/><stop offset='0.6' stop-color='%234A1C05'/><stop offset='1' stop-color='%231E0C03'/></radialGradient></defs><g fill='url(%23c)' stroke='%23FFB347' stroke-width='0.8' stroke-linejoin='round'><path d='M23.5 38.5 31.5 24.5 39.5 38.5Z'/><path d='M60.5 38.5 68.5 24.5 76.5 38.5Z'/><path d='M47.6 43.6 50 39.6 52.4 43.6Z'/><path d='M8 46Q50 56 92 46Q91 62 82 71L77 65 73 75Q50 84 27 75L23 65 18 71Q9 62 8 46Z'/></g></svg>");
  --halloween-jack: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'><path d='M24 14c.4-3.6 2.4-6 5.6-6.6' fill='none' stroke='%237FBF3F' stroke-width='3' stroke-linecap='round'/><ellipse cx='15.5' cy='29' rx='10.5' ry='13' fill='%23C9550B'/><ellipse cx='32.5' cy='29' rx='10.5' ry='13' fill='%23C9550B'/><ellipse cx='24' cy='29' rx='10' ry='14' fill='%23FF7518'/><path d='M19.5 16.5Q17.5 29 19.5 42.5M28.5 16.5Q30.5 29 28.5 42.5' fill='none' stroke='%23B84A0A' stroke-width='1.1' stroke-opacity='0.7'/><g fill='%23FFD25A'><path d='M13.5 26 17.5 19.5 21.5 26Z'/><path d='M26.5 26 30.5 19.5 34.5 26Z'/><path d='M22.4 29.6 24 27 25.6 29.6Z'/><path d='M12.5 31.5Q24 43 35.5 31.5L32.4 32.6 31.4 35.6 28 33.6 24 36.4 20 33.6 16.6 35.6 15.6 32.6Z'/></g></svg>");
  --halloween-vignette: radial-gradient(ellipse 85% 80% at 50% 45%, transparent 55%, rgba(4, 2, 8, 0.55) 100%);
  /* more decoration (the "Halloween extras, part 2" rules): bats across the
     whole page, a graveyard along the bottom, a ghost for empty lists */
  --halloween-grave: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1600 150' preserveAspectRatio='xMidYMax meet'><g fill='%2307040C' fill-opacity='0.82' stroke='%238B4FBF' stroke-opacity='0.14' stroke-width='1.2'><path d='M0 150V130Q200 116 400 127T800 124T1200 129T1600 121V150Z'/><path transform='rotate(-4 207 132)' d='M190 134V99A17 17 0 0 1 224 99V134Z'/><path transform='rotate(3 272 132)' d='M268 134V90H258V82H268V72H276V82H286V90H276V134Z'/><path transform='rotate(6 534 132)' d='M520 134V108A14 14 0 0 1 548 108V134Z'/><path transform='rotate(2 1069 132)' d='M1050 134V95A19 19 0 0 1 1088 95V134Z'/><path transform='rotate(-5 1160 132)' d='M1156 134V98H1146V90H1156V80H1164V90H1174V98H1164V134Z'/><path transform='rotate(-3 1375 132)' d='M1360 134V105A15 15 0 0 1 1390 105V134Z'/></g><g fill='none' stroke='%2307040C' stroke-opacity='0.82' stroke-linecap='round'><path d='M70 140Q74 60 64 18' stroke-width='9'/><path d='M72 92Q100 76 122 48' stroke-width='4'/><path d='M100 76Q114 74 128 80' stroke-width='4'/><path d='M71 72Q44 56 30 30' stroke-width='2.6'/><path d='M44 56Q30 58 18 52' stroke-width='2.6'/><path d='M73 48Q88 34 92 14' stroke-width='2.6'/><path d='M67 34Q56 22 58 6' stroke-width='2.6'/><path d='M1530 140Q1526 60 1536 18' stroke-width='9'/><path d='M1528 92Q1500 76 1478 48' stroke-width='4'/><path d='M1500 76Q1486 74 1472 80' stroke-width='4'/><path d='M1529 72Q1556 56 1570 30' stroke-width='2.6'/><path d='M1556 56Q1570 58 1582 52' stroke-width='2.6'/><path d='M1527 48Q1512 34 1508 14' stroke-width='2.6'/><path d='M1533 34Q1544 22 1542 6' stroke-width='2.6'/></g></svg>");
  --halloween-ghost: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'><defs><g id='b'><path d='M32 10C30.4 10 29.2 11 28.8 12.4L27.6 8.6 26.9 13.1C23 9.6 15 7.6 4 10 8 12.4 9.6 16 8.8 19.6 12.4 18.2 16.4 19 18.4 21.6 19.6 19.6 22 18.8 24.4 19.6 26 22 28.6 25.6 32 28Z'/><path d='M32 10C30.4 10 29.2 11 28.8 12.4L27.6 8.6 26.9 13.1C23 9.6 15 7.6 4 10 8 12.4 9.6 16 8.8 19.6 12.4 18.2 16.4 19 18.4 21.6 19.6 19.6 22 18.8 24.4 19.6 26 22 28.6 25.6 32 28Z' transform='matrix(-1 0 0 1 64 0)'/></g></defs><path d='M11 42V21A13 13 0 0 1 37 21V42L33 38.4 29 42 25 38.4 21 42 17 38.4 13 42Z' fill='%23F5E6D3' fill-opacity='0.88'/><ellipse cx='19.5' cy='22' rx='2.4' ry='3.2' fill='%232A1A3A'/><ellipse cx='28.5' cy='22' rx='2.4' ry='3.2' fill='%232A1A3A'/><ellipse cx='24' cy='30' rx='2.2' ry='2.8' fill='%232A1A3A'/><use href='%23b' fill='%23C394EE' transform='translate(31 2) rotate(-10 6 3) scale(0.26)'/></svg>");
  --halloween-fog: rgba(139, 79, 191, 0.13);
}

/* ==========================================================================
   Thanksgiving — a colour swap of default (tokens only, like halloween) with
   opt-in extras: autumn leaves on the page (a faint tile plus a few drifting
   down), and three component skins — the Timer is a pumpkin pie that loses a
   slice at a time, the Name Wheel is a scatter of named leaves (leaves.js),
   the Noise Meter a turkey. Deep cocoa ground, warm cream text, burnt orange
   for actions, cranberry for selected fills, golden amber for tardy.
   ========================================================================== */
:root[data-theme="thanksgiving"] {
  color-scheme: dark;

  /* ground — deep cocoa, nearly black */
  --bg-0:#1F1511;
  --bg-1:#2A1D16;   /* canvas / radial glow centre */
  --bg: var(--bg-0);
  --bg-glow: rgba(210, 105, 30, 0.12);    /* a faint burnt-orange wash at the top */
  --body-pattern: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240' viewBox='0 0 240 240'><defs><path id='m' d='M50 4 57 22 66 16 64 36 80 26 76 38 92 40 80 52 86 58 64 62 66 72 52 66 52 92 48 92 48 66 34 72 36 62 14 58 20 52 8 40 24 38 20 26 36 36 34 16 43 22Z'/><path id='o' d='M50 6C58 8 60 16 56 20 64 18 70 24 64 30 72 30 76 38 68 42 76 44 78 54 68 56 74 62 70 70 60 66 60 74 56 78 52 78L52 94 48 94 48 78C44 78 40 74 40 66 30 70 26 62 32 56 22 54 24 44 32 42 24 38 28 30 36 30 30 24 36 18 44 20 40 16 42 8 50 6Z'/></defs><g fill-opacity='0.06'><use href='%23m' fill='%23D2691E' transform='translate(14 18) rotate(-18 50 50) scale(0.42)'/><use href='%23o' fill='%23E8A33D' transform='translate(142 54) rotate(24 50 50) scale(0.36)'/><use href='%23m' fill='%23C0392B' transform='translate(70 140) rotate(36 50 50) scale(0.3)'/><use href='%23o' fill='%23B8743C' transform='translate(176 168) rotate(-30 50 50) scale(0.28)'/></g></svg>");   /* faint scattered leaves, like halloween's bats */
  --body-pattern-size: 240px 240px;

  /* surfaces */
  --card:#38281E;   /* cards and panels */
  --card-2:#2E2018;   /* inputs, chips, wells: sunk below the card */
  --card-3:#473325;   /* raised / hover */
  --border:#6B4A33;
  --border-strong:#9C7452;   /* buttons, inputs, chips: 3.4:1 on the card */
  --border-hover:#D2691E;    /* hover brightens toward burnt orange */

  /* text — every one >= 4.5:1 on the page, the card, wells and raised */
  --text:#F5E6D0;   /* warm cream (11.5:1 on the card, 9.7:1 raised) */
  --text-2:#DCC6A8;   /* body (8.5:1, 7.2:1 raised) */
  --text-muted:#C2A381;   /* muted (5.9:1 on the card, 5.0:1 raised); the brief's #B59878 is 4.4:1 on raised */

  /* accents — burnt orange primary, cranberry secondary, golden amber third */
  --accent:#D2691E;   /* burnt orange: buttons, rings, active edges (3.9:1 on the card, so text uses --accent-ink) */
  --accent-deep:#B5561A;
  --accent-soft:rgba(210, 105, 30, 0.18);
  --accent-glow:rgba(210, 105, 30, 0.42);
  --accent-ink:#F09A55;   /* burnt orange as text and icons (6.4:1 on the card, 5.4:1 raised) */
  --lavender:#F08C98;   /* cranberry as text (6.0:1 on the card, 5.0:1 raised); #9B2335 itself is the fill */
  --lavender-soft:rgba(155, 35, 53, 0.34);
  --group-tint: 16%;
  --group-ink-dl: 0;
  --danger:#FF8A7A;   /* (6.2:1, 5.2:1 raised) */
  --ok:#A8D08A;       /* sage (8.1:1) */

  /* the room: a floor darker than the page, walnut seats with tan edges */
  --seat-bg: #4E3626;
  --seat-line: #B08560;   /* 5.8:1 on the floor; names on seats 10.3:1 */
  --seat-empty-bg: #2A1D16;
  --seat-empty-line: #7A5638;
  --chart-floor: #150E0B;
  --room-floor: #150E0B;
  --label-ink:#C2A381;
  --figure-ink:#F2B36B;   /* readouts in warm amber (8.0:1 on the card) */
  --chip-line:rgba(232, 163, 61, 0.62);
  --chip-ink:#E8B866;

  /* group cards: burnt orange, cranberry, gold, sage, cream */
  --group-c1: #F2A66A;
  --group-c2: #F08C98;
  --group-c3: #E8C26A;
  --group-c4: #B8D49A;
  --group-c5: #E3CDB0;

  /* gradients — primary / selected / pressed fills are burnt orange running
     into cranberry (cream text on the brightest part 5.2:1) */
  --gradient-accent:linear-gradient(180deg, rgba(210, 105, 30, 0.62), rgba(155, 35, 53, 0.3));
  --gradient-bar: linear-gradient(90deg, var(--accent-deep), var(--accent), #E8A33D);
  --gradient-glow-top:linear-gradient(90deg, transparent, rgba(210, 105, 30, 0.65), rgba(232, 163, 61, 0.55), transparent);

  /* rings & shadows */
  --ring:rgba(210, 105, 30, 0.6);
  --shadow-card:0 1px 0 rgba(245, 230, 208, 0.05) inset, 0 2px 4px rgba(10, 5, 3, 0.6), 0 10px 26px rgba(10, 5, 3, 0.55);
  --shadow-card-hover:0 1px 0 rgba(245, 230, 208, 0.07) inset, 0 4px 8px rgba(10, 5, 3, 0.6), 0 16px 34px rgba(10, 5, 3, 0.55), 0 0 0 1px rgba(210, 105, 30, 0.45);
  --shadow-pop:0 20px 54px rgba(8, 4, 2, 0.85);
  --shadow-pressed:inset 0 0 0 1px var(--accent), inset 0 0 18px rgba(155, 35, 53, 0.5), 0 0 14px rgba(210, 105, 30, 0.28);
  --shadow-text: rgba(10, 5, 3, 0.5);

  /* depth: panels cast a shadow, clickable things lift with a warm glow */
  --panel-shadow: 0 1px 0 rgba(245, 230, 208, 0.04) inset, 0 3px 6px rgba(10, 5, 3, 0.5), 0 14px 32px rgba(10, 5, 3, 0.5);
  --hover-lift: -1px;
  --hover-glow: 0 3px 12px rgba(210, 105, 30, 0.24);

  /* semantic surfaces */
  --panel-bg:rgba(56, 40, 30, 0.96);
  --panel-bg-hover:rgba(71, 51, 37, 0.97);
  --field-bg:#2E2018;
  --topbar-bg:rgba(31, 21, 17, 0.92);
  --on-accent:#1F1511;   /* cocoa on burnt orange (4.9:1) */

  /* semantic tints — cream alpha over cocoa */
  --surface-tint: rgba(245, 230, 208, 0.06);
  --surface-tint-strong:rgba(232, 163, 61, 0.36);
  --surface-tint-present: rgba(210, 105, 30, 0.38);
  --surface-tint-formula: rgba(240, 140, 152, 0.18);
  --surface-tint-progress: rgba(245, 230, 208, 0.10);
  --canvas-dot: rgba(232, 163, 61, 0.16);

  /* accent overlays */
  --accent-fill-soft:rgba(210, 105, 30, 0.12);
  --accent-line:rgba(210, 105, 30, 0.62);
  --accent-line-strong:rgba(210, 105, 30, 0.82);
  --accent-ring-soft:rgba(210, 105, 30, 0.30);
  --accent-ring-strong:rgba(210, 105, 30, 0.50);
  --accent-inset-soft: rgba(155, 35, 53, 0.14);
  --accent-inset-chip:rgba(210, 105, 30, 0.30);
  --accent-icon-glow:rgba(210, 105, 30, 0.70);

  /* HUD tokens — warm brown panel lines; hover lines and glows go orange */
  --hud:              var(--accent);
  --hud-dim:rgba(156, 116, 82, 0.55);
  --hud-line:rgba(156, 116, 82, 0.6);
  --hud-line-strong:rgba(210, 105, 30, 0.88);
  --hud-glow:0 0 18px rgba(210, 105, 30, 0.28), 0 0 2px rgba(240, 154, 85, 0.6);
  --hud-glow-soft:    0 0 24px rgba(155, 35, 53, 0.18);
  --hud-grid:         rgba(232, 163, 61, 0.035);
  --hud-scan:         rgba(232, 163, 61, 0.025);

  /* attendance */
  --att-absent:      var(--danger);
  --att-absent-soft: rgba(255, 138, 122, 0.16);
  --att-tardy:       #E8A33D;   /* golden amber (6.5:1 on the card, 5.5:1 raised) */
  --att-tardy-soft:  rgba(232, 163, 61, 0.16);

  /* wheel slices — rust, walnut, pumpkin, cranberry, olive, cinnamon (wheel.js
     walks r0 r4 r1 r5 r2 r6 r3 r7); cream names >= 6.2:1 on every one */
  --wheel-r0: #7A2E1C;
  --wheel-r4: #5A3A1E;
  --wheel-r1: #8A4A14;
  --wheel-r5: #6E2230;
  --wheel-r2: #5E5A1C;
  --wheel-r6: #7C3A14;
  --wheel-r3: #4A3020;
  --wheel-r7: #80421A;
  --wheel-divider: rgba(245, 230, 208, 0.45);
  --wheel-ink:        #FFF4E2;
  --wheel-ink-shadow: rgba(15, 8, 4, 0.7);
  --wheel-won:        #A0461A;   /* (5.7:1) */
  --wheel-metal:    #3A2A1F;
  --wheel-metal-2:  #2A1D16;
  --wheel-metal-hi: rgba(210, 105, 30, 0.55);
  --wheel-housing:  #473325;
  --wheel-face-hi:  rgba(245, 230, 208, 0.08);
  --wheel-face-lo:  rgba(10, 5, 3, 0.45);

  /* noise zones */
  --noise-quiet: var(--ok);
  --noise-ok:    var(--att-tardy);
  --noise-loud:  var(--danger);
  --noise-quiet-soft: rgba(168, 208, 138, 0.16);
  --noise-ok-soft:    rgba(232, 163, 61, 0.16);
  --noise-loud-soft:  rgba(255, 138, 122, 0.16);
  /* level bar: a feather — sage → amber → burnt orange → cranberry, with a
     cream quill down the middle and barbs slanting off it — and an acorn at
     the tip */
  --noise-bar-fill:
    linear-gradient(180deg, transparent 45%, rgba(255, 244, 226, 0.55) 45% 55%, transparent 55%),
    repeating-linear-gradient(118deg, rgba(31, 21, 17, 0.2) 0 1.5px, transparent 1.5px 9px),
    linear-gradient(180deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0) 50%, rgba(0, 0, 0, 0.2)),
    linear-gradient(90deg, #6F9A4E 0%, #A8D08A 27%, #E8A33D 33%, #F0B456 67%, #D2691E 73%, #9B2335 100%);
  --noise-bar-track: #1A110D;
  --noise-bar-line:  #6B4A33;
  --noise-bar-mark:  rgba(245, 230, 208, 0.6);
  --noise-bar-tick:  transparent;
  --noise-bar-inset: inset 0 2px 5px rgba(0, 0, 0, 0.5);
  --noise-rider: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 28'><path d='M12 4.5V1.5' stroke='%234A2E1A' stroke-width='1.8' stroke-linecap='round'/><path d='M4 12.5C4 18.5 7.6 24.4 12 26.5 16.4 24.4 20 18.5 20 12.5Z' fill='%23C98A4B' stroke='%232B1609' stroke-width='1.4' stroke-linejoin='round'/><path d='M7.4 14.5C7.6 18.6 9.4 22 11.6 23.6' fill='none' stroke='%23F2C894' stroke-width='1.3' stroke-linecap='round' stroke-opacity='0.75'/><path d='M2.5 12.6C2.5 7.6 6.6 4.2 12 4.2 17.4 4.2 21.5 7.6 21.5 12.6Z' fill='%236B4424' stroke='%232B1609' stroke-width='1.4' stroke-linejoin='round'/><path d='M5 10.4H19M6.4 7.8H17.6' stroke='%23432817' stroke-width='0.9'/></svg>");

  /* the Noise Meter turkey (noise.js TURKEY_SVG; thanksgiving only) */
  --tk-line:     #2B1609;
  --tk-body:     #7A4A2A;
  --tk-body-hi:  #9A6238;
  --tk-chest:    #B07A4A;
  --tk-wing:     #5E3820;
  --tk-head:     #C9946A;
  --tk-beak:     #F0B23A;
  --tk-beak-in:  #7A2E1C;
  --tk-wattle:   #C0392B;
  --tk-wattle-hot: #E04434;
  --tk-feet:     #E39A3A;
  --tk-eye:      #2B1609;
  --tk-glint:    #FFFFFF;
  --tk-blush:    rgba(240, 140, 152, 0.55);
  --tk-f1:       #9B2335;   /* tail feathers: cranberry, burnt orange, gold, walnut */
  --tk-f2:       #D2691E;
  --tk-f3:       #E8A33D;
  --tk-f4:       #7A4A2A;
  --tk-tip:      #F5E6D0;
  --tk-quill:    rgba(43, 22, 9, 0.45);
  --tk-ground:   #3A2A1F;
  --tk-glow-quiet: rgba(168, 208, 138, 0.26);
  --tk-glow-ok:    rgba(232, 163, 61, 0.30);
  --tk-glow-loud:  rgba(210, 105, 30, 0.6);
  --tk-z:        #C2A381;
  --tk-shout:    #F09A55;

  /* the Timer pie (timer.js buildPie; thanksgiving only). The digits are
     cocoa with a cream halo: 5.1:1 on the filling, 12.7:1 on the empty dish */
  --tg-dish:       #EADCC6;
  --tg-dish-rim:   #D6C2A4;
  --tg-dish-well:  #F4EBDD;
  --tg-dish-line:  #8A6A4A;
  --tg-dish-shadow: rgba(8, 4, 2, 0.55);
  --tg-crust:      #D9A05B;
  --tg-crust-hi:   #EDBE7C;
  --tg-crust-dark: #9A6430;
  --tg-fill-1:     #D58A42;
  --tg-fill-2:     #C77A35;
  --tg-fill-3:     #A65E25;
  --tg-cut:        rgba(110, 58, 20, 0.55);
  --tg-crumb:      #D9A05B;
  --tg-cream:      #FFF8EC;
  --tg-steam:      rgba(150, 104, 66, 0.42);   /* warm wisps: they show against the cream dish */
  --tg-glow:       rgba(255, 170, 70, 0.85);
  --tg-fork:       #C9CED6;
  --tg-fork-hi:    #F2F4F7;
  --tg-fork-line:  #5A6270;
  --tg-digit:      #2B1609;
  --tg-digit-halo: rgba(255, 244, 226, 0.92);

  /* the Name Wheel cornucopia (cornucopia.js; thanksgiving only). The name
     plate is cream on near-opaque cocoa (>= 13:1); basket names are cream
     on a dark halo */
  --hn-orange: #D2691E;  --hn-orange-d: #94440F;  --hn-orange-h: #F4A462;
  --hn-cran:   #9B2335;  --hn-cran-d:   #64121F;  --hn-cran-h:   #D4606F;
  --hn-amber:  #E2A23C;  --hn-amber-d:  #A06A16;  --hn-amber-h:  #F8D489;
  --hn-green:  #5A7A2E;  --hn-green-d:  #36491A;  --hn-green-h:  #8DAE58;
  --hn-grape:  #5E2B5A;  --hn-grape-d:  #3A1737;  --hn-grape-h:  #A56A9E;
  --hn-stem:   #5A3A1E;
  --hn-leafy:  #5F7F2E;
  --hn-line:   rgba(30, 14, 6, 0.75);
  --hn-shine:  rgba(255, 246, 228, 0.55);
  --hn-wicker:    #B68449;
  --hn-wicker-d:  #74491F;
  --hn-wicker-hi: #E6C186;
  --hn-weave-a:   rgba(60, 32, 12, 0.32);
  --hn-weave-b:   rgba(255, 226, 170, 0.16);
  --hn-mouth:     #1C110A;
  --hn-mouth-2:   #4A2D17;
  --hn-wall-1: #3B2A1E;
  --hn-wall-2: #221711;
  --hn-wood:   #75492B;
  --hn-wood-2: #4C2F1B;
  --hn-wood-edge:  #A0714A;
  --hn-plank:  rgba(22, 10, 4, 0.5);
  --hn-grain:  rgba(255, 220, 170, 0.1);
  --hn-shadow: rgba(10, 5, 2, 0.6);
  --hn-basket:   #A6763F;
  --hn-basket-d: #6A4520;
  --hn-basket-in: #2A1A10;
  --hn-ink:    #FFF4E2;
  --hn-ink-halo: rgba(24, 10, 4, 0.92);
  --hn-plate:  rgba(31, 21, 17, 0.9);
  --hn-plate-line: #E8A33D;
  --hn-glow:   #F2B04E;
  --hn-leaf-0: #C0392B;
  --hn-leaf-1: #D2691E;
  --hn-leaf-2: #E8A33D;
  --hn-leaf-3: #8A2335;

  /* extras — the drifting leaves */
  --tg-fall: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><path d='M50 4 57 22 66 16 64 36 80 26 76 38 92 40 80 52 86 58 64 62 66 72 52 66 52 92 48 92 48 66 34 72 36 62 14 58 20 52 8 40 24 38 20 26 36 36 34 16 43 22Z' fill='%23D2691E' fill-opacity='0.22'/><path d='M50 66V20M50 50 76 36M50 50 24 36M50 58 70 58M50 58 30 58' stroke='%231F1511' stroke-opacity='0.25' stroke-width='1.6' fill='none'/></svg>");
  --tg-fall-2: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><path d='M50 6C58 8 60 16 56 20 64 18 70 24 64 30 72 30 76 38 68 42 76 44 78 54 68 56 74 62 70 70 60 66 60 74 56 78 52 78L52 94 48 94 48 78C44 78 40 74 40 66 30 70 26 62 32 56 22 54 24 44 32 42 24 38 28 30 36 30 30 24 36 18 44 20 40 16 42 8 50 6Z' fill='%23E8A33D' fill-opacity='0.2'/><path d='M50 78V14' stroke='%231F1511' stroke-opacity='0.25' stroke-width='1.6'/></svg>");
  --tg-fall-3: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><path d='M50 4 57 22 66 16 64 36 80 26 76 38 92 40 80 52 86 58 64 62 66 72 52 66 52 92 48 92 48 66 34 72 36 62 14 58 20 52 8 40 24 38 20 26 36 36 34 16 43 22Z' fill='%239B2335' fill-opacity='0.26'/></svg>");
}


/* ==========================================================================
   Base + fluid scaling
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html { font-size: clamp(14px, 0.6vw + 0.5vh, 22px); }
html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--fs-sm);
  font-weight: var(--fw-regular);
  color: var(--text);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
  /* --body-pattern is a per-theme texture layer (default: none, pink: hearts).
     It is a background layer, so it is always behind every card and every
     glyph — it can never paint over content. */
  background:
    var(--body-pattern),
    radial-gradient(60% 45% at 50% -5%, var(--bg-glow), transparent 70%),
    radial-gradient(90% 70% at 30% 10%, var(--bg-1), transparent 70%),
    var(--bg-0);
  background-size: var(--body-pattern-size, auto), auto, auto, auto;
  background-repeat: repeat, no-repeat, no-repeat, no-repeat;
}

a { color: var(--accent-ink); }
svg { display: block; }
[hidden] { display: none !important; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
::selection { background: var(--accent-soft); }

/* ==========================================================================
   App shell: header across the top, left rail, main fills the rest
   ========================================================================== */

.app {
  width: 100%;
  min-height: 100dvh;
  height: 100dvh;
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: auto 1fr;
  overflow: hidden;
}

header.topbar {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-height: 3.6rem;
  padding: var(--space-2) var(--page-pad);
  border-bottom: 1px solid var(--border);
  /* its sweep-in animation makes it a stacking context; lift it so the gear and
     nav menus open over the page (they slid under the Lesson Plans editor) */
  position: relative;
  z-index: 20;
}
.topbar .spacer { flex: 1; }

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
  color: var(--text);
  text-decoration: none;
  border-radius: var(--radius-sm);
  letter-spacing: -0.01em;
}
.brand .mark {
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 0.5rem;
  background: var(--gradient-accent);
  border: 1px solid var(--border-strong);
  color: var(--accent-ink);
  display: grid;
  place-items: center;
}
.brand .mark svg { width: 0.95rem; height: 0.95rem; }

/* Left rail */
.rail {
  grid-row: 2;
  grid-column: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  width: var(--rail-width);
  padding: var(--space-4) var(--space-3);
  border-right: 1px solid var(--border);
  overflow-y: auto;
}
.tab {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--touch);
  padding: 0 0.85rem;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--transition), color var(--transition), border-color var(--transition);
}
.tab svg { width: 1.05rem; height: 1.05rem; flex: none; }
.tab:hover { color: var(--text); background: var(--card-2); border-color: var(--border); }
/* Setup pages (Roster) sit below the daily tools, behind a thin divider */
.rail-sep { display: block; height: 1px; margin: auto 0.5rem var(--space-3); background: var(--border-strong); }   /* margin-top:auto pins Roster to the bottom of the rail */
.tab.setup { font-size: var(--fs-xs); }

/* Main tool area */
main.page {
  grid-row: 2;
  grid-column: 1;
  min-height: 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  padding: var(--page-pad);
  overflow: auto;
}

/* ==========================================================================
   Layout helpers
   ========================================================================== */

.page-title, .hero h1 {
  margin: 0 0 var(--space-4);
  font-size: var(--fs-xl);
  font-weight: var(--fw-semibold);
  letter-spacing: -0.02em;
  line-height: 1.2;
}
.hero { margin-bottom: var(--space-5); }
.hero h1 { margin-bottom: var(--space-1); }
.hero p { margin: 0; color: var(--text-muted); }

.toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}
.toolbar .spacer { flex: 1; }

/* ---------- Card ---------- */

.card, .neu-raised {
  position: relative;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-5);
  min-width: 0;
  transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition);
}
/* Pink glow line along the top edge */
.card::before, .neu-raised::before, .group::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 12%;
  right: 12%;
  height: 1px;
  background: var(--gradient-glow-top);
  pointer-events: none;
}
.card:hover { border-color: var(--border-hover); }

/* Card header: white medium title left, square outlined icon button right */
.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.card h2, .card-head h2 {
  margin: 0 0 var(--space-4);
  font-size: var(--fs-lg);
  font-weight: var(--fw-medium);
  color: var(--text);
  letter-spacing: -0.01em;
}
.card-head h2 { margin: 0; }
.card h2 .hint { margin-left: 0.5rem; }

.corner-btn {
  flex: none;
  width: 2.3rem;
  height: 2.3rem;
  min-height: 0;
  padding: 0;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--card-2);
  color: var(--text);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition), transform var(--transition);
}
.corner-btn svg { width: 1rem; height: 1rem; }
.corner-btn:hover { border-color: var(--accent); background: var(--accent-soft); color: var(--text); }

.fill { flex: 1; min-height: 0; }

/* ---------- Stats, chips, badges, progress ---------- */

.stat { display: flex; align-items: baseline; gap: 0.6rem; flex-wrap: wrap; }
.stat .value { font-size: var(--fs-stat); font-weight: var(--fw-bold); letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.stat .label { color: var(--text-muted); font-size: var(--fs-sm); }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  min-height: 2.1rem;
  padding: 0.3rem 0.85rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: var(--card-2);
  color: var(--text-2);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  white-space: nowrap;
  user-select: none;
}
.chip svg { width: 0.95rem; height: 0.95rem; color: var(--text-muted); }

.pct {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.8rem;
  padding: 0.1rem 0.45rem;
  border: 1px solid var(--accent);
  border-radius: 0.4rem;
  color: var(--accent-ink);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
}
.pct.lavender { border-color: var(--lavender); color: var(--lavender); }

.progress {
  height: 0.35rem;
  border-radius: var(--radius-pill);
  background: var(--surface-tint-progress);
  overflow: hidden;
}
.progress .fill {
  height: 100%;
  width: 0;
  min-height: 0;
  border-radius: var(--radius-pill);
  background: var(--gradient-bar);
  box-shadow: 0 0 10px var(--accent-glow);
  transition: width var(--transition);
}

/* ==========================================================================
   Controls
   ========================================================================== */

button, .btn, .neu-btn {
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  min-height: var(--touch);
  padding: 0.5rem 1rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--card-2);
  color: var(--text);
  cursor: pointer;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  line-height: 1.2;
  transition: background var(--transition), border-color var(--transition), color var(--transition), transform var(--transition), box-shadow var(--transition), translate var(--transition);
}
button:hover, .btn:hover, .neu-btn:hover { border-color: var(--border-hover); background: var(--card-3); color: var(--text); }
/* depth (theme tokens; 0 / none in default and pink). `translate` is its own
   property, so it never fights a component's `transform`, and :where() keeps
   this at zero specificity so any component's own hover / pressed shadow wins. */
:where(button:not(:disabled), .btn, .wk-cell:not(.off), .lp-att, .mo-day:not(.off)):hover {
  translate: 0 var(--hover-lift);
  box-shadow: var(--hover-glow);
}
button:disabled { opacity: 0.45; cursor: not-allowed; }
button svg, .btn svg, .neu-btn svg { width: 1.05em; height: 1.05em; flex: none; }

button.primary, .btn.primary, .neu-btn.primary {
  background: var(--gradient-accent), var(--card-2);
  border-color: var(--accent-deep);
  color: var(--text);
  box-shadow: 0 0 18px var(--accent-ring-soft);
}
button.primary:hover, .btn.primary:hover, .neu-btn.primary:hover {
  border-color: var(--accent);
  box-shadow: 0 0 24px var(--accent-ring-strong);
}
button.danger:hover { border-color: var(--danger); color: var(--danger); }
button.ghost, .neu-btn.ghost { border-color: transparent; background: transparent; color: var(--text-muted); }
button.ghost:hover, .neu-btn.ghost:hover { background: var(--card-2); border-color: var(--border); color: var(--text); }
button.small { font-size: var(--fs-xs); min-height: 2rem; padding: 0.3rem 0.7rem; }
button.big { font-size: var(--fs-md); min-height: 3rem; padding: 0.75rem 1.5rem; }
.icon-btn { width: var(--touch); height: var(--touch); padding: 0; color: var(--text-muted); }
.icon-btn svg { width: 1.1rem; height: 1.1rem; }
.neu-btn { width: 100%; }
.neu-btn.icon { width: var(--touch); padding: 0; font-size: 1.3rem; line-height: 1; }

.link {
  border: 0;
  background: none;
  padding: 0.25rem 0.2rem;
  min-height: 0;
  color: var(--text-muted);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
}
.link:hover { color: var(--accent-ink); background: none; border: 0; }

input[type="text"], input[type="email"], input[type="password"], input[type="number"], input[type="search"], select, textarea, .search, .neu-inset {
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-regular);
  min-height: var(--touch);
  padding: 0.5rem 0.85rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--card-2);
  color: var(--text);
  transition: border-color var(--transition), box-shadow var(--transition);
}
input::placeholder, textarea::placeholder { color: var(--text-muted); }
input:focus, select:focus, textarea:focus, .search:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 0.18rem var(--ring);
}
input[type="number"] { width: 4.8rem; text-align: center; }
input[type="search"]::-webkit-search-cancel-button { cursor: pointer; }
select {
  min-width: 11rem;
  padding-right: 2.2rem;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%238B8699' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><path d='m6 9 6 6 6-6'/></svg>");
  background-repeat: no-repeat;
  background-position: right 0.7rem center;
  background-size: 1rem;
}
select option { background: var(--card); color: var(--text); }
textarea { width: 100%; min-height: 10rem; resize: vertical; }

label { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--fs-sm); }
label > span.lbl { color: var(--text-muted); font-size: var(--fs-xs); font-weight: var(--fw-medium); }
input[type="radio"] { accent-color: var(--accent); width: 1rem; height: 1rem; margin: 0; }
.hint { color: var(--text-muted); font-size: var(--fs-xs); }

.segment {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--touch);
  padding: 0.3rem 0.4rem 0.3rem 0.85rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--card-2);
  cursor: pointer;
  color: var(--text-muted);
  transition: border-color var(--transition), background var(--transition), color var(--transition);
}
.segment input[type="number"] { min-height: 2rem; width: 4rem; padding: 0.2rem 0.4rem; }

/* ==========================================================================
   PRESSED / ACTIVE / SELECTED — app-wide. Pink gradient fill fading to
   transparent, pink border + inner glow ring, white text, label/icon swap.
   ========================================================================== */

button[aria-pressed="true"],
.neu-btn[aria-pressed="true"],
.neu-btn.active,
[role="switch"][aria-checked="true"],
.tab[aria-current="page"],
.seg input:checked + span,
.segment:has(input:checked),
details[open] > summary,
select:focus {
  background: var(--gradient-accent), var(--card-2);
  border-color: var(--accent);
  box-shadow: var(--shadow-pressed);
  color: var(--text);
}
button[aria-pressed="true"] svg,
.tab[aria-current="page"] svg { color: var(--accent); }
.tab[aria-current="page"]:hover { color: var(--text); }

button:active, .neu-btn:active, .tab:active, summary:active, .seg label:active span, .segment:active { transform: scale(0.97); }

.when-on { display: none; }
[aria-pressed="true"] .when-off, [aria-checked="true"] .when-off { display: none; }
[aria-pressed="true"] .when-on, [aria-checked="true"] .when-on { display: inline-flex; align-items: center; gap: 0.4rem; }
.when-off { display: inline-flex; align-items: center; gap: 0.4rem; }

.seg input:checked + span::before, .segment:has(input:checked)::before {
  content: "✓";
  font-weight: var(--fw-bold);
  color: var(--accent-ink);
  margin-right: 0.35rem;
}

/* ==========================================================================
   Status line
   ========================================================================== */

.status {
  min-height: 1.5rem;
  margin: 0 0 var(--space-4);
}
.hud-page > .status:empty, .groups-page > .status:empty, .seating-page > .status:empty { display: none; }
.status {
  border-radius: var(--radius-sm);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  flex: none;
}
.status-info, .status-ok, .status-error {
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--border-strong);
  background: var(--card-2);
  color: var(--text-2);
}
.status-ok { border-color: rgba(111, 211, 166, 0.4); }
.status-error { border-color: rgba(255, 122, 138, 0.5); color: var(--danger); }

/* ==========================================================================
   Hub (index.html) — a HUD / command-centre view. Thin precise lines, small
   technical labels, monospace data, corner-tick panels, a faint grid +
   scanline texture, and a fast staggered sweep-in. Pink is the HUD colour and
   glows only on interactive elements. Restraint: glow is reserved for hover.
   ========================================================================== */

:root {
  --hud: var(--accent);
  --hud-dim: rgba(240, 127, 174, 0.35);
  --hud-line: rgba(240, 127, 174, 0.28);
  --hud-line-strong: rgba(240, 127, 174, 0.7);
  --hud-glow: 0 0 18px rgba(240, 127, 174, 0.22), 0 0 2px rgba(240, 127, 174, 0.5);
  --hud-glow-soft: 0 0 24px rgba(240, 127, 174, 0.10);
  --hud-grid: rgba(255, 255, 255, 0.035);
  --hud-scan: rgba(255, 255, 255, 0.025);
  --hud-mono: var(--font-mono);
  --hud-tick: 0.9rem;        /* corner tick length at rest */
  --hud-tick-hover: 1.6rem;  /* corner tick length on hover */
  --hud-in: 420ms;           /* per-element sweep-in duration */
  --hud-stagger: 60ms;
  --att-absent: var(--danger);
  --att-absent-soft: rgba(255, 122, 138, 0.14);
  --att-tardy: #F5B84B;                 /* amber — tardy outline + badge */
  --att-tardy-soft: rgba(245, 184, 75, 0.16);
  /* Name wheel - a graduated pink/magenta ramp. wheel.js alternates between
     the dark half (r0-r3) and the light half (r4-r7) so every boundary is an
     obvious step while the set still reads as one family. Names sit on top in
     near-white with a dark outline, so they carry on any of them. */
  --wheel-r0: #3E1028;
  --wheel-r1: #5A1739;
  --wheel-r2: #78204B;
  --wheel-r3: #952B5E;
  --wheel-r4: #B03A72;
  --wheel-r5: #C44D85;
  --wheel-r6: #D66199;
  --wheel-r7: #E67CAE;
  --wheel-divider: rgba(255, 255, 255, 0.55);
  --wheel-ink:        #FFFFFF;       /* slice label */
  --wheel-ink-shadow: rgba(10, 6, 16, 0.6);
  --wheel-won:        #FFD9EC;       /* winning slice lights up */
  /* the machine: dark metal frame with thin accent edges */
  --wheel-metal:    #1C1828;
  --wheel-metal-2:  #14111E;
  --wheel-metal-hi: rgba(240, 127, 174, 0.55);
  --wheel-housing:  #221D31;
  --wheel-face-hi:  rgba(255, 255, 255, 0.10);
  --wheel-face-lo:  rgba(0, 0, 0, 0.35);

  /* noise meter zones — green → amber → red, bright enough for a projector */
  --noise-quiet: var(--ok);
  --noise-ok:    var(--att-tardy);
  --noise-loud:  var(--danger);
  --noise-quiet-soft: rgba(111, 211, 166, 0.16);
  --noise-ok-soft:    rgba(245, 184, 75, 0.16);
  --noise-loud-soft:  rgba(255, 122, 138, 0.16);
  /* the level bar: the fill is one full-width gradient revealed by clip-path,
     so its stops sit on the zone boundaries (30% / 70%, see QUIET_AT in
     noise.js) and the leading edge always wears the current zone's colour.
     default: an instrument — tick marks, a thin bright fill, a glowing cap. */
  --noise-bar-fill:  linear-gradient(90deg, var(--noise-quiet) 0 27%, var(--noise-ok) 33% 67%, var(--noise-loud) 73% 100%);
  --noise-bar-track: var(--card-2);
  --noise-bar-line:  var(--border-strong);
  --noise-bar-mark:  var(--text-2);
  --noise-bar-tick:  var(--border-hover);
  --noise-bar-inset: none;
  --noise-rider:     none;
}

body.hub {
  background:
    var(--body-pattern),
    radial-gradient(70% 50% at 50% 0%, var(--bg-glow), transparent 70%),
    var(--bg-0);
  background-size: var(--body-pattern-size, auto), auto, auto;
  background-repeat: repeat, no-repeat, no-repeat;
  position: relative;
}
/* grid + scanline texture, very low opacity, never on top of content */
body.hub::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    repeating-linear-gradient(0deg, var(--hud-scan) 0 1px, transparent 1px 3px),
    linear-gradient(var(--hud-grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--hud-grid) 1px, transparent 1px);
  background-size: 100% 3px, 48px 48px, 48px 48px;
  mask-image: radial-gradient(80% 70% at 50% 40%, #000 40%, transparent 100%);
}
body.hub .app { position: relative; z-index: 1; }

/* ---------- status bar ---------- */
/* The bar is allowed to wrap: nav-links get priority (never clipped), so when
   the readouts + nav can't share one row, the nav takes its own row below
   rather than losing its last tabs to overflow. */
.hud-bar { border-bottom: 1px solid var(--hud-line); background: var(--topbar-bg); }
/* -----------------------------------------------------------------------------
   Universal top-bar layout. Two rows via CSS grid-template-areas — one layout
   at every viewport width, no wraps that let items collide.
     Row 1 (main):     brand | nav | tail (right)
     Row 2 (readouts): time · date · sched · now, right-aligned, slim
   The two rows are separate grid areas so nothing on row 1 can ever overlap
   anything on row 2. `header.topnav` selector beats `header.topbar` on
   specificity+source order so this display wins over the old flex rule.
   -------------------------------------------------------------------------- */
header.topnav {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr) auto;
  grid-template-areas: "brand nav read tail";
  align-items: center;
  column-gap: var(--space-3);
  row-gap: 0.15rem;
  min-height: 3.4rem;
  padding: var(--space-2) var(--page-pad);
  min-width: 0;
  border-bottom: 1px solid var(--hud-line);
}
/* Four grouped triggers leave room for the readouts on the same row; below
   1100px they drop back to their own row rather than colliding. */
@media (max-width: 1100px) {
  header.topnav {
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "brand nav  tail"
      "read  read read";
  }
}
.topnav .hud-brand  { grid-area: brand; }
.topnav .nav-links {
  grid-area: nav;
  display: flex;
  align-items: center;
  gap: 0.15rem;
  min-width: 0;
  flex-wrap: wrap;
  row-gap: 0.15rem;
}
.topnav .hud-tail {
  grid-area: tail;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  flex-shrink: 0;
  justify-self: end;
}
/* ---- grouped nav: four triggers, each with a dropdown ---- */
.nav-group { position: relative; display: inline-flex; }
.nav-trigger {
  display: inline-flex; align-items: center; gap: 0.3rem;
  background: none;
  cursor: pointer;
}
.nav-caret { width: 0.75rem; height: 0.75rem; opacity: 0.7; transition: transform 160ms ease; }
.nav-group[data-open="1"] .nav-caret { transform: rotate(180deg); }
/* the section holding the current page */
.topnav .nav-links .tab.nav-trigger[data-here="1"] {
  background: var(--gradient-accent), transparent;
  border-color: var(--hud-line-strong);
  box-shadow: var(--hud-glow-soft);
  color: var(--text);
}
.nav-group[data-open="1"] > .nav-trigger { border-color: var(--hud-line-strong); color: var(--text); }

/* the panel is absolutely positioned, so opening it never shifts the bar */
.nav-menu {
  position: absolute;
  top: calc(100% + 0.35rem);
  left: 0;
  z-index: 60;
  min-width: max(100%, 10.5rem);
  display: flex; flex-direction: column; gap: 0.1rem;
  padding: 0.3rem;
  background: var(--card);
  border: 1px solid var(--hud-line-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-pop), var(--hud-glow-soft);
  animation: nav-drop 140ms ease;
}
@keyframes nav-drop {
  from { opacity: 0; transform: translateY(-0.3rem); }
  to   { opacity: 1; transform: none; }
}
.nav-item {
  display: block;
  padding: 0.42rem 0.75rem;
  border-radius: var(--radius-sm);
  color: var(--text-2);
  text-decoration: none;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.04em;
  white-space: nowrap;
}
.nav-item:hover, .nav-item:focus-visible { background: var(--card-3); color: var(--text); outline: none; }
.nav-item[aria-current="page"] {
  background: var(--gradient-accent), transparent;
  color: var(--text);
  box-shadow: inset 0 0 0 1px var(--hud-line-strong);
}
@media (prefers-reduced-motion: reduce) { .nav-menu { animation: none; } }

.topnav .hud-status {
  grid-area: read;
  justify-self: end;
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  min-width: 0;
  overflow: hidden;
  flex-wrap: nowrap;
  padding-top: 0.15rem;
  font-size: 0.72rem;
}

.topnav .nav-links .tab { min-height: 2.2rem; padding: 0 0.6rem; white-space: nowrap; border-radius: 4px; }
@media (max-width: 1400px) { .topnav:not(.no-links) .hud-brand { font-size: 0; gap: 0; } .topnav:not(.no-links) .hud-brand-mark { margin-right: 0.5rem; } .topnav .nav-links .tab { padding: 0 0.5rem; } }
.topnav.no-links .hud-brand { font-size: 0.82rem; }
body.launcher .fs-float .fs-nav { display: none; }
.topnav .nav-links .tab[aria-current="page"] { background: var(--gradient-accent), transparent; border-color: var(--hud-line-strong); box-shadow: var(--hud-glow-soft); }
.topnav .nav-links .tab.setup { color: var(--text-muted); opacity: 0.75; }
.topnav .nav-links .tab.setup:first-of-type, .topnav .nav-links .tab:not(.setup) + .tab.setup { margin-left: 0.6rem; position: relative; }
.topnav .nav-links .tab:not(.setup) + .tab.setup::before { content: ""; position: absolute; left: -0.4rem; top: 25%; height: 50%; width: 1px; background: var(--hud-line); }
.topnav .fs-btn { flex: none; width: 2.2rem; height: 2.2rem; }
/* Drop less-important stats before the whole row overflows */
@media (max-width: 1600px) { .topnav .hud-status .hud-key { display: none; } .topnav .hud-status { gap: var(--space-3); } }
@media (max-width: 1200px) { .topnav .hud-status .hud-stat:nth-child(2) { display: none; } /* drop DATE */ }
@media (max-width: 900px)  { .topnav .hud-status .hud-stat:nth-child(3) { display: none; } /* drop SCHED */ }
body.present .topnav .hud-status .hud-key, body.present .topnav .hud-status .hud-stat:nth-child(2) { display: none; }
.hud-brand {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  color: var(--text);
  text-decoration: none;
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.32em;
  text-transform: uppercase;
}
.hud-brand-mark {
  width: 0.6rem;
  height: 0.6rem;
  border: 1px solid var(--hud);
  transform: rotate(45deg);
  box-shadow: var(--hud-glow);
}
.hud-status { display: flex; align-items: center; gap: var(--space-5); flex-wrap: wrap; justify-content: flex-end; }
.hud-stat { display: inline-flex; align-items: baseline; gap: 0.55rem; font-family: var(--hud-mono); font-size: 0.78rem; white-space: nowrap; }
.hud-key { color: var(--label-ink); letter-spacing: 0.18em; font-size: 0.66rem; }
.hud-val { color: var(--figure-ink); font-variant-numeric: tabular-nums; letter-spacing: 0.04em; }
.hud-ok { color: var(--accent-ink); }
.hud-bad { color: var(--danger); }
.hud-quiet .hud-val { color: var(--text-muted); }
/* a stat that links somewhere (SCHED → schedule.html) */
a.hud-link { text-decoration: none; border-bottom: 1px dashed var(--hud-line); padding-bottom: 1px; transition: border-color 150ms ease; }
a.hud-link:hover .hud-val, a.hud-link:focus-visible .hud-val { color: var(--accent-ink); }
a.hud-link:hover, a.hud-link:focus-visible { border-bottom-color: var(--hud); outline: none; }
/* the live status line: passing / lunch / before-after school read quieter than a period */
#hud-now[data-state="passing"], #hud-now[data-state="break"], #hud-now[data-state="before"] { color: var(--text-2); }
#hud-now[data-state="after"], #hud-now[data-state="weekend"], #hud-now[data-state="no-school"] { color: var(--text-muted); }
/* signed-in account chip + sign-out icon, sit just before the full-screen button */
.hud-account { max-width: 14rem; }
.hud-account .hud-val { overflow: hidden; text-overflow: ellipsis; }
.signout-btn { width: 2.3rem; height: 2.3rem; padding: 0; display: inline-flex; align-items: center; justify-content: center; }
.signout-btn:hover { border-color: var(--danger); color: var(--danger); }
@media (max-width: 1400px) { .hud-account { display: none; } }

/* settings menu (gear icon → theme picker) */
.hud-settings { position: relative; }
.hud-settings > summary { list-style: none; cursor: pointer; width: 2.3rem; height: 2.3rem; padding: 0; display: inline-flex; align-items: center; justify-content: center; border-radius: var(--radius-sm); }
.hud-settings > summary::-webkit-details-marker { display: none; }
.hud-settings > summary::marker { content: ""; }
.hud-menu {
  position: absolute;
  top: calc(100% + 0.5rem);
  right: 0;
  min-width: 12rem;
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-pop);
  padding: 0.5rem;
  display: grid;
  gap: 0.25rem;
  z-index: 40;
}
.hud-menu-title {
  font-family: var(--font-display);
  text-transform: var(--label-transform);
  letter-spacing: var(--track-label);
  font-size: 0.66rem;
  color: var(--text-muted);
  padding: 0.35rem 0.6rem 0.15rem;
}
.hud-menu-item {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.5rem 0.6rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: var(--fs-sm);
}
.hud-menu-item:hover { background: var(--card-2); }
.hud-menu-item input[type="radio"] { accent-color: var(--accent); }

/* "Periods I teach" — compact 4×2 pill grid */
.hud-periods {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.3rem;
  padding: 0.35rem 0.6rem 0.55rem;
}
.hud-period {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 2rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--card-2);
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.85rem;
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}
.hud-period input { position: absolute; opacity: 0; pointer-events: none; }
.hud-period[data-on] {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent-ink);
  font-weight: var(--fw-semibold);
}
.hud-period:hover { border-color: var(--border-hover); color: var(--text); }
.hud-period[data-on]:hover { color: var(--accent-ink); }

/* ---------- panels ---------- */
.hud-page { padding: clamp(24px, 5vh, 64px) var(--page-pad) var(--space-5); display: flex; flex-direction: column; }
/* ---------- attendance view (attendance.js) ---------- */
.attendance-page, .hub-page { padding: var(--space-4) var(--page-pad) var(--space-4); }
/* full mode (attendance.html): the chart is the screen, counts + lists in a side column */
.attendance[data-mode="full"] {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(19rem, 26vw, 26rem);
  grid-template-rows: minmax(0, 1fr);
  gap: var(--space-4);
}
.attendance[data-mode="full"] .att-chart { min-height: 0; padding: 0.75rem 0.9rem 0.65rem; gap: 0.55rem; }
.att-side { display: flex; flex-direction: column; gap: var(--space-4); min-height: 0; }
.att-actions a.btn { text-decoration: none; }
/* hub panel grid: add a panel and it flows in; rows share the height so the column never looks empty */
.hub-panels {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  grid-auto-rows: minmax(7rem, 1fr);
  gap: var(--space-4);
  min-height: 0;
  align-content: stretch;
}
.hub-panels .hud-panel { min-height: 0; }
.hub-panels .hud-panel.setup { border-style: dashed; }
.hub-panels .hud-panel.setup .hud-idx { display: inline; }
/* a HUD box: same faint border + corner ticks as a panel, but not a link */
.hud-box {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--panel-bg);
  border: 1px solid var(--hud-line);
  box-shadow: var(--panel-shadow);
  animation: hud-sweep var(--hud-in) cubic-bezier(0.2, 0.8, 0.2, 1) both;
  animation-delay: calc(120ms + var(--i, 0) * var(--hud-stagger));
}
.exp-only { display: none; }
/* full screen (body.present): attendance page = chart edge to edge with the side column; hub = same layout, panels hidden */
body.present .attendance[data-mode="full"] { grid-template-columns: minmax(0, 1fr) clamp(17rem, 22vw, 24rem); }
body.present .attendance[data-mode="full"] .att-chart { padding: 0.9rem 1.1rem 0.8rem; gap: 0.7rem; }
body.present .hub-panels { display: none; }
body.present .exp-only { display: inline-flex; }
body.present .att-tiles { grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); grid-auto-rows: 4rem; }
body.present .att-tile { font-size: var(--fs-lg); }
body.present .att-names { font-size: 1rem; }
body.present .att-counts b { font-size: 1.9rem; }
/* the floating Nav button brings the top nav links back */
body.present.show-nav .topnav .nav-links { display: flex; }

.dash-head { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.dash-field { display: inline-flex; align-items: center; gap: 0.5rem; }
.dash-field select, .dash-field input[type="date"] { min-height: 2.1rem; padding: 0.25rem 0.6rem; font-size: var(--fs-sm); background: var(--field-bg); border-color: var(--hud-line); }
.dash-field select { padding-right: 2rem; min-width: 10rem; max-width: 14rem; }
.dash-field input[type="date"] { font-family: var(--hud-mono); font-size: 0.8rem; }
.dash-head .spacer { flex: 1; }
.dash-head .hud-stat { font-size: 0.7rem; }
#exp-counts .hud-key { margin-left: 0.4rem; }
#exp-counts .c-absent { color: var(--att-absent); }
#exp-counts .c-tardy { color: var(--att-tardy); }
/* small mono chip button (AUTO / TODAY / links in empty states) */
.hud-chip {
  display: inline-flex;
  align-items: center;
  min-height: 1.7rem;
  padding: 0.15rem 0.6rem;
  border: 1px solid var(--chip-line);
  border-radius: 2px;
  background: transparent;
  color: var(--chip-ink);
  font-family: var(--hud-mono);
  font-size: 0.66rem;
  letter-spacing: 0.18em;
  text-decoration: none;
  cursor: pointer;
}
.hud-chip:hover { border-color: var(--hud); color: var(--text); background: transparent; }
.hud-chip[aria-pressed="true"] { background: transparent; border-color: var(--hud); color: var(--accent-ink); box-shadow: 0 0 10px var(--accent-inset-chip); }
#chart-note a { color: var(--accent-ink); text-decoration: none; border-bottom: 1px dashed var(--hud-line); }

/* the read-only room: same .piece/.seat markup as the builder, scaled to fit */
.hub-chart {
  position: relative;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  border: 1px solid var(--border);
  background-color: var(--chart-floor);
  background-image: radial-gradient(var(--border-strong) 1px, transparent 1px);
  background-size: 24px 24px;
}
.hub-room { position: absolute; left: 0; top: 0; width: 0; height: 0; overflow: visible; transform-origin: 0 0; }
.hub-room .piece { will-change: auto; }
.hub-room .seat-label { font-size: 13px; }   /* the room is scaled down in the compact panel, so labels are large in room units */
.hub-room .seat, .hub-room .desk-block { border-radius: 3px; }
.hub-room .front-text { font-size: 11px; }
.hub-room button.seat { font: inherit; min-height: 0; padding: 0; border-radius: 4px; cursor: pointer; transition: border-color 120ms ease, background 120ms ease, opacity 120ms ease, box-shadow 120ms ease; }
.hub-room button.seat:hover { border-color: var(--hud); background: var(--seat-bg); transform: none; }
.hub-room button.seat:active { transform: none; }
.hub-room button.seat:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--ring); }

/* attendance states — seats and tiles share them */
[data-student][data-status="absent"] { opacity: 0.5; border-color: var(--att-absent) !important; box-shadow: inset 0 0 0 2px var(--att-absent); background: var(--att-absent-soft) !important; }
[data-student][data-status="tardy"] { border-color: var(--att-tardy) !important; box-shadow: inset 0 0 0 2px var(--att-tardy); background: var(--att-tardy-soft) !important; }
[data-student][data-status="absent"]::after, [data-student][data-status="tardy"]::after {
  position: absolute;
  top: -1px;
  right: -1px;
  font-family: var(--hud-mono);
  font-size: 12px;
  font-weight: var(--fw-bold);
  line-height: 1;
  padding: 2px 4px;
  border-radius: 2px;
  color: var(--bg-0);
}
[data-student][data-status="absent"]::after { content: "A"; background: var(--att-absent); }
[data-student][data-status="tardy"]::after { content: "T"; background: var(--att-tardy); }
.att-tile[data-status="absent"]::after, .att-tile[data-status="tardy"]::after { font-size: 0.7rem; top: 0.35rem; right: 0.4rem; padding: 0.1rem 0.3rem; }

/* roster tiles (no seating chart yet) + the "not seated" strip */
.att-tiles {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
  grid-auto-rows: 3.4rem;
  gap: 0.5rem;
  align-content: start;
}
.att-tile {
  position: relative;
  min-height: 0;
  padding: 0.3rem 0.6rem;
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  background: var(--card-2);
  color: var(--text);
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: border-color 120ms ease, background 120ms ease, opacity 120ms ease;
}
.att-tile:hover { border-color: var(--hud); }
.att-tile:active { transform: none; }
.unseated { display: flex; flex-direction: column; gap: 0.4rem; flex: none; }
.att-tiles.small { flex: none; max-height: 7.4rem; grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr)); grid-auto-rows: 2.4rem; gap: 0.4rem; }
.att-tiles.small .att-tile { font-size: var(--fs-sm); }
.chart-empty { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-3); color: var(--text-muted); border: 1px dashed var(--border-strong); }
.chart-empty p { margin: 0; font-size: var(--fs-md); }

.dash-legend { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; font-family: var(--hud-mono); font-size: 0.62rem; letter-spacing: 0.16em; color: var(--text-muted); }
.dash-legend .lg { display: inline-flex; align-items: center; gap: 0.4rem; }
.dash-legend .lg::before { content: ""; width: 0.7rem; height: 0.7rem; border: 1px solid var(--border-strong); border-radius: 2px; background: var(--card-3); }
.dash-legend .lg-absent::before { border-color: var(--att-absent); background: var(--att-absent-soft); }
.dash-legend .lg-tardy::before { border-color: var(--att-tardy); background: var(--att-tardy-soft); }
.dash-legend .lg-hint { margin-left: auto; letter-spacing: 0.06em; text-transform: none; font-family: inherit; font-size: var(--fs-xs); }

/* ---------- attendance summary card ---------- */
.att-card { padding: 0.9rem 1.1rem 0.8rem; gap: 0.6rem; flex: none; }
.att-lists-box { padding: 0.9rem 1.1rem; flex: 1; min-height: 9rem; }
.att-counts { font-size: var(--fs-md); font-weight: var(--fw-medium); color: var(--text-2); letter-spacing: 0.02em; }
.att-counts b { font-size: var(--fs-stat); font-weight: var(--fw-bold); color: var(--text); font-variant-numeric: tabular-nums; margin-right: 0.15em; }
.att-counts i { font-style: normal; color: var(--text-muted); margin: 0 0.35rem; }
.att-counts .c-absent { color: var(--att-absent); }
.att-counts .c-tardy { color: var(--att-tardy); }
/* Absent above Tardy, each the side column's full width, so "14 · First Last"
   and a tardy's time fit on one line. Each list takes the height it needs and
   scrolls only when the two together don't fit. */
.att-lists { display: flex; flex-direction: column; gap: var(--space-4); flex: 1; min-height: 0; }
.att-list { display: flex; flex-direction: column; flex: 0 1 auto; min-height: 0; }
.att-list h3 { color: var(--label-ink); display: flex; align-items: baseline; gap: 0.5rem; margin: 0 0 0.4rem; font-family: var(--hud-mono); font-size: 0.78rem; font-weight: var(--fw-medium); border-bottom: 1px solid var(--hud-line); padding-bottom: 0.35rem; }
.att-names { list-style: none; margin: 0; padding: 0; overflow-y: auto; min-height: 0; font-size: 0.95rem; line-height: 1.2; }
/* one line per student: number · name (ellipsis if it must, full name in the title) · time */
.att-names li { display: grid; grid-template-columns: auto auto minmax(0, 1fr) auto; align-items: baseline; padding: 0.12rem 0; border-bottom: 1px solid var(--border); }
.att-names li .who { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.att-names li .at { padding-left: 0.6rem; font-family: var(--hud-mono); font-size: 0.7rem; color: var(--att-tardy); white-space: nowrap; }
/* the roster number for Aeries: smaller, muted, right-aligned so the names line up */
.att-names li .rn { min-width: 2ch; text-align: right; font-family: var(--hud-mono); font-size: 0.74em; font-weight: 400; font-variant-numeric: tabular-nums; color: var(--text-muted); }
.att-names li .sep { margin: 0 0.4em; font-size: 0.74em; color: var(--text-muted); }
.att-names li.none { display: block; color: var(--text-muted); border: 0; font-style: italic; }
.att-actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.att-actions .spacer { flex: 1; }
.save-state { font-family: var(--hud-mono); font-size: 0.66rem; letter-spacing: 0.16em; color: var(--text-muted); }
.save-state[data-state="saved"] { color: var(--ok); }
.save-state[data-state="error"] { color: var(--danger); }

/* ---------- compact tool panels (quieter than the old full-size ones) ---------- */

.hud-panel.compact { min-width: 0; flex-direction: row; align-items: center; justify-content: flex-start; gap: 0.7rem; padding: 0.7rem 0.9rem; min-height: 3.6rem; border-color: var(--border-strong); }
.hud-panel.compact .hud-icon { margin: 0; width: 1.2rem; height: 1.2rem; flex: none; opacity: 0.8; }
.hud-panel.compact .hud-text { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.hud-panel.compact .hud-name { font-size: var(--fs-xs); letter-spacing: 0.12em; white-space: nowrap; }
.hud-panel.compact .hud-sub { font-size: 0.56rem; letter-spacing: 0.12em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hud-panel.compact .tick { border-color: var(--border-strong); }
.hud-panel.compact:hover { transform: none; box-shadow: var(--hud-glow-soft); border-color: var(--hud-line-strong); }
.hud-panel.compact:hover .tick { width: var(--hud-tick); height: var(--hud-tick); border-color: var(--hud); }

.hud-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 0.35rem;
  padding: 1.5rem 1.6rem 1.4rem;
  color: var(--text);
  text-decoration: none;
  background: var(--panel-bg);
  border: 1px solid var(--hud-line);
  box-shadow: none;
  transition: border-color 150ms ease, box-shadow 150ms ease, transform 150ms ease, background 150ms ease;
}
/* corner ticks: bracket corners drawn over the faint border */
.hud-panel .tick, .hud-box .tick {
  position: absolute;
  width: var(--hud-tick);
  height: var(--hud-tick);
  border: 0 solid var(--hud-line-strong);
  transition: width 150ms ease, height 150ms ease, border-color 150ms ease;
  pointer-events: none;
}
.hud-panel .tl, .hud-box .tl { top: -1px; left: -1px; border-top-width: 1px; border-left-width: 1px; }
.hud-panel .tr, .hud-box .tr { top: -1px; right: -1px; border-top-width: 1px; border-right-width: 1px; }
.hud-panel .bl, .hud-box .bl { bottom: -1px; left: -1px; border-bottom-width: 1px; border-left-width: 1px; }
.hud-panel .br, .hud-box .br { bottom: -1px; right: -1px; border-bottom-width: 1px; border-right-width: 1px; }

.hud-idx { position: absolute; top: 0.9rem; right: 1.1rem; font-family: var(--hud-mono); font-size: 0.66rem; letter-spacing: 0.18em; color: var(--text-muted); }
.hud-icon { width: 1.75rem; height: 1.75rem; color: var(--accent-ink); margin-bottom: auto; opacity: 0.9; transition: filter 150ms ease; }
.hud-name { font-size: 1.05rem; font-weight: var(--fw-semibold); letter-spacing: 0.14em; text-transform: uppercase; }
.hud-sub { font-family: var(--hud-mono); font-size: 0.66rem; letter-spacing: 0.16em; color: var(--text-muted); }

.hud-panel:hover, .hud-panel:focus-visible {
  border-color: var(--hud-line-strong);
  box-shadow: var(--hud-glow), var(--hud-glow-soft);
  transform: translateY(-2px);
  background: var(--panel-bg-hover);
  outline: none;
}
.hud-panel:hover .tick, .hud-panel:focus-visible .tick { width: var(--hud-tick-hover); height: var(--hud-tick-hover); border-color: var(--hud); }
.hud-panel:hover .hud-icon, .hud-panel:focus-visible .hud-icon { filter: drop-shadow(0 0 6px var(--accent-icon-glow)); }

/* quieter modules: Rosters (setup). The .soon variant is kept for any
   future coming-soon tile even though no tool currently uses it. */
.hud-panel.soon { color: var(--text-muted); }
.hud-panel.soon .hud-icon { color: var(--text-muted); opacity: 0.6; }
.hud-panel.soon:hover { transform: none; box-shadow: none; border-color: var(--border-strong); }
.hud-panel.soon:hover .tick { width: var(--hud-tick); height: var(--hud-tick); border-color: var(--hud-line-strong); }
.hud-panel.soon:hover .hud-icon { filter: none; }
.hud-panel.setup { border-style: dashed; border-color: var(--border-strong); background: transparent; }
.hud-panel.setup .tick { border-color: var(--border-strong); }
.hud-panel.setup .hud-icon { color: var(--text-muted); }
.hud-panel.setup .hud-name { color: var(--text-2); font-weight: var(--fw-medium); }
.hud-panel.setup:hover { border-style: solid; box-shadow: var(--hud-glow-soft); }
.hud-panel.setup:hover .hud-icon { color: var(--accent-ink); }

/* ---------- entrance: fast staggered sweep, under 800ms total ---------- */
@keyframes hud-sweep { from { opacity: 0; transform: translateY(10px); clip-path: inset(0 100% 0 0); } 60% { clip-path: inset(0 0 0 0); } to { opacity: 1; transform: none; clip-path: inset(0 0 0 0); } }
@keyframes hud-fade { from { opacity: 0; } to { opacity: 1; } }
.hud-bar { animation: hud-fade 300ms ease both; }
.hud-panel { animation: hud-sweep var(--hud-in) cubic-bezier(0.2, 0.8, 0.2, 1) both; animation-delay: calc(120ms + var(--i, 0) * var(--hud-stagger)); }

@media (max-width: 900px) {
  .attendance[data-mode="full"] { grid-template-columns: 1fr; grid-template-rows: auto; overflow-y: auto; }
  .attendance[data-mode="full"] .att-chart { height: min(55vh, 30rem); }
  /* the page scrolls here, so the lists show in full instead of scrolling in a sliver */
  .attendance[data-mode="full"] .att-names { overflow-y: visible; }
  .attendance[data-mode="full"] :is(.att-side, .att-lists-box, .att-lists, .att-list) { flex: none; min-height: auto; }
  .hub-panels { grid-auto-rows: 7rem; }
}
@media (max-width: 700px) {
  .hud-status { gap: var(--space-3); }
  .hud-status .hud-stat:nth-child(2) { display: none; }   /* drop the date on small screens */
}
@media (prefers-reduced-motion: reduce) {
  .hud-bar, .hud-panel, .hud-box { animation: none; }
  .hud-panel, .hud-panel .tick, .hud-box .tick, .hud-icon, .hub-room button.seat, .att-tile { transition: none; }
  .hud-panel:hover { transform: none; }
}

/* ==========================================================================
   Roster — sticky control panel + one continuous numbered list
   ========================================================================== */

.roster-page { padding: var(--page-pad); }
.roster-layout {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(16rem, 22rem) 1fr;
  gap: var(--space-5);
}

.roster-panel {
  position: sticky;
  top: 0;
  align-self: start;
  max-height: 100%;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.period-pick { display: flex; flex-direction: column; align-items: stretch; gap: var(--space-1); }
.period-pick select { width: 100%; min-width: 0; }
.period-links { display: flex; align-items: center; gap: var(--space-2); color: var(--text-muted); font-size: var(--fs-xs); margin-top: calc(-1 * var(--space-2)); }
.period-head { margin-top: var(--space-2); }
.period-title { margin: 0; font-size: var(--fs-stat); font-weight: var(--fw-bold); letter-spacing: -0.02em; line-height: 1.1; overflow-wrap: anywhere; }
.period-course { margin: 0.2rem 0 0; color: var(--text-muted); font-size: var(--fs-sm); }

/* Segmented control */
.seg {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.25rem;
  padding: 0.25rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--card-2);
}
.seg label { position: relative; display: block; cursor: pointer; }
.seg input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.seg span {
  display: block;
  text-align: center;
  padding: 0.4rem 0.5rem;
  border: 1px solid transparent;
  border-radius: calc(var(--radius-sm) - 0.15rem);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  color: var(--text-muted);
  transition: background var(--transition), color var(--transition), border-color var(--transition), transform var(--transition);
}
.seg input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 1px; }

.panel-actions { display: flex; flex-direction: column; gap: var(--space-3); margin-top: auto; }
.add-form { display: flex; gap: var(--space-2); }
.add-form input { flex: 1; min-width: 0; }
.add-form .neu-btn { width: auto; }

/* Right: list card */
.roster-names { min-height: 0; min-width: 0; display: flex; flex-direction: column; }
.list-head { flex: none; display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-4); }
.list-count { flex: none; }                /* rendered as a .chip */
.search { flex: 1; min-width: 0; max-width: 24rem; }
.list-head .more { display: none; position: relative; margin-left: auto; }
.present-bar { flex: none; display: flex; justify-content: flex-end; margin-bottom: var(--space-3); }
.present-bar .neu-btn { width: auto; }

.name-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  columns: 18rem;
  column-gap: 1rem;
  padding: 2px;
}

.student {
  break-inside: avoid;
  display: flex;
  align-items: center;
  height: 3.5rem;
  margin-bottom: 0.625rem;
  padding: 0 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--card-2);
  transition: border-color var(--transition), background var(--transition), transform var(--transition), opacity var(--transition);
}
.student:hover { border-color: var(--border-hover); background: var(--card-3); transform: translateY(-1px); }
.student.saving { opacity: 0.6; }
.student .num { flex: none; width: 2rem; text-align: right; font-size: 0.85rem; font-variant-numeric: tabular-nums; color: var(--text-muted); }
/* the roster number (Aeries): set by hand = accent, a number two students share = lavender */
.student .num.manual { color: var(--accent-ink); }
.student .num.dupe { color: var(--lavender); }
body.editing .student .num { cursor: text; text-decoration: underline dotted var(--border-strong); text-underline-offset: 0.25em; }
.student .num-edit { flex: none; width: 3rem; min-height: 2.2rem; margin-left: -1rem; padding: 0.2rem 0.4rem; text-align: right; font-variant-numeric: tabular-nums; appearance: textfield; -moz-appearance: textfield; }
.student .num-edit::-webkit-inner-spin-button, .student .num-edit::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.student .divider { flex: none; width: 1px; height: 1.5rem; margin: 0 0.75rem; background: var(--border-strong); }
.student .name { flex: 1; min-width: 0; font-size: 1.05rem; font-weight: var(--fw-semibold); color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.inline-edit { width: 100%; min-height: 2.2rem; padding: 0.25rem 0.6rem; border-radius: calc(var(--radius-sm) - 0.1rem); font-size: 1.05rem; font-weight: var(--fw-semibold); }

.student .remove {
  display: none;
  position: relative;
  flex: none;
  width: var(--touch);
  height: var(--touch);
  min-height: 0;
  margin-left: auto;
  margin-right: -0.6rem;
  padding: 0;
  border: 0;
  background: transparent;
  box-shadow: none;
  color: var(--text-muted);
  border-radius: 50%;
  align-items: center;
  justify-content: center;
}
.student .remove::before {
  content: "";
  position: absolute;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: var(--card);
  transition: border-color var(--transition);
}
.student .remove svg { position: relative; width: 0.85rem; height: 0.85rem; }
.student .remove:hover, .student .remove:focus-visible { color: var(--danger); background: transparent; }
.student .remove:hover::before { border-color: var(--danger); }

body.editing .roster-names { border-color: var(--accent-deep); box-shadow: var(--shadow-card), inset 0 0 40px var(--accent-inset-soft); }
body.editing .student .remove { display: inline-flex; }
body.editing .student .name { cursor: text; }

.no-match { color: var(--text-muted); text-align: center; padding: var(--space-5); }

.empty-state {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  padding: var(--space-6);
  text-align: center;
  color: var(--text-muted);
  background: var(--card-2);
  min-height: auto;
}
.empty-state svg { width: 3.5rem; height: 3.5rem; opacity: 0.6; }
.empty-state p { margin: 0; font-size: var(--fs-lg); font-weight: var(--fw-medium); color: var(--text); }
.empty-state .neu-btn { width: auto; min-width: 12rem; }

.toast {
  position: fixed;
  left: 50%;
  bottom: 1.5rem;
  transform: translateX(-50%);
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding: 0.7rem 1.2rem;
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-pop);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
}
.toast .link { color: var(--accent-ink); font-size: var(--fs-sm); font-weight: var(--fw-semibold); }

.import-dialog { width: min(40rem, 92vw); padding: var(--space-5); border-radius: var(--radius-lg); color: var(--text); box-shadow: var(--shadow-pop); }
.import-dialog::backdrop { background: rgba(8, 6, 14, 0.6); backdrop-filter: blur(4px); }
.import-form { display: flex; flex-direction: column; gap: var(--space-4); }
.import-form h2 { margin: 0; font-size: var(--fs-lg); font-weight: var(--fw-medium); }
.import-form textarea { min-height: 12rem; font-size: var(--fs-md); }
.import-meta { display: flex; flex-wrap: wrap; gap: var(--space-3); margin: 0; font-size: var(--fs-xs); color: var(--text-muted); }
.import-meta .dupes { color: var(--lavender); }
.import-actions { display: flex; justify-content: flex-end; gap: var(--space-3); }
.import-actions .neu-btn { width: auto; }

/* ==========================================================================
   Create Groups — one compact toolbar, then a stage: a loose pool of name
   chips that FLIP-animate into group cards
   ========================================================================== */

/* ---------- Compact toolbar ---------- */

/* ==========================================================================
   Schedule page (schedule.html) — overrides admin + bell schedule reference
   ========================================================================== */
.schedule-page { padding: var(--page-pad); }
.schedule-layout {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(20rem, 30rem) 1fr;
  gap: var(--space-5);
  align-items: start;
}
.schedule-layout > .card { display: flex; flex-direction: column; gap: var(--space-4); max-height: 100%; overflow-y: auto; }
.today-line { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2); margin: 0; font-size: var(--fs-sm); color: var(--text-muted); }
.today-line strong { color: var(--text); font-weight: var(--fw-semibold); }
.ov-form { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); align-items: end; }
.ov-form label { display: flex; flex-direction: column; align-items: stretch; gap: var(--space-1); }
.ov-form label select, .ov-form label input { width: 100%; min-width: 0; }
.ov-form input[type="date"] { /* color-scheme inherited from root */ }
.ov-form .actions { grid-column: 1 / -1; display: flex; gap: var(--space-2); }
.ov-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.35rem; }
.ov-row {
  display: grid;
  grid-template-columns: 10.5rem 1fr auto;
  align-items: center;
  gap: var(--space-3);
  padding: 0.55rem 0.8rem;
  background: var(--card-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.ov-row .ov-date { font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; }
.ov-row .ov-what { color: var(--text-2); font-size: var(--fs-sm); }
.ov-row .ov-note { display: block; color: var(--text-muted); font-size: var(--fs-xs); }
.ov-row.past { opacity: 0.45; }
.ov-row.today { border-color: var(--accent); box-shadow: var(--shadow-pressed); }
.ov-row.today .ov-date { color: var(--accent-ink); }
.ov-empty { color: var(--text-muted); font-size: var(--fs-sm); margin: 0; }
.bell-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.bell-head select { min-width: 14rem; }
.bell-hint { color: var(--text-muted); font-size: var(--fs-xs); margin: 0; }
.bell-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.bell-table th { text-align: left; font-weight: var(--fw-medium); color: var(--text-muted); font-size: var(--fs-xs); letter-spacing: 0.06em; text-transform: uppercase; padding: 0.4rem 0.6rem; border-bottom: 1px solid var(--border-strong); }
.bell-table td { padding: 0.55rem 0.6rem; border-bottom: 1px solid var(--border); font-variant-numeric: tabular-nums; }
.bell-table tr.break td { color: var(--text-muted); background: var(--card-2); }
.bell-table td.course { color: var(--text); font-weight: var(--fw-medium); }
.bell-table td.prep { color: var(--text-muted); font-style: italic; }
.bell-table tr.live td { color: var(--text); background: var(--accent-soft); }
.bell-table tr.live td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
@media (max-width: 900px) {
  .schedule-layout { grid-template-columns: 1fr; }
  .schedule-layout > .card { max-height: none; overflow: visible; }
}

/* Reserve the scrollbar space at all times so a page that grows tall enough
   to scroll doesn't narrow the panels sideways as the gutter appears. */
.groups-page { scrollbar-gutter: stable; }

/* ---------- The three panels across the top ---------- */
/* Group size · Roster · Options — three identical boxes: equal columns
   (`1fr` each), equal height (grid stretch), the same padding, titles on one
   line and each panel's controls centred in the space underneath, so nothing
   sits higher or lower than its neighbours. They all hide together in full
   screen (.no-present on the row) so the projector sees only the cards. */
.groups-panels {
  --ctl: 2.2rem;                    /* control height inside a panel */
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}
.gp-panel {
  display: grid;
  grid-template-rows: auto 1fr;     /* title, then the centred body */
  justify-items: center;
  gap: 0.6rem;
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
  box-shadow: var(--shadow-card);
  min-width: 0;
}
.gp-body {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.45rem 0.6rem;
  width: 100%;
  min-width: 0;
}
.gp-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 0.7rem;
  font-weight: var(--fw-semibold);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--label-ink);
}
.gp-panel button, .gp-panel select, .gp-panel input {
  min-height: var(--ctl);
  font-size: var(--fs-xs);
}
.gp-panel select { min-width: 9.5rem; max-width: 100%; padding-right: 2rem; }
.gp-panel input[type="number"] { width: 4.5rem; text-align: center; }
.gp-panel .mode-seg { grid-template-columns: auto auto; padding: 0.15rem; gap: 0.15rem; }
.gp-panel .mode-seg span { padding: 0.32rem 0.7rem; font-size: var(--fs-xs); white-space: nowrap; }
.gp-panel .icon-btn { width: var(--ctl); height: var(--ctl); padding: 0; flex: none; }
.gp-panel .icon-btn svg { width: 1rem; height: 1rem; }
/* the roster line that replaced the old toolbar counts */
.gp-summary {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.82rem;
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
}
.gp-toggle { white-space: nowrap; }

/* Create Groups / Reshuffle: a standalone primary, centred under the panels
   and a sibling of the stage — never inside it. Keeping it out of the stage
   is what stops it drifting when the group cards appear (that was the old
   "button floats at the bottom" bug); its row is content-sized, so the stage
   below still takes all the remaining height. */
.shuffle-row {
  flex: 0 0 auto;
  display: flex;
  justify-content: center;
  margin-bottom: var(--space-4);
}
.shuffle-row .groups-primary {
  min-width: 16rem;
  min-height: 3.3rem;
  padding: 0 2.2rem;
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  letter-spacing: 0.04em;
  border-radius: var(--radius-md);
  box-shadow: 0 6px 20px var(--accent-glow);
}
.shuffle-row .groups-primary:hover:not(:disabled) { transform: translateY(-1px); }

@media (max-width: 900px) {
  .groups-panels { grid-template-columns: 1fr; }
}

/* --- Settings dialog (first names / formula toggle) --- */
dialog.groups-settings-dialog {
  width: min(26rem, 94vw);
  padding: var(--space-5);
  border: 0;
  border-radius: var(--radius-lg);
  background: var(--card);
  color: var(--text);
  box-shadow: var(--shadow-pop);
}
dialog.groups-settings-dialog::backdrop { background: rgba(8, 6, 14, 0.55); backdrop-filter: blur(3px); }
dialog.groups-settings-dialog h3 { margin: 0 0 var(--space-2); font-size: var(--fs-lg); font-weight: var(--fw-bold); font-family: var(--font-body); text-transform: none; letter-spacing: 0; }
dialog.groups-settings-dialog .gs-note { margin: 0 0 var(--space-4); color: var(--text-muted); font-size: 0.85rem; }
.gs-field { display: grid; gap: 0.4rem; padding: var(--space-3) 0; border-top: 1px solid var(--border); }
.gs-field:first-of-type { border-top: 0; padding-top: 0; }
.gs-label { font-weight: var(--fw-semibold); font-size: 0.9rem; color: var(--text-2); }
.gs-hint { color: var(--text-muted); font-size: 0.8rem; line-height: 1.4; margin-left: 3.4rem; }
.gs-actions { display: flex; justify-content: flex-end; margin-top: var(--space-3); }
.gs-actions button { min-height: 2.4rem; padding: 0 var(--space-4); }
/* Pill-switch (First names / Follow Formula) */
.gs-switch {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  background: none;
  border: 0;
  padding: 0.35rem 0;
  cursor: pointer;
  color: var(--text);
  font-weight: var(--fw-semibold);
}
.gs-switch .gs-switch-track {
  position: relative;
  width: 2.6rem;
  height: 1.5rem;
  background: var(--card-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  flex: none;
  transition: background 160ms ease, border-color 160ms ease;
}
.gs-switch .gs-switch-dot {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 1.05rem;
  height: 1.05rem;
  background: var(--text);
  border-radius: 999px;
  transition: transform 180ms cubic-bezier(.34,1.56,.64,1), background 160ms ease;
}
.gs-switch[aria-checked="true"] .gs-switch-track { background: var(--accent-soft); border-color: var(--accent); }
.gs-switch[aria-checked="true"] .gs-switch-dot { transform: translateX(1.1rem); background: var(--accent); }
.gs-switch-label { font-size: 0.95rem; }

/* ---------- Stage ---------- */

/* The stage is only the results now — it takes every pixel under the three
   panels so the group cards are the focus of the page. Before the first
   Create it is empty and the `#empty` hint sits inside that space.
   Nothing in here may get an `overflow`: the chips fly in from the Roster
   panel above and a scroll container would clip them mid-flight. */
.stage {
  flex: 1 1 auto;
  position: relative;
  display: grid;
  grid-template-rows: 1fr;
  min-height: 0;
}

/* Sitting out (grouping only — never an attendance state) */
.name-chip.sitting-out {
  opacity: 0.45;
  text-decoration: line-through;
  border-style: dashed;
  color: var(--text-2);
}

/* Cards flow into as many columns as there are groups (auto-fit collapses the
   tracks it doesn't need), so six groups land on one row rather than two —
   that is what keeps the whole page on one screen at 1280x720. Full screen
   widens the minimum again for projector-sized type. */
.results {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  align-content: start;
  gap: var(--space-4);
}
/* Once there are cards, groups.html sets --cols so every row holds the same
   number of cards (10 groups = 5 + 5, never 6 + 4 or 9 + 1). Cards size to
   their names; grid stretch keeps the cards of one row the same height. */
.stage.has-groups .results { grid-template-columns: repeat(var(--cols, 4), minmax(0, 1fr)); }
/* before the first Create the stage is a big empty board — say what fills it */
.results:empty {
  display: grid;
  place-content: center;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-lg);
}
.results:empty::before {
  content: 'Press Create Groups';
  font-family: var(--font-display);
  font-size: 0.72rem;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-muted);
}
body.present .results:empty { border: 0; }
.stage.no-students .results:empty { display: none; }   /* the #empty line says it instead */

/* One element, two looks: a pill on its own, a big row inside a group */
.name-chip {
  display: inline-flex;
  align-items: center;
  min-height: 2.3rem;
  padding: 0.35rem 0.9rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: var(--card-2);
  color: var(--text);
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
  white-space: nowrap;
  user-select: none;
}
/* Inside a card: centred, no dividers — spacing separates them — with a small
   dot in the group's colour leading each name */
/* Dot + name are one flex row, so the name can never drop below its dot; the
   name is one line, shrunk by fitNames() and ellipsised past that. */
.group .name-chip {
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  z-index: 1;
  min-width: 0;
  min-height: 0;
  padding: 0.15rem 0.4rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  font-size: var(--fs-xl);
  font-weight: var(--fw-semibold);
  white-space: nowrap;
}
.group .chip-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.group .name-chip::before {
  content: "";
  flex: none;
  width: 0.26em;
  height: 0.26em;
  margin-right: 0.35em;
  border-radius: 50%;
  background: var(--g-ink);
  box-shadow: 0 0 0.4em var(--g-glow);
}
/* click a name in a card to sit them out — off while projecting */
body:not(.present) .group .name-chip { cursor: pointer; }
body:not(.present) .group .name-chip:hover { background: var(--card-2); }
.group .name-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.name-chip.flying { position: relative; z-index: 2; will-change: transform; }

/* Group cards fade + scale in just before their names arrive.
   Each group has its own colour (--g): the theme accent turned round the hue
   wheel and lightened / deepened, cycling through ten tones so groups are
   told apart from the back of the room. It shows in the label, the top glow
   line, the border glow, a faint background wash, the name dots and the big
   number watermark. Never an overflow here — the chips fly through. */
.group {
  --g-shift: 0;      /* hue turn, degrees */
  --g-dl: 0;         /* lightness nudge */
  --g: var(--g-pick, var(--accent));
  --g-ink: var(--g-pick, var(--accent));
  --g-glow: var(--accent-glow);
  --g-edge: var(--accent-soft);
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  background: linear-gradient(165deg, color-mix(in srgb, var(--g) var(--group-tint), var(--card)) 0%, var(--card) 72%);
  border: 1px solid var(--g-edge);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card), 0 0 1.4rem -0.4rem var(--g-glow);
  padding: var(--space-3) var(--space-4) var(--space-4);
  transition: opacity 260ms ease, transform 260ms cubic-bezier(0.34, 1.4, 0.64, 1),
              border-color var(--transition), box-shadow var(--transition), filter var(--transition);
}
@supports (color: oklch(from red l c h)) {
  .group {
    --g: var(--g-pick, oklch(from var(--accent) calc(l + var(--g-dl)) c calc(h + var(--g-shift))));
    --g-ink: oklch(from var(--g) calc(l + var(--group-ink-dl)) c h);
    --g-glow: oklch(from var(--g) l c h / 0.4);
    --g-edge: oklch(from var(--g) l c h / 0.38);
  }
}
/* Pink → violet one way, coral → amber the other; never as far as blue.
   The second five reuse the hue band with lighter / deeper steps. */
.group:nth-child(10n+2)  { --g-shift: -60; }                 /* violet */
.group:nth-child(10n+3)  { --g-shift: 45; }                  /* coral */
.group:nth-child(10n+4)  { --g-shift: -30; }                 /* orchid */
.group:nth-child(10n+5)  { --g-shift: 85; }                  /* amber */
.group:nth-child(10n+6)  { --g-shift: -55; --g-dl: 0.12; }   /* lavender */
.group:nth-child(10n+7)  { --g-shift: 25;  --g-dl: 0.12; }   /* salmon */
.group:nth-child(10n+8)  { --g-shift: -40; --g-dl: -0.12; }  /* plum */
.group:nth-child(10n+9)  { --g-shift: 60;  --g-dl: -0.1; }   /* rust */
.group:nth-child(10n+10) { --g-shift: 0;   --g-dl: -0.16; }  /* raspberry */
/* A theme that defines --group-c1…c5 (halloween, thanksgiving) cycles those instead. In
   themes without them --g-pick is invalid, so --g falls back to the shift. */
.group:nth-child(5n+1) { --g-pick: var(--group-c1); }
.group:nth-child(5n+2) { --g-pick: var(--group-c2); }
.group:nth-child(5n+3) { --g-pick: var(--group-c3); }
.group:nth-child(5n+4) { --g-pick: var(--group-c4); }
.group:nth-child(5n+5) { --g-pick: var(--group-c5); }
/* top glow line in the group's colour (overrides the shared card line) */
.group::before {
  left: 14%;
  right: 14%;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--g), transparent);
}
/* the group number, huge and faint, behind the names */
.group::after {
  content: attr(data-n);
  position: absolute;
  right: 0.12em;
  bottom: -0.06em;
  font-family: var(--font-display);
  font-size: 5.5rem;
  font-weight: 700;
  line-height: 1;
  color: var(--g);
  opacity: 0.09;
  pointer-events: none;
  z-index: 0;
}
.group.enter { opacity: 0; transform: scale(0.94); }
.group:hover {
  border-color: var(--g);
  box-shadow: var(--shadow-card-hover), 0 0 1.8rem -0.3rem var(--g-glow);
  filter: brightness(1.08);
}
body:not(.present) .group:not(.enter):hover { transform: translateY(-3px); }
.group-names {
  flex: 1;
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.2rem;
}
.group h3 {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  margin: 0 0 var(--space-2);
  padding: 0.15rem 0.6rem;
  border: 1px solid var(--g-ink);
  border-radius: 0.4rem;
  background: color-mix(in srgb, var(--g) 12%, transparent);
  color: var(--g-ink);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* Shuffle beat before a reshuffle: chips lift and wiggle */
@keyframes chip-beat {
  0%   { transform: translateY(0) rotate(0); }
  30%  { transform: translateY(-0.35rem) rotate(-2deg); }
  60%  { transform: translateY(-0.2rem) rotate(2deg); }
  100% { transform: translateY(0) rotate(0); }
}
.stage.shuffling .name-chip { animation: chip-beat 320ms ease-in-out; }
.stage.shuffling .name-chip:nth-child(even) { animation-delay: 40ms; }
.stage.shuffling .name-chip:nth-child(3n) { animation-delay: 80ms; }

.pick-one {
  flex: 1;
  min-height: 0;
  display: grid;
  place-items: center;
  text-align: center;
  font-size: var(--fs-hero);
  font-weight: var(--fw-bold);
  line-height: 1.05;
  overflow-wrap: anywhere;
}

/* Attendance panel (dialog) */
.attendance-dialog { width: min(36rem, 92vw); max-height: 85dvh; box-shadow: var(--shadow-pop); }
.attendance-dialog::backdrop { background: rgba(8, 6, 14, 0.6); backdrop-filter: blur(4px); }
.attendance-form { display: flex; flex-direction: column; gap: var(--space-4); max-height: calc(85dvh - 2 * var(--space-5)); }
.attendance-form > .hint { margin: 0; }
.attendance-list:focus-visible { outline: none; }
.attendance-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
  gap: var(--space-2);
  padding: 2px;
}
.attend {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  min-height: var(--touch);
  padding: 0 0.85rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--card-2);
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition), opacity var(--transition);
}
.attend:hover { border-color: var(--border-hover); }
.attend input { accent-color: var(--accent); width: 1.05rem; height: 1.05rem; margin: 0; flex: none; }
/* the checkbox + name are one label (click anywhere to toggle absent); the
   Sit out button sits outside it so it doesn't flip attendance too */
.attend-main { display: flex; align-items: center; gap: 0.7rem; flex: 1; min-width: 0; cursor: pointer; }
.attend-name { flex: 1; min-width: 0; font-weight: var(--fw-medium); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.attend-state { font-size: var(--fs-xs); color: var(--ok); flex: none; }
.attend.is-absent { border-color: rgba(255, 122, 138, 0.35); }
.attend.is-absent .attend-name { color: var(--text-muted); text-decoration: line-through; }
.attend.is-absent .attend-state { color: var(--danger); }
/* sitting out: here today, just not in the grouping */
.attend.is-sitout { border-color: var(--border-hover); background: var(--card-3); }
.attend.is-sitout .attend-name { color: var(--text-muted); }
.attend.is-sitout .attend-state { color: var(--text-2); }
.attend-sitout {
  flex: none;
  padding: 0.2rem 0.5rem;
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--card);
  color: var(--text-muted);
}
.attend-sitout:hover:not(:disabled) { color: var(--text); border-color: var(--border-hover); }
.attend-sitout[aria-pressed="true"] { color: var(--text); border-color: var(--lavender); background: var(--lavender-soft); }
.attend-sitout:disabled { opacity: 0.35; cursor: not-allowed; }
.attendance-actions { display: flex; align-items: center; gap: var(--space-3); }
.attendance-actions .spacer { flex: 1; }

@media (prefers-reduced-motion: reduce) {
  .stage.shuffling .name-chip { animation: none; }
  .group { transition: opacity 160ms ease; }
  .group.enter { transform: none; }
  body:not(.present) .group:not(.enter):hover { transform: none; }
}

/* ==========================================================================
   Present / projector mode — active tool gets ~95% of the screen
   ========================================================================== */

/* ==========================================================================
   Full screen — body.present is the big-screen layout on every page (see
   shared.js). Chrome goes, the content gets the screen, type and borders
   scale up for distance viewing.
   ========================================================================== */
html:has(body.present) { font-size: clamp(16px, 0.72vw + 0.6vh, 26px); }
body.present .no-present { display: none !important; }
.present-only[hidden] { display: none !important; }
body.present .card, body.present .group, body.present .tile, body.present .hud-box, body.present .student { border-width: 2px; }
body.present .hud-bar { min-height: 3.2rem; }
body.present .hud-stat, body.present .hud-val { font-size: 1.05rem; }
body.present .hud-key { font-size: 0.72rem; }
body.present .hud-brand { font-size: 0.85rem; }
/* floating controls (injected by shared.js), only in big-screen mode */
.fs-float { display: none; }
body.present .fs-float {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 70;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  opacity: 0.55;
  transition: opacity 150ms ease;
}
body.present .fs-float:hover, body.present .fs-float:focus-within { opacity: 1; }
.fs-float button {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-height: 2.2rem;
  padding: 0.3rem 0.8rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background: var(--card);
  border: 1px solid var(--border-strong);
}
.fs-float button svg { width: 1rem; height: 1rem; }
.fs-float .fs-hint { font-family: var(--font-mono); font-size: 0.74rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent-ink); padding-right: 0.4rem; }
/* Nav: the rail slides back in over the content while the button is pressed */

@media (prefers-reduced-motion: reduce) { .fs-float { transition: none; } }

body.present .topnav .nav-links { display: none; }
body.present .app { grid-template-columns: 1fr; }
body.present main.page { grid-column: 1; }
body.present header.topbar { min-height: 2.8rem; padding: var(--space-1) var(--page-pad); }
/* one card per group on a single row wherever it fits — six groups across a
   1280px projector still beats two rows the class has to scroll past */
body.present .results { grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: var(--space-5); }
body.present .group .name-chip { font-size: var(--fs-2xl); padding: 0.2rem 0.5rem; }
body.present .group::after { font-size: 7.5rem; }
body.present .group h3 { font-size: var(--fs-sm); }

/* Roster present mode */
body.present .roster-layout { grid-template-columns: 1fr; }
body.present .list-head .search, body.present .list-head .more, body.present .card-head .corner-btn { display: none; }
body.present .name-list { columns: 22rem; column-gap: 1.25rem; }
body.present .student { height: 4.5rem; margin-bottom: 0.75rem; }
body.present .student .num, body.present .student .divider { display: none; }
body.present .student .name { font-size: var(--fs-xl); font-weight: var(--fw-bold); }
body.present .student .remove { display: none !important; }

/* ==========================================================================
   Seating chart — one bar (period · mode · the mode's few buttons · ? · ⋯),
   arrangement cards + Active / Draft banner, then the room: a slim desk
   rail (Arrange) or a "Not seated yet" strip (Assign) around the canvas
   ========================================================================== */

.seat-bar {
  --ctl: 2.1rem;
  position: relative;
  z-index: 6;                     /* the ⋯ menu drops over the cards and canvas */
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.4rem 0.6rem;
  padding: 0.35rem 0.6rem;
  border-radius: var(--radius-md);
  margin-bottom: var(--space-2);
}
.seat-bar::before { display: none; }
.seat-bar button, .seat-bar select, .seat-bar .chip {
  min-height: var(--ctl);
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  font-size: var(--fs-sm);
}
.seat-bar select { min-width: 9.5rem; padding-right: 2rem; }
.seat-bar .bar-field { display: inline-flex; align-items: center; gap: 0.45rem; }
.seat-bar .bar-field > .lbl { white-space: nowrap; color: var(--text-muted); font-size: var(--fs-xs); }
/* Arrange / Assign: both halves read as options; the checked one takes the app-wide pressed fill */
.seat-bar .mode-seg { grid-template-columns: auto auto; padding: 0.15rem; gap: 0.15rem; }
.seat-bar .mode-seg span { padding: 0.35rem 0.9rem; font-size: var(--fs-sm); white-space: nowrap; color: var(--text-2); border-color: var(--border); background: var(--card); }
.seat-bar .mode-seg label:hover input:not(:checked) + span { color: var(--text); border-color: var(--border-hover); background: var(--card-3); }
/* one primary action + its ▾ menu: the bar keeps the same shape in both modes */
.seat-bar .split { position: relative; display: inline-flex; align-items: stretch; }
.seat-bar .split-main { min-width: 8.4rem; border-top-right-radius: 0; border-bottom-right-radius: 0; }
.seat-bar .split-caret { width: 2rem; padding: 0; border-top-left-radius: 0; border-bottom-left-radius: 0; margin-left: -1px; }
.seat-bar .split-caret svg { width: 0.95rem; height: 0.95rem; transition: transform var(--transition); }
.seat-bar .split-caret[aria-expanded="true"] svg { transform: rotate(180deg); }
.action-menu { left: 0; right: auto; width: 15rem; }
.action-menu .am-group { display: grid; gap: 0.35rem; }
.action-menu .am-group[hidden] { display: none; }
.mm-sep { height: 1px; margin: 0.15rem 0; background: var(--border); }
/* ? · ⋯ · full screen: one quiet cluster */
.seat-bar .bar-cluster { display: inline-flex; align-items: center; gap: 2px; padding: 2px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--card-2); }
.seat-bar .bar-cluster .icon-btn { width: 1.95rem; height: 1.95rem; min-height: 0; border-color: transparent; background: transparent; }
.seat-bar .bar-cluster .icon-btn:hover { border-color: var(--border-strong); background: var(--card-3); }
.seat-bar .icon-btn { width: var(--ctl); height: var(--ctl); padding: 0; }
.seat-bar .icon-btn svg { width: 1.05rem; height: 1.05rem; }
.seat-bar .spacer { flex: 1; }
.seat-bar .help-btn { font-family: var(--font-mono); font-size: var(--fs-md); color: var(--text-2); }
.save-state { font-size: var(--fs-xs); color: var(--text-muted); white-space: nowrap; }
.save-state.ok::before { content: "● "; color: var(--ok); }
.save-state.bad { color: var(--danger); }

/* ⋯ menu: undo/redo, view, the selected desk's actions, first names */
.more-wrap { position: relative; }
#btn-more[aria-expanded="true"] { border-color: var(--accent); color: var(--text); }
.more-menu {
  position: absolute;
  top: calc(100% + 0.4rem);
  right: 0;
  width: 17rem;
  display: grid;
  gap: 0.35rem;
  padding: 0.6rem;
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-pop);
}
.more-menu[hidden] { display: none; }
.mm-label { margin: 0.35rem 0.2rem 0; color: var(--text-muted); font-size: var(--fs-xs); }
.mm-row { display: flex; align-items: center; gap: 0.35rem; }
.mm-row > .mm-item { flex: 1; justify-content: center; }
.more-menu .mm-item {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  width: 100%;
  min-height: 2.1rem;
  padding: 0.3rem 0.6rem;
  font-size: var(--fs-sm);
  justify-content: flex-start;
}
.more-menu .mm-item svg { width: 1rem; height: 1rem; flex: none; }
.more-menu .icon-btn { width: 2.1rem; height: 2.1rem; min-height: 0; padding: 0; }
.more-menu .icon-btn svg { width: 1rem; height: 1rem; }
.more-menu .zoom-pct { min-width: 3.2rem; text-align: center; font-size: var(--fs-xs); color: var(--text-2); font-variant-numeric: tabular-nums; }
.more-menu .mm-fit { flex: 1; min-height: 2.1rem; padding: 0.3rem 0.6rem; font-size: var(--fs-sm); }

/* Arrangements: one compact row — a card per arrangement (thumbnail of its
   room, name, ACTIVE / DRAFT tag, icon stats), then + New. No sentences. */
.arr-bar {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.2rem 0.35rem;
  border-radius: var(--radius-md);
  margin-bottom: var(--space-2);
}
.arr-bar::before { display: none; }
.arr-cards { flex: 0 1 auto; min-width: 0; display: flex; gap: 0.3rem; overflow-x: auto; padding: 0.1rem; scrollbar-width: thin; }
.arr-none { margin: 0; padding: 0 0.4rem; color: var(--text-muted); font-size: var(--fs-sm); }
.arr-card {
  flex: none;
  display: flex;
  align-items: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--card-2);
  transition: border-color var(--transition), background var(--transition);
}
.arr-card:hover { border-color: var(--border-hover); background: var(--card-3); }
.arr-card.current { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent), 0 0 0.9rem -0.3rem var(--accent-glow); background: var(--card-3); }
.arr-card .arr-card-main {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 0;
  padding: 0.15rem 0.55rem 0.15rem 0.2rem;
  border: 0;
  border-radius: inherit;
  background: transparent;
  text-align: left;
}
/* the open card is shown by its outline, not by the app-wide pressed fill */
.arr-card .arr-card-main[aria-pressed="true"] { background: transparent; box-shadow: none; border: 0; }
.arr-thumb {
  flex: none;
  display: grid;
  place-items: center;
  width: 3.05rem;
  height: 1.95rem;
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  background: var(--card);
}
.arr-card.current .arr-thumb { border-color: var(--border-strong); }
.arr-card-text { display: grid; gap: 0.15rem; min-width: 0; }
.arr-card-top { display: flex; align-items: center; gap: 0.45rem; }
.arr-card-name { max-width: 10rem; line-height: 1.1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); font-size: var(--fs-sm); font-weight: var(--fw-semibold); line-height: 1.15; }
.arr-tag {
  flex: none;
  padding: 0.05rem 0.35rem;
  border: 1px solid var(--lavender);
  border-radius: 0.3rem;
  color: var(--lavender);
  font-family: var(--font-display);
  font-size: 0.5rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  line-height: 1.5;
  text-transform: uppercase;
}
.arr-tag.on { border-color: var(--accent); color: var(--accent-ink); background: var(--accent-soft); }
.arr-card-stats { display: flex; gap: 0.65rem; color: var(--text-muted); font-family: var(--font-mono); font-size: 0.72rem; font-variant-numeric: tabular-nums; }
.arr-card-stats > span { display: inline-flex; align-items: center; gap: 0.22rem; }
.arr-card-stats svg { width: 0.72rem; height: 0.72rem; }
.arr-card-stats .short { color: var(--text-2); }
.arr-card-tools { display: flex; align-items: center; gap: 0.05rem; padding-right: 0.3rem; }
.arr-card-tools .icon-btn { width: 1.6rem; height: 1.6rem; min-height: 0; padding: 0; border-color: transparent; background: transparent; }
.arr-card-tools .icon-btn:hover:not(:disabled) { border-color: var(--border-strong); background: var(--card-2); }
.arr-card-tools .icon-btn svg { width: 0.9rem; height: 0.9rem; }
.arr-make-active {
  min-height: 1.75rem;
  margin-right: 0.25rem;
  padding: 0.1rem 0.65rem;
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--text);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  white-space: nowrap;
}
.arr-make-active:hover { background: var(--accent); }
.arr-new-btn { flex: none; min-height: 2.15rem; padding: 0.2rem 0.85rem; font-size: var(--fs-sm); font-weight: var(--fw-semibold); white-space: nowrap; border-style: dashed; }
.arr-new-btn:hover:not(:disabled) { border-style: solid; border-color: var(--accent); }

/* a tiny drawing of a room (arrangement thumbnails, starter layouts) */
.room-pic { position: relative; display: block; }
.room-pic i { position: absolute; box-sizing: border-box; border: 1px solid var(--text-muted); border-radius: 1px; background: var(--surface-tint); }
.room-pic i.f { border: 0; background: var(--accent); opacity: 0.8; }
.room-pic i.t { border-style: dashed; background: transparent; }
.arr-thumb .room-pic i { border-width: 0.5px; border-radius: 0.5px; }

/* Small modal used for names, deletes, New arrangement, Change layout, ? */
.arr-dialog { border: 0; padding: 0; background: transparent; max-width: none; }
.arr-dialog::backdrop { background: rgba(8, 6, 14, 0.7); backdrop-filter: blur(6px); }
.arr-dialog-inner { width: min(34rem, 92vw); padding: var(--space-5); background: var(--card); border: 1px solid var(--border-strong); border-radius: var(--radius-lg); box-shadow: var(--shadow-pop); color: var(--text); }
.arr-dialog-inner h3 { margin: 0 0 var(--space-3); font-size: var(--fs-lg); font-weight: var(--fw-semibold); }
.arr-dialog-body { color: var(--text-2); font-size: var(--fs-sm); }
.arr-dialog-body p { margin: 0 0 var(--space-3); }
.arr-dialog-body ul { margin: 0 0 var(--space-3); padding-left: 1.2rem; display: grid; gap: 0.4rem; }
.arr-dialog-body b { color: var(--text); }
.arr-dialog-warn { padding: 0.55rem 0.8rem; border-left: 3px solid var(--lavender); border-radius: var(--radius-sm); background: var(--card-2); color: var(--text-2); }
.arr-dialog-inner input { width: 100%; margin: 0 0 var(--space-4); font-size: var(--fs-md); }
.arr-dialog-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-2); }
.arr-dialog-actions button { min-height: 2.4rem; padding: 0 var(--space-4); }

/* ? — tips and shortcuts: header + close X, then one scrolling column of
   key → action rows. Mouse actions are plain text, keys are kbd chips. */
.help-dialog .help-inner { width: min(36rem, 94vw); max-height: min(44rem, 88vh); padding: 0; display: flex; flex-direction: column; overflow: hidden; }
.help-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); padding: var(--space-4) var(--space-5) var(--space-4) var(--space-6); border-bottom: 1px solid var(--border); }
.help-head h2 { margin: 0; color: var(--text); }
.help-body { flex: 1; min-height: 0; overflow-y: auto; padding: var(--space-2) var(--space-6) var(--space-6); scrollbar-width: thin; }
.help-part { display: flex; align-items: center; gap: 0.5rem; margin: var(--space-5) 0 0; color: var(--accent-ink); font-size: var(--fs-xs); font-weight: var(--fw-semibold); text-transform: uppercase; letter-spacing: 0.14em; }
.help-part svg { width: 1.05rem; height: 1.05rem; flex: none; }
.help-sec { margin-top: var(--space-4); padding-top: var(--space-3); border-top: 1px solid var(--border); }
.help-part + .help-sec { margin-top: var(--space-2); }
.help-sec h3 { margin: 0 0 var(--space-3); color: var(--text-muted); font-size: var(--fs-xs); font-weight: var(--fw-semibold); text-transform: uppercase; letter-spacing: 0.1em; }
.help-list { display: grid; grid-template-columns: 12.5rem minmax(0, 1fr); gap: 0.55rem var(--space-4); align-items: center; margin: 0; font-size: var(--fs-sm); }
.help-list dt { display: flex; align-items: center; gap: 0.3rem; color: var(--text); font-weight: var(--fw-semibold); white-space: nowrap; }
.help-list dd { margin: 0; color: var(--text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.help-list kbd {
  display: inline-flex; align-items: center; justify-content: center;
  box-sizing: border-box; height: 1.6rem; min-width: 1.6rem; padding: 0 0.45rem;
  border: 1px solid var(--border-strong); border-bottom-width: 2px; border-radius: 0.35rem;
  background: var(--card-2); color: var(--text);
  font-family: var(--font-mono); font-size: 0.78rem; font-weight: 400; line-height: 1;
}
@media (max-width: 600px) {
  .help-head { padding: var(--space-3) var(--space-4); }
  .help-body { padding: var(--space-1) var(--space-4) var(--space-5); }
  .help-list { grid-template-columns: 1fr; gap: 0.15rem; }
  .help-list dd { white-space: normal; margin-bottom: 0.45rem; }
}

/* The room area: the left column (desk rail in Arrange, Not seated list in
   Assign) beside the canvas */
.seat-layout {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  gap: var(--space-3);
}
.seat-layout > .viewport { grid-column: 2; grid-row: 1; }
.seat-layout > .desk-rail, .seat-layout > .names-panel { grid-column: 1; grid-row: 1; }

/* Desk rail: icons only, the name shows on hover */
.desk-rail {
  grid-column: 1;
  grid-row: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 0.45rem;
  border-radius: var(--radius-md);
  overflow: visible;
}
.desk-rail::before { display: none; }
.rail-title { color: var(--text-muted); font-size: var(--fs-xs); }
.palette-list { display: flex; flex-direction: column; gap: 0.4rem; }
.palette-item {
  position: relative;
  display: grid;
  place-items: center;
  width: 3.4rem;
  height: 3.1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--card-2);
  cursor: grab;
  touch-action: none;
  user-select: none;
  transition: border-color var(--transition), background var(--transition);
}
.palette-item:hover, .palette-item:focus-visible { border-color: var(--accent); background: var(--card-3); }
.palette-item:active { cursor: grabbing; }
/* name label beside the icon on hover / focus */
.palette-item::after {
  content: attr(data-name);
  position: absolute;
  left: calc(100% + 0.55rem);
  top: 50%;
  transform: translateY(-50%);
  padding: 0.25rem 0.6rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--card);
  box-shadow: var(--shadow-pop);
  color: var(--text);
  font-size: var(--fs-sm);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 120ms ease;
  z-index: 8;
}
.palette-item:hover::after, .palette-item:focus-visible::after { opacity: 1; }
.mini {
  position: relative;
  flex: none;
  width: calc(var(--w) * 0.3rem);
  height: calc(var(--h) * 0.3rem);
  max-width: 2.7rem;
}
.mini span { position: absolute; box-sizing: border-box; border: 1px solid var(--text-muted); border-radius: 2px; background: var(--surface-tint); }
.mini.is-round span { border-radius: 999px; }
.mini-round { position: absolute; inset: 24% 28%; border-radius: 50%; border: 1px solid var(--text-muted); }
.palette-item:hover .mini span, .palette-item:hover .mini-round { border-color: var(--text-2); }

/* Not seated (Assign): names down the left, seated count at the top */
.names-panel {
  width: 13rem;
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 0.55rem 0.45rem 0.45rem;
  border-radius: var(--radius-md);
  transition: border-color var(--transition);
}
.names-panel::before { display: none; }
.names-panel[hidden] { display: none; }
/* a seated name is picked: click the list to take them out */
.names-panel.drop-out { border-style: dashed; border-color: var(--accent); cursor: pointer; }
.names-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  margin: 0 0.25rem 0.45rem;
  padding-bottom: 0.45rem;
  border-bottom: 1px solid var(--border);
}
.names-title { color: var(--text); font-size: var(--fs-sm); font-weight: var(--fw-semibold); }
.names-count { color: var(--text-2); font-family: var(--font-mono); font-size: var(--fs-xs); white-space: nowrap; font-variant-numeric: tabular-nums; }
.names-empty { margin: 0.3rem 0.25rem; color: var(--text-muted); font-size: var(--fs-sm); }
.names-panel .student-list { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 0.3rem; padding: 0.15rem; scrollbar-width: thin; }
.student-chip {
  display: block;
  flex: none;
  min-height: 2.05rem;
  padding: 0.35rem 0.7rem;
  border: 1px solid var(--border-strong);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--card-2);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  line-height: 1.25;
  cursor: grab;
  touch-action: none;
  user-select: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.student-chip:hover { background: var(--card-3); border-color: var(--border-hover); border-left-color: var(--accent); }
.student-chip:active { cursor: grabbing; }
.student-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* clicked, waiting for a seat */
.student-chip.picked { background: var(--accent-soft); box-shadow: 0 0 0 2px var(--accent), 0 0 0.9rem -0.2rem var(--accent-glow); }
.seat.picked { box-shadow: 0 0 0 2px var(--accent), 0 0 0.9rem -0.1rem var(--accent-glow); }

/* Floating Turn / Copy / Remove beside the selected desk */
.piece-tools {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 4;
  display: flex;
  gap: 0.25rem;
  padding: 0.25rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--card);
  box-shadow: var(--shadow-pop);
  pointer-events: auto;
  cursor: default;
}
.piece-tools[hidden] { display: none; }
.piece-tools button {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  min-height: 2rem;
  padding: 0.2rem 0.65rem;
  font-size: var(--fs-sm);
  border-color: transparent;
  background: transparent;
}
.piece-tools button:hover { border-color: var(--border-strong); background: var(--card-2); }
.piece-tools button[hidden] { display: none; }
.piece-tools svg { width: 0.95rem; height: 0.95rem; }
#pt-delete:hover { color: var(--danger); border-color: var(--danger); }

/* Starter layouts over an empty room (or after Change layout) */
.starter {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: grid;
  place-items: center;
  padding: var(--space-4);
  overflow-y: auto;
  background: color-mix(in srgb, var(--card) 55%, transparent);
  backdrop-filter: blur(2px);
  pointer-events: auto;
  cursor: default;
}
.starter[hidden] { display: none; }
.starter-card {
  width: min(52rem, 100%);
  padding: var(--space-5);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  background: var(--card);
  box-shadow: var(--shadow-pop);
  text-align: center;
}
.starter-card h3 { margin: 0 0 0.3rem; font-size: var(--fs-xl); font-weight: var(--fw-semibold); }
.starter-sub { margin: 0 0 var(--space-4); color: var(--text-2); font-size: var(--fs-sm); }
.starter-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr)); gap: var(--space-3); }
.starter-opt {
  display: grid;
  justify-items: center;
  align-content: start;
  gap: 0.35rem;
  padding: 0.8rem 0.6rem 0.7rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--card-2);
}
.starter-opt:hover, .starter-opt:focus-visible { border-color: var(--accent); background: var(--card-3); box-shadow: 0 0 1rem -0.3rem var(--accent-glow); }
.starter-pic { margin: 0 auto 0.3rem; }
.starter-name { color: var(--text); font-size: var(--fs-md); font-weight: var(--fw-semibold); }
.starter-note { color: var(--text-muted); font-size: var(--fs-xs); font-family: var(--font-mono); }
.starter-or { margin: var(--space-4) 0 0; color: var(--text-muted); font-size: var(--fs-sm); }
.starter-or[hidden] { display: none; }
#starter-cancel { margin-top: var(--space-3); }
#starter-cancel[hidden] { display: none; }

/* Canvas */
.viewport {
  position: relative;
  overflow: hidden;
  padding: 0;
  min-height: 0;
  touch-action: none;
  cursor: default;
  outline: none;
}
.viewport { cursor: grab; }
.viewport.pan-mode, .viewport.panning { cursor: grab; }
.viewport.panning { cursor: grabbing; }
.viewport.assign { cursor: grab; }
.viewport:focus-visible { box-shadow: 0 0 0 2px var(--ring); }
/* The viewport paints the dot grid (so it fills the panel at every zoom/pan);
   .room is just a zero-size origin that pieces hang off, in any direction. */
.viewport {
  background-color: var(--room-floor);
  background-image: radial-gradient(var(--canvas-dot) 1px, transparent 1.2px);
}
.room {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 0;
  overflow: visible;
  transform-origin: 0 0;
}

/* Pieces sit at 0,0 and are placed with translate3d() + rotate() — composited,
   no layout work while dragging. .lifting = being dragged (instant, no
   transition); .settling = the 120ms ease into the snapped spot on release. */
.piece { position: absolute; left: 0; top: 0; transform-origin: center; touch-action: none; user-select: none; will-change: transform; }
.piece.lifting { z-index: 3; filter: brightness(1.18); }
.piece.settling { transition: transform 120ms ease; }
.snap-ghost {
  position: absolute;
  left: 0;
  top: 0;
  transform-origin: center;
  border: 1px dashed var(--accent-line-strong);
  border-radius: 4px;
  background: var(--accent-fill-soft);
  pointer-events: none;
  z-index: 2;
}
.snap-ghost.front { border-radius: 4px; }
.viewport:not(.assign) .piece { cursor: move; }
.piece .seat, .piece .desk-block {
  position: absolute;
  box-sizing: border-box;
  border: 1px solid var(--seat-empty-line);
  border-radius: 4px;
  background: var(--seat-empty-bg);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.piece .desk-block { background: var(--card-3); border-color: var(--border-strong); }
.piece.is-round .seat { border-radius: 999px; }
.table-top { position: absolute; inset: 22% 26%; border-radius: 50%; border: 1px solid var(--border-strong); background: var(--card-3); }
.seat-label {
  position: absolute;
  left: 50%;
  top: 50%;
  display: block;
  padding: 0 2px;
  font-size: 10px;
  line-height: 1.15;
  font-weight: var(--fw-semibold);
  color: var(--text);
  text-align: center;
  white-space: normal;
  overflow-wrap: normal;          /* never split a name mid-word; fitSeatLabel shrinks it instead */
  word-break: normal;
  hyphens: none;
}
.seat-label.clip { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.desk-block .seat-label { color: var(--text-muted); font-weight: var(--fw-medium); }
.seat.taken { background: var(--seat-bg); border-color: var(--seat-line); }
.viewport.assign .seat { cursor: grab; }
.viewport.assign .seat:not(.taken) { cursor: default; }
.seat.drop-target { border-color: var(--accent); background: var(--accent-soft); box-shadow: 0 0 0 2px var(--ring); }
.piece.selected .seat, .piece.selected .desk-block, .piece.selected .table-top { border-color: var(--accent); }
.piece.selected::after { content: ""; position: absolute; inset: -4px; border: 1px dashed var(--accent); border-radius: 6px; pointer-events: none; }

.piece.front {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--accent-deep);
  border-radius: 4px;
  background: var(--accent-soft);
}
.piece.front.selected { box-shadow: 0 0 0 2px var(--ring); }
.front-text { font-size: 10px; font-weight: var(--fw-semibold); letter-spacing: 0.18em; text-transform: uppercase; color: var(--accent-ink); white-space: nowrap; }
/* Facing notch: a small triangle on the room side of the marker (its local
   bottom edge), so it's clear which way the front faces once it's turned */
.front-face {
  position: absolute;
  left: 50%;
  top: 100%;
  width: 0;
  height: 0;
  margin-left: -6px;
  border: 6px solid transparent;
  border-top-color: var(--accent-deep);
  border-bottom-width: 0;
  pointer-events: none;
}

/* Rotation gizmo: handle on the selection's local "up", ring + angle while rotating */
.rot-line { position: absolute; width: 1px; background: var(--accent); opacity: 0.7; transform-origin: bottom center; pointer-events: none; }
.rot-handle {
  position: absolute;
  width: 14px;
  height: 14px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 2px solid var(--accent);
  background: var(--card);
  cursor: grab;
  touch-action: none;
  z-index: 4;
  box-shadow: 0 0 0 4px var(--accent-ring-soft);
}
.rot-handle:hover, .viewport.rotating .rot-handle { background: var(--accent); }
.rot-ring { position: absolute; transform: translate(-50%, -50%); border-radius: 50%; border: 1px dashed var(--accent-line); pointer-events: none; }
.rot-label {
  position: absolute;
  transform: translate(-50%, -100%);
  padding: 2px 6px;
  border-radius: 6px;
  background: var(--card-3);
  border: 1px solid var(--border-strong);
  color: var(--text);
  font-size: 11px;
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
  pointer-events: none;
  white-space: nowrap;
}
.viewport.rotating { cursor: crosshair; }
.viewport.rotating .piece { cursor: crosshair; }
.marquee { position: absolute; left: 0; top: 0; border: 1px solid var(--accent); background: var(--accent-fill-soft); pointer-events: none; z-index: 5; }
.drag-ghost {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 50;
  will-change: transform;
  padding: 0.3rem 0.7rem;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--card-3);
  color: var(--text);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  pointer-events: none;
  box-shadow: var(--shadow-pop);
}
.drag-ghost.name { border-radius: var(--radius-pill); }
.empty-room {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  color: var(--text-muted);
  font-size: var(--fs-md);
  pointer-events: none;
}

/* Absent today (from Create Groups' Edit Roster): dimmed, skipped by Randomize */
.student-chip.absent { opacity: 0.35; text-decoration: line-through; }

/* On/off toggle that reads from across the room: outlined when off, filled accent when on */
.toggle { border-color: var(--accent-deep); background: transparent; gap: 0.5rem; }
.toggle .toggle-dot { width: 0.55rem; height: 0.55rem; border-radius: 50%; background: transparent; border: 1.5px solid var(--accent); flex: none; }
.toggle[aria-checked="true"], .toggle[aria-checked="true"]:hover {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  box-shadow: 0 0 16px var(--accent-glow);
}
.toggle[aria-checked="true"] .toggle-dot { background: var(--on-accent); border-color: var(--on-accent); }

/* Formula editor: large centred modal, fade + scale in/out */
.formula-dialog {
  /* The dialog is the full-screen top-layer box; the modal is centred inside it */
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  max-width: none;               /* the browser's own dialog max-width/height would cap it */
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  overflow: visible;
  display: none;
  align-items: center;
  justify-content: center;
}
.formula-dialog[open] { display: flex; }
.formula-dialog::backdrop { background: rgba(8, 6, 14, 0.7); backdrop-filter: blur(6px); }
@keyframes modal-in { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: none; } }
@keyframes modal-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: scale(0.96); } }
@keyframes backdrop-in { from { opacity: 0; } to { opacity: 1; } }
.formula-dialog[open] .formula-inner { animation: modal-in 200ms cubic-bezier(0.2, 0.8, 0.2, 1); }
.formula-dialog[open]::backdrop { animation: backdrop-in 200ms ease; }
.formula-dialog.closing .formula-inner { animation: modal-out 170ms ease forwards; }
.formula-dialog.closing::backdrop { opacity: 0; transition: opacity 170ms ease; }
.formula-inner:focus { outline: none; }
.formula-inner {
  width: 90vw;
  height: 90vh;
  min-width: min(96vw, 56rem);   /* small laptops: keep the list + map usable */
  min-height: min(94vh, 34rem);
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-pop);
  overflow: hidden;
}
.formula-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-5);
  border-bottom: 1px solid var(--border);
}
.formula-header h2 { margin: 0; font-size: 1.8rem; font-weight: var(--fw-semibold); letter-spacing: -0.02em; }
.formula-sub { margin: 0.2rem 0 0; color: var(--text-muted); font-size: var(--fs-md); }
.formula-warnings { margin: var(--space-3) var(--space-5) 0; font-size: var(--fs-sm); padding: 0.6rem 1rem; border: 1px solid rgba(255, 122, 138, 0.5); border-radius: var(--radius-sm); background: rgba(255, 122, 138, 0.06); color: var(--danger); font-size: var(--fs-xs); display: grid; gap: 0.25rem; }
.formula-body {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: clamp(17rem, 22vw, 25rem) minmax(0, 1fr);
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
}
.formula-list:focus-visible { outline: none; }
.formula-roster { display: flex; flex-direction: column; gap: var(--space-2); min-height: 0; }
.fr-head { display: flex; align-items: center; gap: var(--space-2); }
.formula-roster .search { flex: 1; min-width: 0; max-width: none; margin: 0; min-height: 2.3rem; font-size: var(--fs-sm); }
.fr-count { flex: none; color: var(--text-muted); font-family: var(--font-mono); font-size: var(--fs-xs); white-space: nowrap; }
.formula-list { flex: 1; min-height: 0; overflow-y: auto; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.25rem; align-content: start; padding: 2px; scrollbar-width: thin; }
.formula-list > .hint { grid-column: 1 / -1; }
.formula-student { display: flex; align-items: center; justify-content: space-between; gap: 0.3rem; min-width: 0; min-height: 1.95rem; padding: 0.1rem 0.45rem 0.1rem 0.55rem; border-color: var(--border); border-left-width: 3px; border-radius: var(--radius-sm); font-size: var(--fs-sm); text-align: left; box-shadow: none; cursor: grab; user-select: none; touch-action: pan-y; }
.formula-student:hover { border-color: var(--border-hover); }
.formula-student.on-map { border-left-color: var(--accent); }
.formula-student.active { background: var(--gradient-accent), var(--card-2); border-color: var(--accent); }
.formula-student.absent .fs-name { opacity: 0.5; text-decoration: line-through; }
.fs-badge { flex: none; min-width: 1.25rem; padding: 0 0.3rem; border: 1px solid var(--accent); border-radius: var(--radius-pill); color: var(--text); font-family: var(--font-mono); font-size: 0.7rem; line-height: 1.35; text-align: center; }
.fr-hint { margin: 0; color: var(--text-muted); font-size: var(--fs-xs); }
.fs-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.fd-rule {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: var(--space-4);
  padding: 1rem 1.25rem;
  border: 1px solid var(--border);
  border-left: 4px solid var(--rule-color, var(--text-muted));
  border-radius: var(--radius-sm);
  background: var(--card);
  font-size: var(--fs-md);
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition);
}
.fd-rule:hover { border-color: var(--border-hover); border-left-color: var(--rule-color, var(--text-muted)); }
.fd-rule.active { border-color: var(--accent); background: var(--gradient-accent), var(--card); }
.fd-rule:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.fd-rule-type { font-size: 0.75rem; font-weight: var(--fw-semibold); letter-spacing: 0.06em; text-transform: uppercase; color: var(--rule-color, var(--text-muted)); white-space: nowrap; }
.fd-rule-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fd-remove { width: 2.2rem; height: 2.2rem; min-height: 0; padding: 0; border-radius: 50%; color: var(--text-muted); font-size: 1.05rem; line-height: 1; box-shadow: none; }
.fd-remove:hover { color: var(--danger); border-color: var(--danger); }
.fd-rule.is-hard { border-color: var(--surface-tint-formula); }
.prio-toggle {
  min-height: 1.9rem; padding: 0.15rem 0.7rem; border-radius: var(--radius-pill);
  font-size: 0.72rem; font-weight: var(--fw-semibold); letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--text-muted); border-color: var(--border-strong); background: var(--card-2); box-shadow: none; white-space: nowrap;
}
.prio-toggle.hard { color: var(--text); border-color: var(--accent); background: var(--accent-soft); }
.drag-handle { color: var(--text-muted); cursor: grab; font-size: 0.9rem; letter-spacing: -0.2em; user-select: none; }
.fd-rule.dragging { opacity: 0.4; }
.fd-rule.drop-before { box-shadow: 0 -2px 0 0 var(--accent); }
.fd-rule.drop-after { box-shadow: 0 2px 0 0 var(--accent); }

/* Tabs in the modal header */
.formula-tabs { display: inline-flex; gap: 0.25rem; padding: 0.25rem; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--card-2); margin-left: auto; margin-right: var(--space-4); }
.formula-tab { min-height: 2.2rem; padding: 0.3rem 1rem; border: 1px solid transparent; background: transparent; color: var(--text-muted); box-shadow: none; }
.formula-tab[aria-selected="true"] { background: var(--gradient-accent), var(--card); border-color: var(--accent); color: var(--text); }

/* Priority tab: one scrolling list with band dividers */
.formula-priority { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: var(--space-4); padding: var(--space-5) var(--space-6); }
.formula-priority > .hint { margin: 0; font-size: var(--fs-md); }
.priority-list { flex: 1; min-height: 0; overflow-y: auto; display: grid; gap: 0.7rem; align-content: start; padding: 2px; }
.prio-divider { display: flex; align-items: center; gap: var(--space-3); margin: var(--space-2) 0 0; color: var(--text-muted); font-size: var(--fs-xs); font-weight: var(--fw-semibold); letter-spacing: 0.08em; text-transform: uppercase; }
.prio-divider::before, .prio-divider::after { content: ""; flex: 1; height: 1px; background: var(--border-strong); }

/* "Can't all hold" confirm */
.impossible-dialog { border: 0; padding: 0; background: transparent; max-width: none; }
.impossible-dialog::backdrop { background: rgba(8, 6, 14, 0.7); backdrop-filter: blur(6px); }
.impossible-inner { width: min(40rem, 92vw); padding: var(--space-6); background: var(--card); border: 1px solid var(--border-strong); border-radius: var(--radius-lg); box-shadow: var(--shadow-pop); color: var(--text); }
.impossible-inner h3 { margin: 0 0 var(--space-4); font-size: var(--fs-xl); font-weight: var(--fw-semibold); }
.impossible-inner ul { list-style: none; margin: 0 0 var(--space-4); padding: 0; display: grid; gap: 0.6rem; }
.impossible-inner li { display: grid; gap: 0.2rem; padding: 0.7rem 1rem; border: 1px solid rgba(255, 122, 138, 0.4); border-left: 4px solid var(--danger); border-radius: var(--radius-sm); background: var(--card-2); }
.impossible-inner li span { color: var(--text-muted); font-size: var(--fs-sm); }
.impossible-inner .hint { margin: 0 0 var(--space-5); font-size: var(--fs-sm); }
.impossible-actions { display: flex; justify-content: flex-end; gap: var(--space-3); }

/* rule colours: warm red for apart, pink for together, muted for the rest */
.type-apart { --rule-color: color-mix(in srgb, var(--danger) 55%, var(--att-tardy)); }   /* warm red-orange: clear of the pink accent */
.type-together { --rule-color: var(--accent); }
.type-close, .type-front, .type-back { --rule-color: var(--text-muted); }

.rule-types { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.rule-type {
  gap: 0.6rem;
  min-height: 2.9rem;
  padding: 0.6rem 1.2rem;
  border-radius: var(--radius-pill);
  border-color: var(--border-strong);
  color: var(--text-2);
  font-size: var(--fs-md);
  box-shadow: none;
}
.rule-type kbd { font-size: 0.75rem; padding: 0 0.3rem; min-width: 1.1rem; border: 1px solid var(--border-strong); border-radius: 0.3rem; background: var(--card); color: var(--text-muted); line-height: 1.3; font-family: inherit; }
.rule-type[aria-checked="true"] { background: color-mix(in srgb, var(--rule-color, var(--accent)) 18%, var(--card-2)); border-color: var(--rule-color, var(--accent)); color: var(--text); box-shadow: inset 0 0 0 1px var(--rule-color, var(--accent)); }
.rule-type[aria-checked="true"] kbd { color: var(--text); border-color: var(--rule-color, var(--accent)); }

.formula-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  padding: var(--space-3) var(--space-5);
  border-top: 1px solid var(--border);
  background: var(--card);
}
.formula-keys { margin: 0; font-size: var(--fs-xs); line-height: 1.9; color: var(--text-muted); }
.formula-keys .hint { display: block; color: var(--text-2); }
.formula-keys kbd { display: inline-block; min-width: 1.3rem; padding: 0 0.35rem; border: 1px solid var(--border-strong); border-bottom-width: 2px; border-radius: 0.3rem; background: var(--card-2); color: var(--text-2); font: inherit; font-size: 0.72rem; line-height: 1.4; text-align: center; }
.formula-footer .primary.big { flex: none; min-width: 12rem; min-height: 2.9rem; font-size: var(--fs-lg); }
@media (max-width: 800px) { .formula-body { grid-template-columns: 1fr; grid-template-rows: minmax(0, 14rem) minmax(20rem, 1fr); } .formula-footer { flex-direction: column; align-items: stretch; } }
@media (prefers-reduced-motion: reduce) { .formula-dialog[open] .formula-inner, .formula-dialog.closing .formula-inner, .formula-dialog[open]::backdrop, .fm-node.flash { animation: none; } }

/* ---- Formula Rules tab: the relationship map ----
   Default skin (default, halloween, thanksgiving) = a technical node graph: square-cornered
   nodes on a dot grid, straight lines, diamond ports. pink overrides a few
   rules below for paper-and-string: pinned paper notes, round ports, and
   (in formula.js) lines that sag like string. Colours are tokens only. */
.fm-map {
  position: relative;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: radial-gradient(circle, var(--canvas-dot) 1px, transparent 1.6px) 0 0 / 22px 22px, var(--card-2);
  transition: border-color var(--transition), box-shadow var(--transition);
}
.fm-map.drop-hover { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent), inset 0 0 40px var(--accent-fill-soft); }
.fm-edges { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.fm-edge { cursor: pointer; transition: opacity var(--transition); }
.fm-hit { fill: none; stroke: transparent; stroke-width: 16; pointer-events: stroke; }
.fm-line { fill: none; stroke: var(--rule-color); stroke-linecap: round; stroke-linejoin: round; pointer-events: none; transition: stroke-width var(--transition); }
.fm-edge.type-apart .fm-line { stroke-width: 2; }
.fm-edge.type-together .fm-line { stroke-width: 3.6; }
.fm-edge.type-close .fm-line { stroke-width: 1.3; stroke-opacity: 0.85; }
.fm-mark { fill: var(--rule-color); stroke: var(--rule-color); stroke-width: 2; pointer-events: none; }
.fm-edge.try .fm-mark { fill: var(--card-2); }
.fm-edge:hover .fm-line, .fm-edge.hl .fm-line { filter: drop-shadow(0 0 3px var(--rule-color)); }
.fm-edge.sel .fm-line { filter: drop-shadow(0 0 6px var(--rule-color)); }
.fm-edge.sel.type-apart .fm-line { stroke-width: 3; }
.fm-edge.sel.type-together .fm-line { stroke-width: 5; }
.fm-edge.sel.type-close .fm-line { stroke-width: 2.4; }
.fm-edge.sel .fm-mark { stroke-width: 3; }
.fm-edge.conflict .fm-mark { stroke: var(--danger); stroke-width: 3; }
.fm-temp { display: none; stroke: var(--accent); stroke-width: 2; stroke-dasharray: 6 5; stroke-linecap: round; pointer-events: none; }
.fm-temp.on { display: inline; }
.fm-map.hovering .fm-edge:not(.hl) { opacity: 0.12; }
.fm-map.hovering .fm-node:not(.hl) { opacity: 0.3; }

.fm-nodes { position: absolute; inset: 0; pointer-events: none; }
.fm-node {
  position: absolute;
  left: 0;
  top: 0;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.8rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--card);
  color: var(--text);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  line-height: 1.3;
  white-space: nowrap;
  cursor: grab;
  user-select: none;
  touch-action: none;
  pointer-events: auto;
  box-shadow: 0 0 0 3px var(--card-2), var(--shadow-card);
  transition: opacity var(--transition), border-color var(--transition), box-shadow var(--transition);
}
.fm-node:hover, .fm-node.hl { border-color: var(--accent); }
.fm-node.focused { border-color: var(--accent); outline: 2px solid var(--accent); outline-offset: 4px; box-shadow: 0 0 0 3px var(--card-2), 0 0 22px var(--accent-glow); z-index: 1; }
@keyframes fm-flash { 0% { outline-offset: 14px; outline-color: transparent; } 100% { outline-offset: 4px; outline-color: var(--accent); } }
.fm-node.flash { animation: fm-flash 380ms cubic-bezier(0.2, 0.8, 0.2, 1); }
.fm-node.lifting { z-index: 2; cursor: grabbing; box-shadow: 0 0 0 3px var(--card-2), var(--shadow-pop); transition: none; }
.fm-node.drop-target { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft), 0 0 0 5px var(--accent), 0 0 22px var(--accent-glow); }
.fm-node.absent .fm-name { opacity: 0.55; text-decoration: line-through; }
.fm-map.connecting .fm-node { cursor: crosshair; }
.fm-name { max-width: 12rem; overflow: hidden; text-overflow: ellipsis; }
.fm-pin { display: none; }
/* the connector: drag it onto another student */
.fm-port {
  position: absolute;
  right: -0.45rem;
  top: 50%;
  width: 0.75rem;
  height: 0.75rem;
  margin-top: -0.375rem;
  border: 2px solid var(--accent);
  border-radius: 2px;
  background: var(--card);
  transform: rotate(45deg);
  cursor: crosshair;
  opacity: 0.6;
  transition: opacity var(--transition), transform var(--transition), background var(--transition);
}
.fm-node:hover .fm-port, .fm-node.focused .fm-port { opacity: 1; }
.fm-port:hover { background: var(--accent); transform: rotate(45deg) scale(1.25); }
/* front / back: a badge on the node, not a line */
.fm-badge {
  --rule-color: var(--accent);
  min-height: 0;
  padding: 0.02rem 0.42rem;
  border: 1px dashed var(--rule-color);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text);
  font-size: 0.68rem;
  font-weight: var(--fw-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1.5;
  box-shadow: none;
  cursor: pointer;
}
.fm-badge.type-back { --rule-color: var(--lavender); }
.fm-badge.must { border-style: solid; background: color-mix(in srgb, var(--rule-color) 22%, transparent); }
.fm-badge.sel { box-shadow: 0 0 0 2px var(--rule-color); }
.fm-badge.conflict { border-color: var(--danger); }

/* overlays: tip + Tidy up (top right), legend (bottom left), empty state */
.fm-tools { position: absolute; top: var(--space-3); right: var(--space-3); display: flex; align-items: center; gap: var(--space-3); }
.fm-tip { padding: 0.15rem 0.55rem; border-radius: var(--radius-sm); background: var(--panel-bg); color: var(--text-muted); font-size: var(--fs-xs); }
.fm-tools button { min-height: 2rem; padding: 0.15rem 0.85rem; font-size: var(--fs-xs); }
.fm-legend {
  position: absolute;
  left: var(--space-3);
  bottom: var(--space-3);
  max-width: calc(100% - 2 * var(--space-3));
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem 1rem;
  padding: 0.35rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--panel-bg);
  color: var(--text-2);
  font-size: var(--fs-xs);
  pointer-events: none;
}
.fm-leg { display: inline-flex; align-items: center; gap: 0.4rem; }
.fm-leg svg { width: 1.9rem; height: 0.6rem; overflow: visible; }
.fm-leg path { fill: none; stroke: var(--rule-color); stroke-linecap: round; stroke-linejoin: round; }
.fm-leg.type-apart path { stroke-width: 1.4; }
.fm-leg.type-together path { stroke-width: 2.8; }
.fm-leg.type-close path { stroke-width: 1.1; }
.fm-dot { display: inline-block; width: 0.6rem; height: 0.6rem; border: 2px solid var(--text-2); border-radius: 50%; }
.fm-dot.must { background: var(--text-2); }
.fm-empty { position: absolute; inset: 0; display: grid; place-content: center; gap: 0.4rem; padding: var(--space-7); text-align: center; color: var(--text-muted); font-size: var(--fs-md); pointer-events: none; }
.fm-empty strong { color: var(--text); font-size: var(--fs-lg); font-weight: var(--fw-semibold); }

/* the popover: pick a rule type, must/try, delete */
.fm-pop {
  position: absolute;
  z-index: 5;
  width: max-content;
  max-width: min(27rem, calc(100% - 1rem));
  display: grid;
  gap: 0.55rem;
  padding: 0.7rem 0.8rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--card);
  box-shadow: var(--shadow-pop);
}
.fm-pop-title { color: var(--text); font-size: var(--fs-md); font-weight: var(--fw-semibold); }
.fm-pop-types { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.fm-pop .rule-type { min-height: 2.1rem; padding: 0.25rem 0.75rem; font-size: var(--fs-xs); font-weight: var(--fw-semibold); }
.fm-pop-row { display: flex; align-items: center; gap: 0.5rem; }
.fm-pop-row .spacer { flex: 1; }
.fm-del { min-height: 1.9rem; padding: 0.15rem 0.75rem; font-size: var(--fs-xs); color: var(--text-muted); box-shadow: none; }
.fm-del:hover { color: var(--danger); border-color: var(--danger); }
.fm-del svg { width: 0.95rem; height: 0.95rem; }
.fm-del.danger { display: inline-flex; align-items: center; gap: 0.4rem; color: var(--danger); border-color: color-mix(in srgb, var(--danger) 55%, transparent); }
.fm-del.danger:hover { background: color-mix(in srgb, var(--danger) 14%, transparent); border-color: var(--danger); }

/* the selected student's Front / Back toggles, right under their node */
.fm-node-tools { position: absolute; left: 0; top: 0; z-index: 3; display: flex; gap: 0.3rem; transform: translateX(-50%); pointer-events: auto; }
.fm-chip { min-height: 1.65rem; padding: 0.05rem 0.6rem; border: 1px dashed var(--border-strong); border-radius: var(--radius-pill); background: var(--card); color: var(--text-2); font-size: 0.7rem; font-weight: var(--fw-semibold); letter-spacing: 0.08em; text-transform: uppercase; box-shadow: var(--shadow-card); white-space: nowrap; }
.fm-chip:hover { border-style: solid; border-color: var(--accent); color: var(--text); }
.fm-chip[aria-pressed="true"] { border-style: solid; }

/* the selected student's rules: a popover floating on the map, beside their node */
.fm-panel { position: absolute; left: 0; top: 0; z-index: 6; width: clamp(15rem, 17vw, 18rem); max-height: calc(100% - 16px); display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-3) var(--space-3) var(--space-4); border: 1px solid var(--border-strong); border-radius: var(--radius-md); background: var(--card); box-shadow: var(--shadow-pop); overflow: hidden; }
.fm-panel.dragging { box-shadow: var(--shadow-pop), 0 0 0 1px var(--accent); user-select: none; }
.fp-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-2); margin: calc(-1 * var(--space-3)) calc(-1 * var(--space-3)) 0; padding: var(--space-3) var(--space-3) 0; cursor: grab; touch-action: none; }
.fm-panel.dragging .fp-head { cursor: grabbing; }
.fp-title { min-width: 0; }
.fp-name { margin: 0; color: var(--text); font-size: var(--fs-lg); font-weight: var(--fw-semibold); line-height: 1.2; overflow-wrap: anywhere; }
.fp-sub { margin: 0.15rem 0 0; color: var(--text-muted); font-size: var(--fs-xs); }
.fp-out { color: var(--danger); }
.fp-head .corner-btn { width: 2rem; height: 2rem; }
.fp-rules { flex: 1; min-height: 0; overflow-y: auto; display: grid; gap: 0.45rem; align-content: start; padding: 2px; scrollbar-width: thin; }
.fp-rule { display: grid; gap: 0.35rem; padding: 0.5rem 0.55rem; border: 1px solid var(--border); border-left: 3px solid var(--rule-color, var(--text-muted)); border-radius: var(--radius-sm); background: var(--card); cursor: pointer; }
.fp-rule:hover { border-color: var(--border-hover); border-left-color: var(--rule-color, var(--text-muted)); }
.fp-rule.sel { border-color: var(--accent); border-left-color: var(--rule-color, var(--accent)); box-shadow: 0 0 0 1px var(--accent); }
.fp-row { display: flex; align-items: center; gap: 0.4rem; min-width: 0; }
.fp-type { flex: 1; min-width: 0; min-height: 2rem; padding: 0.15rem 0.5rem; font-size: var(--fs-sm); }
.fp-other { flex: 1; min-width: 0; color: var(--text); font-weight: var(--fw-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fp-rule .fd-remove { width: 1.9rem; height: 1.9rem; flex: none; }
.fp-rule .prio-toggle { min-height: 1.7rem; }
.fp-hint { margin: 0; color: var(--text-muted); font-size: var(--fs-xs); }
.fm-pop-hint { margin: 0; color: var(--text-muted); font-size: var(--fs-xs); }
.fm-pop-note { margin: 0; color: var(--danger); font-size: var(--fs-xs); }


/* a name being dragged from the list */
.fm-ghost {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 100;
  padding: 0.3rem 0.8rem;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--card);
  color: var(--text);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  white-space: nowrap;
  pointer-events: none;
  box-shadow: var(--shadow-pop);
}

/* pink: paper notes pinned to a board, joined with string */
:root[data-theme="pink"] .fm-map { background: radial-gradient(circle, var(--border) 1.1px, transparent 1.8px) 0 0 / 26px 26px, var(--card-2); }
:root[data-theme="pink"] .fm-node {
  padding: 0.45rem 0.9rem 0.35rem;
  border-color: var(--border);
  border-radius: 4px;
  transform: translate(-50%, -50%) rotate(var(--tilt, 0deg));
  box-shadow: var(--shadow-card);
}
:root[data-theme="pink"] .fm-node.focused { box-shadow: var(--shadow-card-hover); }
:root[data-theme="pink"] .fm-node.lifting { box-shadow: var(--shadow-pop); }
:root[data-theme="pink"] .fm-node.drop-target { box-shadow: 0 0 0 3px var(--accent), var(--shadow-card-hover); }
:root[data-theme="pink"] .fm-pin {
  display: block;
  position: absolute;
  top: -0.35rem;
  left: 50%;
  width: 0.7rem;
  height: 0.7rem;
  margin-left: -0.35rem;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 2px 3px var(--accent-glow), inset -1px -2px 0 var(--accent-deep);
}
:root[data-theme="pink"] .fm-port { border-radius: 50%; transform: none; }
:root[data-theme="pink"] .fm-port:hover { transform: scale(1.25); }
:root[data-theme="pink"] .fm-edge.type-together .fm-line { stroke-width: 3; }
:root[data-theme="pink"] .fm-node { box-shadow: var(--shadow-card); }

/* Name flying into its seat after Populate / Randomize */
.fly-name {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 60;
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--accent);
  border-radius: var(--radius-pill);
  background: var(--card-3);
  color: var(--text);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  white-space: nowrap;
  pointer-events: none;
  will-change: transform, opacity;
  margin: -0.9rem 0 0 -2.5rem;
}

/* Present: sidebar and bars hidden, room fills the screen, names bigger */
body.present .seat-layout { grid-template-columns: 1fr; }
body.present .seat-layout > .viewport { grid-column: 1; }
body.present .viewport { border-width: 2px; }
body.present .piece .seat, body.present .piece .desk-block { border-width: 2px; }
body.present .piece.front { border-width: 2px; }
body.present .seat.taken { border-color: var(--surface-tint-present); }
body.present .seat-label { font-size: 13px; }
body.present .front-text { font-size: 12px; }
body.present .viewport { cursor: default; }

/* Narrow: the left column lies across the top of the canvas instead */
@media (max-width: 900px) {
  .seat-layout { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); }
  .seat-layout > .desk-rail { flex-direction: row; overflow-x: auto; }
  .seat-layout > .viewport { grid-column: 1; grid-row: 2; }
  .names-panel { width: auto; max-height: 24dvh; }
  .palette-list { flex-direction: row; }
  .palette-item::after { display: none; }
  .arr-bar { flex-wrap: wrap; }
}

/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 900px) {
  .rail-sep { width: 1px; height: auto; align-self: stretch; margin: 0.25rem 0.35rem 0.25rem auto; }
  .topnav .nav-links { overflow-x: auto; }
  .two-col, .seating-layout { grid-template-columns: 1fr; grid-template-rows: auto; }
  .seating-layout > .card:first-child { min-height: 60dvh; }
  .toolbar .spacer { display: none; }
}

@media (max-width: 768px) {
  .roster-layout { grid-template-columns: minmax(0, 1fr); grid-template-rows: 1fr; gap: 0; }
  .roster-panel { display: none; }
  .list-head .more { display: block; }
  .search { max-width: none; }
  .more summary { list-style: none; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; }
  .more summary::-webkit-details-marker { display: none; }
  .more-menu { position: absolute; right: 0; top: calc(100% + 0.5rem); z-index: 20; display: flex; flex-direction: column; gap: var(--space-2); min-width: 12rem; padding: var(--space-3); border-radius: var(--radius-md); box-shadow: var(--shadow-pop); }
  .name-list { columns: 1; }
}

@media (max-width: 600px) {
  .toolbar > label:has(> select) { width: 100%; }
  .toolbar > label:has(> select) > select { flex: 1; min-width: 0; }
  .tiles { grid-template-columns: 1fr; grid-auto-rows: auto; }
  .group .name-chip { font-size: 1.3rem; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; }
  .tile:hover, .student:hover { transform: none; }
  button:active, .neu-btn:active, .tab:active, summary:active, .seg label:active span, .segment:active { transform: none; }
}

/* ==========================================================================
   Type system — Orbitron (display) · Share Tech Mono (data) · Rajdhani (body)
   Orbitron is for SHORT text only: never paragraphs, lists or names.
   Student names are Rajdhani at a heavier weight so they carry on a projector.
   ========================================================================== */

body { letter-spacing: var(--track-body); }
button, .btn, .neu-btn, input, select, textarea { font-family: var(--font-body); letter-spacing: 0.02em; }
button, .btn, .neu-btn { font-weight: var(--fw-semibold); }
.link { letter-spacing: 0.02em; }

/* --- display: headings, brand, tool + rail names, badges --- */
.brand, .hud-brand, .page-title, .hero h1, .card h2, .card-head h2, .period-title, .group h3,
.hud-name, .formula-header h2, .help-head h2, .impossible-inner h3, .import-form h2, #attendance-title,
.tab, .front-text, .pct,
.dash-legend .lg, .chart-empty p, .empty-state p, .att-list h3 .hud-key, .hud-chip, .hud-key, .fd-name {
  font-family: var(--font-display);
  text-transform: var(--label-transform);
}
.brand { font-size: 0.78rem; font-weight: 600; letter-spacing: 0.22em; }
.hud-brand { font-size: 0.74rem; font-weight: 600; letter-spacing: 0.3em; }
.page-title, .hero h1 { font-size: 1.15rem; font-weight: 600; letter-spacing: var(--track-display); line-height: 1.3; }
.card h2, .card-head h2 { font-size: 0.78rem; font-weight: 600; letter-spacing: var(--track-label); color: var(--text-2); }
.period-title { font-size: 1.05rem; font-weight: 700; letter-spacing: 0.1em; line-height: 1.35; }
.group h3 { font-size: 0.7rem; font-weight: 600; letter-spacing: 0.2em; }
.hud-name { font-size: 0.92rem; font-weight: 600; letter-spacing: 0.16em; }
.hud-panel.compact .hud-name { font-size: 0.7rem; letter-spacing: 0.14em; }
.formula-header h2 { font-size: 1.2rem; font-weight: 600; letter-spacing: var(--track-display); }
.help-head h2 { font-size: 0.95rem; font-weight: 600; letter-spacing: var(--track-display); }
.impossible-inner h3 { font-size: 0.95rem; font-weight: 600; letter-spacing: 0.1em; line-height: 1.4; }
.import-form h2, #attendance-title { font-size: 0.85rem; font-weight: 600; letter-spacing: var(--track-label); }
.tab { font-size: 0.6rem; font-weight: 500; letter-spacing: 0.12em; }
.tab.setup { font-size: 0.56rem; }
.front-text { letter-spacing: 0.24em; }
.pct { font-family: var(--font-mono); text-transform: uppercase; font-weight: 400; letter-spacing: 0.08em; }
/* control labels: body face, uppercase + tracked so they still read as HUD but stay narrow */
.mode-seg span, .seg span, .formula-tab, .prio-divider, .rule-type, .fd-add-title, .fd-rule-type { font-family: var(--font-body); text-transform: var(--label-transform); font-weight: 600; letter-spacing: 0.1em; }
.mode-seg span, .seg span { font-size: 0.78rem; }
.formula-tab { font-size: 0.85rem; }
.prio-divider, .fd-add-title { font-size: 0.74rem; letter-spacing: 0.14em; }
.rule-type { font-size: 0.8rem; }
.fd-rule-type { font-size: 0.7rem; }
.rule-type { font-size: 0.6rem; font-weight: 500; letter-spacing: 0.1em; }
.chart-empty p, .empty-state p { font-size: 0.8rem; font-weight: 500; letter-spacing: 0.12em; }
.fd-name { font-size: 0.85rem; font-weight: 600; letter-spacing: 0.08em; }

/* --- mono: every figure and technical label --- */
.hud-stat, .hud-key, .hud-val, .hud-sub, .hud-idx, .hud-chip, .save-state, .dash-legend, .att-names li .at,
.stat .value, .att-counts b, .att-counts i, #exp-counts, .chip, .list-count, .seat-bar .zoom-pct,
.seat-bar .save-state, .period-links, .import-meta, .formula-keys kbd, .rule-type kbd, kbd,
.bell-table td:nth-child(2), .bell-table th, .ov-date, .today-line span, .dash-field input[type="date"],
.hud-panel.compact .hud-sub, .status, .toast, .fd-absent, .fd-count, .formula-warnings, .student .num, .rot-label, .seat-bar .save-state {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: 400;
}
.hud-key { font-family: var(--font-display); font-size: 0.6rem; font-weight: 500; letter-spacing: var(--track-label); }
.hud-val { font-size: 0.86rem; letter-spacing: 0.06em; }
.hud-stat { font-size: 0.86rem; }
.hud-sub, .hud-idx { font-size: 0.7rem; letter-spacing: 0.16em; }
.hud-panel.compact .hud-sub { font-size: 0.62rem; }
.hud-chip { font-size: 0.6rem; font-weight: 500; letter-spacing: 0.16em; }
.save-state { font-size: 0.7rem; letter-spacing: 0.16em; }
.dash-legend { font-size: 0.66rem; letter-spacing: 0.14em; }
.dash-legend .lg { font-size: 0.56rem; }
.dash-legend .lg-hint { font-family: var(--font-body); font-size: var(--fs-xs); letter-spacing: 0.02em; }
.att-counts { font-family: var(--font-display); font-size: 0.64rem; letter-spacing: 0.16em; text-transform: var(--label-transform); }
.att-counts b { font-size: 2rem; margin-right: 0.3rem; }
.att-list h3 .hud-val { font-size: 0.9rem; }
.stat .value { letter-spacing: 0.02em; }
.chip, .list-count { font-size: 0.78rem; letter-spacing: 0.06em; }
.status { font-size: 0.82rem; letter-spacing: 0.03em; }
.toast { letter-spacing: 0.03em; }
.bell-table th { font-family: var(--font-display); font-size: 0.6rem; letter-spacing: 0.16em; }
.bell-table td:nth-child(2) { font-size: 0.95rem; letter-spacing: 0.04em; }
.ov-date { font-size: 0.9rem; letter-spacing: 0.02em; }
.formula-keys kbd, .rule-type kbd, kbd { font-size: 0.72rem; }

/* --- names: Rajdhani, heavier, a touch larger than before --- */
.student .name, .inline-edit { font-size: 1.15rem; font-weight: 700; letter-spacing: 0.01em; }
.name-chip { font-size: 1.12rem; font-weight: 600; }
.group .name-chip { font-size: 1.85rem; font-weight: 700; line-height: 1.3; }
body.present .group .name-chip { font-size: 2.25rem; }
.seat-label { font-family: var(--font-body); font-weight: 700; letter-spacing: 0.01em; font-size: 11px; }
.hub-room .seat-label { font-size: 14px; }
body.present .hub-room .seat-label { font-size: 15px; }
body.present .seat-label { font-size: 14px; }
.desk-block .seat-label { font-weight: 600; }
.student-chip, .att-tile, .attend-name, .att-names li, .fd-rule-text, .formula-student { font-weight: 600; }
.att-tile { font-size: 1.15rem; }
.att-tiles.small .att-tile { font-size: 1rem; }
body.present .att-tile { font-size: 1.35rem; }
.att-names { font-size: 0.95rem; }
.attend-name { font-size: 1.05rem; }

/* bathroom: quarter pass allowance */
/* ==========================================================================
   Bathroom page — click-to-confirm flow, pass-checkbox grid, out-now strip,
   collapsible log/history, settings dialog.
   ========================================================================== */

/* One screen, three bands: the control island, the student grid, and the
   two panels. The grid is the only thing that gives — it scrolls inside
   itself so the fixed-height panels below are always visible and the page
   itself never scrolls. Tight vertical rhythm throughout: the grid is the
   point of the page. */
.bathroom-page {
  --br-panels-h: clamp(10rem, 27vh, 15rem);   /* the Out now / Log row */
  padding: var(--space-2) var(--page-pad) var(--space-3);
  display: flex; flex-direction: column; gap: var(--space-3);
  overflow: hidden;
}
.bathroom { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; gap: var(--space-3); }

/* The one control island: period · Quick tap/Timer · search · gear.
   It shrink-wraps its contents and centres itself instead of spanning the
   page — the full-width element on this page is the student grid.
   Everything else that used to live up here is gone — see CLAUDE.md. */
.br-bar {
  align-self: center;                /* .bathroom is a flex column */
  width: fit-content;
  max-width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin: var(--space-2) 0 var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}
.br-bar > select { min-height: 2.4rem; min-width: 11rem; }
/* A percentage width inside a fit-content box resolves against a provisional
   width and makes the row wrap, so the search gets a flat width in here. */
.br-bar .br-search { width: 16rem; flex: 0 1 16rem; min-width: 8rem; }
.br-bar .icon-btn { width: 2.4rem; height: 2.4rem; padding: 0; display: inline-flex; align-items: center; justify-content: center; }

/* Mode toggle: prominent pill segment right on the main view. Active side
   gets the accent fill so at a glance you can't miss which mode you're
   about to tap into. */
.br-mode-toggle {
  display: inline-flex;
  gap: 0.15rem;
  padding: 0.25rem;
  background: var(--card-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
}
.br-mode {
  border: 0;
  background: transparent;
  padding: 0.5rem 1.25rem;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-weight: var(--fw-semibold);
  font-size: 0.95rem;
  color: var(--text-muted);
  cursor: pointer;
  min-height: 2.2rem;
  transition: background 160ms ease, color 160ms ease, box-shadow 160ms ease;
}
.br-mode:hover { color: var(--text); }
.br-mode.active {
  background: var(--accent);
  color: var(--card);
  box-shadow: 0 4px 12px var(--accent-glow);
}

/* ---- The two panels under the grid: Out now | Log ----
   Fixed height, bodies scroll internally, so the row never resizes as
   students come and go and the student grid above keeps its place. */
.br-panels {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr);
  gap: var(--space-3);
  height: var(--br-panels-h);
}
.br-panel {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}
.br-panel-head {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding: var(--space-3) var(--space-4) var(--space-2);
  border-bottom: 1px solid var(--border);
}
.br-panel-head h3 {
  margin: 0;
  flex: none;
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--track-display);
  text-transform: var(--label-transform);
  color: var(--text);
}
/* the head's first control group takes the slack, so trailing items sit right */
.br-panel-head > :nth-child(2) { margin-right: auto; }
.br-panel-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: var(--space-3) var(--space-4);
}
.br-none { margin: 0; padding: var(--space-3) 0; color: var(--text-muted); font-style: italic; }
.br-out-count {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--accent-ink);
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
}
.br-panel-head .hud-key { color: var(--text-muted); font-size: 0.72rem; }

/* Day switcher: ‹ Today › + calendar */
.br-daynav { display: flex; align-items: center; gap: 0.2rem; }
.br-day-step {
  width: 1.9rem; height: 1.9rem; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border-strong);
  background: var(--card-2);
  color: var(--text-2);
  border-radius: var(--radius-sm);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}
.br-day-step svg { width: 0.95rem; height: 0.95rem; }
.br-day-step:hover:not(:disabled) { color: var(--text); border-color: var(--border-hover); }
.br-day-step:disabled { opacity: 0.3; cursor: default; }
.br-day-label {
  min-width: 7.5rem;
  padding: 0.25rem 0.5rem;
  border: 0;
  background: none;
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 0.82rem;
  letter-spacing: 0.02em;
  text-align: center;
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.br-day-label:hover:not(:disabled) { background: var(--card-2); }
.br-day-label:disabled { cursor: default; color: var(--text-2); }
/* The real date input hides under the calendar button; `.bare` reveals it
   when showPicker() isn't available. */
.br-cal { position: relative; display: inline-flex; margin-left: 0.15rem; }
.br-cal input[type="date"] {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; pointer-events: none; border: 0; padding: 0;
}
.br-cal.bare { position: static; }
.br-cal.bare > button { display: none; }
.br-cal.bare input[type="date"] {
  position: static; opacity: 1; pointer-events: auto;
  width: auto; height: auto; min-height: 1.9rem;
  border: 1px solid var(--border-strong);
  background: var(--card-2);
  color: var(--text);
  border-radius: var(--radius-sm);
  padding: 0.1rem 0.4rem;
}
.br-hist-btn {
  flex: none;
  padding: 0.25rem 0.7rem;
  border: 1px solid var(--border-strong);
  background: var(--card-2);
  color: var(--text-muted);
  border-radius: var(--radius-pill);
  font-size: 0.8rem;
  font-weight: var(--fw-semibold);
  cursor: pointer;
}
.br-hist-btn:hover { color: var(--text); border-color: var(--border-hover); }

/* Out-now cards: one compact row each, stacked down the panel */
.br-out-cards { display: flex; flex-direction: column; gap: var(--space-2); }
.br-out-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0.75rem;
  background: var(--card-2);
  border: 1.5px solid var(--accent);
  border-radius: var(--radius-md);
  box-shadow: 0 0 0 0 var(--accent-glow);
  transition: box-shadow 200ms ease, border-color 200ms ease, background 200ms ease;
}
.br-out-card .br-out-name {
  font-weight: var(--fw-bold);
  font-size: var(--fs-sm);
  color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.br-out-card .br-out-timer {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  color: var(--accent-ink);
  letter-spacing: 0.02em;
}
.br-out-card .br-end-btn { min-height: 2rem; padding: 0 0.7rem; font-size: 0.8rem; white-space: nowrap; }
/* Flagged: past the limit — turn red and pulse */
.br-out-card.flagged {
  border-color: var(--danger);
  background: color-mix(in oklab, var(--card-2) 82%, var(--danger) 18%);
  animation: br-pulse 1.6s ease-in-out infinite;
}
.br-out-card.flagged .br-out-timer { color: var(--danger); }
@keyframes br-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 122, 138, 0.35); }
  50%      { box-shadow: 0 0 0 8px rgba(255, 122, 138, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .br-out-card.flagged { animation: none; }
}

/* Student grid */
/* The flexible band: scrolls internally rather than pushing the panels off */
.br-grid-wrap { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding-right: 2px; }
.br-search {
  width: min(18rem, 100%);
  min-height: 2.4rem;
  padding: 0.4rem 0.9rem;
  border-radius: var(--radius-pill);
  background: var(--card-2);
  border: 1px solid var(--border-strong);
  color: var(--text);
  font-size: var(--fs-sm);
}
.br-search:focus { border-color: var(--accent); outline: none; box-shadow: 0 0 0 3px var(--ring); }
.br-search-empty { padding: var(--space-3); color: var(--text-muted); font-style: italic; text-align: center; }
.br-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: var(--space-3);
}
.br-card.br-hidden { display: none; }
.br-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  padding: var(--space-4) var(--space-3);
  min-height: 5.2rem;
  background: var(--card);
  border: 1.5px solid var(--border-strong);
  border-radius: var(--radius-md);
  cursor: pointer;
  color: var(--text);
  transition: transform 140ms ease, border-color 140ms ease, background 140ms ease, box-shadow 140ms ease;
}
.br-card:hover { border-color: var(--accent); background: var(--card-3); }
.br-card:active { transform: scale(0.98); }
.br-card-name {
  font-family: var(--font-body);
  font-weight: var(--fw-bold);
  font-size: 1.05rem;
  line-height: 1.2;
  text-align: center;
}
.br-passes {
  display: flex;
  gap: 0.3rem;
  align-items: center;
}
.br-pass {
  width: 0.9rem;
  height: 0.9rem;
  border: 1.5px solid var(--border-hover);
  border-radius: 3px;
  background: transparent;
  transition: background 140ms ease, border-color 140ms ease;
}
.br-pass.used {
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: inset 0 0 0 2px var(--card);
}
/* Pending — the trip is in progress; checkbox fills fully when they return */
.br-pass.pending {
  border-color: var(--accent);
  border-style: dashed;
  background: var(--accent-soft);
}
.br-pass-unlimited {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.85rem;
  color: var(--text-muted);
}
/* "Out" state: dimmed, non-actionable, badge */
.br-card.out {
  opacity: 0.5;
  border-color: var(--border);
  cursor: not-allowed;
  background: var(--card-2);
}
.br-card.out:hover { border-color: var(--border); background: var(--card-2); transform: none; }
/* "Locked" state: no passes left */
.br-card.locked {
  border-style: dashed;
  border-color: var(--danger);
  cursor: not-allowed;
  opacity: 0.75;
}
.br-card.locked:hover { transform: none; background: var(--card); }
.br-card.locked .br-pass.used { background: var(--danger); border-color: var(--danger); }
/* Quick-tap confirmation flash */
.br-card.br-flash {
  animation: br-card-flash 800ms ease-out;
  border-color: var(--ok);
}
@keyframes br-card-flash {
  0%   { transform: scale(1);    box-shadow: 0 0 0 0 rgba(111, 211, 166, 0.55); }
  25%  { transform: scale(1.04); box-shadow: 0 0 0 8px rgba(111, 211, 166, 0.35); }
  100% { transform: scale(1);    box-shadow: 0 0 0 0 rgba(111, 211, 166, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .br-card.br-flash { animation: none; }
}

/* Corner badges */
.br-badge {
  position: absolute;
  top: 0.4rem;
  right: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 0.1rem 0.5rem;
  border-radius: var(--radius-pill);
}
.br-badge.out-badge    { background: var(--accent-soft); color: var(--accent-ink); border: 1px solid var(--accent); }
.br-badge.locked-badge { background: transparent; color: var(--danger); border: 1px solid var(--danger); }

/* Empty state */
.bathroom .chart-empty { text-align: center; padding: var(--space-6) var(--space-4); color: var(--text-muted); }
.bathroom .chart-empty a { margin-top: var(--space-3); display: inline-block; }

/* Log list: name · out → back · duration · remove */
.br-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.25rem; }
.br-list li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto auto;
  align-items: baseline;
  gap: 0.75rem;
  padding: 0.5rem 0.6rem;
  border-radius: var(--radius-sm);
  background: var(--card-2);
}
.br-log-dur {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.85rem;
  color: var(--text-2);
  min-width: 3.6rem;
  text-align: right;
}
.br-list li.none { justify-content: center; text-align: center; color: var(--text-muted); font-style: italic; display: block; padding: var(--space-3); }
.br-list li.flagged { border-left: 3px solid var(--danger); }
.br-log-name { font-weight: var(--fw-semibold); font-family: var(--font-body); }
.br-log-times { font-family: var(--font-mono); font-size: 0.85rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.br-list .link { justify-self: end; font-size: 1rem; color: var(--text-muted); background: none; border: 0; padding: 0.1rem 0.35rem; cursor: pointer; border-radius: var(--radius-sm); }
.br-list .link:hover { color: var(--danger); background: var(--card-3); }

/* History detail view */
.br-hist-manual { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; padding: 0.5rem 0.6rem; background: var(--card-2); border-radius: var(--radius-sm); margin-bottom: var(--space-3); }
.br-hist-manual button.small { min-height: 1.9rem; width: 2rem; padding: 0; border-radius: var(--radius-sm); }
.br-hist-manual .br-manual-n { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 1.1rem; min-width: 1.4rem; text-align: center; }
/* ---- Quarter review (History) — a large dialog over the bathroom page ---- */
dialog.br-review-dialog { width: min(62rem, 94vw); height: min(88dvh, 52rem); padding: var(--space-5); border: 0; border-radius: var(--radius-lg); background: var(--card); color: var(--text); box-shadow: var(--shadow-pop); flex-direction: column; gap: var(--space-3); overflow: hidden; }
dialog.br-review-dialog[open] { display: flex; }
dialog.br-review-dialog::backdrop { background: rgba(8, 6, 14, 0.55); backdrop-filter: blur(3px); }
.rv-head { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.rv-head h3 { margin: 0; font-size: var(--fs-lg); font-weight: var(--fw-bold); font-family: var(--font-body); text-transform: none; letter-spacing: 0; }
.rv-head .br-mode { padding: 0.35rem 0.95rem; min-height: 2rem; position: relative; }
.rv-head .br-mode[aria-pressed="true"] { border: 0; }
.rv-now { position: absolute; top: 0.3rem; right: 0.38rem; width: 0.32rem; height: 0.32rem; border-radius: 50%; background: var(--ok); }
.rv-head select { min-height: 2.2rem; min-width: 12rem; }
.rv-spacer { flex: 1 1 auto; }
.rv-head .icon-btn { width: 2.2rem; height: 2.2rem; padding: 0; display: inline-flex; align-items: center; justify-content: center; }
.rv-head .icon-btn svg { width: 1.1rem; height: 1.1rem; }
.rv-range { margin: 0; font-family: var(--font-mono); font-size: 0.85rem; color: var(--text-2); font-variant-numeric: tabular-nums; }
.rv-src { font-family: var(--font-body); color: var(--text-muted); font-size: 0.85rem; margin-left: 0.4rem; }
.rv-msg { color: var(--danger); margin-left: 0.6rem; }
.rv-body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.rv-empty { color: var(--text-muted); font-style: italic; text-align: center; padding: var(--space-4); margin: 0; }
.rv-empty.error { color: var(--danger); font-style: normal; }
.rv-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-3); }
.rv-stat { background: var(--card-2); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 0.6rem 0.9rem; display: flex; flex-direction: column; gap: 0.1rem; }
.rv-stat b { font-family: var(--font-mono); font-weight: 400; font-size: 1.9rem; line-height: 1.1; font-variant-numeric: tabular-nums; color: var(--text); }
.rv-stat span { color: var(--text-muted); font-size: var(--fs-sm); }
.rv-table-wrap { flex: 1 1 auto; min-height: 0; overflow: auto; border: 1px solid var(--border); border-radius: var(--radius-md); }
.rv-table { width: 100%; border-collapse: collapse; }
.rv-table th { position: sticky; top: 0; z-index: 1; background: var(--card-2); text-align: left; font-family: var(--font-body); font-size: 0.8rem; font-weight: var(--fw-semibold); text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-muted); padding: 0.45rem 0.8rem; border-bottom: 1px solid var(--border-strong); white-space: nowrap; }
.rv-table td { padding: 0.4rem 0.8rem; border-bottom: 1px solid var(--border); vertical-align: middle; }
.rv-table tbody tr:last-child td { border-bottom: 0; }
.rv-table tbody tr:hover td { background: var(--card-3); }
.rv-table tr.zero td { color: var(--text-muted); }
.rv-table tr.maxed td:first-child { box-shadow: inset 3px 0 0 var(--danger); }
.rv-table tr.maxed .br-pass.used { background: var(--danger); border-color: var(--danger); }
.rv-sort { all: unset; cursor: pointer; display: inline-flex; align-items: center; gap: 0.25rem; }
.rv-sort:hover, .rv-sort.on { color: var(--text); }
.rv-sort:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
.rv-arrow { color: var(--accent-ink); min-width: 0.6rem; }
.rv-who { all: unset; cursor: pointer; font-family: var(--font-body); font-weight: var(--fw-semibold); font-size: var(--fs-md); color: var(--text); }
.rv-table tr.zero .rv-who { color: var(--text-2); }
.rv-who:hover { text-decoration: underline; text-decoration-color: var(--accent); text-underline-offset: 3px; }
.rv-who:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
.rv-mono { font-family: var(--font-mono); font-size: 0.88rem; color: var(--text-2); font-variant-numeric: tabular-nums; white-space: nowrap; }
.rv-used { display: inline-flex; align-items: center; gap: 0.55rem; white-space: nowrap; }
td.rv-used { display: table-cell; }
td.rv-used > * { vertical-align: middle; }
td.rv-used .br-passes { display: inline-flex; margin-right: 0.55rem; }
.rv-n { font-family: var(--font-mono); font-size: 0.88rem; color: var(--text-2); font-variant-numeric: tabular-nums; }
.rv-over { font-family: var(--font-mono); font-size: 0.8rem; color: var(--danger); margin: 0 0.4rem 0 -0.2rem; }
.rv-hint { margin: 0; color: var(--text-muted); font-size: var(--fs-sm); }
.rv-detail-head { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.rv-detail-head h4 { margin: 0; font-size: var(--fs-xl); font-weight: var(--fw-bold); font-family: var(--font-body); }
.rv-detail-head .link { background: none; border: 0; color: var(--text-muted); cursor: pointer; padding: 0.2rem 0.3rem; font-size: var(--fs-sm); }
.rv-detail-head .link:hover { color: var(--text); }
.rv-detail-head .rv-used { margin-left: auto; }
.rv-body .br-hist-manual { margin-bottom: 0; }
@media (max-width: 700px) {
  dialog.br-review-dialog { height: 92dvh; padding: var(--space-4); }
  .rv-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .rv-spacer { display: none; }
  .rv-head select { min-width: 0; flex: 1 1 10rem; }
  .rv-table td, .rv-table th { padding: 0.35rem 0.5rem; }
}

/* Undo toast — quick-tap actions get a 6-second undo bubble at the bottom
   of the viewport. Fades in/out; timer is JS-managed. */
/* Rides above the Out now / Log row rather than covering it */
.br-undo-toast {
  position: fixed;
  bottom: calc(var(--br-panels-h, 0px) + 1.5rem);
  left: 50%;
  transform: translateX(-50%);
  z-index: 30;
  display: inline-flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.5rem 0.5rem 0.5rem 1.15rem;
  background: var(--card);
  color: var(--text);
  border: 1px solid var(--accent);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-pop);
  animation: br-toast-in 200ms ease-out;
}
.br-undo-toast.hiding { animation: br-toast-out 180ms ease-in forwards; }
.br-undo-toast .br-undo-msg { font-weight: var(--fw-semibold); font-size: 0.9rem; }
.br-undo-toast button {
  padding: 0.35rem 0.9rem;
  min-height: 2rem;
  border-radius: var(--radius-pill);
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  color: var(--accent-ink);
  font-weight: var(--fw-semibold);
  cursor: pointer;
}
.br-undo-toast button:hover { background: var(--accent); color: var(--card); }
@keyframes br-toast-in  { from { opacity: 0; transform: translateX(-50%) translateY(1.2rem); } to { opacity: 1; transform: translateX(-50%) translateY(0); } }
@keyframes br-toast-out { from { opacity: 1; transform: translateX(-50%) translateY(0); } to { opacity: 0; transform: translateX(-50%) translateY(0.6rem); } }
@media (prefers-reduced-motion: reduce) {
  .br-undo-toast, .br-undo-toast.hiding { animation: none; }
}

/* Narrow: stop pinning the panels — stack them and let the page scroll.
   Must come after the .br-panels / .br-grid-wrap rules above to win. */
@media (max-width: 900px) {
  .bathroom-page { overflow: auto; }
  .br-grid-wrap { flex: 0 0 auto; overflow: visible; }
  .br-panels { grid-template-columns: 1fr; height: auto; }
  .br-panel { height: 15rem; }
  .br-undo-toast { bottom: 1.5rem; }
}

/* Confirm dialog */
dialog.br-confirm { width: min(24rem, 92vw); padding: var(--space-5); border: 0; border-radius: var(--radius-lg); background: var(--card); color: var(--text); box-shadow: var(--shadow-pop); }
dialog.br-confirm::backdrop { background: rgba(8, 6, 14, 0.55); backdrop-filter: blur(3px); }
dialog.br-confirm h3 { margin: 0 0 var(--space-3); font-size: var(--fs-lg); font-weight: var(--fw-bold); font-family: var(--font-body); text-transform: none; letter-spacing: 0; }
dialog.br-confirm .br-confirm-body { margin: 0 0 var(--space-4); color: var(--text-2); font-size: var(--fs-sm); }
dialog.br-confirm strong { color: var(--accent-ink); }
.br-dialog-actions { display: flex; gap: var(--space-2); justify-content: flex-end; }
.br-dialog-actions.between { justify-content: space-between; margin: 0.3rem 0 var(--space-3); }
.br-dialog-actions button { min-height: 2.4rem; padding: 0 var(--space-4); }

/* Settings dialog */
dialog.br-settings-dialog { width: min(34rem, 94vw); max-height: 88dvh; padding: var(--space-5); border: 0; border-radius: var(--radius-lg); background: var(--card); color: var(--text); box-shadow: var(--shadow-pop); overflow: auto; }
dialog.br-settings-dialog::backdrop { background: rgba(8, 6, 14, 0.55); backdrop-filter: blur(3px); }
dialog.br-settings-dialog h3 { margin: 0 0 var(--space-4); font-size: var(--fs-lg); font-weight: var(--fw-bold); font-family: var(--font-body); text-transform: none; letter-spacing: 0; }
dialog.br-settings-dialog h4 { margin: var(--space-4) 0 var(--space-2); font-size: var(--fs-md); font-weight: var(--fw-semibold); }
dialog.br-settings-dialog .br-settings-grid { display: grid; gap: var(--space-3); }
.br-settings-field { display: grid; gap: 0.35rem; padding: var(--space-3); background: var(--card-2); border-radius: var(--radius-md); }
.br-settings-field > span:first-child { font-weight: var(--fw-semibold); }
.br-settings-field input { max-width: 6rem; }
.br-settings-field small { color: var(--text-muted); font-size: 0.82rem; }
.br-settings-note { margin: 0 0 var(--space-3); color: var(--text-muted); font-size: 0.85rem; }
.br-settings-note code { font-family: var(--font-mono); background: var(--card-2); padding: 0.05rem 0.35rem; border-radius: 4px; }
.br-quarters-grid { display: flex; flex-direction: column; gap: 0.4rem; }
.br-quarter-row { display: grid; grid-template-columns: 2.4rem 1fr auto 1fr; align-items: center; gap: 0.5rem; padding: 0.4rem 0.6rem; background: var(--card-2); border-radius: var(--radius-sm); }
.br-quarter-row .hud-key { font-family: var(--font-mono); font-weight: var(--fw-bold); color: var(--accent-ink); font-size: 0.85rem; text-align: center; }
.br-quarter-row input { min-height: 2rem; }
.br-quarter-row > span { color: var(--text-muted); font-size: 0.8rem; text-align: center; }

/* --- Manual backfill section inside the settings dialog --- */
.br-backfill { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-3); background: var(--card-2); border-radius: var(--radius-md); }
.br-bf-student { display: grid; gap: 0.3rem; }
.br-bf-student > span:first-child { font-weight: var(--fw-semibold); font-size: 0.9rem; }
.br-bf-student select { min-height: 2.2rem; }
.br-bf-row {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: 0.6rem 0.9rem;
  padding-top: 0.35rem;
  border-top: 1px dashed var(--border);
}
.br-bf-row > label { display: grid; gap: 0.2rem; font-size: 0.82rem; }
.br-bf-row > label .hud-key { font-size: 0.68rem; letter-spacing: 0.02em; }
.br-bf-row input { min-height: 2rem; }
.br-bf-row input[type="date"], .br-bf-row input[type="time"] { min-width: 8.5rem; }
.br-bf-row input[type="number"] { width: 4.2rem; text-align: center; }
.br-bf-row button { min-height: 2rem; margin-left: auto; padding: 0 0.9rem; }
.br-bf-msg { min-height: 1.2rem; font-family: var(--font-mono); font-size: 0.82rem; color: var(--ok); }
.br-bf-msg[data-kind="error"] { color: var(--danger); }
.br-bf-list-head { font-weight: var(--fw-semibold); font-size: 0.88rem; padding-top: 0.2rem; border-top: 1px dashed var(--border); }
.br-bf-list { list-style: none; padding: 0; margin: 0; max-height: 16rem; overflow-y: auto; display: flex; flex-direction: column; gap: 0.2rem; }
.br-bf-list li {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: 0.75rem;
  padding: 0.4rem 0.6rem;
  background: var(--card);
  border-radius: var(--radius-sm);
  font-size: 0.88rem;
}
.br-bf-list li.none { display: block; text-align: center; color: var(--text-muted); font-style: italic; padding: var(--space-3); }
.br-bf-list .bf-desc { font-weight: var(--fw-medium); }
.br-bf-list .bf-q { font-family: var(--font-mono); font-size: 0.72rem; color: var(--accent-ink); margin-left: 0.35rem; }
.br-bf-list .bf-times { font-family: var(--font-mono); font-size: 0.78rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.br-bf-list .bf-count { display: inline-flex; align-items: center; gap: 0.4rem; }
.br-bf-list .bf-count button.small { min-height: 1.7rem; width: 1.8rem; padding: 0; border-radius: var(--radius-sm); }
.br-bf-list .bf-n { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 1rem; min-width: 1.2rem; text-align: center; font-weight: var(--fw-semibold); }
.br-bf-list .bf-del { justify-self: end; font-size: 1rem; color: var(--text-muted); background: none; border: 0; padding: 0.1rem 0.35rem; cursor: pointer; border-radius: var(--radius-sm); }
.br-bf-list .bf-del:hover { color: var(--danger); background: var(--card-3); }
.br-bf-list .bf-manual { background: color-mix(in oklab, var(--card) 78%, var(--accent-soft) 22%); }

/* ==========================================================================
   Hub launcher (index.html): sections of tool panels, each with a small
   heading. New sections drop in by adding one more <section class="hub-section">
   in index.html — the CSS auto-lays-out.
   ========================================================================== */
.launcher-page { display: flex; flex-direction: column; padding: var(--space-5) var(--page-pad); overflow-y: auto; }
.hub-sections {
  display: flex;
  flex-direction: column;
  gap: clamp(1.5rem, 3vw, 2.5rem);
  width: 100%;
  max-width: 96rem;
  align-self: center;
  margin-block: auto;   /* keeps the group vertically centred when the viewport is tall enough */
}
.hub-section { display: flex; flex-direction: column; gap: var(--space-3); }
.hub-section-title {
  margin: 0 0 0.25rem 0.25rem;
  font-family: var(--font-display);
  text-transform: var(--label-transform);
  letter-spacing: var(--track-label);
  font-size: 0.72rem;
  font-weight: var(--fw-medium);
  color: var(--label-ink);
  line-height: 1;
}
.hub-section.setup .hub-section-title { opacity: 0.7; }

/* Cards inside each section: a responsive grid that fills the section's
   width — auto-fit + minmax means panels stretch to consume any leftover
   space rather than clumping on the left, and a section with 4 panels
   drops to 2 columns before it starts to feel cramped. */
.hub-section .hub-panels {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: clamp(1rem, 2vw, 1.5rem);
}
/* Icon + name only: centre them as a pair rather than leaving the bottom-
   aligned stack the subtitle used to sit in, and drop the height that held
   that third line. */
.hub-section .hud-panel {
  min-height: clamp(6.5rem, 13vh, 8.5rem);
  padding: 1.35rem 1.25rem;
  justify-content: center;
  align-items: center;
  text-align: center;
  gap: var(--space-2);
}
.hub-section .hud-panel .hud-icon {
  width: 2.6rem; height: 2.6rem;   /* bigger, to carry the card on its own */
  margin-bottom: 0;                /* base rule uses margin-bottom:auto to push the text down */
}
.hub-section .hud-panel .hud-name { font-size: 1rem; }
.hub-section .hud-panel .hud-sub { font-size: 0.66rem; }
.hub-section .hud-panel.setup { border-style: dashed; }

/* Below 500px the grid already collapses to a single column via auto-fit. */


/* ============================================================================
   Pink extras — the theme is otherwise a straight color swap of default
   (only :root[data-theme="pink"] tokens differ). This section holds the
   two structural affordances that don't fit inside tokens:
     • Original mascot data URIs (bunny, axolotl, cat, cloud, star)
     • Mascot placement on empty states, the login hero, and the hub corner
   The 12-hour clock preference lives in nav.js (fmtWallClock based on
   currentTheme() === 'pink').
   ========================================================================== */

:root[data-theme="pink"] {
  --pink-mascot-bunny:   url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'><ellipse cx='17' cy='11' rx='4' ry='9' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2.5' stroke-linejoin='round'/><ellipse cx='31' cy='11' rx='4' ry='9' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2.5' stroke-linejoin='round'/><ellipse cx='17' cy='12' rx='1.6' ry='5.5' fill='%23FF89A9'/><ellipse cx='31' cy='12' rx='1.6' ry='5.5' fill='%23FF89A9'/><circle cx='24' cy='30' r='13' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='3'/><path d='M24 15 L20 12 L20 18 Z' fill='%23E8467C' stroke='%234A2C3D' stroke-width='1.5' stroke-linejoin='round'/><path d='M24 15 L28 12 L28 18 Z' fill='%23E8467C' stroke='%234A2C3D' stroke-width='1.5' stroke-linejoin='round'/><circle cx='24' cy='15' r='1.8' fill='%23E8467C' stroke='%234A2C3D' stroke-width='1.2'/><circle cx='19' cy='28' r='2.8' fill='%234A2C3D'/><circle cx='29' cy='28' r='2.8' fill='%234A2C3D'/><circle cx='19.9' cy='27.1' r='0.9' fill='white'/><circle cx='29.9' cy='27.1' r='0.9' fill='white'/><circle cx='14' cy='32' r='2.2' fill='%23FF6B9E' opacity='0.55'/><circle cx='34' cy='32' r='2.2' fill='%23FF6B9E' opacity='0.55'/><ellipse cx='24' cy='31.5' rx='1' ry='0.8' fill='%23E8467C'/><path d='M22 34 Q24 36 26 34' stroke='%234A2C3D' stroke-width='1.5' fill='none' stroke-linecap='round'/></svg>");
  --pink-mascot-axolotl: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'><g fill='%23E8467C' stroke='%234A2C3D' stroke-width='2.2' stroke-linejoin='round'><ellipse cx='9' cy='22' rx='5' ry='3' transform='rotate(-18 9 22)'/><ellipse cx='6' cy='28' rx='5.5' ry='3'/><ellipse cx='9' cy='34' rx='5' ry='3' transform='rotate(18 9 34)'/><ellipse cx='39' cy='22' rx='5' ry='3' transform='rotate(18 39 22)'/><ellipse cx='42' cy='28' rx='5.5' ry='3'/><ellipse cx='39' cy='34' rx='5' ry='3' transform='rotate(-18 39 34)'/></g><ellipse cx='24' cy='28' rx='13' ry='12' fill='%23FFB088' stroke='%234A2C3D' stroke-width='3'/><circle cx='19' cy='26' r='3' fill='%234A2C3D'/><circle cx='29' cy='26' r='3' fill='%234A2C3D'/><circle cx='20' cy='25.1' r='1' fill='white'/><circle cx='30' cy='25.1' r='1' fill='white'/><circle cx='15' cy='31' r='2.2' fill='%23E8467C' opacity='0.5'/><circle cx='33' cy='31' r='2.2' fill='%23E8467C' opacity='0.5'/><path d='M21 31 Q24 34 27 31' stroke='%234A2C3D' stroke-width='1.6' fill='none' stroke-linecap='round'/></svg>");
  --pink-mascot-cat:     url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'><path d='M12 20 L10 5 L21 16 Z' fill='%23C7ADF5' stroke='%234A2C3D' stroke-width='3' stroke-linejoin='round'/><path d='M36 20 L38 5 L27 16 Z' fill='%23C7ADF5' stroke='%234A2C3D' stroke-width='3' stroke-linejoin='round'/><path d='M14 17 L13 10 L18 14.5 Z' fill='%23B794F4'/><path d='M34 17 L35 10 L30 14.5 Z' fill='%23B794F4'/><circle cx='24' cy='28' r='14' fill='%23C7ADF5' stroke='%234A2C3D' stroke-width='3'/><path d='M33 14 L37 10 L37 18 Z' fill='%23E8467C' stroke='%234A2C3D' stroke-width='1.6' stroke-linejoin='round'/><path d='M33 14 L29 10 L29 18 Z' fill='%23E8467C' stroke='%234A2C3D' stroke-width='1.6' stroke-linejoin='round'/><circle cx='33' cy='14' r='1.8' fill='%23E8467C' stroke='%234A2C3D' stroke-width='1.2'/><circle cx='19' cy='27' r='2.8' fill='%234A2C3D'/><circle cx='29' cy='27' r='2.8' fill='%234A2C3D'/><circle cx='19.9' cy='26.1' r='0.9' fill='white'/><circle cx='29.9' cy='26.1' r='0.9' fill='white'/><circle cx='14' cy='31' r='2.2' fill='%23E8467C' opacity='0.5'/><circle cx='34' cy='31' r='2.2' fill='%23E8467C' opacity='0.5'/><ellipse cx='24' cy='30' rx='0.9' ry='0.7' fill='%23E8467C'/><path d='M22 32 Q24 34 26 32' stroke='%234A2C3D' stroke-width='1.5' fill='none' stroke-linecap='round'/></svg>");
  --pink-mascot-cloud:   url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'><path d='M12 36 C 4 36 3 22 13 21 C 13 12 22 10 26 16 C 30 10 40 12 38 22 C 46 22 46 36 36 36 Z' fill='%2395E3BE' stroke='%234A2C3D' stroke-width='3' stroke-linejoin='round'/><path d='M17 25 Q19 28 21 25' stroke='%234A2C3D' stroke-width='2.2' fill='none' stroke-linecap='round'/><path d='M27 25 Q29 28 31 25' stroke='%234A2C3D' stroke-width='2.2' fill='none' stroke-linecap='round'/><circle cx='18' cy='30' r='2' fill='%23E8467C' opacity='0.5'/><circle cx='30' cy='30' r='2' fill='%23E8467C' opacity='0.5'/><ellipse cx='24' cy='30' rx='1.5' ry='1.3' fill='%234A2C3D'/></svg>");
  --pink-mascot-star:    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'><path d='M24 3 L29 17 L44 18 L32 27 L36 43 L24 33 L12 43 L16 27 L4 18 L19 17 Z' fill='%23FFD97D' stroke='%234A2C3D' stroke-width='3' stroke-linejoin='round'/><circle cx='19' cy='22' r='2.6' fill='%234A2C3D'/><circle cx='29' cy='22' r='2.6' fill='%234A2C3D'/><circle cx='19.9' cy='21.1' r='0.9' fill='white'/><circle cx='29.9' cy='21.1' r='0.9' fill='white'/><circle cx='16' cy='26' r='1.9' fill='%23E8467C' opacity='0.5'/><circle cx='32' cy='26' r='1.9' fill='%23E8467C' opacity='0.5'/><path d='M21 26 Q24 29 27 26' stroke='%234A2C3D' stroke-width='1.5' fill='none' stroke-linecap='round'/></svg>");
  /* Timer zero celebration — the bits that fly out of the dial */
  --pink-burst-heart: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M8 14.5C8 14.5 1 10.2 1 5.6 1 3 3 1.2 5.2 1.2 6.6 1.2 7.5 2 8 2.8 8.5 2 9.4 1.2 10.8 1.2 13 1.2 15 3 15 5.6 15 10.2 8 14.5 8 14.5Z' fill='%23E8467C'/></svg>");
  --pink-burst-spark: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M8 0 L9.6 6.4 L16 8 L9.6 9.6 L8 16 L6.4 9.6 L0 8 L6.4 6.4 Z' fill='%239F7AEA'/></svg>");
  /* the Timer bunny (the bunny IS the dial): pale and coloured bodies, each in
     a calm and a perked-ears pose, and the always-on front (face + the heart
     it holds, which shows the time) in three moods */
  --pink-bunny-lit: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><g stroke-linejoin='round'><ellipse cx='50' cy='80' rx='28' ry='16.5' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2.4'/><ellipse cx='35' cy='93.6' rx='9' ry='4.3' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2.4'/><ellipse cx='65' cy='93.6' rx='9' ry='4.3' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2.4'/><g transform='rotate(-14 37 38)'><ellipse cx='37' cy='21' rx='8.6' ry='19' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2.4'/><ellipse cx='37' cy='23.5' rx='4.2' ry='13' fill='%23FF89A9'/></g><g transform='rotate(14 63 38)'><ellipse cx='63' cy='21' rx='8.6' ry='19' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2.4'/><ellipse cx='63' cy='23.5' rx='4.2' ry='13' fill='%23FF89A9'/></g><ellipse cx='50' cy='45' rx='28' ry='22.5' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2.4'/><g transform='translate(62 26.5) rotate(8.4)' stroke='%234A2C3D' stroke-width='1.6'><path d='M0 0 L-8 -4.6 L-8 4.6Z' fill='%23E8467C'/><path d='M0 0 L8 -4.6 L8 4.6Z' fill='%23E8467C'/><circle r='2.4' fill='%23F2729F'/></g></g><ellipse cx='37' cy='36' rx='6' ry='4' fill='%23FFFFFF' fill-opacity='0.35' transform='rotate(-25 37 36)'/></svg>");
  --pink-bunny-lit-perk: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><g stroke-linejoin='round'><ellipse cx='50' cy='80' rx='28' ry='16.5' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2.4'/><ellipse cx='35' cy='93.6' rx='9' ry='4.3' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2.4'/><ellipse cx='65' cy='93.6' rx='9' ry='4.3' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2.4'/><g transform='rotate(-3 37 38)'><ellipse cx='37' cy='21' rx='8.6' ry='19' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2.4'/><ellipse cx='37' cy='23.5' rx='4.2' ry='13' fill='%23FF89A9'/></g><g transform='rotate(3 63 38)'><ellipse cx='63' cy='21' rx='8.6' ry='19' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2.4'/><ellipse cx='63' cy='23.5' rx='4.2' ry='13' fill='%23FF89A9'/></g><ellipse cx='50' cy='45' rx='28' ry='22.5' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2.4'/><g transform='translate(62 26.5) rotate(1.7999999999999998)' stroke='%234A2C3D' stroke-width='1.6'><path d='M0 0 L-8 -4.6 L-8 4.6Z' fill='%23E8467C'/><path d='M0 0 L8 -4.6 L8 4.6Z' fill='%23E8467C'/><circle r='2.4' fill='%23F2729F'/></g></g><ellipse cx='37' cy='36' rx='6' ry='4' fill='%23FFFFFF' fill-opacity='0.35' transform='rotate(-25 37 36)'/></svg>");
  --pink-bunny-spent: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><g stroke-linejoin='round'><ellipse cx='50' cy='80' rx='28' ry='16.5' fill='%23FFF7FA' stroke='%23F0BFD3' stroke-width='2.4'/><ellipse cx='35' cy='93.6' rx='9' ry='4.3' fill='%23FFF7FA' stroke='%23F0BFD3' stroke-width='2.4'/><ellipse cx='65' cy='93.6' rx='9' ry='4.3' fill='%23FFF7FA' stroke='%23F0BFD3' stroke-width='2.4'/><g transform='rotate(-14 37 38)'><ellipse cx='37' cy='21' rx='8.6' ry='19' fill='%23FFF7FA' stroke='%23F0BFD3' stroke-width='2.4'/><ellipse cx='37' cy='23.5' rx='4.2' ry='13' fill='%23FCE6EF'/></g><g transform='rotate(14 63 38)'><ellipse cx='63' cy='21' rx='8.6' ry='19' fill='%23FFF7FA' stroke='%23F0BFD3' stroke-width='2.4'/><ellipse cx='63' cy='23.5' rx='4.2' ry='13' fill='%23FCE6EF'/></g><ellipse cx='50' cy='45' rx='28' ry='22.5' fill='%23FFF7FA' stroke='%23F0BFD3' stroke-width='2.4'/><g transform='translate(62 26.5) rotate(8.4)' stroke='%23F0BFD3' stroke-width='1.6'><path d='M0 0 L-8 -4.6 L-8 4.6Z' fill='%23FCE6EF'/><path d='M0 0 L8 -4.6 L8 4.6Z' fill='%23FCE6EF'/><circle r='2.4' fill='%23FCE6EF'/></g></g></svg>");
  --pink-bunny-spent-perk: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><g stroke-linejoin='round'><ellipse cx='50' cy='80' rx='28' ry='16.5' fill='%23FFF7FA' stroke='%23F0BFD3' stroke-width='2.4'/><ellipse cx='35' cy='93.6' rx='9' ry='4.3' fill='%23FFF7FA' stroke='%23F0BFD3' stroke-width='2.4'/><ellipse cx='65' cy='93.6' rx='9' ry='4.3' fill='%23FFF7FA' stroke='%23F0BFD3' stroke-width='2.4'/><g transform='rotate(-3 37 38)'><ellipse cx='37' cy='21' rx='8.6' ry='19' fill='%23FFF7FA' stroke='%23F0BFD3' stroke-width='2.4'/><ellipse cx='37' cy='23.5' rx='4.2' ry='13' fill='%23FCE6EF'/></g><g transform='rotate(3 63 38)'><ellipse cx='63' cy='21' rx='8.6' ry='19' fill='%23FFF7FA' stroke='%23F0BFD3' stroke-width='2.4'/><ellipse cx='63' cy='23.5' rx='4.2' ry='13' fill='%23FCE6EF'/></g><ellipse cx='50' cy='45' rx='28' ry='22.5' fill='%23FFF7FA' stroke='%23F0BFD3' stroke-width='2.4'/><g transform='translate(62 26.5) rotate(1.7999999999999998)' stroke='%23F0BFD3' stroke-width='1.6'><path d='M0 0 L-8 -4.6 L-8 4.6Z' fill='%23FCE6EF'/><path d='M0 0 L8 -4.6 L8 4.6Z' fill='%23FCE6EF'/><circle r='2.4' fill='%23FCE6EF'/></g></g></svg>");
  --pink-bunny-face: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><defs><linearGradient id='h' x1='0' y1='0' x2='0' y2='1'><stop offset='0' stop-color='%23FFFFFF'/><stop offset='1' stop-color='%23FFE1EC'/></linearGradient></defs><g transform='matrix(1.25 0 0 1.1 -12.5 -9.9)'><path d='M50 72C50 64 39 59 31.5 63C22.5 67.5 23 80 33 88L50 99L67 88C77 80 77.5 67.5 68.5 63C61 59 50 64 50 72Z' fill='url(%23h)' stroke='%23E8467C' stroke-width='2.3' stroke-linejoin='round'/><path d='M30 68Q33 64.6 37.4 65.2' fill='none' stroke='%23FFFFFF' stroke-width='1.4' stroke-linecap='round'/></g><g transform='translate(20 77) rotate(-60)'><ellipse rx='6' ry='5' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2'/><path d='M-2 -2.4v2.2M2 -2.4v2.2' stroke='%234A2C3D' stroke-width='1.1' stroke-linecap='round' transform='rotate(90)'/></g><g transform='translate(80 77) rotate(60)'><ellipse rx='6' ry='5' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2'/><path d='M-2 -2.4v2.2M2 -2.4v2.2' stroke='%234A2C3D' stroke-width='1.1' stroke-linecap='round' transform='rotate(90)'/></g><ellipse cx='31' cy='52.5' rx='4.6' ry='2.8' fill='%23FF6B9E' fill-opacity='0.5'/><ellipse cx='69' cy='52.5' rx='4.6' ry='2.8' fill='%23FF6B9E' fill-opacity='0.5'/><ellipse cx='39' cy='43.5' rx='3' ry='3.6' fill='%234A2C3D'/><circle cx='40.05' cy='42.06' r='1.1' fill='%23FFFFFF'/><circle cx='37.95' cy='44.94' r='0.49500000000000005' fill='%23FFFFFF'/><ellipse cx='61' cy='43.5' rx='3' ry='3.6' fill='%234A2C3D'/><circle cx='62.05' cy='42.06' r='1.1' fill='%23FFFFFF'/><circle cx='59.95' cy='44.94' r='0.49500000000000005' fill='%23FFFFFF'/><path d='M47.8 48.6Q50 47.6 52.2 48.6L50 51.2Z' fill='%23E8467C' stroke='%234A2C3D' stroke-width='1.1' stroke-linejoin='round'/><path d='M45.6 52.6Q47.8 55.4 50 52.6Q52.2 55.4 54.4 52.6' fill='none' stroke='%234A2C3D' stroke-width='1.8' stroke-linecap='round'/></svg>");
  --pink-bunny-face-wide: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><defs><linearGradient id='h' x1='0' y1='0' x2='0' y2='1'><stop offset='0' stop-color='%23FFFFFF'/><stop offset='1' stop-color='%23FFE1EC'/></linearGradient></defs><g transform='matrix(1.25 0 0 1.1 -12.5 -9.9)'><path d='M50 72C50 64 39 59 31.5 63C22.5 67.5 23 80 33 88L50 99L67 88C77 80 77.5 67.5 68.5 63C61 59 50 64 50 72Z' fill='url(%23h)' stroke='%23E8467C' stroke-width='2.3' stroke-linejoin='round'/><path d='M30 68Q33 64.6 37.4 65.2' fill='none' stroke='%23FFFFFF' stroke-width='1.4' stroke-linecap='round'/></g><g transform='translate(20 77) rotate(-60)'><ellipse rx='6' ry='5' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2'/><path d='M-2 -2.4v2.2M2 -2.4v2.2' stroke='%234A2C3D' stroke-width='1.1' stroke-linecap='round' transform='rotate(90)'/></g><g transform='translate(80 77) rotate(60)'><ellipse rx='6' ry='5' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2'/><path d='M-2 -2.4v2.2M2 -2.4v2.2' stroke='%234A2C3D' stroke-width='1.1' stroke-linecap='round' transform='rotate(90)'/></g><ellipse cx='31' cy='52.5' rx='4.6' ry='2.8' fill='%23FF6B9E' fill-opacity='0.72'/><ellipse cx='69' cy='52.5' rx='4.6' ry='2.8' fill='%23FF6B9E' fill-opacity='0.72'/><ellipse cx='39' cy='42.8' rx='4.4' ry='5.2' fill='%234A2C3D'/><circle cx='40.54' cy='40.72' r='1.6' fill='%23FFFFFF'/><circle cx='37.46' cy='44.879999999999995' r='0.7200000000000001' fill='%23FFFFFF'/><ellipse cx='61' cy='42.8' rx='4.4' ry='5.2' fill='%234A2C3D'/><circle cx='62.54' cy='40.72' r='1.6' fill='%23FFFFFF'/><circle cx='59.46' cy='44.879999999999995' r='0.7200000000000001' fill='%23FFFFFF'/><path d='M47.8 48.6Q50 47.6 52.2 48.6L50 51.2Z' fill='%23E8467C' stroke='%234A2C3D' stroke-width='1.1' stroke-linejoin='round'/><path d='M34.5 34.6Q39 32 43.5 34.2M56.5 34.2Q61 32 65.5 34.6' fill='none' stroke='%234A2C3D' stroke-width='1.6' stroke-linecap='round'/><ellipse cx='50' cy='54.6' rx='2.2' ry='2.6' fill='%23C93168' stroke='%234A2C3D' stroke-width='1.5'/></svg>");
  --pink-bunny-face-party: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><defs><linearGradient id='h' x1='0' y1='0' x2='0' y2='1'><stop offset='0' stop-color='%23FFFFFF'/><stop offset='1' stop-color='%23FFE1EC'/></linearGradient></defs><g transform='matrix(1.25 0 0 1.1 -12.5 -9.9)'><path d='M50 72C50 64 39 59 31.5 63C22.5 67.5 23 80 33 88L50 99L67 88C77 80 77.5 67.5 68.5 63C61 59 50 64 50 72Z' fill='url(%23h)' stroke='%23E8467C' stroke-width='2.3' stroke-linejoin='round'/><path d='M30 68Q33 64.6 37.4 65.2' fill='none' stroke='%23FFFFFF' stroke-width='1.4' stroke-linecap='round'/></g><g transform='translate(20 77) rotate(-60)'><ellipse rx='6' ry='5' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2'/><path d='M-2 -2.4v2.2M2 -2.4v2.2' stroke='%234A2C3D' stroke-width='1.1' stroke-linecap='round' transform='rotate(90)'/></g><g transform='translate(80 77) rotate(60)'><ellipse rx='6' ry='5' fill='%23FFB6C9' stroke='%234A2C3D' stroke-width='2'/><path d='M-2 -2.4v2.2M2 -2.4v2.2' stroke='%234A2C3D' stroke-width='1.1' stroke-linecap='round' transform='rotate(90)'/></g><ellipse cx='31' cy='52.5' rx='4.6' ry='2.8' fill='%23FF6B9E' fill-opacity='0.8'/><ellipse cx='69' cy='52.5' rx='4.6' ry='2.8' fill='%23FF6B9E' fill-opacity='0.8'/><path d='M34.6 45Q39 39 43.4 45M56.6 45Q61 39 65.4 45' fill='none' stroke='%234A2C3D' stroke-width='2.3' stroke-linecap='round'/><path d='M47.8 48.6Q50 47.6 52.2 48.6L50 51.2Z' fill='%23E8467C' stroke='%234A2C3D' stroke-width='1.1' stroke-linejoin='round'/><path d='M42.4 52.4Q50 63.5 57.6 52.4Z' fill='%23C93168' stroke='%234A2C3D' stroke-width='1.8' stroke-linejoin='round'/><path d='M45.6 57.2Q50 55 54.4 57.2Q50 61.6 45.6 57.2Z' fill='%23FF89A9'/></svg>");
}

/* Halloween extras. Everything is decoration BEHIND content: background
   layers, or pseudo-elements at z-index -1 with pointer-events: none, so
   nothing can cover text or take a click. The scope skips the timer / noise /
   wheel pop-outs (and the timer's PiP window), which stay clean for students. */

/* the page background (webs, graveyard, bat tile) is in part 2 below */

/* bats drifting across just under the top bar, behind everything (z -1 in the
   root context sits under every panel); two flocks at different speeds */
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout))::before,
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout))::after {
  content: "";
  position: fixed;
  left: 0;
  z-index: -1;
  pointer-events: none;
  background: var(--halloween-flock) center / contain no-repeat;
  animation: halloween-drift 46s linear infinite;
}
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout))::before { top: 3.9rem; width: 13rem; height: 4.1rem; transform: translate(14vw, 0); }
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout))::after {
  top: 6.4rem; width: 8.5rem; height: 2.7rem; transform: translate(68vw, 0);
  background-image: var(--halloween-flock-far);
  animation-duration: 71s;
  animation-delay: -30s;
}
@keyframes halloween-drift {
  0%   { transform: translate(-12rem, 0); }
  25%  { transform: translate(25vw, 0.9rem); }
  50%  { transform: translate(50vw, -0.3rem); }
  75%  { transform: translate(75vw, 0.7rem); }
  100% { transform: translate(calc(100vw + 2rem), 0); }
}
/* reduced motion: the flocks stop where their base transform puts them */
@media (prefers-reduced-motion: reduce) {
  :root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout))::before, :root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout))::after { animation: none; }
}

/* panels, cards and modals get webs + a drip (drawn in part 2) under
   the box's own content (isolation gives the -1 layer a box to sit in) */
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .hud-box,
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .card,
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) dialog[open]:not(.arr-dialog),
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .arr-dialog-inner { isolation: isolate; }

/* a grinning jack-o'-lantern, glowing, above every empty state */
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .list-empty::before,
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .chart-empty::before,
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .empty-state::before,
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .lesson-empty::before {
  content: "";
  display: block;
  width: 4rem;
  height: 4rem;
  margin: 0 auto var(--space-3);
  background: var(--halloween-jack) no-repeat center / contain;
  filter: drop-shadow(0 0 0.7rem rgba(255, 117, 24, 0.55)) drop-shadow(0 0 0.2rem rgba(255, 210, 90, 0.5));
  pointer-events: none;
}

/* a bat beside section headings (hub sections, card titles) */
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .hub-section-title::before,
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .card-head h2::before {
  content: "";
  display: inline-block;
  width: 1.9em;
  height: 0.95em;
  margin-right: 0.5em;
  vertical-align: -0.1em;
  background: var(--halloween-head-bat) no-repeat center / contain;
  pointer-events: none;
}

/* Halloween extras, part 2. Same rules as above: behind content, no clicks,
   off the pop-outs, still under reduced motion. */

/* the graveyard along the bottom of the page: one wide silhouette (dead trees
   at the ends, a few stones), under the corner webs */
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) body {
  background:
    var(--halloween-web-tl) left top / clamp(11rem, 20vw, 19rem) auto no-repeat,
    var(--halloween-web-tr) right top / clamp(11rem, 20vw, 19rem) auto no-repeat,
    var(--halloween-web-bl) left bottom / clamp(9rem, 16vw, 15rem) auto no-repeat,
    var(--halloween-web-br) right bottom / clamp(9rem, 16vw, 15rem) auto no-repeat,
    var(--halloween-grave) center bottom / max(100%, 70rem) auto no-repeat,
    var(--halloween-vignette),
    var(--body-pattern) 0 0 / var(--body-pattern-size) repeat,
    radial-gradient(60% 45% at 50% -5%, var(--bg-glow), transparent 70%) no-repeat,
    radial-gradient(90% 70% at 30% 10%, var(--bg-1), transparent 70%) no-repeat,
    var(--bg-0);
}

/* purple fog drifting across the bottom: a strip two screens wide whose
   pattern repeats every screen, so sliding it one screen loops seamlessly */
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) body::after {
  content: "";
  position: fixed;
  left: 0;
  bottom: 0;
  width: 200vw;
  height: 32vh;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(28% 60% at 18% 100%, var(--halloween-fog), transparent 70%) 0 0 / 100vw 100% repeat-x,
    radial-gradient(34% 75% at 62% 100%, var(--halloween-fog), transparent 72%) 0 0 / 100vw 100% repeat-x,
    radial-gradient(22% 45% at 88% 100%, var(--halloween-fog), transparent 70%) 0 0 / 100vw 100% repeat-x;
  animation: halloween-fog 140s linear infinite;
}
@keyframes halloween-fog {
  from { transform: translateX(0); }
  to   { transform: translateX(-100vw); }
}

/* webs in the top corners of every panel and card (the boxes above, plus
   the Create Groups and Bathroom panels and the hub's tool panels) */
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .gp-panel,
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .br-panel { position: relative; }
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .gp-panel,
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .br-panel,
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .hud-panel { isolation: isolate; }
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .hud-box::after,
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .card::after,
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) dialog[open]:not(.arr-dialog)::after,
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .arr-dialog-inner::after,
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .gp-panel::after,
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .br-panel::after,
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .hud-panel::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  background:
    var(--halloween-card-web-l) left top / 2.8rem auto no-repeat,
    var(--halloween-card-web-r) right top / 2.8rem auto no-repeat;
}

/* a mark beside every section heading: a jack-o'-lantern on the hub's
   sections, a bat on panel titles (the hub/card rule above draws the bat) */
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .hub-section-title::before {
  width: 1.25em;
  height: 1.25em;
  vertical-align: -0.28em;
  background-image: var(--halloween-jack);
  filter: drop-shadow(0 0 0.3rem rgba(255, 117, 24, 0.45));
}
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .gp-title::before,
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .br-panel-head h3::before {
  content: "";
  display: inline-block;
  width: 1.9em;
  height: 0.95em;
  margin-right: 0.5em;
  vertical-align: -0.1em;
  background: var(--halloween-head-bat) no-repeat center / contain;
  pointer-events: none;
}

/* empty lists: a little ghost (with a bat) instead of the jack-o'-lantern,
   which stays on the big empty states (no chart, no plan) */
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .list-empty::before { background-image: var(--halloween-ghost); filter: drop-shadow(0 0 0.6rem rgba(195, 148, 238, 0.4)); }
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .att-names li.none::before,
:root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) .br-none::before {
  content: "";
  display: inline-block;
  width: 1.15em;
  height: 1.15em;
  margin-right: 0.4em;
  vertical-align: -0.22em;
  background: var(--halloween-ghost) no-repeat center / contain;
  opacity: 0.8;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  :root[data-theme="halloween"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout)) body::after { animation: none; }
}

/* Thanksgiving extras: a few autumn leaves drifting slowly down the page,
   over the faint leaf tile (--body-pattern). Like the halloween bats they
   are root pseudo-elements at z-index -1, so they pass behind every panel and
   show only in the gaps, and they skip the projector pop-outs. Each layer is
   a full-viewport box carrying two leaves, falling one screen height on a
   long loop with a gentle sway; the two layers run at different speeds. */
:root[data-theme="thanksgiving"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout))::before,
:root[data-theme="thanksgiving"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout))::after {
  content: "";
  position: fixed;
  left: 0; top: 0;
  width: 100vw; height: 100vh;
  z-index: -1;
  pointer-events: none;
  background:
    var(--tg-fall) 14% 18% / clamp(1.8rem, 2.6vw, 2.8rem) auto no-repeat,
    var(--tg-fall-2) 82% 64% / clamp(1.5rem, 2.1vw, 2.3rem) auto no-repeat;
  animation: tg-fall 44s linear infinite;
}
:root[data-theme="thanksgiving"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout))::after {
  background:
    var(--tg-fall-3) 58% 8% / clamp(1.4rem, 1.9vw, 2.1rem) auto no-repeat,
    var(--tg-fall) 33% 72% / clamp(1.2rem, 1.6vw, 1.8rem) auto no-repeat;
  animation-duration: 63s;
  animation-delay: -27s;
}
@keyframes tg-fall {
  0%   { transform: translate(0, -100vh); }
  20%  { transform: translate(2.2vw, -60vh); }
  40%  { transform: translate(-1.4vw, -20vh); }
  60%  { transform: translate(1.8vw, 20vh); }
  80%  { transform: translate(-1.2vw, 60vh); }
  100% { transform: translate(0, 100vh); }
}
/* reduced motion: the leaves rest where they sit, on screen */
@media (prefers-reduced-motion: reduce) {
  :root[data-theme="thanksgiving"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout))::before,
  :root[data-theme="thanksgiving"]:not(:has(body.timer-popout, body.noise-popout, body.wheel-popout))::after { animation: none; }
}

/* Drop-a-mascot utility: <span class="pink-mascot" data-mascot="cat"></span>
   Automatically collapses in any non-pink theme (bg vars are undefined). */
.pink-mascot {
  display: none;
  width: 3rem; height: 3rem;
  vertical-align: middle;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
}
:root[data-theme="pink"] .pink-mascot { display: inline-block; }
:root[data-theme="pink"] .pink-mascot[data-mascot="bunny"]   { background-image: var(--pink-mascot-bunny); }
:root[data-theme="pink"] .pink-mascot[data-mascot="axolotl"] { background-image: var(--pink-mascot-axolotl); }
:root[data-theme="pink"] .pink-mascot[data-mascot="cat"]     { background-image: var(--pink-mascot-cat); }
:root[data-theme="pink"] .pink-mascot[data-mascot="cloud"]   { background-image: var(--pink-mascot-cloud); }
:root[data-theme="pink"] .pink-mascot[data-mascot="star"]    { background-image: var(--pink-mascot-star); }

/* Friendly mascot in the top-of-empty-state area. Distinct mascot per
   context so different empty views feel varied. */
:root[data-theme="pink"] .list-empty::before,
:root[data-theme="pink"] .chart-empty::before,
:root[data-theme="pink"] .empty-state::before,
:root[data-theme="pink"] .lesson-empty::before {
  content: "";
  display: block;
  width: 4.5rem;
  height: 4.5rem;
  margin: 0 auto var(--space-3);
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
}
:root[data-theme="pink"] .list-empty::before   { background-image: var(--pink-mascot-cat); }
:root[data-theme="pink"] .chart-empty::before  { background-image: var(--pink-mascot-bunny); }
:root[data-theme="pink"] .empty-state::before  { background-image: var(--pink-mascot-axolotl); }
:root[data-theme="pink"] .lesson-empty::before { background-image: var(--pink-mascot-star); }

/* Login page: axolotl above the sign-in card. Only in pink. Overrides
   the pink glow line that login.html defines inline on .login-box::before. */
:root[data-theme="pink"] .login-box::before {
  content: "";
  position: static;
  display: block;
  width: 5.5rem;
  height: 5.5rem;
  inset: auto;
  margin: 0 auto var(--space-3);
  background: var(--pink-mascot-axolotl) no-repeat center / contain;
}

/* Hub cards: on the cream ground the default HUD line (--hud-line, 0.24)
   nearly disappears, so pink's hub panels sit at roughly default's hover
   strength at rest, and hover steps up again — brighter line, darker corner
   ticks and a soft rose shadow on top of the shared lift + glow. Scoped to
   the hub on purpose: the tool pages' .hud-box keeps the quieter line. */
:root[data-theme="pink"] .hub-section .hud-panel {
  border-color: rgba(232, 70, 124, 0.55);
}
:root[data-theme="pink"] .hub-section .hud-panel .tick {
  border-color: rgba(232, 70, 124, 0.75);
}
/* System (Schedule / Rosters) stays the quieter pair, just no longer ghostly */
:root[data-theme="pink"] .hub-section .hud-panel.setup {
  border-color: rgba(232, 70, 124, 0.34);
}
:root[data-theme="pink"] .hub-section .hud-panel.setup .tick {
  border-color: rgba(232, 70, 124, 0.45);
}
:root[data-theme="pink"] .hub-section .hud-panel:hover,
:root[data-theme="pink"] .hub-section .hud-panel:focus-visible {
  border-color: rgba(232, 70, 124, 0.95);
  box-shadow: var(--hud-glow), 0 6px 18px rgba(232, 70, 124, 0.18);
}
:root[data-theme="pink"] .hub-section .hud-panel:hover .tick,
:root[data-theme="pink"] .hub-section .hud-panel:focus-visible .tick {
  border-color: var(--accent-deep);
}

/* Hub launcher corner flourish: a small bunny bottom-left, low z-index,
   pointer-events off so it never intercepts a click. */
:root[data-theme="pink"] body.launcher::after {
  content: "";
  position: fixed;
  left: 1.5rem;
  bottom: 1.5rem;
  width: 4.5rem;
  height: 4.5rem;
  background: var(--pink-mascot-bunny) no-repeat center / contain;
  filter: drop-shadow(0 3px 8px rgba(232, 70, 124, 0.20));
  opacity: 0.9;
  pointer-events: none;
  z-index: 5;
}
@media (max-width: 900px) {
  :root[data-theme="pink"] body.launcher::after { display: none; }
}

/* ============================================================================
   Timer tool

   The Timer page is the *control* view: everything (mode toggle, label, ring,
   controls, presets, pop out) must fit inside the viewport without scrolling
   even at 1280×720. The ring is sized from the smaller of viewport height and
   width, and the digits inside the ring scale off viewport height too — so
   the whole stage tracks the window. The projector-size display lives in the
   pop-out window and full-screen mode.
   ============================================================================ */
.timer-page {
  display: grid; place-items: center;
  padding: var(--space-3) var(--space-4);
  min-height: 0;
}

.timer-stage {
  width: min(52rem, 96vw);
  max-height: calc(100dvh - 5.5rem);
  display: grid;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  justify-items: center;
  text-align: center;
}

.timer-mode-row {
  display: flex; flex-wrap: wrap; gap: var(--space-3);
  justify-content: center; align-items: center;
  width: 100%;
}
.timer-mode-seg { min-width: 16rem; }
.timer-label-in {
  flex: 1 1 18rem; max-width: 28rem;
  background: var(--card-2); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  padding: 0.5rem 0.85rem;
  font: var(--fw-medium) var(--fs-md)/1.2 var(--font);
  letter-spacing: var(--track-body);
}
.timer-label-in::placeholder { color: var(--text-muted); }
.timer-label-in:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); outline: none; }

/* Label sits ABOVE the ring, in the grid flow — never overlaps digits. */
.timer-label {
  font: var(--fw-semibold) clamp(0.95rem, 1.6vw, 1.25rem)/1 var(--font-display);
  letter-spacing: var(--track-display);
  text-transform: uppercase;
  color: var(--text-2);
  max-width: 90%;
  /* overflow: CLIP, not hidden — a hidden-overflow grid item's minimum height is
     0, so when the stage ran short of room the label's row collapsed and the
     label slid behind the dial. clip keeps the ellipsis without that. */
  white-space: nowrap; overflow: clip; overflow-clip-margin: 0.6em; text-overflow: ellipsis;
  line-height: 1.25;
}
.timer-label.empty { display: none; }
/* on the Timer page its line is always reserved (an empty label keeps its
   height), so typing a label or starting the timer never moves the dial */
.timer-stage .timer-label.empty { display: block; visibility: hidden; }
.timer-stage .timer-label.empty::before { content: "\00a0"; }

.timer-display-wrap {
  position: relative;
  width: min(46vh, 44vw, 28rem);
  aspect-ratio: 1 / 1;
  display: grid; place-items: center;
}
/* ---- the dial: one SVG, two skins -------------------------------------
   default   → segment ring (each tick goes dark as time runs down) over an
              instrument-style inner dial with degree markings
   pink    → one thick rounded gradient arc; segments/marks hidden
   timer.js fills .dial-segs, .dial-marks and .timer-burst once per document
   and sets data-state ('' | warn | alert) + data-mode on the wrap.        */
.timer-dial { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.dial-track { fill: none; stroke: var(--border-strong); stroke-width: 0.8; opacity: 0.7; }
.dial-arc {
  fill: none;
  stroke: url(#timer-arc-grad);
  stroke-width: 7;
  stroke-linecap: round;
  transition: stroke-dashoffset 500ms linear;
}
#timer-arc-grad .arc-from { stop-color: var(--accent); }
#timer-arc-grad .arc-to   { stop-color: var(--lavender); }

.dial-segs line {
  stroke: var(--accent);
  stroke-width: 2.6;
  stroke-linecap: round;
  transition: stroke 260ms ease, opacity 260ms ease;
  filter: drop-shadow(0 0 3px var(--accent-glow));
}
.dial-segs line.off { stroke: var(--border-strong); opacity: 0.5; filter: none; }
.dial-inner { fill: none; stroke: var(--hud-line); stroke-width: 0.5; }
.dial-marks line { stroke: var(--border-strong); stroke-width: 0.5; opacity: 0.8; }
.dial-marks line.long { stroke: var(--hud-line-strong); stroke-width: 0.8; }

/* stopwatch has nothing to drain — keep the frame, drop the progress */
.timer-display-wrap[data-mode="stopwatch"] .dial-arc,
.timer-display-wrap[data-mode="stopwatch"] .dial-segs { display: none; }

/* final minute: the ring breathes */
.timer-display-wrap[data-state="warn"] .dial-segs,
.timer-display-wrap[data-state="warn"] .dial-arc { animation: dial-pulse 1.6s ease-in-out infinite; }
@keyframes dial-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}

/* zero: a clean flash across the whole dial (default) */
.timer-flash {
  position: absolute; inset: 0; border-radius: 50%;
  pointer-events: none; opacity: 0;
  background: radial-gradient(circle at 50% 50%, var(--accent-soft) 0%, transparent 72%);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.timer-display-wrap[data-state="alert"] .timer-flash { animation: dial-flash 900ms ease-out infinite; }
@keyframes dial-flash {
  0%   { opacity: 0; }
  20%  { opacity: 0.9; }
  100% { opacity: 0; }
}

.timer-display {
  position: relative; z-index: 1;
  font: var(--fw-regular) clamp(2.4rem, 11vh, 5.5rem)/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text);
  letter-spacing: 0.02em;
  text-shadow: 0 0 18px var(--shadow-text), 0 0 42px var(--accent-glow);
}
.timer-display.warn { color: var(--accent-ink); text-shadow: 0 0 26px var(--accent-glow); }
.timer-display.alert {
  color: var(--danger);
  animation: timer-flash 900ms ease-in-out infinite;
}
@keyframes timer-flash {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.45; }
}

/* the celebration bits are pink-only — see the pink block below */
.timer-burst { position: absolute; inset: 0; pointer-events: none; display: none; }
.timer-mascot { display: none; }

@media (prefers-reduced-motion: reduce) {
  .timer-display.alert,
  .timer-display-wrap[data-state="warn"] .dial-segs,
  .timer-display-wrap[data-state="warn"] .dial-arc,
  .timer-display-wrap[data-state="alert"] .timer-flash { animation: none; }
  .dial-arc { transition: none; }
}

/* Controls sit in one tidy bar under the dial; Start is unmistakably primary */
.timer-actions {
  display: flex; flex-wrap: wrap; gap: var(--space-2);
  justify-content: center; align-items: center;
  padding: 0.4rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--card-2);
}
/* Start / Pause / Resume keep ONE size: the sizing hangs off .big (the
   button drops .primary while running), so the row never resizes */
.timer-actions .big {
  min-width: 10rem; min-height: 2.8rem;
  padding: 0.5rem 1.6rem;
  border-radius: var(--radius-pill);
  font-size: var(--fs-lg); letter-spacing: 0.08em;
}
.timer-actions .primary.big { box-shadow: 0 4px 14px var(--accent-glow); }
.timer-actions button {
  min-height: 2.4rem; padding: 0.4rem 1.1rem;
  border-radius: var(--radius-pill);
  font-size: var(--fs-sm);
}

/* Custom min/sec + presets on ONE row */
.timer-setup {
  display: flex; flex-wrap: wrap; gap: var(--space-3);
  justify-content: center; align-items: end; width: 100%;
}
/* running / paused / stopwatch: the row stays (so nothing moves), just inert */
.timer-setup.locked { opacity: 0.45; }
.timer-setup.locked button:disabled { opacity: 1; }   /* the row is already dimmed */
.timer-custom { display: flex; align-items: end; gap: var(--space-2); }
.timer-custom label { display: grid; gap: 0.15rem; justify-items: start; }
.timer-custom label .hud-key { font-size: 0.62rem; }
.timer-custom input[type="number"] {
  width: 4.2rem;
  background: var(--card-2); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  padding: 0.35rem 0.5rem;
  font: var(--fw-regular) var(--fs-md)/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  text-align: center;
}
.timer-colon {
  align-self: center; padding-bottom: 0.2rem;
  font: var(--fw-regular) var(--fs-lg)/1 var(--font-mono); color: var(--text-2);
}
.timer-presets { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.timer-presets button {
  min-height: 2.2rem; padding: 0.3rem 1rem;
  background: var(--card-2); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--radius-pill);
  font: var(--fw-semibold) var(--fs-xs)/1 var(--font);
  letter-spacing: 0.06em;
}
.timer-presets button:hover { border-color: var(--accent); color: var(--accent-ink); background: var(--accent-soft); }

.timer-extras {
  width: 100%;
  display: flex; flex-wrap: wrap; gap: var(--space-2);
  align-items: center; justify-content: center;
  padding-top: var(--space-2);
  border-top: 1px solid var(--border);
}
.timer-extras .spacer { flex: 1 1 auto; }
.timer-extras button {
  min-height: 2.2rem; padding: 0.35rem 0.85rem;
  background: var(--card-2); color: var(--text-2);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font: var(--fw-semibold) var(--fs-xs)/1 var(--font);
  letter-spacing: 0.04em;
}
.timer-extras button:hover { color: var(--text); border-color: var(--border-hover); }
.timer-extras button[aria-checked="true"] { border-color: var(--accent); color: var(--accent-ink); background: var(--accent-soft); }
#timer-popout { color: var(--text); border-color: var(--border-strong); }
.timer-extras [role="switch"] .when-off, .timer-extras [role="switch"] .when-on { display: inline; }   /* switches only: the full-screen button keeps the app-wide icon swap */
.timer-extras button[aria-checked="false"] .when-on { display: none; }
.timer-extras button[aria-checked="true"]  .when-off { display: none; }

/* Full screen: hide chrome, blow up the display */
body.present .timer-stage { border: none; box-shadow: none; background: transparent; max-height: none; }
body.present .timer-mode-row,
body.present .timer-setup,
body.present .timer-actions,
body.present .timer-extras { display: none; }
body.present .timer-display-wrap { width: min(72vh, 88vw); }
body.present .timer-display { font-size: clamp(6rem, 26vh, 20rem); }
body.present .timer-label { font-size: clamp(1.4rem, 3vw, 2.4rem); }

/* Pop-out / PiP window — no app chrome, ring fills the window */
body.timer-popout {
  margin: 0; min-height: 100vh; min-height: 100dvh;
  background: var(--bg-0);
  background-image: none;      /* projector view stays flat — no theme texture */
  display: grid; place-items: center; gap: 0.6rem;
  overflow: hidden;
}
body.timer-popout .timer-popout-inner {
  width: min(96vw, 96vh);
  display: grid; place-items: center; gap: 0.5rem;
}
body.timer-popout .timer-display-wrap {
  width: min(84vh, 92vw);
  aspect-ratio: 1 / 1;
}
body.timer-popout .timer-display { font-size: clamp(4rem, 26vmin, 14rem); }
body.timer-popout .timer-label { font-size: clamp(1rem, 3.6vmin, 2rem); }
/* the projector views get the same themed dial, just bigger */
body.timer-popout .dial-segs line { stroke-width: 2.2; }
body.present .dial-segs line, body.present .dial-arc { stroke-width: 2.2; }
body.present .dial-arc, body.timer-popout .dial-arc { stroke-width: 6; }

/* ---------------------------------------------------------------------------
   Timer — default skin: arc-reactor dial. The segment ring and the
   instrument marks carry it; the smooth arc belongs to pink and stays off.
   --------------------------------------------------------------------------- */
:root[data-theme="default"] .dial-arc, :root[data-theme="halloween"] .dial-arc, :root[data-theme="thanksgiving"] .dial-arc, :root:not([data-theme]) .dial-arc { display: none; }
.timer-label { text-transform: uppercase; letter-spacing: var(--track-label); }

/* ---------------------------------------------------------------------------
   Timer — pink skin: the bunny IS the dial (like halloween's pumpkin). No ring
   and no little mascot under it. Three layers fill the wrap:
     ::before      the spent bunny — a pale outline
     ::after       the coloured bunny, clipped from the TOP down by --remaining
                   (one constant sweep from the ear tips, y 2, to the feet,
                   y 99.2: 2% + (1 − r) × 97.2%)
     .timer-flash  the front, always shown: the face, and the paws holding a
                   big heart with the time in it
   Personality from data-mood (set by paintDial): calm → breathes softly;
   excited (final minute) → ears perk up, eyes widen, an "oh!"; party (zero) →
   the colour refills from the feet up, ears bounce, the bunny hops, a big
   smile, and the hearts + sparkles burst. Digits: Comfortaa, sized and centred
   in the heart per text length (see the numbers below).
   --------------------------------------------------------------------------- */
:root[data-theme="pink"] .timer-dial { display: none; }
:root[data-theme="pink"] .timer-mascot { display: none; }              /* the bunny is the dial now */
:root[data-theme="pink"] .timer-display-wrap { container-type: inline-size; isolation: isolate; width: min(50vh, 46vw, 31rem); }
:root[data-theme="pink"] body.present .timer-display-wrap { width: min(80vh, 90vw); }
:root[data-theme="pink"] body.timer-popout .timer-display-wrap { width: min(94vh, 96vw); }
:root[data-theme="pink"] body.timer-popout .timer-label:not(.empty) ~ .timer-display-wrap { width: min(84vh, 96vw); }
:root[data-theme="pink"] .timer-display-wrap::before,
:root[data-theme="pink"] .timer-display-wrap::after {
  content: "";
  position: absolute; inset: 0; z-index: 0;
  background: var(--pink-bunny-spent) center / contain no-repeat;
  pointer-events: none;
}
:root[data-theme="pink"] .timer-display-wrap::before { filter: drop-shadow(0 0.5rem 1rem rgba(232, 70, 124, 0.16)); }   /* the whole silhouette, never clipped */
:root[data-theme="pink"] .timer-display-wrap::after {
  background-image: var(--pink-bunny-lit);
  clip-path: inset(calc(2% + (1 - var(--remaining, 1)) * 97.2%) 0 0 0);
  transition: clip-path 500ms linear;   /* no drop-shadow here: the clip would cut it into a box */
}
:root[data-theme="pink"] .timer-display-wrap[data-mood="excited"]::before, :root[data-theme="pink"] .timer-display-wrap[data-mood="party"]::before { background-image: var(--pink-bunny-spent-perk); }
:root[data-theme="pink"] .timer-display-wrap[data-mood="excited"]::after { background-image: var(--pink-bunny-lit-perk); }
:root[data-theme="pink"] .timer-flash {
  display: block; z-index: 1;
  position: absolute; inset: 0; border-radius: 0; box-shadow: none; opacity: 1;
  background: var(--pink-bunny-face) center / contain no-repeat;
  animation: none;
}
:root[data-theme="pink"] .timer-display-wrap[data-mood="excited"] .timer-flash { background-image: var(--pink-bunny-face-wide); }
:root[data-theme="pink"] .timer-display-wrap[data-mood="party"] .timer-flash { background-image: var(--pink-bunny-face-party); }

/* the time, in the heart */
:root[data-theme="pink"] .timer-display-wrap .timer-display {
  z-index: 2;
  color: var(--text);
  /* Sized and centred per text length: the largest size whose ink box keeps
     an even ~2.5 units of clearance inside the heart and clear of both paws,
     at the spot where that clearance is most even. Comfortaa's ink is 0.81em
     tall and its centre sits 0.06em above the line-box centre (hence + 0.06em).
     Re-measure if the heart, the paws or the face change. */
  font-size: 14cqi;
  translate: 0 calc(29cqi + 0.06em);                  /* mm:ss → y 79 */
}
:root[data-theme="pink"] .timer-display-wrap .timer-display[data-len="4"] { font-size: 15.25cqi; translate: 0 calc(29.4cqi + 0.06em); }   /* m:ss */
:root[data-theme="pink"] .timer-display-wrap .timer-display[data-len="7"] { font-size: 12.25cqi; translate: 0 calc(28.3cqi + 0.06em); }   /* h:mm:ss */
:root[data-theme="pink"] .timer-display-wrap .timer-display[data-len="8"] { font-size: 11.25cqi; translate: 0 calc(28cqi + 0.06em); }     /* hh:mm:ss */
:root[data-theme="pink"] .timer-display-wrap .timer-display.warn { color: var(--accent-deep); }
:root[data-theme="pink"] .timer-display-wrap .timer-display.alert { color: var(--accent-deep); animation: none; }

/* calm: a slow breath while it runs */
:root[data-theme="pink"] .timer-display-wrap[data-mood="calm"] { animation: bunny-breathe 3.2s ease-in-out infinite; }
@keyframes bunny-breathe { 0%, 100% { scale: 1; } 50% { scale: 1.018; } }
/* zero: the colour pours back in from the feet, the ears bounce (the pose
   flips between perked and calm), the bunny hops, the burst flies */
:root[data-theme="pink"] .timer-display-wrap[data-mood="party"]::after {
  clip-path: inset(0 0 0 0);
  transition: clip-path 700ms ease-out;
  animation: bunny-ears 520ms steps(1, end) infinite;
}
@keyframes bunny-ears {
  0%   { background-image: var(--pink-bunny-lit-perk); }
  50%  { background-image: var(--pink-bunny-lit); }
}
:root[data-theme="pink"] .timer-display-wrap[data-mood="party"] { animation: bunny-hop 1.04s cubic-bezier(.34, 1.56, .64, 1) infinite; }
@keyframes bunny-hop {
  0%, 100% { translate: 0 0; }
  35%      { translate: 0 -4cqi; }
  60%      { translate: 0 0; }
}
:root[data-theme="pink"] .timer-burst { display: block; z-index: 3; }
:root[data-theme="pink"] .timer-display-wrap .timer-burst i { width: 9cqi; height: 9cqi; margin: -4.5cqi 0 0 -4.5cqi; }
:root[data-theme="pink"] .timer-display-wrap[data-state="alert"] .timer-burst i { animation: bunny-burst 1.5s ease-out infinite; animation-delay: calc(var(--i) * 90ms); }
@keyframes bunny-burst {
  0%   { opacity: 0; transform: rotate(calc(var(--i) * 45deg)) translateY(0) scale(0.5); }
  25%  { opacity: 1; }
  100% { opacity: 0; transform: rotate(calc(var(--i) * 45deg)) translateY(-48cqi) scale(1.2); }
}
@media (prefers-reduced-motion: reduce) {
  :root[data-theme="pink"] .timer-display-wrap, :root[data-theme="pink"] .timer-display-wrap::after, :root[data-theme="pink"] .timer-display-wrap .timer-burst i { animation: none !important; }
  :root[data-theme="pink"] .timer-display-wrap::after { transition: none; }
}

:root[data-theme="pink"] .timer-display {
  font-family: "Comfortaa", "Quicksand", var(--font-body);
  font-weight: 700;
  letter-spacing: 0.01em;
  text-shadow: none;
}
:root[data-theme="pink"] .timer-label {
  font-family: var(--font-body);
  text-transform: none;              /* sentence case, on request */
  letter-spacing: 0.01em;
  font-weight: var(--fw-semibold);
  color: var(--text-2);
}
/* the zero burst: hearts + sparkles flying outward (pink) */
.timer-burst i {
  position: absolute;
  left: 50%; top: 50%;
  width: 1.1rem; height: 1.1rem;
  margin: -0.55rem 0 0 -0.55rem;
  opacity: 0;
  background: no-repeat center / contain;
  transform: rotate(calc(var(--i) * 45deg)) translateY(0);
}
.timer-burst i.heart { background-image: var(--pink-burst-heart); }
.timer-burst i.spark { background-image: var(--pink-burst-spark); }
/* the wheel's mascot still uses these */
@keyframes mascot-breathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.05); } }
@keyframes mascot-wiggle { 0%, 100% { transform: rotate(-7deg); } 50% { transform: rotate(7deg); } }
@keyframes mascot-bounce { 0%, 100% { transform: translateY(0) scale(1); } 40% { transform: translateY(-0.7rem) scale(1.08); } }

/* ---------------------------------------------------------------------------
   Timer — halloween skin: the pumpkin IS the dial. No ring: three stacked
   layers fill the whole wrap —
     ::before      the spent pumpkin (dark purple, dark stem)
     ::after       the lit pumpkin, clipped from the TOP down by --remaining
                   (0..1, set by paintDial), so the dark spreads downward and
                   the glow shrinks toward the base; the graphic spans y 1.8–96.3
                   of the box, hence 1.8% + (1 − r) × 94.5% (stem included)
     .timer-flash  the carved face, always lit like a candle inside
   The digits sit in the grin (box x 28–72, y 50–68 of the dial), sized in cqi
   by their length (data-len, also set by paintDial) so they never spill past
   the carving. Final minute: the pumpkin pulses and the glow builds. Zero:
   the face flares, then only the face glows on a dark pumpkin. A component
   skin, so it shows in the pop-out and PiP too.
   --------------------------------------------------------------------------- */
:root[data-theme="halloween"] .timer-display-wrap { container-type: inline-size; isolation: isolate; width: min(48vh, 46vw, 31rem); }   /* 48vh: the whole stage fits 1366×768 with the label line and setup row always present */
:root[data-theme="halloween"] body.present .timer-display-wrap { width: min(80vh, 90vw); }
:root[data-theme="halloween"] body.timer-popout .timer-display-wrap { width: min(94vh, 96vw); }
/* with a label above it, leave the label its line */
:root[data-theme="halloween"] body.timer-popout .timer-label:not(.empty) ~ .timer-display-wrap { width: min(84vh, 96vw); }
:root[data-theme="halloween"] .timer-dial { display: none; }
:root[data-theme="halloween"] .timer-display-wrap::before,
:root[data-theme="halloween"] .timer-display-wrap::after {
  content: "";
  position: absolute; inset: 0; z-index: 0;
  background: var(--halloween-pumpkin-spent) center / contain no-repeat;
  pointer-events: none;
}
:root[data-theme="halloween"] .timer-display-wrap::after {
  background-image: var(--halloween-pumpkin-lit);
  /* ONE constant-speed sweep over the whole graphic, stem included: from the
     top of the stem (y 1.8) to the bottom of the body (y 96.3), so the stem
     drains in its own share of the time (the first ~19%), never instantly.
     No drop-shadow here: the clip would cut its glow into a hard box. */
  clip-path: inset(calc(1.8% + (1 - var(--remaining, 1)) * 94.5%) 0 0 0);
  transition: clip-path 500ms linear, opacity 400ms ease;
}
/* at zero everything is dark; fading the layer as well makes it certain */
:root[data-theme="halloween"] .timer-display-wrap[data-state="alert"]::after { opacity: 0; }
:root[data-theme="halloween"] .timer-flash {
  display: block; z-index: 1;
  inset: 0; border-radius: 0; box-shadow: none; opacity: 1;
  background: var(--halloween-pumpkin-face) center / contain no-repeat;
  filter: drop-shadow(0 0 0.5rem rgba(255, 179, 71, 0.75)) drop-shadow(0 0 1.4rem rgba(255, 117, 24, 0.45));
  animation: none;
}
:root[data-theme="halloween"] .timer-display-wrap .timer-display {
  z-index: 2;
  font-family: var(--font-spooky);
  letter-spacing: 0.04em;
  font-size: 18.5cqi;
  color: var(--halloween-candle);
  text-shadow: 0 0 2px var(--halloween-carve), 0 0 0.35em rgba(255, 179, 71, 0.85), 0 0 0.9em rgba(255, 117, 24, 0.55);
  /* Sized and centred per text length on the grin's VISUAL centre, not its
     bounding box: the largest size whose ink box keeps an even ~5 units of
     clearance from the carved edge on every side, at the spot where that
     clearance is most even. Pirata One's ink is 0.8em tall and its centre sits
     0.02em above the line-box centre (hence + 0.02em in y); x: letter-spacing
     trails the last glyph. Re-measure if the mouth path or the face changes. */
  translate: 0.02em calc(13.6cqi + 0.02em);        /* mm:ss → 18.5cqi at y 63.6 */
}
:root[data-theme="halloween"] .timer-display-wrap .timer-display[data-len="4"] { font-size: 20cqi; translate: 0.02em calc(14.1cqi + 0.02em); }     /* m:ss */
:root[data-theme="halloween"] .timer-display-wrap .timer-display[data-len="7"] { font-size: 16cqi; translate: 0.02em calc(12.6cqi + 0.02em); }     /* h:mm:ss */
:root[data-theme="halloween"] .timer-display-wrap .timer-display[data-len="8"] { font-size: 13.5cqi; translate: 0.02em calc(11.6cqi + 0.02em); }   /* hh:mm:ss */
:root[data-theme="halloween"] .timer-display-wrap .timer-display.warn { color: var(--accent); }
/* the label above the pumpkin in the same gothic face, a size up, with the
   candle glow */
:root[data-theme="halloween"] .timer-label {
  font-family: var(--font-spooky);
  font-weight: 400;
  font-size: clamp(1.5rem, 2.8vw, 2.2rem);
  letter-spacing: 0.04em;
  text-transform: none;            /* all-caps blackletter is unreadable: keep what was typed */
  color: var(--halloween-candle);
  text-shadow: 0 0 0.4em rgba(255, 117, 24, 0.6);
}
:root[data-theme="halloween"] body.present .timer-label,
:root[data-theme="halloween"] body.timer-popout .timer-label { font-size: clamp(2.2rem, 6vmin, 3.8rem); }
:root[data-theme="halloween"] .timer-display-wrap .timer-display.alert { color: var(--halloween-candle); animation: none; }

/* final minute: the whole pumpkin breathes and the glow builds */
:root[data-theme="halloween"] .timer-display-wrap[data-state="warn"] { animation: pumpkin-pulse 2.4s ease-in-out infinite; }
:root[data-theme="halloween"] .timer-display-wrap[data-state="warn"]::after { animation: pumpkin-glow 2.4s ease-in-out infinite; }
@keyframes pumpkin-glow { 0%, 100% { filter: brightness(1); } 50% { filter: brightness(1.22) saturate(1.1); } }
:root[data-theme="halloween"] .timer-display-wrap[data-state="warn"] .timer-flash { animation: face-build 2.4s ease-in-out infinite; }
@keyframes pumpkin-pulse { 0%, 100% { scale: 1; } 50% { scale: 1.035; } }
@keyframes face-build {
  0%, 100% { filter: drop-shadow(0 0 0.6rem rgba(255, 179, 71, 0.8)) drop-shadow(0 0 1.6rem rgba(255, 117, 24, 0.5)); }
  50%      { filter: drop-shadow(0 0 1rem rgba(255, 210, 90, 1)) drop-shadow(0 0 2.8rem rgba(255, 140, 40, 0.8)); }
}
/* zero: the face flares bright, then settles to a steady glow on the dark pumpkin */
:root[data-theme="halloween"] .timer-display-wrap[data-state="alert"] .timer-flash { animation: face-flare 1.6s ease-out forwards; }
:root[data-theme="halloween"] .timer-display-wrap[data-state="alert"] .timer-display { animation: face-flare-text 1.6s ease-out forwards; }
@keyframes face-flare {
  0%   { filter: brightness(1) drop-shadow(0 0 0.6rem rgba(255, 179, 71, 0.8)); }
  18%  { filter: brightness(1.9) drop-shadow(0 0 2.2rem rgba(255, 230, 140, 1)) drop-shadow(0 0 4.5rem rgba(255, 140, 40, 1)); }
  100% { filter: brightness(1.15) drop-shadow(0 0 0.9rem rgba(255, 190, 80, 0.95)) drop-shadow(0 0 2.4rem rgba(255, 117, 24, 0.7)); }
}
@keyframes face-flare-text { 0% { opacity: 1; } 18% { opacity: 1; scale: 1.08; } 100% { opacity: 1; scale: 1; } }
@media (prefers-reduced-motion: reduce) {
  :root[data-theme="halloween"] .timer-display-wrap, :root[data-theme="halloween"] .timer-display-wrap::after, :root[data-theme="halloween"] .timer-display-wrap .timer-flash, :root[data-theme="halloween"] .timer-display-wrap .timer-display { animation: none !important; }
  :root[data-theme="halloween"] .timer-display-wrap::after { transition: none; }
}

/* ---------------------------------------------------------------------------
   Timer — thanksgiving skin: a pumpkin pie IS the dial, seen from above.
   timer.js buildDial() adds .tg-pie (one SVG, viewBox 0 0 100 100) to every
   wrap — page, pop-out and PiP — and paintDial() marks slices .gone as time
   runs down: lit = ceil(remaining × 8), so a slice lifts away (slides out
   along its own angle and fades) every eighth of the time, leaving the empty
   dish under it. Layers: shadow · dish (rim, well, crumbs) · the eight
   slices (crust wedge, filling wedge, crimps on the edge, the cut lines) ·
   steam · the fork. The digits sit large in the middle, cocoa with a cream
   halo, readable on the filling and on the bare dish alike.
   Final minute: the slices left glow warm (a pulsing drop-shadow) and steam
   curls up. Zero: the last slice goes, and a fork drops in beside the dish
   with a little bounce. Outside thanksgiving .tg-pie stays hidden.
   --------------------------------------------------------------------------- */
.tg-pie { display: none; }
:root[data-theme="thanksgiving"] .timer-dial { display: none; }
:root[data-theme="thanksgiving"] .timer-flash,
:root[data-theme="thanksgiving"] .timer-burst { display: none; }
:root[data-theme="thanksgiving"] .timer-display-wrap { container-type: inline-size; isolation: isolate; width: min(48vh, 46vw, 31rem); }
:root[data-theme="thanksgiving"] body.present .timer-display-wrap { width: min(80vh, 90vw); }
:root[data-theme="thanksgiving"] body.timer-popout .timer-display-wrap { width: min(94vh, 96vw); }
:root[data-theme="thanksgiving"] body.timer-popout .timer-label:not(.empty) ~ .timer-display-wrap { width: min(84vh, 96vw); }
:root[data-theme="thanksgiving"] .tg-pie {
  display: block;
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  overflow: visible;
  pointer-events: none;
}
.tg-pie * { transform-box: view-box; }

/* gradients: one colour per stop (SVG gradients don't premultiply) */
#tg-g-fill stop:nth-child(1) { stop-color: var(--tg-fill-1); }
#tg-g-fill stop:nth-child(2) { stop-color: var(--tg-fill-2); }
#tg-g-fill stop:nth-child(3) { stop-color: var(--tg-fill-3); }
#tg-g-well stop:nth-child(1) { stop-color: var(--tg-dish-well); }
#tg-g-well stop:nth-child(2) { stop-color: var(--tg-dish); }

/* the dish */
.tg-shadow { fill: var(--tg-dish-shadow); filter: blur(1.6px); }
.tg-dish { fill: var(--tg-dish); stroke: var(--tg-dish-line); stroke-width: 0.8; }
.tg-dish-rim { fill: none; stroke: var(--tg-dish-rim); stroke-width: 1.4; }
.tg-dish-well { fill: url(#tg-g-well); stroke: var(--tg-dish-rim); stroke-width: 0.7; }
.tg-crumbs circle { fill: var(--tg-crumb); opacity: 0.75; }

/* a slice: crust, filling, crimps, cuts */
.tg-crust { fill: var(--tg-crust); stroke: var(--tg-crust-dark); stroke-width: 0.6; stroke-linejoin: round; }
.tg-filling { fill: url(#tg-g-fill); }
.tg-crimp { fill: var(--tg-crust); stroke: var(--tg-crust-dark); stroke-width: 0.4; }
.tg-crimp-hi { fill: none; stroke: var(--tg-crust-hi); stroke-width: 0.9; stroke-linecap: round; }
.tg-cut { fill: none; stroke: var(--tg-cut); stroke-width: 0.5; stroke-linecap: round; }
.tg-shine { fill: none; stroke: var(--tg-cream); stroke-opacity: 0.12; stroke-width: 1.4; stroke-linecap: round; }
.tg-slice {
  transform-origin: 50px 50px;
  transition: opacity 650ms ease, translate 650ms cubic-bezier(.3, .7, .4, 1), scale 650ms ease;
}
.tg-slice.gone { opacity: 0; translate: calc(var(--dx) * 1px) calc(var(--dy) * 1px); scale: 0.94; }

/* final minute: what's left glows, and steam curls up off it */
.tg-steam path { fill: none; stroke: var(--tg-steam); stroke-width: 2.2; stroke-linecap: round; opacity: 0; transform-origin: 50px 40px; }
:root[data-theme="thanksgiving"] .timer-display-wrap[data-state="warn"] .tg-slices { animation: tg-glow 2.2s ease-in-out infinite; }
:root[data-theme="thanksgiving"] .timer-display-wrap[data-state="warn"] .tg-steam path { animation: tg-steam 3.4s ease-out infinite; }
.tg-steam path:nth-child(2) { animation-delay: -1.1s !important; }
.tg-steam path:nth-child(3) { animation-delay: -2.2s !important; }
@keyframes tg-glow {
  0%, 100% { filter: drop-shadow(0 0 1.2px var(--tg-glow)) brightness(1.02); }
  50%      { filter: drop-shadow(0 0 3.4px var(--tg-glow)) brightness(1.12); }
}
@keyframes tg-steam {
  0%   { opacity: 0; transform: translateY(8px) scale(0.8, 0.7); }
  30%  { opacity: 0.9; }
  100% { opacity: 0; transform: translateY(-12px) scale(1.2, 1.1); }
}

/* zero: a fork drops in beside the empty dish */
.tg-fork { opacity: 0; transform-origin: 88px 88px; }
.tg-fork .tg-tines, .tg-fork .tg-handle { fill: var(--tg-fork); stroke: var(--tg-fork-line); stroke-width: 0.5; stroke-linejoin: round; }
.tg-fork .tg-fork-hi { fill: none; stroke: var(--tg-fork-hi); stroke-width: 0.6; stroke-linecap: round; }
:root[data-theme="thanksgiving"] .timer-display-wrap[data-state="alert"] .tg-fork { animation: tg-fork 760ms cubic-bezier(.3, .6, .4, 1) forwards; }
@keyframes tg-fork {
  0%   { opacity: 0; transform: translate(10px, -26px) rotate(-38deg) scale(1.15); }
  55%  { opacity: 1; transform: translate(0, 1.5px) rotate(4deg) scale(1); }
  75%  { transform: translate(0, -1.2px) rotate(-2deg); }
  100% { opacity: 1; transform: none; }
}

/* the digits, large in the middle of the pie */
:root[data-theme="thanksgiving"] .timer-display-wrap .timer-display {
  z-index: 2;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 21cqi;
  letter-spacing: 0.01em;
  color: var(--tg-digit);
  text-shadow: 0 0 2px var(--tg-digit-halo), 0 0 0.12em var(--tg-digit-halo), 0 0 0.3em rgba(255, 244, 226, 0.55);
}
:root[data-theme="thanksgiving"] .timer-display-wrap .timer-display[data-len="4"] { font-size: 23cqi; }
:root[data-theme="thanksgiving"] .timer-display-wrap .timer-display[data-len="7"] { font-size: 17cqi; }
:root[data-theme="thanksgiving"] .timer-display-wrap .timer-display[data-len="8"] { font-size: 15cqi; }
:root[data-theme="thanksgiving"] .timer-display-wrap .timer-display:is(.warn, .alert) { color: var(--tg-digit); animation: none; }
:root[data-theme="thanksgiving"] .timer-display-wrap[data-state="alert"] .timer-display { animation: tg-done 1.2s ease-out 1; }
@keyframes tg-done { 0% { scale: 1; } 25% { scale: 1.1; } 100% { scale: 1; } }

@media (prefers-reduced-motion: reduce) {
  :root[data-theme="thanksgiving"] .timer-display-wrap .tg-slices,
  :root[data-theme="thanksgiving"] .timer-display-wrap .tg-steam path,
  :root[data-theme="thanksgiving"] .timer-display-wrap .timer-display { animation: none !important; }
  /* the steam still shows, still; the fork just appears */
  :root[data-theme="thanksgiving"] .timer-display-wrap[data-state="warn"] .tg-steam path { opacity: 0.6; }
  :root[data-theme="thanksgiving"] .timer-display-wrap[data-state="warn"] .tg-slices { filter: drop-shadow(0 0 2.4px var(--tg-glow)); }
  :root[data-theme="thanksgiving"] .timer-display-wrap[data-state="alert"] .tg-fork { animation: none !important; opacity: 1; }
  .tg-slice { transition: opacity 400ms ease !important; }
}

/* The window buttons on Timer, Noise Meter and Name Wheel: "Full screen
   window" (the projector pop-out) and "Mini window" (the Timer's
   always-on-top Picture-in-Picture), each an icon + label. */
.win-btn { display: inline-flex; align-items: center; gap: 0.45rem; }
.win-btn svg { width: 1.05em; height: 1.05em; flex: none; }
.win-btn[hidden] { display: none; }

/* ============================================================================
   Noise Meter (noise.html + noise-popout.html)
   One stage box: presets, the meter (big number + bar + zone word), controls,
   the two zone handles, and the privacy line. Zone colour comes from
   data-zone on .noise-meter, so the pop-out and full screen reuse the same
   rules. Colours are the --noise-* tokens (defined per theme).
   ============================================================================ */
.noise-page {
  display: grid; place-items: center;
  padding: var(--space-3) var(--space-4);
  min-height: 0;
}
.noise-stage {
  width: min(56rem, 96vw);
  max-height: calc(100dvh - 5.5rem);
  display: grid;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  justify-items: center;
  text-align: center;
}
.noise-preset-seg { grid-template-columns: repeat(3, auto); }

.noise-meter { width: 100%; display: grid; gap: var(--space-2); justify-items: center; }
.noise-read { display: flex; align-items: baseline; gap: 0.4rem; }
.noise-db {
  font: var(--fw-regular) clamp(3rem, 13vh, 6rem)/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text);
  letter-spacing: 0.02em;
  text-shadow: 0 0 24px var(--shadow-text);
}
.noise-unit {
  font-family: var(--font-mono);
  font-size: clamp(0.8rem, 1.6vh, 1.05rem);
  letter-spacing: 0.12em;
  color: var(--text-muted);
  text-transform: uppercase;
}
.noise-unit .approx { margin-left: 0.1em; }

/* The level bar, every theme: one continuous track filling left → right.
   noise.js sets --level (0..1, zone-aligned: quiet 0–30%, OK 30–70%, too
   loud 70–100%) on .noise-meter at every poll; the fill and the rider ease
   between polls, so the bar glides with the (already 500ms-smoothed) level
   instead of stepping. The fill is a full-width gradient revealed by
   clip-path, so its colour shifts through the zones exactly as it crosses
   each marker. Look per theme comes from the --noise-bar-* tokens. */
.noise-bar {
  --bar-h: clamp(1.5rem, 4vh, 2.4rem);
  --bar-ease: 260ms linear;
  position: relative;
  width: 100%;
  height: var(--bar-h);
  border-radius: var(--radius-pill);
  background: var(--noise-bar-track);
  border: 1px solid var(--noise-bar-line);
  box-shadow: var(--noise-bar-inset);
  transition: border-color 400ms ease, box-shadow 400ms ease;
}
.noise-fill {
  position: absolute; inset: 0;
  border-radius: inherit;
  background: var(--noise-bar-fill);
  clip-path: inset(0 calc((1 - var(--level, 0)) * 100%) 0 0 round var(--radius-pill));
  transition: clip-path var(--bar-ease);
}
/* the two thresholds: thin markers standing a little proud of the track */
.noise-mark {
  position: absolute; top: -0.3rem; bottom: -0.3rem; z-index: 1;
  width: 2px; margin-left: -1px;
  border-radius: 1px;
  background: var(--noise-bar-mark);
  pointer-events: none;
}
#mark-quiet { left: 30%; }
#mark-loud  { left: 70%; }
/* the rider at the fill's leading edge: default a bright cap line in the
   zone colour, the other themes their --noise-rider picture */
.noise-rider {
  position: absolute; top: 50%; left: calc(var(--level, 0) * 100%); z-index: 2;
  width: 1rem; height: calc(var(--bar-h) + 0.7rem);
  translate: -50% -50%;
  display: grid; place-items: center;
  pointer-events: none;
  transition: left var(--bar-ease), opacity 300ms ease;
}
.noise-rider i { display: block; width: 3px; height: 100%; border-radius: 2px; background: currentColor; box-shadow: 0 0 0.5rem currentColor, 0 0 1rem currentColor; }
.noise-meter[data-zone="quiet"] .noise-rider { color: var(--noise-quiet); }
.noise-meter[data-zone="ok"]    .noise-rider { color: var(--noise-ok); }
.noise-meter[data-zone="loud"]  .noise-rider { color: var(--noise-loud); }
.noise-meter[data-running="0"] .noise-rider { opacity: 0; }
/* default: an instrument. Tick marks along the track (short every 5%, long
   every 10%), and a thin bright fill down the middle of it. */
.noise-bar::before {
  content: ""; position: absolute; inset: 0 0.6rem; pointer-events: none;
  background:
    repeating-linear-gradient(90deg, var(--noise-bar-tick) 0 1px, transparent 1px 10%) center / 100% 70% no-repeat,
    repeating-linear-gradient(90deg, var(--noise-bar-tick) 0 1px, transparent 1px 5%) center / 100% 34% no-repeat;
  opacity: 0.8;
}
:root:not([data-theme]) .noise-fill, :root[data-theme="default"] .noise-fill { inset: 36% 0; }
.noise-scale {
  display: grid; grid-template-columns: 30% 40% 30%;   /* under their own bands */
  text-align: center;
  width: 100%;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.noise-zone {
  font: var(--fw-semibold) clamp(1.1rem, 3.2vh, 1.9rem)/1 var(--font-display);
  letter-spacing: var(--track-display);
  text-transform: var(--label-transform);
  color: var(--text-2);
}

/* zone colours — the zone word, and (in the red) the bar outline */
.noise-meter[data-zone="quiet"] .noise-zone { color: var(--noise-quiet); }
.noise-meter[data-zone="ok"]    .noise-zone { color: var(--noise-ok); }
.noise-meter[data-zone="loud"]  .noise-zone { color: var(--noise-loud); }
.noise-meter[data-running="0"] .noise-zone { color: var(--text-muted); }

/* held in the red: the whole meter states it, not just the colour */
.noise-meter[data-alert="1"] .noise-bar {
  border-color: var(--noise-loud);
  box-shadow: 0 0 0 2px var(--noise-loud-soft), 0 0 22px var(--noise-loud-soft);
}
.noise-meter[data-alert="1"] .noise-zone,
.noise-meter[data-alert="1"] .noise-db {
  color: var(--noise-loud);
  animation: noise-flash 1100ms ease-in-out infinite;
}
@keyframes noise-flash {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.5; }
}

.noise-controls { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; }
.noise-controls .primary.big { min-width: 9rem; min-height: 2.8rem; padding: 0.5rem 1.6rem; font-size: var(--fs-lg); letter-spacing: 0.08em; }
.noise-controls button { min-height: 2.4rem; padding: 0.4rem 1rem; }
.noise-foot-row .icon-btn { width: 2.4rem; min-height: 2.4rem; padding: 0; flex: none; }
/* the chime switch's two labels (scoped: the full-screen button's icons use the app-wide aria-pressed swap) */
#noise-mute .when-off, #noise-mute .when-on { display: inline; }
#noise-mute[aria-checked="false"] .when-on { display: none; }
#noise-mute[aria-checked="true"]  .when-off { display: none; }

/* the two zone handles side by side: one row, short labels */
.noise-zones { flex: 1 1 24rem; min-width: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); }
.noise-zone-field { display: grid; grid-template-columns: auto minmax(4rem, 1fr) 2ch; align-items: center; gap: var(--space-2); }
.noise-zone-field .hud-key { white-space: nowrap; }
.noise-zone-field .hud-key { text-align: left; }
.noise-zone-field input[type="range"] { width: 100%; min-width: 0; accent-color: var(--accent); }   /* its intrinsic ~130px would push the stage past a phone screen */
.noise-out { font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--text-2); text-align: right; }

/* header row: presets centred, the status line (Listening / Microphone off /
   errors) at the right, inside the card instead of floating above it */
.noise-head {
  width: 100%;
  display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center; gap: var(--space-3);
}
.noise-head .noise-preset-seg { grid-column: 2; }
.noise-head .status { grid-column: 3; justify-self: end; margin: 0; min-height: 0; font-size: var(--fs-xs); text-align: right; }
.noise-head .status:empty { display: none; }
@media (max-width: 900px) {
  .noise-head { grid-template-columns: minmax(0, 1fr); justify-items: center; }
  .noise-head .noise-preset-seg, .noise-head .status { grid-column: 1; justify-self: center; text-align: center; }
}

/* footer: the controls, then the one-line privacy note (full text behind "?") */
/* footer: [buttons] [two handles] [full screen] on one row (wrapping on a
   narrow screen), the privacy line centred under it */
.noise-foot { width: 100%; display: grid; gap: var(--space-2); justify-items: center; }
.noise-foot-row { width: 100%; display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--space-3) var(--space-5); }
.noise-privacy {
  margin: 0;
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-size: var(--fs-xs);
  color: var(--text-muted);
}
.noise-privacy svg { width: 0.95em; height: 0.95em; flex: none; }
.noise-info-btn {
  width: 1.35rem; height: 1.35rem; min-height: 0; padding: 0;
  border-radius: 50%;
  font-size: 0.72rem; font-weight: var(--fw-bold); line-height: 1;
}
dialog.noise-dialog { width: min(30rem, 94vw); }
dialog.noise-dialog p:not(.gs-note) { margin: 0 0 var(--space-3); color: var(--text-2); line-height: 1.5; }

/* Full screen: chrome goes (.no-present), the meter takes the room */
body.present .noise-stage { border: none; box-shadow: none; background: transparent; max-height: none; width: 96vw; }
body.present .noise-db { font-size: clamp(6rem, 30vh, 18rem); }
body.present .noise-bar { --bar-h: clamp(2.6rem, 8vh, 5rem); border-width: 2px; }
body.present .noise-zone { font-size: clamp(2rem, 7vh, 4.5rem); }
body.present .noise-scale { font-size: clamp(0.8rem, 1.6vh, 1.1rem); }

/* Pop-out window — meter only, no app chrome */
body.noise-popout {
  margin: 0; min-height: 100vh; min-height: 100dvh;
  background: var(--bg-0);
  background-image: none;      /* projector view stays flat — no theme texture */
  display: grid; place-items: center;
  overflow: hidden;
}
body.noise-popout .noise-popout-inner {
  width: min(94vw, 60rem);
  display: grid; place-items: center; gap: 0.9rem;
}
body.noise-popout .noise-db { font-size: clamp(4rem, 22vmin, 12rem); }
body.noise-popout .noise-bar { --bar-h: clamp(2rem, 7vmin, 4rem); }
body.noise-popout .noise-zone { font-size: clamp(1.2rem, 5vmin, 3rem); }
.noise-link { margin: 0; font-size: var(--fs-sm); color: var(--text-muted); }
.noise-link[hidden] { display: none; }

/* Reduced motion: the bar keeps its easing — it IS the reading, and an eased
   value is calmer than a jumping one (the app-wide `* { transition: none
   !important }` needs the !important). The riders' bob / flap / sway and
   the flame flicker stop. */
@media (prefers-reduced-motion: reduce) {
  .noise-fill { transition: clip-path 260ms linear !important; }
  .noise-rider { transition: left 260ms linear, opacity 300ms ease !important; }
  .noise-rider i, .noise-fill { animation: none !important; }
  .noise-meter[data-alert="1"] .noise-zone,
  .noise-meter[data-alert="1"] .noise-db { animation: none; }
}

/* ---------------------------------------------------------------------------
   Noise Meter — pink skin: the bunny IS the meter (like the Timer bunny).
   noise.js builds .nm-stage (one inline SVG) into every .noise-meter;
   outside pink it stays hidden. In pink the bunny shows the room's mood and
   the level bar under it (lavender → pink → rose, a heart at the tip) the level:
     data-mood="off"    asleep and greyed (mic off)
     data-mood="quiet"  asleep: closed eyes, ears relaxed, soft breath, z's,
                        a mint glow
     data-mood="ok"     awake and happy: ears up and wiggling, a gentle bob,
                        little hearts, the pink glow
     data-mood="loud"   paws clamped over its ears, ears pressed flat, wide
                        worried eyes, a wobbly mouth, a shake; the fur warms to
                        coral and the hearts become sweat drops and stars
     data-alert="1"     (the existing 3s hold) a harder shake, a pulsing warm
                        halo, and the zone word as a solid red pill
   data-mood is the zone debounced by MOOD_HOLD_MS in noise.js, and every pose
   change is a transition, so a reading on a boundary never makes it flicker.
   Transform origins are in viewBox units (transform-box: view-box), so a
   moving ear never shifts the head's pivot.
   --------------------------------------------------------------------------- */
.nm-stage, .nh-stage, .nt-stage { display: none; }
/* Shared by the mascot themes (pink's bunny, halloween's ghost, thanksgiving's turkey): the bar,
   its scale and the dB unit go; the card fills the page and the mascot takes
   whatever height is left, in three groups: presets (+ status) · mascot,
   level, word, reading · the footer row. A flex column, not a grid, so full
   screen can drop the head and foot (.no-present) and the meter simply grows
   into their space. */
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) .noise-scale,
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) .noise-unit { display: none; }
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) .noise-meter[data-running="1"] .noise-db::before { content: "~"; }   /* "~55", not "55 DB≈" */
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) .noise-page { padding-top: var(--space-4); }
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) .noise-stage {
  flex: 1 1 auto; align-self: center;         /* main.page is a flex column */
  width: min(76rem, 100%); max-height: none; min-height: 26rem;
  display: flex; flex-direction: column; align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-5) var(--space-3);
}
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) .noise-meter {
  flex: 1 1 0; min-height: 0;
  display: flex; flex-direction: column; align-items: center; gap: 0;
}
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) .noise-meter .noise-mascot { order: 0; flex: 1 1 0; min-height: 9rem; max-width: 100%; aspect-ratio: 1; }
:root[data-theme="pink"] .noise-meter .nm-stage,
:root[data-theme="halloween"] .noise-meter .nh-stage { display: block; }
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) .noise-meter .noise-bar { order: 1; width: min(40rem, 92%); margin-top: var(--space-1, 0.25rem); flex: none; }
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) .noise-meter .noise-zone { order: 2; margin-top: var(--space-2); }
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) .noise-meter .noise-read { order: 3; margin-top: 0.15rem; }
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) .noise-db { font-size: clamp(0.95rem, 2.2vh, 1.25rem); color: var(--text-muted); text-shadow: none; }
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) .noise-meter[data-alert="1"] .noise-db { color: var(--text-muted); animation: none; }
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) .noise-head .status:not(:empty) {
  padding: 0.3rem 0.85rem;
  border-radius: var(--radius-pill);
  background: var(--card-2);
}
/* the footer row sits on a hairline so it reads as one strip */
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) .noise-foot { border-top: 1px solid var(--border); padding-top: var(--space-3); }
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) body.present .noise-stage { padding-block: var(--space-2); }
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) body.noise-popout .noise-popout-inner { gap: 0.6rem; }
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) body.noise-popout .noise-meter .noise-mascot { flex: none; width: min(calc(100vh - 11.5rem), 92vw); min-width: 10rem; }
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) body.noise-popout .noise-db { font-size: clamp(0.9rem, 2.6vh, 1.4rem); }

.nm-svg { display: block; width: 100%; height: 100%; overflow: visible; }
.nm-svg * { transform-box: view-box; }

/* drawing */
.nm-fur { fill: var(--nm-fur); stroke: var(--nm-line); stroke-width: 4.6; stroke-linejoin: round; transition: fill 700ms ease; }
.nm-ear-in { fill: var(--nm-ear); transition: fill 700ms ease; }
.nm-tummy { fill: var(--nm-tummy); transition: fill 700ms ease; }
.nm-sheen { fill: var(--nm-sheen); }
.nm-shadow { fill: var(--nm-shadow); }
.nm-bow path { fill: var(--nm-bow); stroke: var(--nm-line); stroke-width: 3; stroke-linejoin: round; }
.nm-knot { fill: var(--nm-bow-knot); stroke: var(--nm-line); stroke-width: 2.4; }
.nm-blush { fill: var(--nm-blush); opacity: 0.5; transition: fill 700ms ease, opacity 500ms ease; }
.nm-eyes ellipse { fill: var(--nm-line); }
.nm-glint { fill: var(--nm-glint); }
.nm-eyes.sleep path, .nm-brow, .nm-mouth, .nm-toe { fill: none; stroke: var(--nm-line); stroke-linecap: round; stroke-linejoin: round; }
.nm-eyes.sleep path { stroke-width: 4.2; }
.nm-brow { stroke-width: 3.6; }
.nm-mouth { stroke-width: 3.4; }
.nm-toe { stroke-width: 2.4; }
.nm-nose { fill: var(--nm-bow); stroke: var(--nm-line); stroke-width: 2.2; stroke-linejoin: round; }

/* glows behind — one per zone, crossfaded */
/* both stops share the colour and the outer one fades by stop-opacity: SVG
   gradients don't premultiply, so fading to `transparent` (black) goes grey */
#nm-g-quiet stop { stop-color: var(--nm-glow-quiet); }
#nm-g-ok    stop { stop-color: var(--nm-glow-ok); }
#nm-g-loud  stop { stop-color: var(--nm-glow-loud); }
.nm-svg radialGradient stop:last-child { stop-opacity: 0; }
.nm-halo { opacity: 0; transform-origin: 100px 112px; transition: opacity 800ms ease; }
.noise-meter[data-mood="quiet"] .nm-halo.quiet,
.noise-meter[data-mood="ok"]    .nm-halo.ok,
.noise-meter[data-mood="loud"]  .nm-halo.loud { opacity: 1; }
.noise-meter[data-alert="1"] .nm-halo.loud { animation: nm-halo-pulse 1.1s ease-in-out infinite; }
@keyframes nm-halo-pulse { 0%, 100% { opacity: 0.75; transform: scale(0.94); } 50% { opacity: 1; transform: scale(1.06); } }

/* faces — one set shown per mood; the eyes open/close as they fade */
.nm-eyes, .nm-mouth { opacity: 0; transition: opacity 320ms ease, transform 380ms ease; }
.nm-eyes { transform-origin: 100px 88px; transform: scaleY(0.25); }
.nm-mouth { transform-origin: 100px 112px; }
.noise-meter:is([data-mood="off"], [data-mood="quiet"]) :is(.nm-eyes.sleep, .nm-mouth.sleep),
.noise-meter[data-mood="ok"]   :is(.nm-eyes.open, .nm-mouth.open),
.noise-meter[data-mood="loud"] :is(.nm-eyes.wide, .nm-mouth.wide) { opacity: 1; transform: none; }

/* ears: relaxed asleep, up when happy, pressed flat when it's too loud */
.nm-ear { transition: transform 650ms cubic-bezier(.34, 1.4, .64, 1); }
.nm-ear.l, .nm-ear.l .nm-ear-wig { transform-origin: 72px 76px; }
.nm-ear.r, .nm-ear.r .nm-ear-wig { transform-origin: 128px 76px; }
.nm-ear.l { transform: rotate(-26deg); }
.nm-ear.r { transform: rotate(26deg); }
.noise-meter[data-mood="ok"] .nm-ear.l { transform: rotate(-6deg); }
.noise-meter[data-mood="ok"] .nm-ear.r { transform: rotate(6deg); }
.noise-meter[data-mood="loud"] .nm-ear.l { transform: rotate(-64deg) scaleY(0.84); }
.noise-meter[data-mood="loud"] .nm-ear.r { transform: rotate(64deg) scaleY(0.84); }
.noise-meter[data-mood="ok"] .nm-ear-wig { animation: nm-ear-wig 2.8s ease-in-out infinite; }
.noise-meter[data-mood="ok"] .nm-ear.r .nm-ear-wig { animation-delay: -1.4s; }
@keyframes nm-ear-wig { 0%, 70%, 100% { transform: rotate(0); } 80% { transform: rotate(-7deg); } 90% { transform: rotate(4deg); } }

/* asleep, the head tips a little to one side */
.nm-head { transform-origin: 100px 137px; transition: transform 800ms ease; }
.noise-meter:is([data-mood="off"], [data-mood="quiet"]) .nm-head { transform: rotate(-5deg); }

/* paws: folded on the tummy, up over the ears when it's too loud */
.nm-paw { transition: transform 520ms cubic-bezier(.34, 1.45, .64, 1); }
.nm-paw.l { transform-origin: 80px 150px; }
.nm-paw.r { transform-origin: 120px 150px; }
.noise-meter[data-mood="loud"] .nm-paw.l { transform: translate(-28px, -82px) rotate(-35deg); }
.noise-meter[data-mood="loud"] .nm-paw.r { transform: translate(28px, -82px) rotate(35deg); }

/* too loud: the colour warms */
.noise-meter[data-mood="loud"] .nm-fur   { fill: var(--nm-fur-hot); }
.noise-meter[data-mood="loud"] .nm-ear-in { fill: var(--nm-ear-hot); }
.noise-meter[data-mood="loud"] .nm-tummy { fill: var(--nm-tummy-hot); }
.noise-meter[data-mood="loud"] .nm-blush { fill: var(--nm-blush-hot); opacity: 0.7; }
.noise-meter[data-mood="ok"] .nm-blush { opacity: 0.62; }

/* whole-body motion: breathe asleep, bob when happy, shake when loud */
.nm-move { transform-origin: 100px 195px; transition: opacity 600ms ease, filter 600ms ease; }
.noise-meter[data-mood="off"] .nm-move { opacity: 0.62; filter: saturate(0.35); }
.noise-meter[data-mood="quiet"] .nm-move { animation: nm-breathe 3.6s ease-in-out infinite; }
.noise-meter[data-mood="ok"]    .nm-move { animation: nm-bob 1.5s ease-in-out infinite; }
.noise-meter[data-mood="loud"]  .nm-move { animation: nm-shake 380ms ease-in-out infinite; }
.noise-meter[data-mood="loud"][data-alert="1"] .nm-move { animation: nm-shake-hard 260ms linear infinite; }
@keyframes nm-breathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.022, 1.016); } }
@keyframes nm-bob     { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
@keyframes nm-shake   { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-2px) rotate(-0.6deg); } 75% { transform: translateX(2px) rotate(0.6deg); } }
@keyframes nm-shake-hard { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-4px) rotate(-1.4deg); } 75% { transform: translateX(4px) rotate(1.4deg); } }

/* the little extras around it — z's, hearts, then stars + sweat drops */
.nm-fx { opacity: 0; transition: opacity 500ms ease; }
.nm-fx > g { transform-box: fill-box; transform-origin: center; }
.noise-meter[data-mood="quiet"] .nm-fx.zzz,
.noise-meter[data-mood="ok"]    .nm-fx.hearts,
.noise-meter[data-mood="loud"]  .nm-fx.stars,
.noise-meter[data-mood="loud"]  .nm-fx.drops { opacity: 1; }
.nm-fx.zzz text { fill: var(--nm-z); font: 700 16px "Comfortaa", "Quicksand", var(--font-body); }
.nm-fx.zzz g:nth-child(2) text { font-size: 20px; }
.nm-fx.zzz g:nth-child(3) text { font-size: 25px; }
.nm-fx.zzz > g   { animation: nm-z 3.3s ease-in-out infinite; animation-delay: calc(var(--i) * 1.1s); }
.nm-fx.hearts path { fill: var(--nm-bow); }
.nm-fx.hearts > g { animation: nm-float 3s ease-in-out infinite; animation-delay: calc(var(--i) * 0.75s); }
.nm-fx.stars path { fill: var(--nm-star); stroke: var(--nm-line); stroke-width: 1.4; stroke-linejoin: round; }
.nm-fx.stars > g { animation: nm-twinkle 0.9s ease-in-out infinite alternate; animation-delay: calc(var(--i) * -0.3s); }
.nm-fx.drops path { fill: var(--nm-drop); stroke: var(--nm-line); stroke-width: 1.4; }
.nm-fx.drops > g { animation: nm-drip 1.3s ease-in infinite; animation-delay: calc(var(--i) * 0.42s); }
@keyframes nm-z       { 0% { opacity: 0; transform: translate(-4px, 6px) scale(0.7); } 30%, 60% { opacity: 1; } 100% { opacity: 0; transform: translate(6px, -8px) scale(1); } }
@keyframes nm-float   { 0% { opacity: 0; transform: translateY(8px) scale(0.6); } 30%, 65% { opacity: 1; } 100% { opacity: 0; transform: translateY(-16px) scale(1); } }
@keyframes nm-twinkle { from { transform: rotate(-12deg) scale(0.75); } to { transform: rotate(18deg) scale(1.15); } }
@keyframes nm-drip    { 0% { opacity: 0; transform: translateY(-4px); } 20% { opacity: 1; } 100% { opacity: 0; transform: translateY(12px); } }

/* the level bar's rider: a heart that bobs at the tip */
:root[data-theme="pink"] .noise-rider,
:root[data-theme="halloween"] .noise-rider { width: calc(var(--bar-h) * 1.55); height: calc(var(--bar-h) * 1.55); }
:root:is([data-theme="pink"], [data-theme="halloween"], [data-theme="thanksgiving"]) .noise-rider i {
  width: 100%; height: 100%; border-radius: 0; box-shadow: none;
  background: var(--noise-rider) center / contain no-repeat;
}
:root[data-theme="pink"] .noise-rider i { filter: drop-shadow(0 2px 3px rgba(201, 49, 104, 0.3)); animation: nm-ride 1.1s ease-in-out infinite; }
@keyframes nm-ride { 0%, 100% { transform: translateY(-6%) scale(1); } 50% { transform: translateY(6%) scale(1.06); } }

/* words: the zone in Comfortaa; the number small and secondary */
:root[data-theme="pink"] .noise-zone {
  font-family: "Comfortaa", "Quicksand", var(--font-body);
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0.01em;
  font-size: clamp(1.2rem, 3.4vh, 2rem);
  padding: 0.2em 0.8em;
  border-radius: var(--radius-pill);
  transition: background-color 400ms ease, color 400ms ease;
}
:root[data-theme="pink"] .noise-meter[data-alert="1"] .noise-zone { background: var(--noise-loud); color: var(--on-accent); }

/* the presets as pill buttons */
:root[data-theme="pink"] .noise-preset-seg { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.6rem; padding: 0; border: none; background: none; }
:root[data-theme="pink"] .noise-preset-seg span {
  font-family: "Comfortaa", "Quicksand", var(--font-body);
  font-weight: 700; font-size: var(--fs-sm);
  text-transform: none; letter-spacing: 0.01em;
  padding: 0.5rem 1.25rem;
  border: 2px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: var(--card);
  color: var(--text-2);
  box-shadow: var(--shadow-card);
}
:root[data-theme="pink"] .noise-preset-seg label:hover span { border-color: var(--accent); color: var(--text); }
:root[data-theme="pink"] .noise-preset-seg input:checked + span {
  background: var(--accent); border-color: var(--accent); color: var(--on-accent);
  box-shadow: 0 4px 14px var(--accent-glow);
}
:root[data-theme="pink"] .noise-preset-seg input:checked + span::before {
  content: ""; display: inline-block;
  width: 0.85em; height: 0.85em; margin-right: 0.4em; vertical-align: -0.08em;
  background: currentColor;
  -webkit-mask: var(--nm-heart-mask) center / contain no-repeat;
          mask: var(--nm-heart-mask) center / contain no-repeat;
}

/* full screen + the pop-out: the bunny fills the room */
:root[data-theme="pink"] body.present .noise-zone { font-size: clamp(2rem, 6.5vh, 4.2rem); }
:root[data-theme="pink"] body.noise-popout .noise-zone { font-size: clamp(1.3rem, 6vh, 3.6rem); }

/* Reduced motion: the loops stop (no breathing, bob, shake, drifting extras),
   but the pose changes still crossfade instead of snapping — fades and colour
   shifts aren't motion, and a hard swap is the flicker this skin avoids. The
   app-wide `* { transition: none !important }` needs the !important here. */
@media (prefers-reduced-motion: reduce) {
  .nm-move, .nm-ear-wig, .nm-fx > g, .nm-halo { animation: none !important; }
  .nm-svg :is(.nm-fur, .nm-ear-in, .nm-tummy, .nm-blush, .nm-eyes, .nm-mouth, .nm-halo, .nm-fx, .nm-move) {
    transition: opacity 400ms ease, fill 600ms ease, filter 600ms ease !important;
  }
}

/* ---------------------------------------------------------------------------
   Noise Meter — halloween skin: a ghost rising from a grave. Its HEIGHT is the
   level: noise.js sets --rise on .noise-meter (0..1 on the zone-aligned scale
   pink's hearts use: 0–0.3 quiet, 0.3–0.7 OK, 0.7–1 too loud; −0.45 = sunk,
   mic off) and .nh-rise follows it on a 450ms ease, so it rises and sinks
   with the room instead of stepping. The ghost is clipped at the grave's mouth
   (#nh-clip) and the front lip of the ground is drawn over it, so it really
   comes up out of the ground; its tattered tail fades into the hole.
     data-mood="quiet"  barely out, translucent, eyes closed, a slow drift,
                        pale blue-white
     data-mood="ok"     half risen, more solid, a friendly face, a gentle hover,
                        warm bone
     data-mood="loud"   fully risen and solid, arms thrown up, wailing, hot
                        orange with a glow over the whole scene, shaking; the
                        bats on the headstone and the cross scatter
     data-alert="1"     a harder shake, the scene glow pulses, the zone word
                        becomes a solid pumpkin pill
   --nh-ghost / --nh-glow are registered so the colour shift interpolates.
   Parts inside the moving ghost use transform-box: fill-box (their own
   boxes), since view-box origins wouldn't follow the ghost.
   --------------------------------------------------------------------------- */
@property --nh-ghost { syntax: '<color>'; inherits: true; initial-value: #D2E4FF; }
@property --nh-glow  { syntax: '<color>'; inherits: true; initial-value: rgba(140, 180, 255, 0.3); }
:root[data-theme="halloween"] .noise-meter {
  --nh-ghost: var(--nh-ghost-quiet); --nh-glow: var(--nh-glow-quiet);
  transition: --nh-ghost 900ms ease, --nh-glow 900ms ease;
}
:root[data-theme="halloween"] .noise-meter[data-mood="ok"]   { --nh-ghost: var(--nh-ghost-ok);   --nh-glow: var(--nh-glow-ok); }
:root[data-theme="halloween"] .noise-meter[data-mood="loud"] { --nh-ghost: var(--nh-ghost-loud); --nh-glow: var(--nh-glow-loud); }

.nh-svg { display: block; width: 100%; height: 100%; overflow: visible; }
.nh-svg * { transform-box: view-box; }
.nh-svg .nh-rise *, .nh-svg .nh-bat * { transform-box: fill-box; }

/* gradients: one colour per gradient, faded by stop-opacity (never to
   transparent: SVG gradients don't premultiply, so that goes grey) */
#nh-g-glow stop { stop-color: var(--nh-glow); }
#nh-g-glow stop:nth-child(2) { stop-opacity: 0.45; }
#nh-g-glow stop:last-child { stop-opacity: 0; }
#nh-g-body stop, #nh-g-wisp stop { stop-color: var(--nh-ghost); }
#nh-g-body stop:last-child { stop-opacity: 0.3; }
#nh-g-wisp stop:last-child { stop-opacity: 0; }

/* the grave */
.nh-ground { fill: var(--nh-ground); }
.nh-hole { fill: var(--nh-hole); stroke: var(--nh-ground-hi); stroke-width: 2; }
.nh-stone-body { fill: var(--nh-stone); stroke: var(--nh-stone-line); stroke-width: 3; stroke-linejoin: round; }
.nh-stone-hi { fill: none; stroke: var(--nh-stone-hi); stroke-width: 3; stroke-linecap: round; opacity: 0.75; }
.nh-rip { font: 25px var(--font-spooky); letter-spacing: 1px; fill: var(--nh-stone-line); }
.nh-crack { fill: none; stroke: var(--nh-stone-line); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.nh-cross rect { fill: var(--nh-wood); stroke: var(--nh-stone-line); stroke-width: 2; }
.nh-grass { fill: var(--nh-grass); }

/* the glow over the whole scene: only when it's too loud */
.nh-aura { opacity: 0; transform-origin: 148px 118px; transition: opacity 700ms ease; }
.noise-meter[data-mood="loud"] .nh-aura { opacity: 0.95; }
.noise-meter[data-alert="1"] .nh-aura { animation: nh-aura-pulse 900ms ease-in-out infinite; }
@keyframes nh-aura-pulse { 0%, 100% { opacity: 0.7; transform: scale(0.92); } 50% { opacity: 1; transform: scale(1.08); } }

/* the rise: the level indicator itself */
.nh-rise { transform: translateY(calc((1 - var(--rise, -0.45)) * 120px)); transition: transform 450ms cubic-bezier(.3, .7, .4, 1); }
.nh-halo { opacity: 0.85; }

/* the ghost: more solid the higher it gets, fully solid and glowing when loud */
.nh-ghost { opacity: calc(0.4 + 0.6 * clamp(0, var(--rise, 0), 1)); transition: opacity 500ms ease, filter 600ms ease; }
.noise-meter[data-mood="loud"] .nh-ghost { opacity: 1; filter: drop-shadow(0 0 5px var(--nh-glow-loud)) drop-shadow(0 0 16px var(--nh-glow-loud)); }
.nh-body { fill: url(#nh-g-body); stroke: var(--nh-ghost-line); stroke-width: 2.2; stroke-linejoin: round; }
.nh-arm  { fill: var(--nh-ghost); stroke: var(--nh-ghost-line); stroke-width: 2.2; stroke-linejoin: round; transition: transform 550ms cubic-bezier(.34, 1.5, .64, 1); }
.nh-arm.l { transform-origin: 96% 4%; }
.nh-arm.r { transform-origin: 4% 4%; }
.noise-meter[data-mood="loud"] .nh-arm.l { transform: rotate(88deg); }
.noise-meter[data-mood="loud"] .nh-arm.r { transform: rotate(-88deg); }
.nh-tail path { fill: none; stroke: url(#nh-g-wisp); stroke-width: 7; stroke-linecap: round; }
.nh-sheen { fill: none; stroke: var(--nh-glint); stroke-opacity: 0.5; stroke-width: 3; stroke-linecap: round; }
.nh-blush { fill: var(--nh-blush); opacity: 0; transition: opacity 400ms ease; }
.noise-meter[data-mood="ok"] .nh-blush { opacity: 1; }

/* faces: one per mood, crossfaded */
.nh-face { opacity: 0; transition: opacity 350ms ease; }
.noise-meter:is([data-mood="off"], [data-mood="quiet"]) .nh-face.sleep,
.noise-meter[data-mood="ok"]   .nh-face.happy,
.noise-meter[data-mood="loud"] .nh-face.wail { opacity: 1; }
.nh-face ellipse, .nh-mouth { fill: var(--nh-eye); }
.nh-face.sleep path, .nh-smile { fill: none; stroke: var(--nh-eye); stroke-width: 3; stroke-linecap: round; }
.nh-glint { fill: var(--nh-glint); }
.nh-mouth { transform-origin: center; }
.noise-meter[data-mood="loud"] .nh-mouth { animation: nh-wail 520ms ease-in-out infinite alternate; }
@keyframes nh-wail { from { transform: scale(0.86, 0.8); } to { transform: scale(1.04, 1.12); } }

/* motion: drift asleep, hover when OK, shake when too loud */
.nh-float { transform-origin: 50% 40%; }
.noise-meter[data-mood="quiet"] .nh-float { animation: nh-drift 6s ease-in-out infinite; }
.noise-meter[data-mood="ok"]    .nh-float { animation: nh-hover 2.6s ease-in-out infinite; }
.noise-meter[data-mood="loud"]  .nh-float { animation: nh-shake 190ms linear infinite; }
.noise-meter[data-mood="loud"][data-alert="1"] .nh-float { animation: nh-shake-hard 140ms linear infinite; }
@keyframes nh-drift { 0%, 100% { transform: translate(-4px, 0) rotate(-2deg); } 50% { transform: translate(4px, -3px) rotate(2deg); } }
@keyframes nh-hover { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }
@keyframes nh-shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-2.5px) rotate(-1.2deg); } 75% { transform: translateX(2.5px) rotate(1.2deg); } }
@keyframes nh-shake-hard { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-5px) rotate(-2.4deg); } 75% { transform: translateX(5px) rotate(2.4deg); } }

/* wisps peeling off its sides as it rises */
.nh-wisps { opacity: 0.45; transition: opacity 600ms ease; }
.noise-meter:is([data-mood="ok"], [data-mood="loud"]) .nh-wisps { opacity: 1; }
.noise-meter[data-mood="off"] .nh-wisps { opacity: 0; }
.nh-wisps path { fill: none; stroke: var(--nh-ghost); stroke-width: 2.4; stroke-linecap: round; opacity: 0; animation: nh-wisp 2.8s ease-out infinite; }
.nh-wisps path:nth-child(2) { animation-delay: -0.95s; --dx: 10px; }
.nh-wisps path:nth-child(3) { animation-delay: -1.9s; }
@keyframes nh-wisp { 0% { opacity: 0; transform: translate(0, 0); } 25% { opacity: 0.6; } 100% { opacity: 0; transform: translate(var(--dx, -10px), 18px) scale(0.8); } }

/* bats: perched on the headstone and the cross; scattered by "too loud" */
.nh-bat path { fill: var(--nh-bat); }
.nh-bat-eye { fill: var(--nh-bat-eye); }
.nh-bat-wings { opacity: 0; transform-origin: center; }
.nh-bat-fly { transform-origin: center; transition: transform 1.5s cubic-bezier(.45, 0, .75, .5), opacity 1.5s ease-in; transition-delay: calc(var(--i) * 140ms); }
.noise-meter[data-mood="loud"] .nh-bat-perch { opacity: 0; }
.noise-meter[data-mood="loud"] .nh-bat-wings { opacity: 1; animation: nh-flap 120ms linear infinite alternate; }
.noise-meter[data-mood="loud"] .nh-bat.b0 .nh-bat-fly { transform: translate(-70px, -150px) rotate(-18deg) scale(0.7); opacity: 0; }
.noise-meter[data-mood="loud"] .nh-bat.b1 .nh-bat-fly { transform: translate(30px, -175px) rotate(14deg) scale(0.65); opacity: 0; }
.noise-meter[data-mood="loud"] .nh-bat.b2 .nh-bat-fly { transform: translate(60px, -160px) rotate(20deg) scale(0.7); opacity: 0; }
@keyframes nh-flap { from { transform: scaleY(1); } to { transform: scaleY(0.3); } }

/* the level bar: a bat flapping at the tip (riding a little above the
   trail), the trail glowing, and flickering like flame once it's too loud */
:root[data-theme="halloween"] .noise-rider { width: calc(var(--bar-h) * 2.8); height: calc(var(--bar-h) * 1.5); translate: -50% -74%; }
:root[data-theme="halloween"] .noise-rider i { transform-origin: 50% 45%; animation: nh-ride 150ms ease-in-out infinite alternate; filter: drop-shadow(0 0 4px rgba(255, 117, 24, 0.55)); }
@keyframes nh-ride { from { transform: scaleY(1.1) translateY(-3%); } to { transform: scaleY(0.72) translateY(5%); } }
:root[data-theme="halloween"] .noise-meter[data-zone="loud"] .noise-bar { box-shadow: var(--noise-bar-inset), 0 0 1.1rem var(--nh-glow-loud); }
:root[data-theme="halloween"] .noise-meter[data-zone="loud"] .noise-fill { animation: nh-flame 620ms steps(1, end) infinite; }
@keyframes nh-flame {
  0%   { filter: brightness(1)    saturate(1); }
  17%  { filter: brightness(1.28) saturate(1.2); }
  31%  { filter: brightness(0.9)  saturate(1); }
  46%  { filter: brightness(1.18) saturate(1.3); }
  63%  { filter: brightness(1.36) saturate(1.1); }
  78%  { filter: brightness(0.95) saturate(1); }
  90%  { filter: brightness(1.22) saturate(1.25); }
}

/* words: the zone in the spooky face; "too loud" held = a pumpkin pill */
:root[data-theme="halloween"] .noise-zone {
  font-family: var(--font-spooky);
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0.03em;
  font-size: clamp(1.6rem, 4.6vh, 2.8rem);
  padding: 0.05em 0.75em 0.12em;
  border-radius: var(--radius-pill);
  transition: background-color 400ms ease, color 400ms ease, box-shadow 400ms ease;
}
:root[data-theme="halloween"] .noise-meter[data-alert="1"] .noise-zone { background: var(--accent); color: var(--on-accent); box-shadow: 0 0 1.6rem var(--nh-glow-loud); }
:root[data-theme="halloween"] body.present .noise-zone { font-size: clamp(2.4rem, 7vh, 4.6rem); }
:root[data-theme="halloween"] body.noise-popout .noise-zone { font-size: clamp(1.6rem, 7vh, 4rem); }

/* Reduced motion: the loops stop and the bats only fade; the rise itself, the
   colour shift and the face/arm crossfades keep their easing (the rise IS the
   reading, and an eased value is calmer than a jumping one). The app-wide
   `* { transition: none !important }` needs the !important here. */
@media (prefers-reduced-motion: reduce) {
  .nh-float, .nh-wisps path, .nh-bat-wings, .nh-aura, .nh-mouth { animation: none !important; }
  .nh-rise { transition: transform 450ms cubic-bezier(.3, .7, .4, 1) !important; }
  :root[data-theme="halloween"] .noise-meter { transition: --nh-ghost 900ms ease, --nh-glow 900ms ease !important; }
  .nh-svg :is(.nh-face, .nh-ghost, .nh-aura, .nh-blush, .nh-wisps) { transition: opacity 400ms ease !important; }
  .nh-bat-fly { transition: opacity 600ms ease !important; }
}

/* ---------------------------------------------------------------------------
   Noise Meter — thanksgiving skin: a turkey IS the meter. noise.js builds
   .nt-stage (TURKEY_SVG, viewBox 0 0 200 200) into every .noise-meter; it
   uses the mascot layout shared with pink and halloween. Poses are CSS off
   data-mood (the zone debounced by MOOD_HOLD_MS):
     off    asleep and greyed
     quiet  sitting low and content: head down, eyes half shut, tail folded
            in (--fan 6deg, short), a slow breath, z's
     ok     alert and happy: head up, bright eyes, tail half fanned (--fan
            14deg), a little bob, leaves drifting by
     loud   tail fully fanned (--fan 21deg, full length), chest puffed, wide
            eyes and raised brows, beak open mid-gobble, the wattle swinging,
            "GOBBLE!" and shout lines, the whole bird vibrating
     alert  (the 3s hold) a harder vibrate and the zone word as a solid pill
   Each feather turns about (100, 130) by rotate(--o × --fan) and scales by
   --flen, so the fan opens and closes as one transition. Transform origins
   are viewBox units (transform-box: view-box).
   --------------------------------------------------------------------------- */
:root[data-theme="thanksgiving"] .noise-meter .nt-stage { display: block; }
.nt-svg { display: block; width: 100%; height: 100%; overflow: visible; }
.nt-svg * { transform-box: view-box; }

/* drawing */
.nt-vane { stroke: var(--tk-line); stroke-width: 2.4; stroke-linejoin: round; }
.nt-feather.f0 .nt-vane { fill: var(--tk-f1); }
.nt-feather.f1 .nt-vane { fill: var(--tk-f2); }
.nt-feather.f2 .nt-vane { fill: var(--tk-f3); }
.nt-feather.f3 .nt-vane { fill: var(--tk-f4); }
.nt-tip { fill: var(--tk-line); opacity: 0.45; }
.nt-band { fill: none; stroke: var(--tk-tip); stroke-width: 2; stroke-linecap: round; opacity: 0.55; }
.nt-quill { fill: none; stroke: var(--tk-quill); stroke-width: 1.6; stroke-linecap: round; }
.nt-body, .nt-wing, .nt-face { stroke: var(--tk-line); stroke-width: 3; stroke-linejoin: round; }
.nt-body { fill: var(--tk-body); }
.nt-wing { fill: var(--tk-wing); }
.nt-chest { fill: var(--tk-chest); }
.nt-scallop { fill: none; stroke: var(--tk-body-hi); stroke-width: 2; stroke-linecap: round; }
.nt-neck { fill: var(--tk-wattle); opacity: 0.85; }
.nt-face { fill: var(--tk-head); }
.nt-blush { fill: var(--tk-blush); }
.nt-eyes circle { fill: var(--tk-eye); }
.nt-eyes .nt-glint { fill: var(--tk-glint); }
.nt-eyes.sleep path, .nt-brow { fill: none; stroke: var(--tk-eye); stroke-width: 2.6; stroke-linecap: round; }
.nt-beak path { fill: var(--tk-beak); stroke: var(--tk-line); stroke-width: 1.6; stroke-linejoin: round; }
.nt-beak .nt-mouth { fill: var(--tk-beak-in); }
.nt-wattle { fill: var(--tk-wattle); stroke: var(--tk-line); stroke-width: 1.6; stroke-linejoin: round; transform-origin: 104px 91px; transition: fill 500ms ease; }
.nt-foot { fill: none; stroke: var(--tk-feet); stroke-width: 3.4; stroke-linecap: round; stroke-linejoin: round; }
.nt-ground { fill: var(--tk-ground); opacity: 0.8; }

/* glows behind, one per zone, crossfaded (faded by stop-opacity, never to
   transparent: SVG gradients don't premultiply) */
#nt-g-quiet stop { stop-color: var(--tk-glow-quiet); }
#nt-g-ok    stop { stop-color: var(--tk-glow-ok); }
#nt-g-loud  stop { stop-color: var(--tk-glow-loud); }
.nt-svg radialGradient stop:last-child { stop-opacity: 0; }
.nt-halo { opacity: 0; transition: opacity 800ms ease; transform-origin: 100px 112px; }
.noise-meter[data-mood="quiet"] .nt-halo.quiet,
.noise-meter[data-mood="ok"]    .nt-halo.ok,
.noise-meter[data-mood="loud"]  .nt-halo.loud { opacity: 1; }
.noise-meter[data-alert="1"] .nt-halo.loud { animation: nm-halo-pulse 1.1s ease-in-out infinite; }

/* the tail: folded → half fanned → fully fanned */
.nt-svg { --fan: 6deg; --flen: 0.74; }
.noise-meter[data-mood="ok"]   .nt-svg { --fan: 14deg; --flen: 0.9; }
.noise-meter[data-mood="loud"] .nt-svg { --fan: 21deg; --flen: 1.04; }
.nt-feather {
  transform-origin: 100px 130px;
  transform: rotate(calc(var(--o) * var(--fan))) scale(var(--flen));
  transition: transform 700ms cubic-bezier(.34, 1.4, .64, 1);
}

/* faces — one set shown per mood */
.nt-eyes, .nt-beak { opacity: 0; transition: opacity 280ms ease; }
.noise-meter:is([data-mood="off"], [data-mood="quiet"]) :is(.nt-eyes.sleep, .nt-beak.shut),
.noise-meter[data-mood="ok"]   :is(.nt-eyes.open, .nt-beak.shut),
.noise-meter[data-mood="loud"] :is(.nt-eyes.wide, .nt-beak.open) { opacity: 1; }

/* head: tucked low when content, up when alert, thrown back to gobble */
.nt-head { transform-origin: 100px 120px; transition: transform 600ms cubic-bezier(.34, 1.4, .64, 1); }
.noise-meter:is([data-mood="off"], [data-mood="quiet"]) .nt-head { transform: translateY(7px) rotate(-5deg); }
.noise-meter[data-mood="ok"] .nt-head { transform: translateY(-4px); }
.noise-meter[data-mood="loud"] .nt-head { transform: translateY(-3px) rotate(-3deg); animation: nt-gobble 520ms ease-in-out infinite; }
@keyframes nt-gobble { 0%, 100% { translate: 0 0; } 30% { translate: 0 -3px; } 60% { translate: 0 2px; } }

/* chest: puffed out when it's too loud */
.nt-body-g { transform-origin: 100px 172px; transition: transform 500ms cubic-bezier(.34, 1.5, .64, 1); }
.noise-meter[data-mood="loud"] .nt-body-g { transform: scale(1.09, 1.06); }

/* the wattle swings when it gobbles, and goes a hotter red */
.noise-meter[data-mood="loud"] .nt-wattle { fill: var(--tk-wattle-hot); animation: nt-wattle 300ms ease-in-out infinite alternate; }
@keyframes nt-wattle { from { transform: rotate(-16deg); } to { transform: rotate(18deg); } }

/* whole-body motion: breathe, bob, vibrate */
.nt-move { transform-origin: 100px 186px; transition: opacity 600ms ease, filter 600ms ease; }
.noise-meter[data-mood="off"] .nt-move { opacity: 0.6; filter: saturate(0.3); }
.noise-meter[data-mood="quiet"] .nt-move { animation: nm-breathe 3.8s ease-in-out infinite; }
.noise-meter[data-mood="ok"]    .nt-move { animation: nm-bob 1.6s ease-in-out infinite; }
.noise-meter[data-mood="loud"]  .nt-move { animation: nt-vibrate 120ms linear infinite; }
.noise-meter[data-mood="loud"][data-alert="1"] .nt-move { animation: nt-vibrate-hard 90ms linear infinite; }
@keyframes nt-vibrate { 0%, 100% { transform: translate(0, 0); } 25% { transform: translate(-1.6px, 0.6px) rotate(-0.7deg); } 50% { transform: translate(1.2px, -0.6px); } 75% { transform: translate(1.6px, 0.4px) rotate(0.7deg); } }
@keyframes nt-vibrate-hard { 0%, 100% { transform: translate(0, 0); } 25% { transform: translate(-3px, 1px) rotate(-1.4deg); } 50% { transform: translate(2px, -1px); } 75% { transform: translate(3px, 1px) rotate(1.4deg); } }

/* extras: z's, drifting leaves, the gobble */
.nt-fx { opacity: 0; transition: opacity 500ms ease; }
.nt-fx > g { transform-box: fill-box; transform-origin: center; }
.noise-meter[data-mood="quiet"] .nt-fx.zzz,
.noise-meter[data-mood="ok"]    .nt-fx.leaves,
.noise-meter[data-mood="loud"]  .nt-fx.shout { opacity: 1; }
.nt-fx.zzz text { fill: var(--tk-z); font: 700 16px var(--font-body); }
.nt-fx.zzz g:nth-child(2) text { font-size: 20px; }
.nt-fx.zzz g:nth-child(3) text { font-size: 25px; }
.nt-fx.zzz > g { animation: nm-z 3.3s ease-in-out infinite; animation-delay: calc(var(--i) * 1.1s); }
.nt-fx.leaves path { fill: var(--tk-f2); }
.nt-fx.leaves g:nth-child(2) path { fill: var(--tk-f3); }
.nt-fx.leaves g:nth-child(3) path { fill: var(--tk-f1); }
.nt-fx.leaves > g { animation: nt-leaf 4s ease-in-out infinite; animation-delay: calc(var(--i) * -1.3s); }
@keyframes nt-leaf { 0% { opacity: 0; transform: translate(0, -10px) rotate(-20deg); } 25%, 70% { opacity: 1; } 100% { opacity: 0; transform: translate(6px, 16px) rotate(40deg); } }
.nt-fx.shout path { fill: none; stroke: var(--tk-shout); stroke-width: 3.2; stroke-linecap: round; }
.nt-fx.shout text { fill: var(--tk-shout); font: 700 19px var(--font-display); letter-spacing: 0.08em; text-anchor: middle; }
.nt-fx.shout > g { animation: nt-shout 520ms ease-out infinite; }
@keyframes nt-shout { 0% { transform: scale(0.85); opacity: 0.6; } 40% { transform: scale(1.08); opacity: 1; } 100% { transform: scale(1); opacity: 0.85; } }

/* the level bar: a feather with an acorn riding its tip */
:root[data-theme="thanksgiving"] .noise-rider { width: calc(var(--bar-h) * 1.35); height: calc(var(--bar-h) * 1.55); translate: -50% -58%; }
:root[data-theme="thanksgiving"] .noise-rider i { filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.55)); transform-origin: 50% 20%; animation: nt-acorn 1.6s ease-in-out infinite; }
@keyframes nt-acorn { 0%, 100% { transform: rotate(-8deg); } 50% { transform: rotate(8deg); } }
:root[data-theme="thanksgiving"] .noise-meter[data-zone="loud"] .noise-bar { box-shadow: var(--noise-bar-inset), 0 0 1rem var(--tk-glow-loud); }

/* the zone word: a solid pill once it's been too loud for a while */
:root[data-theme="thanksgiving"] .noise-zone { border-radius: 999px; padding: 0.1em 0.7em; transition: background 300ms ease, color 300ms ease; }
:root[data-theme="thanksgiving"] .noise-meter[data-alert="1"] .noise-zone { background: var(--accent); color: var(--on-accent); box-shadow: 0 0 1.4rem var(--tk-glow-loud); animation: none; }

@media (prefers-reduced-motion: reduce) {
  .nt-move, .nt-head, .nt-wattle, .nt-halo, .nt-fx > g, :root[data-theme="thanksgiving"] .noise-rider i { animation: none !important; }
  .nt-feather { transition: transform 700ms ease !important; }
  .nt-eyes, .nt-beak, .nt-fx, .nt-halo { transition: opacity 300ms ease !important; }
}

/* ============================================================================
   Name Wheel (wheel.html + wheel-popout.html)
   The wheel is the page: a control row on top, then the dial filling the rest.
   Slices are drawn by wheel.js into #wheel-rotor (the only thing that spins);
   the pointer, rim and hub sit still on top of it.
   ============================================================================ */
.wheel-page { display: flex; flex-direction: column; min-height: 0; padding: var(--space-3) var(--page-pad) var(--space-4); }

.wheel-bar {
  --ctl: 2.1rem;
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
  padding: 0.6rem 0.9rem;
  border-radius: var(--radius-md);
  margin-bottom: var(--space-3);
}
.wheel-bar::before { display: none; }
.wheel-bar button, .wheel-bar select, .wheel-bar .chip {
  min-height: var(--ctl); padding-top: 0.25rem; padding-bottom: 0.25rem; font-size: var(--fs-xs);
}
.wheel-bar select { min-width: 9.5rem; padding-right: 2rem; }
.wheel-bar .bar-field { display: inline-flex; align-items: center; gap: 0.45rem; min-width: 0; }
.wheel-bar .bar-spacer { flex: 1 1 0; min-width: 0; }
.wheel-bar .icon-btn { width: var(--ctl); height: var(--ctl); padding: 0; }
.wheel-bar .icon-btn svg { width: 1rem; height: 1rem; }
.wheel-bar button[aria-checked="true"] { border-color: var(--accent); color: var(--accent-ink); background: var(--accent-soft); }
.wheel-bar [role="switch"] .when-off, .wheel-bar [role="switch"] .when-on { display: inline; }   /* switches only: the full-screen button keeps the app-wide icon swap */
.wheel-bar button[aria-checked="false"] .when-on { display: none; }
.wheel-bar button[aria-checked="true"] .when-off { display: none; }

/* Two columns: the machine fills the left, the result + controls the right.
   Both have to fit 1280x720 without scrolling, so everything is sized off the
   row height rather than allowed to grow. */
.wheel-layout {
  flex: 1; min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(17rem, 0.9fr);
  gap: var(--space-4);
  align-items: center;
}
@media (max-width: 900px) {
  .wheel-layout { grid-template-columns: 1fr; align-items: start; overflow-y: auto; }
}

.wheel-stage {
  height: 100%; min-height: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-2);
  padding: var(--space-2);          /* the wheel's only margin - it fills the rest */
}
/* Square SVG (no stand), sized to fill its column: take the column's height,
   derive the width from the 1:1 ratio, and clamp to the column's width when
   that is the tighter of the two. The stage's own padding is the margin. */
.wheel-wrap {
  position: relative;
  width: auto;
  height: 100%;
  max-width: 100%;
  aspect-ratio: 1 / 1;
  margin: 0 auto;
}
.wheel-svg { width: 100%; height: 100%; overflow: visible; }
/* the rotor is the ONLY thing that spins; CSS transform so the spin can run
   as a transition (see wheel.js runSpin) */
/* transform-box + an explicit origin in user units: the rotor must turn about
   the exact centre of the 100x100 viewBox (50,50), not the bounding box of
   whatever happens to be drawn in it, or the wheel wobbles as it spins. */
#wheel-rotor {
  transform-box: view-box;
  transform-origin: 50px 50px;
  will-change: transform;
}

.slice { stroke: none; transition: fill 260ms ease; }
.slice.r0 { fill: var(--wheel-r0); }
.slice.r1 { fill: var(--wheel-r1); }
.slice.r2 { fill: var(--wheel-r2); }
.slice.r3 { fill: var(--wheel-r3); }
.slice.r4 { fill: var(--wheel-r4); }
.slice.r5 { fill: var(--wheel-r5); }
.slice.r6 { fill: var(--wheel-r6); }
.slice.r7 { fill: var(--wheel-r7); }
.slice.won { fill: var(--wheel-won); }
/* thin bright dividers between slices */
.slice-dividers line { stroke: var(--wheel-divider); stroke-width: 0.3; }
/* names ride their slice, in near-white over a dark outline so they read on
   every step of the ramp (paint-order puts the stroke behind the fill) */
.slice-label {
  fill: var(--wheel-ink);
  stroke: var(--wheel-ink-shadow);
  stroke-width: 0.55;
  paint-order: stroke;
  stroke-linejoin: round;
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* ---- depth: a sheen across the face and an inner shadow at the rim ---- */
.face-sheen { fill: url(#wheel-face-grad); pointer-events: none; }
.face-ish { fill: url(#wheel-ish); pointer-events: none; }
#wheel-face-grad .face-hi { stop-color: var(--wheel-face-hi); }
#wheel-face-grad .face-lo { stop-color: var(--wheel-face-lo); }
#wheel-ish .ish-clear { stop-color: rgba(0, 0, 0, 0); }
#wheel-ish .ish-mid { stop-color: rgba(0, 0, 0, 0.16); }
#wheel-ish .ish-edge { stop-color: rgba(0, 0, 0, 0.42); }

.wheel-rim { fill: none; stroke: var(--hud-line); stroke-width: 0.6; transition: filter 300ms ease; }
.wheel-hub { fill: var(--card); stroke: none; }
.wheel-hub-ring { fill: none; stroke: var(--hud-line-strong); stroke-width: 0.5; }
.wheel-axle .bolt { fill: var(--wheel-metal-2); stroke: var(--wheel-metal-hi); stroke-width: 0.25; }

/* ---- the machine: housing, legs, base plate ---- */
.housing { fill: none; stroke: var(--wheel-housing); stroke-width: 4.4; }
.housing-foot { fill: none; stroke: var(--wheel-housing); stroke-width: 7.4; stroke-linecap: round; }

/* ---- pointer on its bracket, knocked by each passing segment ---- */
.bracket { fill: var(--wheel-metal); }
.bracket-hi { fill: var(--wheel-metal-hi); opacity: 0.8; }
.pointer-arm {
  transform-box: view-box;
  transform-origin: 50px 1.6px;
  filter: drop-shadow(0 0 1.2px var(--accent-glow));
}
.pointer-arm.flick { animation: pointer-flick 130ms ease-out; }
@keyframes pointer-flick {
  0%   { transform: rotate(0deg); }
  35%  { transform: rotate(-7deg); }
  100% { transform: rotate(0deg); }
}
.pointer-hud { fill: none; stroke: var(--accent); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.pointer-soft { display: none; fill: var(--accent); stroke: none; }

/* instrument ring outside the wheel: degree ticks + corner brackets */
.wheel-dial line { stroke: var(--hud-line); stroke-width: 0.35; }
.wheel-dial line.long { stroke: var(--hud-line-strong); stroke-width: 0.55; }
.wheel-dial .bracket { fill: none; stroke: var(--accent); stroke-width: 0.7; opacity: 0.75; }

/* while spinning the rim picks up a faint motion glow; on landing it pulses
   once and the winning slice keeps its accent wash */
.wheel-stage[data-state="spinning"] .wheel-rim { filter: drop-shadow(0 0 4px var(--accent-glow)); }
.wheel-stage[data-state="picked"] .wheel-rim { animation: wheel-ring-pulse 700ms ease-out 1; }
@keyframes wheel-ring-pulse {
  0%   { filter: drop-shadow(0 0 10px var(--accent)); }
  100% { filter: drop-shadow(0 0 0 transparent); }
}


/* the picked name, centred in the hub and readable across the room */
/* ---- the hub IS the Spin button (page only; the pop-out keeps a disc) ----
   It sits outside #wheel-rotor, so the wheel turns around it while it stays
   still. 28% of the wrap = radius 14 in viewBox units, just inside the bolt
   circle at 15.6. */
.wheel-hub-btn {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 28%; height: 28%;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid var(--hud-line-strong);
  background: var(--card);
  color: var(--text);
  font-family: var(--font-display);
  text-transform: var(--label-transform);
  letter-spacing: var(--track-display);
  font-weight: var(--fw-semibold);
  font-size: clamp(0.7rem, 2.2vh, 1.15rem);
  cursor: pointer;
  box-shadow: inset 0 0 0 1px var(--hud-line), 0 0 14px var(--shadow-text);
  transition: border-color 160ms ease, box-shadow 160ms ease, color 160ms ease, transform 120ms ease;
}
.wheel-hub-btn:hover:not(:disabled) {
  border-color: var(--accent);
  color: var(--accent-ink);
  box-shadow: inset 0 0 0 1px var(--accent-soft), 0 0 22px var(--accent-glow);
  transform: translate(-50%, -50%) scale(1.04);
}
.wheel-hub-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.wheel-hub-btn:active:not(:disabled) {
  transform: translate(-50%, -50%) scale(0.95);
  background: var(--card-2);
  box-shadow: inset 0 2px 8px var(--shadow-text);
}
.wheel-hub-btn:disabled {
  cursor: default;
  color: var(--text-muted);
  border-color: var(--border-strong);
  box-shadow: none;
}
.wheel-hub-btn[data-spinning="1"] { color: var(--accent-ink); border-color: var(--hud-line); opacity: 0.75; }

/* The viewBox is square, so the wheel's centre is the box's centre. */
.wheel-hub-text {
  position: absolute; left: 0; right: 0;
  top: 50%;
  transform: translateY(-50%);
  display: grid; place-items: center;
  pointer-events: none;
  padding: 0 24%;
  text-align: center;
}
.wheel-hub-name {
  font-family: var(--font-display);
  text-transform: var(--label-transform);
  letter-spacing: var(--track-display);
  font-weight: var(--fw-semibold);
  font-size: clamp(0.85rem, 3.2vh, 1.7rem);
  line-height: 1.2;
  color: var(--text);
  overflow-wrap: anywhere;
  text-shadow: 0 0 18px var(--accent-glow);
}
.wheel-hub-name[data-len="long"] { font-size: clamp(0.72rem, 2.4vh, 1.2rem); letter-spacing: 0.04em; }
.wheel-stage[data-state="picked"] .wheel-hub-name { animation: wheel-pop 420ms cubic-bezier(.34,1.56,.64,1); }
@keyframes wheel-pop { from { transform: scale(0.7); opacity: 0; } to { transform: scale(1); opacity: 1; } }

.wheel-empty {
  position: absolute; inset: auto 8% 8%;
  margin: 0; text-align: center;
  color: var(--text-muted); font-size: var(--fs-sm);
}

/* ---- right column: result panel over the controls ---- */
.wheel-side {
  height: 100%; min-height: 0;
  display: flex; flex-direction: column;
  gap: var(--space-3);
  justify-content: center;
}

.wheel-result {
  position: relative;
  flex: 1 1 auto; min-height: 9rem;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-3);
  border: 1px solid var(--hud-line);
  border-radius: var(--radius-lg);
  background: var(--panel-bg);
  overflow: hidden;
  text-align: center;
}
.wheel-placeholder {
  margin: 0;
  font-family: var(--font-display);
  text-transform: var(--label-transform);
  letter-spacing: var(--track-label);
  font-size: 0.74rem;
  color: var(--text-muted);
}
.wheel-result[data-state="picked"] .wheel-placeholder,
.wheel-result[data-state="spinning"] .wheel-placeholder { display: none; }

.wheel-result-name {
  margin: 0;
  font-family: var(--font-display);
  text-transform: var(--label-transform);
  letter-spacing: var(--track-display);
  font-weight: var(--fw-bold);
  font-size: clamp(1.6rem, 5.4vh, 3.2rem);
  line-height: 1.1;
  color: var(--text);
  overflow-wrap: anywhere;
  opacity: 0;
}
.wheel-result-name[data-len="long"] { font-size: clamp(1.2rem, 3.8vh, 2.2rem); }
/* in: scales up from small with a spring overshoot as it fades in. The
   picked state sets opacity itself, so the animation is pure enhancement -
   if it never runs (reduced motion, a dropped frame), the name is still
   there rather than stuck at opacity 0. */
.wheel-result[data-state="picked"] .wheel-result-name {
  opacity: 1;
  animation: result-in 520ms cubic-bezier(.34, 1.56, .64, 1);
}
/* out: shrinks away as the next spin starts */
.wheel-result[data-state="spinning"] .wheel-result-name {
  animation: result-out 260ms ease-in forwards;
}
@keyframes result-in {
  0%   { opacity: 0; transform: scale(0.45); }
  100% { opacity: 1; transform: scale(1); }
}
@keyframes result-out {
  0%   { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(0.8); }
}

/* the flash behind the name (default) */
.wheel-glow {
  position: absolute; inset: 0;
  pointer-events: none; opacity: 0;
  background: radial-gradient(circle at 50% 45%, var(--accent-soft) 0%, transparent 62%);
}
.wheel-result[data-state="picked"] .wheel-glow { animation: result-glow 900ms ease-out 1; }
@keyframes result-glow {
  0%   { opacity: 0; }
  25%  { opacity: 1; }
  100% { opacity: 0; }
}
/* sparkles (pink) - hidden in default, which uses the glow alone */
.wheel-sparks { position: absolute; inset: 0; pointer-events: none; display: none; }
.wheel-sparks i {
  position: absolute; left: 50%; top: 45%;
  width: 0.9rem; height: 0.9rem; margin: -0.45rem 0 0 -0.45rem;
  opacity: 0;
  background: no-repeat center / contain;
}

/* corner actions - deliberately quiet so the name stays the focus */
.wheel-result-actions {
  position: absolute; top: var(--space-2); right: var(--space-2);
  display: flex; gap: 0.3rem;
}
.wheel-mini {
  min-height: 1.6rem; padding: 0.1rem 0.55rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text-muted);
  font-size: 0.68rem; letter-spacing: 0.04em;
}
.wheel-mini:hover { color: var(--accent-ink); border-color: var(--accent); }

.wheel-controls { flex: none; display: flex; flex-direction: column; gap: var(--space-2); align-items: center; }
.wheel-control-row {
  display: flex; flex-wrap: wrap; gap: 0.4rem;
  align-items: center; justify-content: center;
  width: 100%;
}
.wheel-control-row button, .wheel-control-row select {
  min-height: 2rem; padding: 0.25rem 0.7rem; font-size: var(--fs-xs);
}
.wheel-control-row select { min-width: 8.5rem; padding-right: 1.8rem; }
.wheel-control-row .bar-field { display: inline-flex; align-items: center; gap: 0.4rem; min-width: 0; }
.wheel-control-row .icon-btn { width: 2rem; height: 2rem; padding: 0; }
.wheel-control-row .icon-btn svg { width: 1rem; height: 1rem; }
.wheel-control-row button[aria-checked="true"] { border-color: var(--accent); color: var(--accent-ink); background: var(--accent-soft); }
/* Scoped to the switches — an aria-pressed button (the full-screen toggle)
   keeps the app-wide .when-off/.when-on swap, or it shows both icons. */
.wheel-control-row button[aria-checked] .when-off,
.wheel-control-row button[aria-checked] .when-on { display: inline; }
.wheel-control-row button[aria-checked="false"] .when-on { display: none; }
.wheel-control-row button[aria-checked="true"] .when-off { display: none; }

.wheel-hint { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-muted); }

.wheel-picked { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; justify-content: center; }
.wheel-picked-name { font-weight: var(--fw-semibold); color: var(--text-2); }
.wheel-picked button { min-height: 2.1rem; padding: 0.3rem 0.9rem; border-radius: var(--radius-pill); font-size: var(--fs-xs); }

/* ---- "Edit wheel" button + the roster modal behind it ----
   The page carries no lists any more: one button with the count, and a
   dialog of toggle chips. Managing who's on the wheel happens there and
   (as a shortcut) by clicking a slice. */
.wheel-edit-n {
  margin-left: 0.4rem;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.72rem;
  color: var(--accent-ink);
}
dialog.wheel-roster-dialog { width: min(42rem, 92vw); max-height: 85dvh; box-shadow: var(--shadow-pop); }
dialog.wheel-roster-dialog::backdrop { background: rgba(8, 6, 14, 0.6); backdrop-filter: blur(4px); }
.wheel-roster-form { display: flex; flex-direction: column; gap: var(--space-3); max-height: calc(85dvh - 2 * var(--space-5)); }
.wheel-roster-form > .hint { margin: 0; }
.wheel-roster-list {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  display: flex; flex-wrap: wrap; align-content: flex-start;
  gap: 0.4rem;
  padding: 2px;
}
/* On = accent fill; off = dim, dashed and struck through. The two states
   have to read apart across a room, not just on a laptop. */
.wr-chip {
  display: inline-flex; align-items: center; gap: 0.4rem;
  min-height: var(--touch);
  padding: 0.3rem 0.85rem;
  border: 1px solid var(--accent);
  border-radius: var(--radius-pill);
  background: var(--gradient-accent);
  color: var(--text);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition), opacity var(--transition);
}
.wr-chip:hover:not(:disabled) { border-color: var(--accent); }
/* The tick carries the state where the fill alone is subtle (pink's pale
   accent on a white card). Its space is always reserved so toggling a chip
   doesn't reflow the row. */
.wr-chip::before {
  content: '✓';
  width: 0.85em;
  text-align: center;
  font-weight: var(--fw-bold);
  color: var(--accent-ink);
  opacity: 0;
}
.wr-chip[aria-pressed="true"]::before { opacity: 1; }
.wr-chip[aria-pressed="false"] {
  border-style: dashed;
  border-color: var(--border-strong);
  background: var(--card-2);
  color: var(--text-muted);
  font-weight: var(--fw-regular);
}
.wr-chip[aria-pressed="false"] .wr-name { text-decoration: line-through; }
.wr-chip:disabled { cursor: not-allowed; opacity: 0.55; }
.wr-why {
  font-family: var(--font-mono); font-size: 0.6rem; letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.05rem 0.35rem; border-radius: var(--radius-pill);
  background: var(--card-3); color: var(--text-muted);
}
.wr-why[data-why="absent"]      { color: var(--danger); }
.wr-why[data-why="sitting out"] { color: var(--lavender); }
.wr-why[data-why="called"]      { color: var(--accent-ink); }
.wheel-roster-actions { display: flex; align-items: center; gap: var(--space-2); }
.wheel-roster-actions .spacer { flex: 1; }
.wheel-roster-actions button { min-height: 2.4rem; padding: 0 var(--space-4); }

/* ---- pick lists: the List select + "Who's picked" / Reset, and one line
   under them ("Board work · 12 of 28 picked", or Round complete) ---- */
.pick-list-bar { width: 100%; display: flex; flex-direction: column; align-items: center; gap: 0.3rem; }
.pick-list-bar[hidden] { display: none; }
.pick-list-line {
  margin: 0;
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: 0.72rem; letter-spacing: 0.04em;
  color: var(--text-muted);
  text-align: center;
}
.pick-list-line b { font-weight: inherit; color: var(--text); }
.pick-list-bar[data-done="1"] .pick-list-line b { color: var(--ok); }

/* the dialog: two columns, picked (numbered, with the date) and still to go */
dialog.pick-list-dialog { width: min(48rem, 94vw); }
.pl-cols {
  flex: 1 1 auto; min-height: 0;
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-3);
}
.pl-col { display: flex; flex-direction: column; min-height: 0; gap: var(--space-2); }
.pl-col h3 {
  margin: 0;
  font-family: var(--font-display); font-size: 0.68rem; font-weight: 600;
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--label-ink);
}
.pl-n { margin-left: 0.3rem; font-family: var(--font-mono); color: var(--accent-ink); }
.pl-list {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  margin: 0; padding: 2px; list-style: none;
  display: flex; flex-direction: column; gap: 0.3rem;
  counter-reset: pl;
}
.pl-item {
  width: 100%;
  display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "who act" "when act";
  align-items: center; column-gap: var(--space-2);
  min-height: var(--touch);
  padding: 0.3rem 0.7rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--card-2);
  color: var(--text);
  text-align: left;
}
.pl-item:hover { border-color: var(--accent); }
.pl-who {
  grid-area: who;
  font-size: var(--fs-sm); font-weight: var(--fw-semibold);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pl-when { grid-area: when; font-family: var(--font-mono); font-size: 0.68rem; color: var(--text-muted); }
.pl-when:empty { display: none; }
.pl-act {
  grid-area: act;
  font-size: 0.68rem; letter-spacing: 0.04em;
  color: var(--text-muted);
  opacity: 0; transition: opacity var(--transition);
}
.pl-item:hover .pl-act, .pl-item:focus-visible .pl-act { opacity: 1; color: var(--accent-ink); }
/* the picked side is numbered in the order they were called */
#pl-picked .pl-who::before {
  counter-increment: pl; content: counter(pl) ". ";
  font-family: var(--font-mono); font-weight: 400; color: var(--text-muted);
}
.pl-none { padding: 0.4rem 0.2rem; font-size: var(--fs-sm); color: var(--text-muted); }
@media (max-width: 600px) {
  .pl-cols { grid-template-columns: minmax(0, 1fr); overflow-y: auto; }
  .pl-list { overflow: visible; }
}

/* slices are clickable (take that student off); the label must not swallow it */
.slice { cursor: pointer; }
.slice-label { pointer-events: none; }
body.wheel-popout .slice { cursor: default; }

/* full screen: controls go, the wheel takes the room */
body.present .wheel-wrap { width: min(80vh, 94vw); }
body.present .wheel-hub-name { font-size: clamp(1.6rem, 6vh, 3.6rem); }

/* pop-out: wheel only, flat background like the other projector views */
body.wheel-popout {
  margin: 0; min-height: 100vh; min-height: 100dvh;
  background: var(--bg-0);
  background-image: none;
  display: grid; place-items: center;
  overflow: hidden;
}
body.wheel-popout .wheel-stage { gap: var(--space-2); }
/* the machine is 100 wide x 134 tall, so on the projector size it by HEIGHT
   and let the aspect ratio pick the width - sizing by width overflowed */
body.wheel-popout .wheel-wrap { width: auto; height: min(92vh, 92vw); max-width: 92vw; }
body.wheel-popout .wheel-hub-name { font-size: clamp(1.4rem, 5.5vmin, 3.2rem); }
body.wheel-popout .wheel-period {
  font-family: var(--font-display); text-transform: var(--label-transform);
  letter-spacing: var(--track-label); font-size: 0.8rem; color: var(--text-muted);
}

/* ---------------------------------------------------------------------------
   Name Wheel - pink skin: the prettiest wheel. Five pastels in turn (pink,
   lavender, mint, peach, butter; wheel.js's p0-p4) split by thin white lines,
   plum Comfortaa names, a soft rounded housing ring with a gentle gradient
   and a gloss, a plump heart for the pointer that tips as each slice passes,
   a bunny face on the Spin hub, and a bunny beside the wheel that reacts.
   No instrument ring, no bracket, no legs.
   --------------------------------------------------------------------------- */
.pointer-heart, .pointer-heart-hi, .housing-hi, .hub-face { display: none; }
#wheel-housing-grad stop { stop-color: var(--wheel-housing); }

:root[data-theme="pink"] .slice.p0 { fill: var(--wheel-p0); }
:root[data-theme="pink"] .slice.p1 { fill: var(--wheel-p1); }
:root[data-theme="pink"] .slice.p2 { fill: var(--wheel-p2); }
:root[data-theme="pink"] .slice.p3 { fill: var(--wheel-p3); }
:root[data-theme="pink"] .slice.p4 { fill: var(--wheel-p4); }
:root[data-theme="pink"] .slice.won { fill: var(--wheel-won); }
:root[data-theme="pink"] .slice-dividers line { stroke: var(--wheel-divider); stroke-width: 0.45; }
:root[data-theme="pink"] .slice-label {
  font-family: "Comfortaa", var(--font-body);
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0.01em;
  stroke-width: 0.3;                /* the faintest white halo, not an outline */
}
/* a whisper of sheen; the rim shadow is plum, never grey on the pastels */
:root[data-theme="pink"] #wheel-ish .ish-mid  { stop-color: rgba(74, 44, 61, 0.03); }
:root[data-theme="pink"] #wheel-ish .ish-edge { stop-color: rgba(74, 44, 61, 0.13); }

/* the housing: one soft ring, light at the top to rose at the bottom, a
   white bead inside it and a gloss arc on its upper left */
:root[data-theme="pink"] .housing {
  stroke: url(#wheel-housing-grad);
  stroke-width: 5.6;
  filter: drop-shadow(0 0.9px 1.4px var(--wheel-housing-shadow));
}
:root[data-theme="pink"] .housing-hi {
  display: block;
  fill: none;
  stroke: rgba(255, 255, 255, 0.9);
  stroke-width: 0.8;
  stroke-linecap: round;
  stroke-dasharray: 44 400;
  transform-box: view-box;
  transform-origin: 50px 50px;
  transform: rotate(198deg);
}
#wheel-housing-grad .hg-top { stop-color: var(--wheel-housing-top, var(--wheel-housing)); }
#wheel-housing-grad .hg-bot { stop-color: var(--wheel-housing-bot, var(--wheel-housing)); }
:root[data-theme="pink"] .housing-foot,
:root[data-theme="pink"] .bracket,
:root[data-theme="pink"] .bracket-hi,
:root[data-theme="pink"] .wheel-dial { display: none; }
:root[data-theme="pink"] .wheel-rim { stroke: var(--wheel-divider); stroke-width: 1.1; }
:root[data-theme="pink"] .wheel-axle .bolt { fill: #FFFFFF; stroke: var(--wheel-metal-2); stroke-width: 0.4; }

/* the pointer: a plump heart on the ring, tipping as each slice knocks it */
:root[data-theme="pink"] .pointer-hud,
:root[data-theme="pink"] .pointer-soft { display: none; }
:root[data-theme="pink"] .pointer-heart {
  display: block;
  fill: var(--accent);
  stroke: #FFFFFF;
  stroke-width: 0.7;
  stroke-linejoin: round;
}
:root[data-theme="pink"] .pointer-heart-hi { display: block; fill: rgba(255, 255, 255, 0.75); }
:root[data-theme="pink"] .pointer-arm {
  transform-origin: 50px 1.2px;
  filter: drop-shadow(0 0.7px 1px rgba(201, 49, 104, 0.35));
}
:root[data-theme="pink"] .pointer-arm.flick { animation: heart-tip 200ms ease-out; }
@keyframes heart-tip {
  0%   { transform: rotate(0deg); }
  30%  { transform: rotate(-11deg); }
  70%  { transform: rotate(4deg); }
  100% { transform: rotate(0deg); }
}

/* the hub is the Spin button: a white disc with a bunny face over "Spin" */
:root[data-theme="pink"] .wheel-hub-btn {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3%;
  padding: 0;
  border: 2px solid var(--accent);
  background: radial-gradient(circle at 38% 30%, #FFFFFF 0%, #FFF6F9 58%, #FFE4EE 100%);
  color: var(--accent-deep);
  font-family: "Comfortaa", var(--font-body);
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0.02em;
  font-size: clamp(0.62rem, 1.9vh, 1.05rem);
  box-shadow: 0 0 0 0.3rem rgba(255, 255, 255, 0.95), 0 0.35rem 1rem var(--accent-glow);
}
:root[data-theme="pink"] .hub-face {
  display: block;
  width: 52%;
  aspect-ratio: 40 / 37;
  background: var(--pink-hub-face) no-repeat center / contain;
}
:root[data-theme="pink"] .wheel-hub-btn:hover:not(:disabled) {
  color: var(--accent-deep);
  box-shadow: 0 0 0 0.3rem #FFFFFF, 0 0 0 0.45rem var(--accent-soft), 0 0.45rem 1.4rem var(--accent-glow);
}
:root[data-theme="pink"] .wheel-hub-btn:hover:not(:disabled) .hub-face { animation: hub-wiggle 600ms ease-in-out; }
:root[data-theme="pink"] .wheel-hub-btn:active:not(:disabled) {
  background: radial-gradient(circle at 50% 40%, #FFF0F5 0%, #FFDDE9 100%);
  box-shadow: 0 0 0 0.3rem #FFFFFF, inset 0 2px 6px rgba(201, 49, 104, 0.18);
}
:root[data-theme="pink"] .wheel-hub-btn:disabled {
  color: var(--text-muted);
  border-color: var(--wheel-metal-2);
  box-shadow: 0 0 0 0.3rem rgba(255, 255, 255, 0.95);
}
:root[data-theme="pink"] .wheel-hub-btn:disabled .hub-face { opacity: 0.6; }
:root[data-theme="pink"] .wheel-hub-btn[data-spinning="1"] { color: var(--accent-deep); border-color: var(--accent); opacity: 1; }
:root[data-theme="pink"] .wheel-hub-btn[data-spinning="1"] .hub-face { opacity: 1; animation: hub-bob 420ms ease-in-out infinite alternate; }
@keyframes hub-wiggle { 0%, 100% { rotate: 0deg; } 30% { rotate: -9deg; } 65% { rotate: 7deg; } }
@keyframes hub-bob { from { translate: 0 0; } to { translate: 0 -6%; } }

/* the result flash becomes hearts and stars flying outward */
:root[data-theme="pink"] .wheel-sparks { display: block; }
:root[data-theme="pink"] .wheel-glow { background: radial-gradient(circle at 50% 45%, var(--accent-soft) 0%, transparent 58%); }
:root[data-theme="pink"] .wheel-sparks i.heart { background-image: var(--pink-burst-heart); }
:root[data-theme="pink"] .wheel-sparks i.spark { background-image: var(--pink-burst-spark); }
:root[data-theme="pink"] .wheel-result[data-state="picked"] .wheel-sparks i {
  animation: result-spark 1100ms ease-out 1;
  animation-delay: calc(var(--i) * 70ms);
}
@keyframes result-spark {
  0%   { opacity: 0; transform: rotate(calc(var(--i) * 60deg)) translateY(0) scale(0.4); }
  25%  { opacity: 1; }
  100% { opacity: 0; transform: rotate(calc(var(--i) * 60deg)) translateY(-5.5rem) scale(1); }
}
:root[data-theme="pink"] .wheel-result-name {
  font-family: "Comfortaa", var(--font-body);
  text-transform: none;
  letter-spacing: 0.01em;
}
:root[data-theme="pink"] .wheel-placeholder {
  font-family: var(--font-body);
  text-transform: none;
  letter-spacing: 0.01em;
  font-size: var(--fs-sm);
}
/* the pop-out keeps a disc for the winner's name */
:root[data-theme="pink"] .wheel-hub { fill: #FFFFFF; }
:root[data-theme="pink"] .wheel-hub-ring { stroke: var(--accent); stroke-width: 0.8; }
:root[data-theme="pink"] .wheel-hub-name {
  font-family: "Comfortaa", var(--font-body);
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0.01em;
  font-size: clamp(1rem, 3.6vh, 1.9rem);
  text-shadow: none;
}

/* ---- the bunny beside the wheel (wheel.js BUDDY_SVG) ----
   Sits in the wheel's lower-left corner, in the square's empty corner just
   off the housing, so it costs the wheel no space. Poses come from data-mood
   on #wheel-mascot: idle (calm eyes, ears relaxed out, a slow breath), spin
   (ears perk up, eyes go wide, an "oh" mouth, a little sway) and party
   (^ ^ eyes, a big smile, three hops and a fan of hearts). */
.wheel-mascot { display: none; }
:root[data-theme="pink"] .wheel-mascot {
  display: block;
  position: absolute;
  left: -4%; bottom: -2%;
  width: 17%;
  aspect-ratio: 80 / 96;
  z-index: 2;
  pointer-events: none;
}
.wb-svg { display: block; width: 100%; height: 100%; overflow: visible; }
.wb-svg g, .wb-svg ellipse, .wb-svg path, .wb-svg circle { transform-box: fill-box; }
.wb-fur    { fill: var(--wb-fur); stroke: var(--wb-line); stroke-width: 2.4; }
.wb-inner  { fill: var(--wb-inner); }
.wb-belly  { fill: var(--wb-belly); }
.wb-bow path, .wb-bow circle { fill: var(--wb-bow); stroke: var(--wb-line); stroke-width: 1.4; stroke-linejoin: round; }
.wb-blush  { fill: var(--wb-blush); opacity: 0.45; transition: opacity 300ms ease; }
.wb-ink    { fill: var(--wb-line); }
.wb-shine  { fill: #FFFFFF; }
.wb-line   { fill: none; stroke: var(--wb-line); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.wb-thick  { stroke-width: 2.3; }
.wb-nose   { fill: var(--wb-bow); }
.wb-tongue { fill: var(--wb-blush); }
.wb-shadow { fill: var(--wb-shadow); }

.wb-eyes, .wb-mouth { opacity: 0; transition: opacity 160ms ease; }
.wheel-mascot[data-mood="idle"]  .wb-eyes-calm,  .wheel-mascot[data-mood="idle"]  .wb-mouth-calm,
.wheel-mascot[data-mood="spin"]  .wb-eyes-wide,  .wheel-mascot[data-mood="spin"]  .wb-mouth-wow,
.wheel-mascot[data-mood="party"] .wb-eyes-happy, .wheel-mascot[data-mood="party"] .wb-mouth-happy { opacity: 1; }
.wheel-mascot[data-mood="spin"] .wb-blush,
.wheel-mascot[data-mood="party"] .wb-blush { opacity: 0.8; }

/* ears pivot at their base, inside the head */
.wb-ear { transform-origin: 50% 92%; transition: transform 340ms cubic-bezier(.34, 1.56, .64, 1); }
.wheel-mascot[data-mood="idle"] .wb-ear-l { transform: rotate(-17deg) scaleY(0.94); }
.wheel-mascot[data-mood="idle"] .wb-ear-r { transform: rotate(17deg) scaleY(0.94); }
.wheel-mascot[data-mood="spin"] .wb-ear   { transform: translateY(-7%) scaleY(1.1); }
.wheel-mascot[data-mood="party"] .wb-ear-l { transform: rotate(-8deg); animation: wb-ear-l 300ms ease-in-out 6 alternate; }
.wheel-mascot[data-mood="party"] .wb-ear-r { transform: rotate(8deg);  animation: wb-ear-r 300ms ease-in-out 6 alternate; }
@keyframes wb-ear-l { from { transform: rotate(-4deg); } to { transform: rotate(-22deg); } }
@keyframes wb-ear-r { from { transform: rotate(4deg); }  to { transform: rotate(22deg); } }

.wb-breathe, .wb-hop { transform-origin: 50% 100%; }
.wheel-mascot[data-mood="idle"] .wb-breathe { animation: wb-breathe 3.2s ease-in-out infinite; }
.wheel-mascot[data-mood="spin"] .wb-breathe { animation: wb-sway 700ms ease-in-out infinite alternate; }
.wheel-mascot[data-mood="party"] .wb-hop    { animation: wb-hop 620ms cubic-bezier(.3, .7, .4, 1) 3; }
@keyframes wb-breathe { 0%, 100% { transform: scale(1, 1); } 50% { transform: scale(1.015, 1.03); } }
@keyframes wb-sway    { from { transform: rotate(-3deg); } to { transform: rotate(3deg); } }
@keyframes wb-hop {
  0%, 100% { transform: translateY(0) scale(1, 1); }
  12%      { transform: translateY(0) scale(1.08, 0.9); }
  45%      { transform: translateY(-16%) scale(0.96, 1.06); }
  80%      { transform: translateY(0) scale(1.05, 0.94); }
}

/* the hearts fan up out of the bunny's head */
.wb-burst { position: absolute; left: 50%; top: 32%; width: 0; height: 0; }
.wb-burst i {
  position: absolute;
  left: -0.6em; top: -0.6em;
  width: 1.2em; height: 1.2em;
  font-size: clamp(0.7rem, 2.3vh, 1.35rem);
  opacity: 0;
  background: var(--pink-burst-heart) no-repeat center / contain;
}
.wb-burst i:nth-child(3n) { background-image: var(--pink-burst-spark); }
.wb-burst i:nth-child(even) { width: 0.9em; height: 0.9em; }
.wheel-mascot[data-mood="party"] .wb-burst i {
  animation: wb-heart 1300ms cubic-bezier(.15, .7, .35, 1) forwards;
  animation-delay: calc(var(--i) * 45ms);
}
@keyframes wb-heart {
  0%   { opacity: 0; transform: rotate(calc(-84deg + var(--i) * 24deg)) translateY(0) scale(0.3); }
  18%  { opacity: 1; }
  100% { opacity: 0; transform: rotate(calc(-84deg + var(--i) * 24deg)) translateY(-4.6em) scale(1.1); }
}

@media (prefers-reduced-motion: reduce) {
  /* Decorative motion still goes: the pop-in, the ring pulse, the bunny's
     loops and hops, the hub's wiggle. The poses themselves still change. */
  .wheel-stage[data-state="picked"] .wheel-hub-name,
  .wheel-stage[data-state="picked"] .wheel-rim,
  .wheel-mascot *,
  .hub-face { animation: none !important; }
  .wb-burst { display: none; }

  /* ...but the SPIN stays. The app-wide `* { transition: none !important }`
     reduced-motion rule beats inline styles, which silently killed the
     wheel's transition and made it teleport to the answer - the spin looked
     broken and repeated clicks looked dead. The spin is the entire point of
     this tool and only ever runs when the teacher presses Spin, so it is
     exempt. wheel.js drives the duration/easing through these custom
     properties (it sets them alongside the shorthand), which is how it keeps
     per-phase control against the !important. */
  #wheel-rotor {
    transition-property: transform !important;
    transition-duration: var(--wheel-dur, 0s) !important;
    transition-timing-function: var(--wheel-ease, linear) !important;
  }
}

/* ---------------------------------------------------------------------------
   Name Wheel — halloween skin: a CAULDRON instead of the wheel (cauldron.js).
   Every name floats in the green brew as a bubble (HTML pills moved by a rAF
   physics loop); a pick boils the brew (--boil, 0..1, set by cauldron.js
   every frame: brighter brew, higher flames, more steam and embers), lifts
   the winner's bubble above the surface, hovers, pops it in green sparks, and
   the name rises above the cauldron in Pirata One. Clicking anywhere on the
   cauldron is the Spin button here (the hub button and the wheel SVG hide).
   Layers in the 400x400 box: .cd-back (glow, steam, pot, rim, brew) →
   .cd-brew (the bubbles, over the brew surface: left 15%, top 29.5%, 70% ×
   26.5%) → .cd-front (the near lip of the rim, logs, flames) → embers →
   the burst → the winner's name.
   --------------------------------------------------------------------------- */
.cauldron, .for-brew { display: none; }
:root[data-theme="halloween"] .cauldron { display: block; }
:root[data-theme="halloween"] .for-brew { display: inline; }
:root[data-theme="halloween"] .for-wheel,
:root[data-theme="halloween"] .wheel-wrap > .wheel-svg,
:root[data-theme="halloween"] #wheel-spin,
:root[data-theme="halloween"] .wheel-hub-text { display: none; }
:root[data-theme="halloween"] .wheel-result-name { font-family: var(--font-spooky); text-transform: none; letter-spacing: 0.02em; }

.cauldron {
  position: absolute; inset: 0;
  container-type: inline-size;
  cursor: pointer;
  border-radius: var(--radius-lg);
  -webkit-tap-highlight-color: transparent;
}
.cauldron[aria-disabled="true"] { cursor: default; }
body.wheel-popout .cauldron { cursor: default; }
.cauldron:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.cd-back, .cd-front { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.cd-back { z-index: 1; }
.cd-front { z-index: 3; }
.cauldron svg * { transform-box: fill-box; }

/* gradients: one colour per gradient, faded by stop-opacity (SVG gradients
   don't premultiply, so fading to `transparent` goes grey) */
#cd-g-fire stop { stop-color: var(--cd-fire-glow); }
#cd-g-under stop { stop-color: var(--cd-under-glow); }
#cd-g-haze stop { stop-color: var(--cd-haze); }
#cd-g-fire stop:last-child, #cd-g-under stop:last-child, #cd-g-haze stop:last-child { stop-opacity: 0; }
#cd-g-pool stop { stop-color: var(--cd-pool); }
#cd-g-pool stop:nth-child(2) { stop-opacity: 0.45; }
#cd-g-bed stop:nth-child(1) { stop-color: var(--cd-bed); }
#cd-g-bed stop:nth-child(2) { stop-color: var(--cd-bed-mid); }
#cd-g-bed stop:nth-child(3) { stop-color: var(--cd-bed-mid); }
#cd-g-pool stop:last-child, #cd-g-bed stop:last-child { stop-opacity: 0; }
#cd-g-f-out stop:nth-child(1)  { stop-color: var(--cd-flame-out); }
#cd-g-f-out stop:nth-child(2)  { stop-color: var(--cd-flame-tip); }
#cd-g-f-mid stop:nth-child(1)  { stop-color: var(--cd-flame-mid); }
#cd-g-f-mid stop:nth-child(2)  { stop-color: var(--cd-flame-mid-tip); }
#cd-g-f-core stop:nth-child(1) { stop-color: var(--cd-flame-core); }
#cd-g-f-core stop:nth-child(2) { stop-color: var(--cd-flame-core-tip); }
#cd-g-rimlight stop { stop-color: var(--cd-rimlight); stop-opacity: 0; }
#cd-g-rimlight stop:last-child { stop-opacity: 1; }
#cd-g-brew stop:nth-child(1) { stop-color: var(--cd-brew-1); }
#cd-g-brew stop:nth-child(2) { stop-color: var(--cd-brew-2); }
#cd-g-brew stop:nth-child(3) { stop-color: var(--cd-brew-3); }
#cd-g-pot stop:nth-child(1) { stop-color: var(--cd-pot-1); }
#cd-g-pot stop:nth-child(2) { stop-color: var(--cd-pot-2); }
#cd-g-pot stop:nth-child(3) { stop-color: var(--cd-pot-3); }

/* the pot */
.cd-pot { stroke: var(--cd-pot-line); stroke-width: 3; stroke-linejoin: round; }
.cd-pot-glow { opacity: calc(0.6 + var(--boil, 0) * 0.4); }
/* firelight catching the pot's underside: a stroke along its lower curve */
.cd-pot-rimlight {
  fill: none; stroke-width: 7; stroke-linecap: round;
  opacity: calc(0.7 + var(--boil, 0) * 0.3);
  animation: cd-light 1.3s ease-in-out infinite alternate;
}
.cd-pot-hi { fill: none; stroke: var(--cd-pot-hi); stroke-width: 7; stroke-linecap: round; }
.cd-leg { fill: var(--cd-leg); }
.cd-rim, .cd-lip { fill: var(--cd-pot-1); stroke: var(--cd-pot-line); stroke-width: 3; stroke-linejoin: round; }
.cd-lip-hi { fill: none; stroke: var(--cd-pot-hi); stroke-width: 3; stroke-linecap: round; }
/* the logs: crossed under the fire, dark, with cracks glowing orange */
.cd-log line { stroke-linecap: round; }
.cd-log .lg-edge { stroke: var(--cd-log-line); stroke-width: 17; }
.cd-log .lg-bark { stroke: var(--cd-log); stroke-width: 13; }
.cd-log .lg-end  { fill: var(--cd-log-end); stroke: var(--cd-log-line); stroke-width: 2; }
.cd-log .lg-ring { fill: none; stroke: var(--cd-log-ring); stroke-width: 1.4; }
.cd-log .lg-crack {
  fill: none; stroke: var(--cd-crack); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
  filter: drop-shadow(0 0 2px var(--cd-crack-glow));
  opacity: calc(0.75 + var(--boil, 0) * 0.25);
  animation: cd-crack 1.9s ease-in-out infinite alternate;
}
.cd-log .lg-crack:nth-of-type(2n) { animation-duration: 2.6s; animation-delay: -0.9s; }
@keyframes cd-crack { from { stroke-opacity: 0.55; } to { stroke-opacity: 1; } }
/* the bed of coals the flames rise out of */
.cd-bed { opacity: calc(0.8 + var(--boil, 0) * 0.2); animation: cd-light 0.9s ease-in-out infinite alternate; }

/* the brew: glowing, brighter as it boils */
.cd-brew-surface {
  filter: brightness(calc(1 + var(--boil, 0) * 0.5)) drop-shadow(0 0 calc(6px + var(--boil, 0) * 14px) var(--cd-glow));
}
.cd-brew-sheen { fill: var(--cd-sheen); }
.cd-haze { opacity: calc(0.4 + var(--boil, 0) * 0.6); }
/* the warm light on the ground: a wide halo and a bright pool under the pot,
   flickering with the fire and swelling with the boil */
.cd-light { opacity: calc(0.7 + var(--boil, 0) * 0.3); animation: cd-light 1.1s ease-in-out infinite alternate; }
@keyframes cd-light { 0% { stroke-opacity: 0.8; fill-opacity: 0.82; } 40% { stroke-opacity: 1; fill-opacity: 1; } 100% { stroke-opacity: 0.88; fill-opacity: 0.9; } }
.cauldron:not([aria-disabled="true"]):hover .cd-brew-surface { filter: brightness(1.15) drop-shadow(0 0 10px var(--cd-glow)); }

/* steam curling up off the top */
.cd-steam path { fill: none; stroke: var(--cd-steam); stroke-width: 10; stroke-linecap: round; opacity: 0; transform-origin: 50% 100%; animation: cd-steam 4.6s ease-out infinite; }
.cd-steam path:nth-child(2) { animation-delay: -1.5s; }
.cd-steam path:nth-child(3) { animation-delay: -3s; }
.cd-steam { opacity: calc(0.6 + var(--boil, 0) * 0.4); }
@keyframes cd-steam {
  0%   { opacity: 0; transform: translateY(16px) scale(0.7, 0.6); }
  30%  { opacity: 1; }
  100% { opacity: 0; transform: translateY(-26px) scale(1.25, 1.1); }
}

/* the fire: every flame is three layers (deep orange, bright orange, a
   yellow-white core) that lick on their own beats, each pivoting on its
   base, so the flame tears and sways instead of pulsing as one piece. The
   back flames sit behind the pot and lick up round its sides; the front ones
   rise over its base. With the boil they leap (taller, a touch wider) and
   burn brighter. */
/* placed by its transform attribute in viewBox units (fill-box would flip a
   mirrored flame about its own edge) */
.cauldron svg .cd-flame { transform-box: view-box; }
.cd-flame .f-out  { fill: url(#cd-g-f-out); }
.cd-flame .f-mid  { fill: url(#cd-g-f-mid); }
.cd-flame .f-core { fill: url(#cd-g-f-core); }
.cd-flame path { transform-origin: 50% 100%; animation: cd-lick 1.15s ease-in-out infinite; }
.cd-flame .f-out  { animation-delay: calc(var(--i) * -0.37s); }
.cd-flame .f-mid  { animation-name: cd-lick-mid;  animation-duration: 0.74s; animation-delay: calc(var(--i) * -0.23s); }
.cd-flame .f-core { animation-name: cd-lick-core; animation-duration: 0.49s; animation-delay: calc(var(--i) * -0.13s); }
.cd-flame:nth-child(odd) .f-out { animation-duration: 1.37s; }
.cd-flame:nth-child(3n) .f-mid  { animation-duration: 0.88s; }
@keyframes cd-lick {
  0%, 100% { transform: scale(1, 1) skewX(0deg); }
  22% { transform: scale(0.94, 1.09) skewX(-6deg); }
  48% { transform: scale(1.05, 0.93) skewX(3deg); }
  74% { transform: scale(0.97, 1.06) skewX(7deg); }
}
@keyframes cd-lick-mid {
  0%, 100% { transform: scale(1, 1) skewX(0deg); }
  30% { transform: scale(1.06, 0.9) skewX(5deg); }
  62% { transform: scale(0.93, 1.12) skewX(-7deg); }
}
@keyframes cd-lick-core {
  0%, 100% { transform: scale(1, 1) translateX(0); }
  35% { transform: scale(0.9, 1.16) translateX(1px); }
  70% { transform: scale(1.08, 0.86) translateX(-1px); }
}
.cd-flame-boil {
  transform-origin: 50% 100%;
  scale: calc(1 + var(--boil, 0) * 0.14) calc(1 + var(--boil, 0) * 0.6);   /* the tallest tip stays under the brew */
  transition: scale 220ms ease-out;
}
.cd-flames { filter: brightness(calc(1 + var(--boil, 0) * 0.3)) drop-shadow(0 0 calc(3px + var(--boil, 0) * 7px) var(--cd-crack-glow)); }
.cd-flames.back { filter: brightness(calc(0.9 + var(--boil, 0) * 0.3)); }

/* embers drifting up from the fire */
.cd-embers { position: absolute; inset: 0; z-index: 4; pointer-events: none; opacity: calc(0.45 + var(--boil, 0) * 0.55); }
.cd-embers i {
  position: absolute; bottom: 14%; left: calc(31% + var(--i) * 3.3%);
  width: 0.8cqi; height: 0.8cqi; border-radius: 50%;
  background: var(--cd-ember-hot); box-shadow: 0 0 0.6cqi var(--cd-ember), 0 0 1.4cqi var(--cd-ember);
  opacity: 0;
  animation: cd-ember 2.8s ease-out infinite; animation-delay: calc(var(--i) * -0.29s);
}
.cd-embers i:nth-child(odd)  { animation-duration: 2.2s; --dx: -5cqi; }
.cd-embers i:nth-child(3n)   { width: 0.55cqi; height: 0.55cqi; animation-duration: 3.3s; --dx: 3cqi; }
.cd-embers i:nth-child(4n+1) { width: 1.05cqi; height: 1.05cqi; }
/* they rise in a wavering line, cool from yellow to orange and wink out */
@keyframes cd-ember {
  0%   { opacity: 0; transform: translate(0, 0) scale(1); }
  12%  { opacity: 1; }
  40%  { transform: translate(calc(var(--dx, 5cqi) * -0.4), -11cqi) scale(0.85); background: var(--cd-ember-hot); }
  70%  { opacity: 0.8; transform: translate(calc(var(--dx, 5cqi) * 0.6), -21cqi) scale(0.6); background: var(--cd-ember); }
  100% { opacity: 0; transform: translate(var(--dx, 5cqi), -29cqi) scale(0.3); background: var(--cd-ember); }
}

/* the bubbles */
.cd-brew { position: absolute; left: 15%; top: 29.5%; width: 70%; height: 26.5%; z-index: 2; pointer-events: none; }
.cd-bubble {
  position: absolute; left: 50%; top: 50%;
  display: grid; place-items: center;
  border-radius: 999px;
  background:
    radial-gradient(circle at 16% 26%, var(--cd-bubble-hi) 0 0.14em, transparent 0.2em),
    radial-gradient(ellipse at 50% 62%, var(--cd-bubble), var(--cd-bubble-edge));
  border: 2px solid var(--cd-bubble-line);
  box-shadow: 0 0 0.6em var(--cd-glow-soft), inset 0 -0.2em 0.4em rgba(0, 0, 0, 0.3);
  color: var(--cd-ink);
  font-family: var(--font-body); font-weight: 700; line-height: 1;
  white-space: nowrap;
  will-change: transform;
  transform-origin: center;
}
.cd-name { max-width: 100%; padding: 0 0.35em; overflow: hidden; text-overflow: ellipsis; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9); }
.cd-bubble.born { animation: cd-born 650ms cubic-bezier(.34, 1.56, .64, 1); }
.cd-bubble.lifted { z-index: 5; box-shadow: 0 0 1.2em var(--cd-glow), 0 0 2.4em var(--cd-glow-soft), inset 0 -0.2em 0.4em rgba(0, 0, 0, 0.3); }
.cd-bubble.pop { animation: cd-pop 300ms ease-out forwards; }
.cd-bubble.popped { visibility: hidden; }
@keyframes cd-born { from { scale: 0.2; opacity: 0; } to { scale: 1; opacity: 1; } }
@keyframes cd-pop  { 0% { scale: 1; opacity: 1; } 40% { scale: 1.25; opacity: 1; } 100% { scale: 1.9; opacity: 0; } }

/* the pop: green sparks flying out from where the bubble was */
.cd-burst { position: absolute; z-index: 6; width: 0; height: 0; pointer-events: none; }
.cd-burst i {
  position: absolute; left: -1.1cqi; top: -1.1cqi;
  width: 2.2cqi; height: 2.2cqi; border-radius: 50%;
  background: var(--cd-spark); box-shadow: 0 0 1.4cqi var(--cd-spark), 0 0 3cqi var(--cd-glow);
  opacity: 0;
}
.cd-burst.go i { animation: cd-spark 700ms cubic-bezier(.2, .7, .3, 1) forwards; }
@keyframes cd-spark {
  0%   { opacity: 1; transform: rotate(var(--a)) translateY(0) scale(1.2); }
  100% { opacity: 0; transform: rotate(var(--a)) translateY(calc(var(--d) * -27cqi)) scale(0.3); }
}

/* the winner, above the cauldron */
.cd-winner {
  position: absolute; left: 3%; right: 3%; top: 1%; height: 24%; z-index: 7;
  margin: 0; display: grid; place-items: center;
  font-family: var(--font-spooky); font-size: 12cqi; line-height: 1;
  color: var(--cd-winner);
  text-align: center; white-space: nowrap; overflow: visible;
  text-shadow: 0 0 0.12em var(--cd-glow), 0 0 0.4em var(--cd-glow), 0 0 0.9em var(--cd-glow-soft);
  opacity: 0; scale: 0.6;
  transition: opacity 300ms ease, scale 300ms ease;
  pointer-events: none;
}
.cd-winner[data-len="long"] { font-size: 8.5cqi; }
.cauldron[data-state="picked"] .cd-winner { opacity: 1; scale: 1; transition: opacity 260ms ease, scale 520ms cubic-bezier(.34, 1.56, .64, 1); }
.cauldron[data-state="picked"] .cd-winner { animation: cd-glow 2.4s ease-in-out 600ms infinite; }
@keyframes cd-glow { 50% { text-shadow: 0 0 0.16em var(--cd-glow), 0 0 0.6em var(--cd-glow), 0 0 1.3em var(--cd-glow-soft); } }

/* full screen + the projector windows: the cauldron fills the view */
:root[data-theme="halloween"] body.present .wheel-wrap { width: auto; height: min(86vh, 94vw); }

/* Reduced motion: the loops that are only decoration stop (steam, embers,
   the flames' licking, the firelight flicker, the name's glow pulse); the brew itself — bubbles drifting, the
   boil, the rise and the pop — is the tool, run on a click, so it stays. The
   flames' boil scale keeps its easing against the app-wide `* { transition:
   none !important }`. */
@media (prefers-reduced-motion: reduce) {
  .cd-steam path, .cd-embers i, .cd-flame path, .cd-light, .cd-bed, .cd-pot-rimlight, .cd-log .lg-crack,
  .cauldron[data-state="picked"] .cd-winner { animation: none !important; }
  .cd-embers { display: none; }
  .cd-flame-boil { transition: scale 220ms ease-out !important; }
  .cd-winner { transition: opacity 300ms ease !important; }
}

/* ---------------------------------------------------------------------------
   Name Wheel — thanksgiving skin: a CORNUCOPIA instead of the wheel
   (cornucopia.js). A woven horn on its side on a wooden table, produce in
   its mouth and spilling out, one piece per student (no names). A pick tips
   and shakes the horn (.horn[data-state="tipping"]), the produce tumbles out
   and rolls over the table, the chosen piece lights up (.chosen) and rolls
   to the front, and the name rises above it in a burst of leaves. With No
   repeats on, called pieces collect in the basket on the right (names on
   them) until "Tip it back in". The whole scene is the button.
   --------------------------------------------------------------------------- */
.horn, .for-horn { display: none; }
:root[data-theme="thanksgiving"] .horn { display: block; }
:root[data-theme="thanksgiving"] .for-horn { display: inline; }
:root[data-theme="thanksgiving"] .for-wheel,
:root[data-theme="thanksgiving"] .wheel-wrap > .wheel-svg,
:root[data-theme="thanksgiving"] #wheel-spin,
:root[data-theme="thanksgiving"] .wheel-hub-text,
:root[data-theme="thanksgiving"] .wheel-mascot { display: none; }

.horn {
  position: absolute; inset: 0;
  container-type: inline-size;
  overflow: hidden;
  cursor: pointer;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  background: radial-gradient(ellipse 80% 70% at 45% 40%, var(--hn-wall-1), var(--hn-wall-2));
  -webkit-tap-highlight-color: transparent;
}
.horn[aria-disabled="true"] { cursor: default; }
body.wheel-popout .horn { cursor: default; border: 0; }
.horn:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.hn-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }

/* the table */
.hn-table { fill: url(#hn-g-table); }
#hn-g-table stop:first-child { stop-color: var(--hn-wood); }
#hn-g-table stop:last-child { stop-color: var(--hn-wood-2); }
.hn-table-planks { fill: none; stroke: var(--hn-plank); stroke-width: 1.6; }
.hn-table-grain { fill: none; stroke: var(--hn-grain); stroke-width: 1.4; stroke-linecap: round; }
.hn-table-edge { fill: none; stroke: var(--hn-wood-edge); stroke-width: 2.4; }
.hn-horn-shadow { fill: var(--hn-shadow); filter: url(#hn-blur); }
.hn-shadow { fill: var(--hn-shadow); }

/* the horn: wicker body, woven texture, ring bands, a dark mouth */
.hn-horn { transform-box: view-box; transform-origin: 196px 274px; }
.hn-horn-body { fill: var(--hn-wicker); }
.hn-horn-weave { fill: url(#hn-weave); }
.hn-weave-a { stroke: var(--hn-weave-a); stroke-width: 1.8; fill: none; }
.hn-weave-b { stroke: var(--hn-weave-b); stroke-width: 1.4; fill: none; }
.hn-horn-shade { fill: none; stroke: var(--hn-wicker-d); stroke-width: 18; stroke-linecap: round; opacity: 0.45; }
.hn-horn-hi { fill: none; stroke: var(--hn-wicker-hi); stroke-width: 6; stroke-linecap: round; opacity: 0.5; }
.hn-horn-bands { fill: none; stroke: var(--hn-wicker-d); stroke-width: 3; stroke-linecap: round; opacity: 0.85; }
.hn-horn-edge { fill: none; stroke: var(--hn-line); stroke-width: 2.6; stroke-linejoin: round; }
.hn-mouth-in { fill: url(#hn-g-mouth); }
#hn-g-mouth stop:first-child { stop-color: var(--hn-mouth); }
#hn-g-mouth stop:last-child { stop-color: var(--hn-mouth-2); }
.hn-mouth-rim { fill: none; stroke: var(--hn-wicker-d); stroke-width: 9; }
.hn-mouth-braid { fill: none; stroke: var(--hn-wicker-hi); stroke-width: 3.4; stroke-dasharray: 5 4; stroke-linecap: round; }

/* tip and shake: the tail lifts, the mouth dips, a rattle, then it settles */
.horn[data-state="tipping"] .hn-horn { animation: hn-tip 1500ms cubic-bezier(.3, .1, .3, 1) both; }
@keyframes hn-tip {
  0%   { transform: rotate(0); }
  16%  { transform: rotate(12deg); }
  24%  { transform: rotate(9deg) translate(2px, 0); }
  32%  { transform: rotate(13deg) translate(-2px, 1px); }
  40%  { transform: rotate(9.5deg) translate(2px, 0); }
  48%  { transform: rotate(12.5deg) translate(-1px, 1px); }
  56%  { transform: rotate(10deg); }
  100% { transform: rotate(0); }
}

/* produce (drawn at radius 10; --m / --d / --h set per piece) */
:is(.hn-piece, .hn-mini) .f-m { fill: var(--m); }
:is(.hn-piece, .hn-mini) .f-d { fill: var(--d); }
:is(.hn-piece, .hn-mini) .f-h { fill: var(--h); }
:is(.hn-piece, .hn-mini) .f-st { fill: var(--hn-stem); }
:is(.hn-piece, .hn-mini) .f-lf { fill: var(--hn-leafy); }
:is(.hn-piece, .hn-mini) .f-band { fill: var(--hn-cran); }
:is(.hn-piece, .hn-mini) .o { stroke: var(--hn-line); stroke-width: 1.05; stroke-linejoin: round; }
:is(.hn-piece, .hn-mini) .o.thin { stroke-width: 0.6; }
:is(.hn-piece, .hn-mini) .ln-d { fill: none; stroke: var(--d); stroke-width: 1.1; stroke-linecap: round; }
:is(.hn-piece, .hn-mini) .ln-s { fill: none; stroke: var(--h); stroke-width: 0.9; stroke-linecap: round; opacity: 0.8; }
:is(.hn-piece, .hn-mini) .st { fill: none; stroke: var(--hn-stem); stroke-width: 1.6; stroke-linecap: round; }
:is(.hn-piece, .hn-mini) .ln-stalk { fill: none; stroke: var(--hn-amber-d); stroke-width: 1.1; stroke-linecap: round; }
.hn-piece .f-sh { fill: var(--hn-shine); }
.hn-piece { transition: opacity 400ms ease; }
.hn-piece.born { animation: hn-born 450ms ease-out; }
@keyframes hn-born { from { opacity: 0; } }
.hn-piece.leaving { opacity: 0; }
.hn-piece.chosen > .hn-roll { filter: url(#hn-glowf); }
.hn-glow-c { flood-color: var(--hn-glow); }

/* the basket (page only, with No repeats on) */
.hn-basket { display: none; transform-box: view-box; transform-origin: 300px 392px; transition: transform 380ms cubic-bezier(.3, .1, .3, 1.3); }
.horn.has-basket .hn-basket { display: inline; }
.hn-basket[data-state="pouring"] { transform: rotate(-24deg) translate(-6px, -10px); }
.hn-basket-handle { fill: none; stroke: var(--hn-basket-d); stroke-width: 6; stroke-linecap: round; }
.hn-basket-in { fill: var(--hn-basket-in); }
.hn-basket-body { fill: var(--hn-basket); stroke: var(--hn-line); stroke-width: 2.4; stroke-linejoin: round; }
.hn-basket-weave { fill: url(#hn-weave); pointer-events: none; }
.hn-basket-bands { fill: none; stroke: var(--hn-basket-d); stroke-width: 3; }
.hn-basket-rim { fill: none; stroke: var(--hn-basket-d); stroke-width: 6; stroke-linecap: round; }
.hn-mini-name {
  text-anchor: middle;
  font-family: var(--font-body); font-weight: 700;
  fill: var(--hn-ink);
  stroke: var(--hn-ink-halo); stroke-width: 1.6px; stroke-linejoin: round;
  paint-order: stroke;
}

/* counts and the pour button */
.hn-left, .hn-picked {
  position: absolute;
  font: 0.86rem/1.2 var(--font-mono);
  color: var(--text-2);
  pointer-events: none;
  white-space: nowrap;
}
.hn-left { left: 4%; top: 4%; }
.hn-picked { left: 86%; top: 35%; translate: -50% 0; }
.hn-pour {
  position: absolute; left: 86%; top: 40%; translate: -50% 0;
  min-height: 0; padding: 0.3rem 0.7rem;
  font-size: var(--fs-sm);
  white-space: nowrap;
}
.hn-pour[hidden] { display: none; }

/* the name over the chosen piece, and the leaves bursting round it */
.hn-name {
  position: absolute;
  translate: -50% 0;
  width: max-content;
  max-width: 94%;
  font: 700 var(--fs, 10cqi)/1.1 var(--font-body);
  letter-spacing: 0.01em;
  text-align: center;
  overflow-wrap: normal;
  pointer-events: none;
  opacity: 0;
  z-index: 3;
}
.hn-name span {
  display: inline-block;
  padding: 0.12em 0.5em 0.16em;
  border-radius: 0.4em;
  color: var(--hn-ink);
  background: var(--hn-plate);
  border: 2px solid var(--hn-plate-line);
  box-shadow: 0 0 0.4em color-mix(in srgb, var(--hn-glow) 35%, transparent);
}
.hn-name.go { animation: hn-rise 700ms cubic-bezier(.2, 1.4, .4, 1) forwards; }
@keyframes hn-rise {
  from { opacity: 0; transform: translateY(40%) scale(0.6); }
  to   { opacity: 1; transform: none; }
}
.hn-burst { position: absolute; width: 0; height: 0; pointer-events: none; z-index: 2; }
.hn-burst i { position: absolute; left: 0; top: 0; width: 6cqi; height: 6cqi; margin: -3cqi; opacity: 0; }
.hn-burst i svg { width: 100%; height: 100%; fill: var(--c); filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.5)); }
.hn-burst.go i { animation: hn-leaf 1600ms cubic-bezier(.2, .7, .4, 1) var(--d) forwards; }
@keyframes hn-leaf {
  0%   { opacity: 0; transform: translate(0, 0) rotate(0) scale(0.4); }
  12%  { opacity: 1; }
  45%  { opacity: 1; transform: translate(var(--dx), var(--dy)) rotate(calc(var(--r) * 0.5)) scale(1); }
  100% { opacity: 0; transform: translate(var(--dx), calc(var(--dy) + var(--fall))) rotate(var(--r)) scale(0.9); }
}

@media (prefers-reduced-motion: reduce) {
  .horn[data-state="tipping"] .hn-horn { animation-duration: 600ms !important; }
  .hn-burst.go i { animation: none !important; }
  .hn-name.go { animation: none !important; opacity: 1; }
}

/* ---------------------------------------------------------------------------
   Name Wheel — pink skin: a BUBBLE TEA cup instead of the wheel (boba.js).
   A clear cup of pale pink milk tea with a domed lid and a fat lavender
   straw; one soft dark pearl per student settled at the bottom (no names,
   so any class size fits, and the cup empties as No repeats takes them).
   The straw is the Spin button. A pick (~4s, boba.js's T timeline): the cup
   shakes, the pearls swirl up into the tea, ONE lights up (.bt-pearl.picked:
   a pink ring, a size up), glides to the straw's mouth, travels up through
   the see-through straw in sips and pops out of the top in hearts. The name
   shows only in the result panel. The left card is a real card here (blush
   surface, border, radius) so the cup never sits on bare white.
   Layers in the 400x400 box: .bt-back (shadow, cup back, tea, foam, the
   straw's inside) → .bt-pearls (HTML, px from the box width) → .bt-front
   (cup outline, shine, condensation, lid, the straw's translucent wall) →
   the straw button, the burst, the "N left" line under the cup.
   --------------------------------------------------------------------------- */
.boba, .for-boba { display: none; }
:root[data-theme="pink"] .boba { display: block; }
:root[data-theme="pink"] .for-boba { display: inline; }
:root[data-theme="pink"] .for-wheel,
:root[data-theme="pink"] .wheel-wrap > .wheel-svg,
:root[data-theme="pink"] #wheel-spin,
:root[data-theme="pink"] .wheel-hub-text,
:root[data-theme="pink"] .wheel-mascot { display: none; }

/* inset: a comfortable margin round the cup inside its card (boba.js's DY
   centres the drawing vertically in this box) */
.boba { position: absolute; inset: 3%; container-type: inline-size; }
.bt-back, .bt-front { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.bt-back { z-index: 1; }
.bt-front { z-index: 3; }
.boba { transform-origin: 50% 96%; }
.boba[data-state="shaking"] { animation: bt-shake 125ms ease-in-out 8, bt-churn 180ms ease-in-out 1000ms 7; }
@keyframes bt-churn { 25% { rotate: -1deg; translate: -0.3% 0; } 75% { rotate: 1deg; translate: 0.3% 0; } }
/* idle: a gentle bob; the reveal: three bounces */
.boba[data-mood="idle"]:not([data-state="shaking"]) { animation: bt-bob 3.4s ease-in-out infinite; }
@keyframes bt-bob { 50% { translate: 0 -1%; } }
.boba[data-mood="party"] { animation: bt-hop 520ms cubic-bezier(.3, .9, .4, 1) 3; }
@keyframes bt-hop { 0%, 100% { translate: 0 0; scale: 1 1; } 12% { scale: 1.04 0.95; } 45% { translate: 0 -5%; scale: 0.97 1.04; } 80% { translate: 0 0; scale: 1.03 0.97; } }
@keyframes bt-shake { 25% { rotate: -2.2deg; translate: -0.8% 0; } 75% { rotate: 2.2deg; translate: 0.8% 0; } }

/* the cup - flat-cute shading: a soft contact shadow, a three-step tea, a
   shadow where the tea meets the plastic, a bright rim of light down the
   left side */
.bt-contact { fill: url(#bt-contact-grad); }
#bt-contact-grad .bt-contact-in  { stop-color: var(--bt-shadow); stop-opacity: 1; }
#bt-contact-grad .bt-contact-out { stop-color: var(--bt-shadow); stop-opacity: 0; }
.bt-contact-core { fill: var(--bt-shadow-core); }
.bt-cup-back { fill: var(--bt-cup); }
.bt-cup { fill: none; stroke: var(--bt-cup-line); stroke-width: 4.2; stroke-linejoin: round; }
.bt-shine { fill: none; stroke: var(--bt-shine-soft); stroke-width: 9; stroke-linecap: round; }
.bt-rim-light { fill: none; stroke: var(--bt-rim-light); stroke-width: 2.6; stroke-linecap: round; }
.bt-rim-light.faint { stroke-width: 1.6; opacity: 0.55; }
.bt-tea { fill: url(#bt-tea-grad); }
#bt-tea-grad .bt-tea-top { stop-color: var(--bt-tea); }
#bt-tea-grad .bt-tea-mid { stop-color: var(--bt-tea-mid); }
#bt-tea-grad .bt-tea-bot { stop-color: var(--bt-tea-deep); }
.bt-tea-edge { fill: none; stroke: var(--bt-tea-edge); stroke-width: 12; }   /* clipped to the tea: a 6-unit inner band */
.bt-meniscus { fill: none; stroke: var(--bt-tea-edge); stroke-width: 2; }
.bt-foam { fill: var(--bt-foam); }
/* the milk swirl: soft cream ribbons through the middle of the tea */
.bt-milk { fill: none; stroke: var(--bt-milk); stroke-width: 15; stroke-linecap: round; }
.bt-milk.thin { stroke-width: 7; opacity: 0.7; }
.bt-milk.wisp { stroke-width: 5; opacity: 0.6; }
.bt-dot, .bt-drip { fill: var(--bt-drop); stroke: var(--bt-drop-line); stroke-width: 0.6; }
/* the lid: its own piece of plastic - a tinted dome and band with their own
   highlights, casting a thin shadow on the cup under it */
.bt-lid-shadow { fill: var(--bt-lid-shadow); }
.bt-dome { fill: var(--bt-lid); stroke: var(--bt-lid-line); stroke-width: 3.4; stroke-linejoin: round; }
.bt-dome-shine { fill: none; stroke: var(--bt-rim-light); stroke-width: 5; stroke-linecap: round; }
.bt-dome-glint { fill: none; stroke: var(--bt-rim-light); stroke-width: 3; stroke-linecap: round; opacity: 0.7; }
.bt-band { fill: var(--bt-band); stroke: var(--bt-lid-line); stroke-width: 3.4; }
.bt-band-hi { fill: none; stroke: var(--bt-rim-light); stroke-width: 2; stroke-linecap: round; }
/* the straw: a dim inside behind the pearls, a translucent pastel wall in
   front, so a pearl going up it shows through */
.bt-straw-in { fill: none; stroke: var(--bt-straw-in); stroke-width: 24; }
.bt-straw { fill: none; stroke: var(--bt-straw); stroke-width: 24; }
.bt-straw-shine { fill: none; stroke: var(--bt-shine); stroke-width: 3; stroke-linecap: round; }
.bt-straw-rim { fill: var(--bt-straw-rim); stroke: var(--bt-straw-line); stroke-width: 1.6; }
.boba:has(.bt-handle:hover:not(:disabled)) .bt-straw { stroke: var(--bt-straw-hover); }

/* The whole cup is the button: one hit area from the straw's top to the
   feet, arms included (box units: x 72–328, y 12–372 once DY is applied).
   The "N left" line under it stays outside. */
.bt-handle {
  position: absolute; left: 18%; top: 3%; width: 64%; height: 90%; z-index: 4;
  margin: 0; padding: 0; min-height: 0;
  border: 0; border-radius: 28% 28% 22% 22%;
  background: transparent; box-shadow: none;
  cursor: pointer;
}
/* hover: the cup lifts a little, blushes and winks (only at rest: not
   mid-pick, not while disabled). The lift is a transform on the cup itself
   (.boba, absolute in .wheel-wrap), so nothing reflows. It is `transform`,
   not `translate`: the bob / hop / shake animations own the individual
   `translate` / `rotate` / `scale` properties, and the two compose. */
.boba { transition: transform 200ms cubic-bezier(.34, 1.56, .64, 1); }
.boba[data-mood="idle"]:has(.bt-handle:hover:not(:disabled)) { transform: translateY(-1.8%); }
/* ...but the click area stays put: it lives inside .boba, so it would lift
   too, and a pointer near the cup's bottom edge would fall off it, drop the
   cup, land on it again, and flicker. 2% of the handle (90% of .boba tall)
   = the 1.8% lift, on the same curve, so the two cancel at every frame. */
.bt-handle { transition: translate 200ms cubic-bezier(.34, 1.56, .64, 1); }
.boba[data-mood="idle"]:has(.bt-handle:hover:not(:disabled)) .bt-handle { translate: 0 2%; }
.boba[data-mood="idle"]:has(.bt-handle:hover:not(:disabled)) .bf-blush { scale: 1.2; opacity: 0.95; }
.bf-wink { display: none; fill: none; stroke: var(--bt-ink); stroke-width: 3.4; stroke-linecap: round; }
.boba[data-mood="idle"]:has(.bt-handle:hover:not(:disabled)) .bf-eye.r { display: none; }
.boba[data-mood="idle"]:has(.bt-handle:hover:not(:disabled)) .bf-wink { display: inline; }
.boba[data-mood="idle"]:has(.bt-handle:focus-visible) .bf-eye.r { display: none; }
.boba[data-mood="idle"]:has(.bt-handle:focus-visible) .bf-wink { display: inline; }
/* it is invisible in every state: the app-wide button hover / active / disabled
   looks (a card fill, the 0.97 press, 45% opacity) would paint a box over the cup */
.bt-handle:is(:hover, :active, :focus, :disabled) { background: transparent; border-color: transparent; box-shadow: none; translate: none; transform: none; opacity: 1; }
.bt-handle:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.bt-handle:disabled { cursor: default; }
body.wheel-popout .bt-handle { display: none; }

/* the pearls: real boba - deep brown, almost black, a glossy highlight on
   the upper left, a soft shadow underneath; each a touch off the others in
   size (--s, from boba.js) */
.bt-pearls { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.bt-pearl {
  position: absolute; left: 0; top: 0;
  width: calc(var(--pr, 24px) * var(--s, 1)); height: calc(var(--pr, 24px) * var(--s, 1));
  border-radius: 50%;
  background:
    radial-gradient(ellipse 26% 18% at 30% 24%, rgba(255, 255, 255, 0.55) 0%, rgba(255, 255, 255, 0) 100%),
    radial-gradient(circle at 38% 34%, var(--bt-pearl-hi) 0%, var(--bt-pearl) 46%, var(--bt-pearl-deep) 100%);
  box-shadow: 0 calc(var(--pr, 24px) * 0.07) calc(var(--pr, 24px) * 0.12) var(--bt-pearl-shadow);
  will-change: transform;
}
.bt-pearl.gone { display: none; }
.bt-pearl { display: grid; place-items: center; }
/* The name, dead centre. It is pinned to the middle with translate(-50%)
   rather than laid out, so a long name can't shift it sideways, and its size
   is the measured fit from boba.js (--fit, in pearl diameters). Vertically
   it sits on the cap-height centre, not the line box: with line-height 1,
   Comfortaa's baseline is 0.825em down and its caps 0.78em tall, so the ink
   centre is 0.065em above the box centre (measured with canvas metrics). */
.bt-pearl-name {
  position: absolute; left: 50%; top: 50%;
  translate: -50% calc(-50% + 0.065em);
  white-space: nowrap;
  font: 700 calc(var(--pr, 24px) * var(--s, 1) * var(--fit, 0.18)) / 1 "Comfortaa", "Quicksand", var(--font-body);
  color: var(--bt-pearl-ink);
  letter-spacing: 0;
  text-shadow: 0 0.05em 0.1em rgba(0, 0, 0, 0.45);
  pointer-events: none;
}
/* the chosen one: a pink ring and a glow, so the eye can follow it */
.bt-pearl.picked {
  box-shadow: 0 0 0 2.5px var(--accent), 0 0 0.8em 0.25em var(--accent-glow);
  animation: bt-pick 280ms cubic-bezier(.34, 1.56, .64, 1);
}
@keyframes bt-pick { 0% { filter: brightness(1.6); } 100% { filter: none; } }

/* ---- the cup as a character (boba.js: .bt-face, .bt-arm, .bt-foot) ----
   Pink mascot style: thick soft plum outlines, flat fills. The mood is
   data-mood on .boba: idle · wow · look · party. */
.bt-foot { fill: var(--bt-limb); stroke: var(--bt-ink); stroke-width: 3.4; }
.bt-limb { fill: none; stroke: var(--bt-ink); stroke-width: 17; stroke-linecap: round; }
.bt-limb-fill { fill: none; stroke: var(--bt-limb); stroke-width: 10.2; stroke-linecap: round; }
.bt-arm { transform-box: fill-box; transition: rotate 260ms cubic-bezier(.34, 1.56, .64, 1); }
.bt-arm.l { transform-origin: 100% 0; rotate: 6deg; }
.bt-arm.r { transform-origin: 0 0; rotate: -6deg; }
.boba[data-mood="wow"] .bt-arm.l { rotate: 100deg; animation: bt-flap-l 240ms ease-in-out infinite alternate; }
.boba[data-mood="wow"] .bt-arm.r { rotate: -100deg; animation: bt-flap-r 240ms ease-in-out infinite alternate; }
.boba[data-mood="look"] .bt-arm.l { rotate: 48deg; }
.boba[data-mood="look"] .bt-arm.r { rotate: -48deg; }
.boba[data-mood="party"] .bt-arm.l { rotate: 116deg; animation: bt-wave-l 300ms ease-in-out infinite alternate; }
.boba[data-mood="party"] .bt-arm.r { rotate: -116deg; animation: bt-wave-r 300ms ease-in-out infinite alternate; }
@keyframes bt-flap-l { to { rotate: 86deg; } }
@keyframes bt-flap-r { to { rotate: -86deg; } }
@keyframes bt-wave-l { to { rotate: 96deg; } }
@keyframes bt-wave-r { to { rotate: -96deg; } }

.bf-white { fill: var(--bt-eye-white); stroke: var(--bt-ink); stroke-width: 3; }
.bf-pupil { fill: var(--bt-ink); }
.bf-hi { fill: var(--bt-eye-white); }
.bf-blush { fill: var(--bt-blush); opacity: 0.75; }
.bf-happy, .bf-smile { fill: none; stroke: var(--bt-ink); stroke-width: 3.4; stroke-linecap: round; stroke-linejoin: round; }
.bf-o, .bf-open { fill: var(--bt-ink); }
.bt-face :is(.bf-eye, .bf-white, .bf-blush, .bf-o) { transform-box: fill-box; transform-origin: center; }
.bf-white { transition: scale 200ms ease; }
.bf-blush { transition: scale 220ms cubic-bezier(.34, 1.56, .64, 1), opacity 220ms ease; }
/* the pupils (and their highlights) follow --lx / --ly, set by boba.js */
.bf-look { translate: calc(var(--lx, 0) * 4.5px) calc(var(--ly, 0) * 5.5px); transition: translate 160ms ease-out; }
.bf-happy, .bf-o, .bf-open { display: none; }
/* idle: a blink now and then */
.boba[data-mood="idle"] .bf-eye { animation: bt-blink 4.8s infinite; }
@keyframes bt-blink { 0%, 94%, 100% { scale: 1 1; } 97% { scale: 1 0.08; } }
/* wow: wide eyes, puffed cheeks, a little "o" */
.boba[data-mood="wow"] .bf-white { scale: 1.14; }
.boba[data-mood="wow"] .bf-pupil { r: 7.5px; }
.boba[data-mood="wow"] .bf-blush { scale: 1.5; opacity: 0.95; }
.boba[data-mood="wow"] .bf-smile,
.boba[data-mood="look"] .bf-smile { display: none; }
.boba[data-mood="wow"] .bf-o, .boba[data-mood="look"] .bf-o { display: inline; }
/* look: watching the pearl go, mouth a small expectant "o" */
.boba[data-mood="look"] .bf-o { scale: 0.7; }
.boba[data-mood="look"] .bf-white { scale: 1.06; }
/* party: ^ ^ eyes, a big open smile, rosy cheeks */
.boba[data-mood="party"] .bf-eye, .boba[data-mood="party"] .bf-smile { display: none; }
.boba[data-mood="party"] .bf-happy, .boba[data-mood="party"] .bf-open { display: inline; }
.boba[data-mood="party"] .bf-blush { scale: 1.25; opacity: 0.95; }

/* ---- the spotlight (boba.js spotlight()): the picked pearl, big and in
   front, everything behind it dimmed. A click / Space / Esc closes it. ---- */
.bt-spot {
  position: fixed; inset: 0; z-index: 1000;
  display: grid; place-items: center;
  background: var(--bt-spot-dim);
  backdrop-filter: blur(3px);
  cursor: pointer;
  opacity: 0;
  transition: opacity 280ms ease;
}
.bt-spot.in { opacity: 1; }
.bt-spot.out { opacity: 0; transition-duration: 240ms; }
.bt-spot-pearl {
  position: relative;
  width: min(56vmin, 32rem); aspect-ratio: 1;
  container-type: inline-size;
  border-radius: 50%;
  display: grid; place-items: center;
  background:
    radial-gradient(ellipse 24% 16% at 31% 24%, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0) 100%),
    radial-gradient(circle at 38% 34%, var(--bt-pearl-hi) 0%, var(--bt-pearl) 46%, var(--bt-pearl-deep) 100%);
  box-shadow: 0 0 0 0.6cqi var(--accent), 0 2cqi 6cqi rgba(36, 14, 6, 0.45), 0 0 10cqi 2cqi var(--accent-glow);
  transition: transform 760ms cubic-bezier(.3, 1.35, .5, 1);
}
.bt-spot-name {
  max-width: 86%;
  font: 700 var(--fs, 18cqi) / 1.1 "Comfortaa", "Quicksand", var(--font-body);
  color: var(--bt-pearl-ink);
  text-align: center;
  overflow-wrap: normal;   /* wrap only between words; boba.js sizes by the longest word */
  text-shadow: 0 0.04em 0.1em rgba(0, 0, 0, 0.45);
  translate: 0 0.065em;    /* line-height 1.1: the cap-height centre sits 0.065em above the box centre */
}
.bt-spot-hint {
  position: absolute; left: 0; right: 0; bottom: max(4vh, 1.5rem);
  text-align: center;
  margin: 0;
  font: 600 var(--fs-sm) / 1.2 "Comfortaa", var(--font-body);
  color: var(--bt-pearl-ink);
  opacity: 0;
  transition: opacity 300ms ease 700ms;
}
.bt-spot.in .bt-spot-hint { opacity: 0.85; }
body.wheel-popout .bt-spot-hint { display: none; }   /* the projector can't be clicked from the room */
.bt-spot-burst { position: absolute; left: 50%; top: 50%; width: 0; height: 0; pointer-events: none; }
.bt-spot-burst i { position: absolute; left: -4cqi; top: -4cqi; width: calc(8cqi * var(--s, 1)); height: calc(8cqi * var(--s, 1)); opacity: 0; background: no-repeat center / contain; }
.bt-spot-burst i.heart { background-image: var(--pink-burst-heart); }
.bt-spot-burst i.spark { background-image: var(--pink-burst-spark); }
.bt-spot.in .bt-spot-burst i { animation: bt-spot-burst 1.6s cubic-bezier(.15, .7, .35, 1) 380ms forwards; }
@keyframes bt-spot-burst {
  0%   { opacity: 0; transform: rotate(var(--a)) translateY(-46cqi) scale(0.5); }
  20%  { opacity: 1; }
  75%  { opacity: 1; }
  100% { opacity: 0; transform: rotate(var(--a)) translateY(calc(-52cqi - var(--d) * 14cqi)) scale(1.2); }
}
/* closing with No repeats on: the backdrop clears and the pearl arcs over
   into the jar, spinning and shrinking (boba.js flyToJar drives the
   transform on rAF), its name still on it */
.bt-spot.flying { background: transparent; backdrop-filter: none; pointer-events: none; transition: background 420ms ease, backdrop-filter 420ms ease; }
.bt-spot.flying .bt-spot-hint,
.bt-spot.flying .bt-spot-burst { opacity: 0; transition: opacity 200ms ease; }

/* hearts and sparkles bursting from the top of the straw */
.bt-burst { position: absolute; width: 0; height: 0; z-index: 5; pointer-events: none; }
.bt-burst i { position: absolute; left: -3cqi; top: -3cqi; width: calc(6cqi * var(--s, 1)); height: calc(6cqi * var(--s, 1)); opacity: 0; background: no-repeat center / contain; }
.bt-burst i.heart { background-image: var(--pink-burst-heart); }
.bt-burst i.spark { background-image: var(--pink-burst-spark); }
.bt-burst.go i { animation: bt-burst 1.3s cubic-bezier(.15, .7, .35, 1) forwards; }
.bt-burst.cup.go i { animation-name: bt-burst-wide; animation-duration: 1.5s; }
@keyframes bt-burst-wide {
  0%   { opacity: 0; transform: rotate(var(--a)) translateY(-18cqi) scale(0.4); }
  20%  { opacity: 1; }
  70%  { opacity: 1; }
  100% { opacity: 0; transform: rotate(var(--a)) translateY(calc(-26cqi - var(--d) * 24cqi)) scale(1.2); }
}
@keyframes bt-burst {
  0%   { opacity: 0; transform: rotate(var(--a)) translateY(0) scale(0.4); }
  20%  { opacity: 1; }
  70%  { opacity: 1; }
  100% { opacity: 0; transform: rotate(var(--a)) translateY(calc(var(--d) * -26cqi)) scale(1.15); }
}

/* the count: small flat text under the cup */
.bt-counter {
  position: absolute; left: 0; right: 0; top: 95%; z-index: 4;
  margin: 0; display: flex; align-items: baseline; justify-content: center; gap: 0.3em;
  font: 600 3cqi/1 "Comfortaa", "Quicksand", var(--font-body);
  color: var(--text-muted); pointer-events: none;
}
.bt-counter b { color: var(--accent-deep); font-weight: 700; }
.boba[data-empty="1"] .bt-counter b { color: var(--text-muted); }

/* The page around the cup: ONE row across the width, three columns —
   the controls card · the name over the cup · the Picked tray. The side
   cards hug the cup (justify-self end / start) and the spare width goes to
   the outer edges, so the three read as one group with the cup dominating.
   The name sits right on top of the cup, the cup takes every pixel of
   height left. .wheel-side becomes display: contents so its result and
   controls can be placed in the grid; the HTML is shared with default /
   halloween, which keep their two columns. The pop-out is untouched, hence
   .wheel-page. */
:root[data-theme="pink"] .wheel-page .wheel-layout {
  --side-w: clamp(13rem, 19vw, 20rem);
  grid-template-columns: minmax(var(--side-w), 1fr) auto minmax(var(--side-w), 1fr);
  grid-template-rows: auto minmax(0, 1fr);
  grid-template-areas:
    ".        result ."
    "controls stage  tray";
  column-gap: clamp(1rem, 2.2vw, 2.5rem);
  row-gap: 0;
  align-items: center;
  overflow: visible;
}
:root[data-theme="pink"] main.wheel-page { padding-top: var(--space-2); padding-bottom: var(--space-2); }   /* every pixel of height to the cup */
/* the three-column layout always fits the screen, so the page never needs to
   scroll here: clip instead, so hearts flying past the bottom edge can't
   bring in a scrollbar that shifts everything 7.5px sideways */
@media (min-width: 901px) { :root[data-theme="pink"] main.wheel-page { overflow: clip; } }
:root[data-theme="pink"] .wheel-page .wheel-side { display: contents; }
:root[data-theme="pink"] .wheel-page .wheel-stage {
  grid-area: stage;
  align-self: stretch;
  padding: 0;
  border: 0; background: none; box-shadow: none;
}
/* the cup: as tall as the row allows (~75vh at 1366x768), never wider
   than what the two side cards leave */
:root[data-theme="pink"] .wheel-page .wheel-stage > .wheel-wrap {
  height: 100%;
  max-width: calc(100vw - 2 * var(--side-w) - 7rem);
}

/* ---- the jar (boba.js renderJar / flyToJar / pour): a second cup beside
   the main one that catches everyone picked this round (No repeats on).
   The same drawing classes as the cup (outline, shine, arms, feet, face,
   pearls with names) minus the lid, the straw and the tea: an open cup with
   its own face. data-fill = sleepy (empty: closed eyes, z's) → content →
   happy (half full: a big smile) → full (^ ^); data-mood="party" is the
   celebration when the last one lands. Pink page only, grid area "tray". */
.bt-jar { display: none; }
:root[data-theme="pink"] .wheel-page .bt-jar:not([hidden]) {
  grid-area: tray;
  display: block; position: relative;
  justify-self: start; align-self: end;        /* it stands on the same floor as the cup */
  width: min(100%, 24rem); aspect-ratio: 1;    /* its whole column: the names on its pearls stay legible */
  margin-bottom: 2%;
}
.bt-jar-cup {
  position: absolute; inset: 3%;
  container-type: inline-size;
  transform-origin: 18% 92%;                   /* tips over its left foot, toward the cup */
  transition: rotate 420ms cubic-bezier(.5, 0, .3, 1.2), translate 420ms ease;
}
.bt-jar[data-state="pouring"] .bt-jar-cup { rotate: -58deg; translate: -6% -4%; }
.bt-jar-cup .bt-pearls { z-index: 2; }
.bt-jar-mouth { fill: var(--bt-straw-in); opacity: 0.55; }            /* the inside of the open top */
.bt-jar-lip { fill: none; stroke: var(--bt-cup-line); stroke-width: 4.2; stroke-linecap: round; }
/* the count under it, like the cup's "N left" */
.bt-jar-count {
  position: absolute; left: 0; right: 0; top: 93%; z-index: 4;
  margin: 0; text-align: center;
  font: 600 var(--fs-sm) / 1 "Comfortaa", "Quicksand", var(--font-body);
  color: var(--text-muted); pointer-events: none;
}
.bt-jar-count b { color: var(--accent-deep); font-weight: 700; font-size: 1.25em; }
/* Pour them back: over the open top (there's no lid or straw up there);
   the primary pill once the jar is full */
.bt-jar-pour {
  position: absolute; left: 50%; top: 6%; z-index: 5;
  translate: -50% 0;
  min-height: 2rem; padding: 0.25rem 0.9rem;
  white-space: nowrap;
  font-family: "Comfortaa", var(--font-body); font-size: var(--fs-xs);
}
.bt-jar-pour[hidden] { display: none; }
.bt-jar[data-full="1"] .bt-jar-pour { animation: bt-pour-in 420ms cubic-bezier(.34, 1.56, .64, 1); }
@keyframes bt-pour-in { 0% { scale: 0.6; opacity: 0; } 100% { scale: 1; opacity: 1; } }
:root[data-theme="pink"] body.present .wheel-page .bt-jar { display: none; }

/* its face: centred, and its own moods */
.bf-sleep, .bf-z { display: none; }
.bf-sleep { fill: none; stroke: var(--bt-ink); stroke-width: 3.4; stroke-linecap: round; }
.bf-z { font: 700 22px "Comfortaa", var(--font-body); fill: var(--bt-ink); opacity: 0; }
.bt-jar-cup .bf-blush { opacity: 0.5; }
/* sleepy (empty): eyes shut, a small smile, z's drifting up, a slow breath */
.bt-jar-cup[data-fill="sleepy"] .bf-eye { display: none; }
.bt-jar-cup[data-fill="sleepy"] .bf-sleep,
.bt-jar-cup[data-fill="sleepy"] .bf-z { display: inline; }
.bt-jar-cup[data-fill="sleepy"] .bf-smile { scale: 0.75; transform-box: fill-box; transform-origin: center; }
.bt-jar-cup[data-fill="sleepy"] .bf-z { animation: bt-z 3.2s ease-in-out infinite; transform-box: fill-box; }
.bt-jar-cup[data-fill="sleepy"] .bf-z.b { animation-delay: 1.6s; font-size: 16px; }
@keyframes bt-z { 0% { opacity: 0; translate: 0 6px; } 30%, 70% { opacity: 0.8; } 100% { opacity: 0; translate: 8px -14px; } }
.bt-jar-cup[data-fill="sleepy"] { animation: bt-breathe 4.2s ease-in-out infinite; }
@keyframes bt-breathe { 50% { scale: 1.015 0.985; } }
/* content: awake, blinking now and then */
.bt-jar-cup:is([data-fill="content"], [data-fill="happy"]) .bf-eye { animation: bt-blink 5.4s infinite; }
/* happy (half full or more): a big open smile, rosier */
.bt-jar-cup[data-fill="happy"] .bf-smile { display: none; }
.bt-jar-cup[data-fill="happy"] .bf-open { display: inline; }
.bt-jar-cup[data-fill="happy"] .bf-blush { opacity: 0.8; scale: 1.15; }
/* full: ^ ^ and the big smile */
.bt-jar-cup[data-fill="full"] .bf-eye, .bt-jar-cup[data-fill="full"] .bf-smile { display: none; }
.bt-jar-cup[data-fill="full"] .bf-happy, .bt-jar-cup[data-fill="full"] .bf-open { display: inline; }
.bt-jar-cup[data-fill="full"] .bf-blush { opacity: 0.95; scale: 1.25; }
.bt-jar-cup[data-fill="full"] .bt-arm.l { rotate: 40deg; }
.bt-jar-cup[data-fill="full"] .bt-arm.r { rotate: -40deg; }
/* the celebration: hops, arms waving, hearts all round (the burst) */
.bt-jar-cup[data-mood="party"] { animation: bt-hop 520ms cubic-bezier(.3, .9, .4, 1) 4; }
.bt-jar-cup[data-mood="party"] .bt-arm.l { rotate: 116deg; animation: bt-wave-l 300ms ease-in-out infinite alternate; }
.bt-jar-cup[data-mood="party"] .bt-arm.r { rotate: -116deg; animation: bt-wave-r 300ms ease-in-out infinite alternate; }
/* pouring: a surprised little "o" */
.bt-jar[data-state="pouring"] .bt-jar-cup :is(.bf-smile, .bf-open, .bf-sleep, .bf-z, .bf-happy) { display: none; }
.bt-jar[data-state="pouring"] .bt-jar-cup .bf-eye { display: inline; animation: none; }
.bt-jar[data-state="pouring"] .bt-jar-cup .bf-o { display: inline; }
.bt-jar[data-state="pouring"] .bt-jar-cup { animation: none; }
/* the pearls tumbling back to the cup: fixed on the page, over everything */
.bt-pearl.bt-flyer { position: fixed; z-index: 1001; pointer-events: none; will-change: transform, opacity; }
:root[data-theme="pink"] .wheel-page .wheel-stage > .tick { display: none; }

/* the result strip: nothing before a pick, then the name between two
   hearts, with put back / take off beside it */
:root[data-theme="pink"] .wheel-page .wheel-result {
  grid-area: result;
  flex: none;
  min-height: clamp(3rem, 8.5vh, 4.6rem);
  width: 100%;
  align-self: end;
  margin-bottom: calc(-1 * clamp(0.5rem, 2vh, 1.25rem));   /* tuck into the box's empty top margin */
  position: relative; z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);   /* the name dead centre over the cup */
  align-items: center;
  gap: 0.9rem;
  padding: 0;
  border: 0; background: none; box-shadow: none;
  overflow: visible;
}
:root[data-theme="pink"] .wheel-page .wheel-placeholder { display: none; }
:root[data-theme="pink"] .wheel-page .wheel-result-name { grid-column: 2; grid-row: 1; }
:root[data-theme="pink"] .wheel-page .wheel-result-actions { position: static; grid-column: 3; grid-row: 1; justify-self: start; }
:root[data-theme="pink"] .wheel-result-name {
  display: flex; align-items: center; gap: 0.35em;
  font-weight: 700;
  font-size: clamp(2rem, 6.5vh, 3.6rem);
  line-height: 1.1;
}
:root[data-theme="pink"] .wheel-result-name[data-len="long"] { font-size: clamp(1.6rem, 5vh, 2.7rem); }
:root[data-theme="pink"] .wheel-result-name::before,
:root[data-theme="pink"] .wheel-result-name::after {
  content: "";
  flex: none;
  width: 0.42em; height: 0.42em;
  background: var(--pink-burst-heart) no-repeat center / contain;
  opacity: 0.85;
}
/* no name yet (the very first pick animating out): no hearts */
:root[data-theme="pink"] .wheel-result-name:empty::before,
:root[data-theme="pink"] .wheel-result-name:empty::after { display: none; }
/* the sparkles fly from the name, not from the middle of a box */
:root[data-theme="pink"] .wheel-page .wheel-sparks i { top: 50%; }

/* the controls: a vertical card left of the cup, the same card as the
   Picked tray on the right. Two groups on a hairline: the period (select +
   AUTO) and Edit who's in, then the switches and windows two to a row. No
   hint line: hovering the cup says it. */
:root[data-theme="pink"] .wheel-page .wheel-controls {
  grid-area: controls;
  justify-self: end; align-self: center;
  width: var(--side-w);
  display: flex; flex-direction: column; align-items: stretch;
  gap: var(--space-3);
  padding: var(--space-3);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}
:root[data-theme="pink"] .wheel-page .wheel-control-row {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 0.4rem;
  width: auto;
}
:root[data-theme="pink"] .wheel-page .wheel-control-row + .wheel-control-row {
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}
:root[data-theme="pink"] .wheel-page .wheel-control-row > * { min-width: 0; }
:root[data-theme="pink"] .wheel-page .wheel-control-row button { justify-content: center; }
/* row 1: [Period ▾      ][AUTO] / [Edit who's in · 28 on] */
:root[data-theme="pink"] .wheel-page .wheel-control-row:first-of-type { grid-template-columns: minmax(0, 1fr) auto; }
:root[data-theme="pink"] .wheel-page .wheel-control-row .bar-field { display: flex; }
:root[data-theme="pink"] .wheel-page .wheel-control-row .bar-field select { flex: 1; min-width: 0; }
:root[data-theme="pink"] .wheel-page #wheel-edit { grid-column: 1 / -1; }
/* row 2: [No repeats][Sound] / [First names][full screen] / windows across */
:root[data-theme="pink"] .wheel-page .wheel-control-row .win-btn { grid-column: 1 / -1; order: 2; }
:root[data-theme="pink"] .wheel-page .wheel-control-row .icon-btn { width: auto; justify-self: stretch; order: 1; }
:root[data-theme="pink"] .wheel-page .wheel-control-row .icon-btn svg { margin: 0 auto; }
:root[data-theme="pink"] .wheel-page .wheel-hint { display: none; }
/* the pick list group between them, on its own hairline: [List ▾] across,
   then Who's picked / Reset, then the count line */
:root[data-theme="pink"] .wheel-page .pick-list-bar,
:root[data-theme="pink"] .wheel-page .pick-list-bar + .wheel-control-row {
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}
:root[data-theme="pink"] .wheel-page .pick-list-bar { align-items: stretch; gap: 0.4rem; }
:root[data-theme="pink"] .wheel-page .pick-list-bar .wheel-control-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
:root[data-theme="pink"] .wheel-page .pick-list-bar .bar-field { grid-column: 1 / -1; }
/* narrow screens: one column — name, cup, Picked, controls — and the page
   scrolls. After the card rules above so it wins the cascade. */
@media (max-width: 900px) {
  :root[data-theme="pink"] .wheel-page .wheel-layout {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto auto auto;
    grid-template-areas: "result" "stage" "tray" "controls";
    row-gap: var(--space-3);
    flex: none; height: auto; max-height: none;   /* grow past the screen; main.page scrolls */
  }
  :root[data-theme="pink"] .wheel-page .wheel-result { margin-bottom: 0; }
  :root[data-theme="pink"] .wheel-page .wheel-stage { align-self: start; height: auto; min-height: 0; overflow: visible; }
  :root[data-theme="pink"] .wheel-page .wheel-stage > .wheel-wrap { height: auto; width: min(100%, 34rem); max-width: none; max-height: none; aspect-ratio: 1; margin: 0 auto; }
  :root[data-theme="pink"] .wheel-page .wheel-controls { justify-self: stretch; width: auto; max-height: none; }
  :root[data-theme="pink"] .wheel-page .bt-jar:not([hidden]) { justify-self: center; align-self: start; width: min(100%, 18rem); margin-bottom: var(--space-4); }
}

/* full screen: keep the name strip above the cup, drop the controls (the
   shared .no-present rule would hide the whole side, name included) */
:root[data-theme="pink"] body.present .wheel-page .wheel-side { display: contents !important; }
:root[data-theme="pink"] body.present .wheel-page .wheel-controls,
:root[data-theme="pink"] body.present .wheel-page .wheel-result-actions { display: none !important; }

/* full screen + the projector windows: the cup fills the view */
:root[data-theme="pink"] body.present .wheel-wrap { width: auto; height: min(86vh, 94vw); }
:root[data-theme="pink"] body.present .wheel-page .wheel-wrap { height: 100%; }

/* Reduced motion: the shake stops; the pick itself (the swirl, the trip up
   the straw, the pop, the name) is the tool, run on a click, so it plays. */
@media (prefers-reduced-motion: reduce) {
  .boba, .boba[data-state="shaking"], .bt-face *, .bt-arm, .bt-spot-burst i { animation: none !important; }
}

/* ==========================================================================
   Lesson Plans (lessons.html + lesson.js): a calendar on the left — Day /
   Week / Month — and the editor for the selected period's day on the right.
   ========================================================================== */
.lessons-page { padding: var(--space-4) var(--page-pad) var(--space-4); overflow: hidden; }
.lessons-layout {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1.45fr) minmax(23rem, 1fr);
  gap: var(--space-4);
}
.cal-box { min-width: 0; padding: var(--space-3) var(--space-4) var(--space-4); gap: var(--space-3); }

/* ---- the bar: ‹ TODAY › · label · jump · view toggle · search · expand ---- */
.cal-bar { flex: none; display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.cal-bar .spacer { flex: 1; }
.cal-nav { display: inline-flex; align-items: center; gap: 0.3rem; }
.cal-nav .icon-btn { width: 2rem; height: 2rem; min-height: 0; padding: 0; display: inline-flex; align-items: center; justify-content: center; }
.cal-nav .icon-btn svg { width: 1.1rem; height: 1.1rem; }
.cal-label { margin: 0; min-width: 9.5rem; color: var(--text); font-family: var(--font-body); font-size: var(--fs-lg); font-weight: var(--fw-bold); letter-spacing: 0.01em; text-transform: none; white-space: nowrap; }
.cal-jump { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 2rem; height: 2rem; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--card-2); color: var(--text-muted); cursor: pointer; }
.cal-jump:hover { color: var(--text); border-color: var(--border-hover); }
.cal-jump svg { width: 1.05rem; height: 1.05rem; pointer-events: none; }
.cal-jump input { position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%; min-height: 0; padding: 0; border: 0; cursor: pointer; }
.cal-jump input::-webkit-calendar-picker-indicator { position: absolute; inset: 0; width: 100%; height: 100%; cursor: pointer; }
.cal-views { grid-template-columns: repeat(3, auto); padding: 0.15rem; gap: 0.15rem; }
.cal-views span { padding: 0.3rem 0.7rem; font-size: var(--fs-sm); text-transform: uppercase; letter-spacing: 0.1em; font-weight: var(--fw-semibold); color: var(--text-2); }
.cal-views label:hover input:not(:checked) + span { color: var(--text); background: var(--card-3); }

/* ---- search ---- */
.cal-search { position: relative; flex: 1 1 9rem; min-width: 9rem; max-width: 18rem; margin-left: auto; }
.cal-search input { width: 100%; min-height: 2.1rem; padding: 0.3rem 0.6rem 0.3rem 2rem; font-size: var(--fs-sm); }
.cal-search-icon { position: absolute; left: 0.6rem; top: 50%; width: 1rem; height: 1rem; transform: translateY(-50%); color: var(--text-muted); pointer-events: none; }
.cal-results {
  position: absolute; left: 0; top: calc(100% + 0.4rem); z-index: 30;
  width: min(30rem, 88vw); max-height: min(30rem, 65vh); overflow-y: auto; scrollbar-width: thin;
  padding: var(--space-2); border: 1px solid var(--border-strong); border-radius: var(--radius-md);
  background: var(--card); box-shadow: var(--shadow-pop);
}
.cal-results[hidden] { display: none; }
.cr-group { padding: var(--space-2) var(--space-2) 0.3rem; color: var(--label-ink); font-size: var(--fs-xs); font-weight: var(--fw-semibold); text-transform: uppercase; letter-spacing: 0.12em; }
.cr-group:not(:first-child) { margin-top: var(--space-2); border-top: 1px solid var(--border); padding-top: var(--space-3); }
.cr-row { display: flex; align-items: flex-start; justify-content: flex-start; gap: 0.65rem; width: 100%; padding: 0.45rem 0.55rem; border: 1px solid transparent; border-radius: var(--radius-sm); background: transparent; color: var(--text); text-align: left; font-weight: var(--fw-regular); }
.cr-row:hover, .cr-row.active { background: var(--card-3); border-color: var(--border-strong); }
.cr-icon { flex: none; display: inline-flex; width: 1.6rem; height: 1.6rem; align-items: center; justify-content: center; color: var(--text-muted); background: none; }
.cr-icon svg { width: 1.15rem; height: 1.15rem; }
.cr-main { min-width: 0; display: flex; flex-direction: column; gap: 0.1rem; }
.cr-main b { font-weight: var(--fw-semibold); overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow-wrap: anywhere; }
.cr-main > span { color: var(--text-muted); font-family: var(--font-mono); font-size: 0.72rem; }
.cr-main mark { background: var(--accent-soft); color: var(--text); border-radius: 2px; padding: 0 1px; }
.cr-empty { margin: 0; padding: var(--space-3); color: var(--text-muted); font-size: var(--fs-sm); }

.cal-view { flex: 1; min-height: 0; overflow: auto; scrollbar-width: thin; }

/* ---- week: periods down the left, Mon–Fri across ---- */
.wk-grid {
  display: grid;
  grid-template-columns: clamp(6rem, 8vw, 8.5rem) repeat(5, minmax(7rem, 1fr));
  /* rows fill the height when they fit, and grow (the calendar scrolls) when an agenda needs more */
  grid-template-rows: min-content repeat(var(--rows), minmax(6rem, auto));   /* min-content: the header row never takes a share of the stretch */
  align-content: stretch;   /* spare height is shared out; an empty row stays short when another needs room */
  gap: 4px;
  min-height: 100%;
}
.wk-corner, .wk-day { position: sticky; top: 0; z-index: 2; }
.wk-corner { background: var(--card); }
.wk-day {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: center; column-gap: 0.35rem; row-gap: 0;
  min-height: 0; padding: 0.2rem 0.25rem;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--card); color: var(--text-2); font-weight: var(--fw-regular);
}
.wk-day:hover { border-color: var(--border-hover); color: var(--text); background: var(--card-3); }
/* one compact line: "MON 19" (a no-school note wraps under it) */
.wk-dow { font-family: var(--font-mono); font-size: 0.66rem; letter-spacing: 0.16em; color: var(--label-ink); }
.wk-dnum { font-family: var(--font-mono); font-size: 0.9rem; line-height: 1.3; color: var(--text); }
.wk-day.today { border-color: var(--accent); box-shadow: inset 0 -2px 0 var(--accent); }
.wk-day.today .wk-dnum, .wk-day.today .wk-dow { color: var(--accent-ink); }
.wk-day.off, .wk-day.off .wk-dnum { color: var(--text-muted); }
.wk-off-note { flex-basis: 100%; text-align: center; line-height: 1.2; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-xs); color: var(--text-muted); }
.wk-period {
  display: flex; flex-direction: column; justify-content: center; gap: 0.1rem;
  min-width: 0; padding: 0.4rem 0.6rem;
  border-left: 2px solid var(--hud-line);
}
.wk-pnum { font-family: var(--font-mono); font-size: 0.8rem; letter-spacing: 0.12em; color: var(--accent-ink); }
.wk-pname { color: var(--text); font-weight: var(--fw-semibold); font-size: var(--fs-sm); line-height: 1.2; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }

.wk-cell {
  position: relative;
  min-width: 0; min-height: 0;
  display: flex; flex-direction: column; align-items: stretch; justify-content: space-between; gap: 0.3rem;
  padding: 0.45rem 0.55rem 0.4rem;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--card-2);
  color: var(--text); text-align: left; font-weight: var(--fw-regular);
  overflow: hidden;
}
.wk-cell:hover { border-color: var(--border-hover); background: var(--card-3); }
.wk-cell.has-plan { background: var(--card-3); border-color: var(--border-strong); }
.wk-cell.today { box-shadow: inset 3px 0 0 var(--accent); }
.wk-cell.selected { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent), 0 0 16px var(--accent-glow); }
.wk-cell.drop-target { border-color: var(--accent); border-style: dashed; background: var(--accent-soft); }
/* empty: a quiet + that only speaks up on hover */
.wk-plus { align-self: center; margin: auto; color: var(--text-muted); opacity: 0.45; font-family: var(--font-mono); font-size: 1.35rem; line-height: 1; transition: opacity var(--transition), color var(--transition); }
.wk-cell:hover .wk-plus, .wk-cell:focus-visible .wk-plus, .wk-cell.selected .wk-plus { opacity: 1; color: var(--accent-ink); }
.wk-text { font-size: var(--fs-sm); line-height: 1.25; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow-wrap: anywhere; }
.wk-text.quiet { color: var(--text-2); font-style: italic; }
/* a planned day: the whole agenda, one ellipsised line per activity */
.wk-obj { flex: none; font-size: var(--fs-sm); font-weight: var(--fw-semibold); line-height: 1.3; color: var(--text); overflow: hidden; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.wk-agenda { flex: none; display: flex; flex-direction: column; gap: 0.3rem; margin: 0; padding: 0; list-style: none; counter-reset: wk; color: var(--text-2); font-size: var(--fs-sm); line-height: 1.4; }
/* each activity wraps (up to two lines) under a hanging number */
.wk-agenda li { position: relative; padding-left: 1.05rem; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.wk-agenda li::before { counter-increment: wk; content: counter(wk); position: absolute; left: 0; top: 0.12em; color: var(--text-muted); font-family: var(--font-mono); font-size: 0.7rem; }
.wk-agenda li.more { padding-left: 1.05rem; color: var(--text-muted); font-family: var(--font-mono); font-size: 0.72rem; }
.wk-agenda li.more::before { content: none; }
.wk-cell:has(.wk-agenda) { justify-content: flex-start; gap: 0.35rem; min-height: 6rem; padding: 0.55rem 0.6rem 0.45rem; }
.wk-agenda + .wk-meta { margin-top: auto; padding-top: 0.15rem; }
.wk-meta { display: flex; align-items: center; gap: 0.55rem; min-height: 1rem; color: var(--text-muted); }
.wk-meta .cm { display: inline-flex; align-items: center; gap: 0.15rem; font-family: var(--font-mono); font-size: 0.72rem; }
.wk-meta .cm svg { width: 0.85rem; height: 0.85rem; }
.wk-meta .cm-num { margin-left: auto; }
/* no school: hatched out, not clickable */
.wk-cell.off, .mo-day.off { background: repeating-linear-gradient(135deg, var(--card-2) 0 7px, transparent 7px 14px); border-style: dashed; }
.wk-cell.off:hover { border-color: var(--border); }

/* ---- day: the zoomed-in view, one card per period ---- */
.day-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr)); gap: var(--space-3); align-content: start; }
.day-card { justify-content: flex-start; gap: 0.5rem; min-height: 8.5rem; padding: var(--space-3) var(--space-4); }
.dc-head { display: flex; align-items: baseline; gap: 0.5rem; }
.dc-name { flex: 1; min-width: 0; font-weight: var(--fw-bold); font-size: var(--fs-md); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dc-head .wk-meta { gap: 0.5rem; }
.dc-head .cm-num { margin-left: 0; }
.dc-obj { margin: 0; font-size: var(--fs-md); line-height: 1.3; }
.dc-notes { margin: 0; color: var(--text-2); font-size: var(--fs-sm); line-height: 1.35; white-space: pre-line; display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
.dc-agenda { margin: 0; padding-left: 1.3rem; color: var(--text-2); font-size: var(--fs-sm); line-height: 1.35; }
.dc-agenda li span { margin-left: 0.4rem; color: var(--text-muted); font-family: var(--font-mono); font-size: 0.7rem; }
.dc-agenda li.more { list-style: none; margin-left: -1.3rem; color: var(--text-muted); }
.dc-hw { display: flex; gap: 0.4rem; margin: 0; color: var(--text-2); font-size: var(--fs-sm); }
.dc-hw svg { flex: none; width: 1rem; height: 1rem; margin-top: 0.1rem; color: var(--text-muted); }
.dc-atts { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.dc-att { display: inline-flex; align-items: center; gap: 0.3rem; max-width: 100%; padding: 0.15rem 0.5rem; border: 1px solid var(--border-strong); border-radius: 999px; background: var(--card-2); font-size: var(--fs-xs); }
.dc-att span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); }
.dc-att svg { flex: none; width: 0.9rem; height: 0.9rem; }
.dc-empty { display: flex; align-items: center; gap: 0.5rem; margin: auto; color: var(--text-muted); font-size: var(--fs-sm); }
.dc-empty .wk-plus { margin: 0; }
.day-card:hover .dc-empty { color: var(--text); }
.day-off { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.35rem; height: 100%; min-height: 10rem; border: 1px dashed var(--border-strong); border-radius: var(--radius-md); color: var(--text-muted); background: repeating-linear-gradient(135deg, var(--card-2) 0 7px, transparent 7px 14px); }
.day-off b { color: var(--text-2); font-size: var(--fs-lg); }
.day-off a { color: var(--accent-ink); }

/* ---- month: Mon–Fri weeks, one mark per period that has a plan ---- */
.mo-grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(5.5rem, 1fr));
  grid-template-rows: auto repeat(var(--weeks), minmax(4.6rem, 1fr));
  gap: 4px;
  min-height: 100%;
}
.mo-dow { padding: 0.15rem 0 0.35rem; text-align: center; font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.18em; color: var(--label-ink); }
.mo-day {
  display: flex; flex-direction: column; align-items: flex-start; gap: 0.3rem;
  min-width: 0; padding: 0.4rem 0.5rem;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--card-2); color: var(--text); font-weight: var(--fw-regular); text-align: left;
}
.mo-day:hover { border-color: var(--border-hover); background: var(--card-3); }
.mo-day.out { opacity: 0.45; }
.mo-day.today { border-color: var(--accent); }
.mo-day.today .mo-num { color: var(--accent-ink); }
.mo-day.selected { box-shadow: 0 0 0 1px var(--accent); }
.mo-num { font-family: var(--font-mono); font-size: 1rem; line-height: 1; color: var(--text-2); }
.mo-dots { display: flex; flex-wrap: wrap; gap: 3px; }
.mo-dots i {
  display: inline-flex; align-items: center; justify-content: center; min-width: 1.45rem; height: 1.1rem; padding: 0 0.2rem;
  border: 1px solid var(--border-strong); border-radius: 3px;
  font-style: normal; font-family: var(--font-mono); font-size: 0.6rem; color: var(--text-muted);
}
.mo-dots i:not(.on) { border-color: transparent; opacity: 0.6; }
.mo-dots i.on { border-color: var(--accent); background: var(--accent-soft); color: var(--text); }
.mo-clip { display: inline-flex; align-items: center; gap: 0.15rem; margin-top: auto; color: var(--text-muted); font-family: var(--font-mono); font-size: 0.7rem; }
.mo-clip svg { width: 0.85rem; height: 0.85rem; }
.mo-off { color: var(--text-muted); font-size: var(--fs-xs); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }

/* ---- the editor (lesson.js) ---- */
.lesson-panel { min-width: 0; padding: var(--space-3) var(--space-4) var(--space-3); gap: var(--space-2); overflow: hidden; }
.lesson-head { flex: none; display: flex; align-items: flex-start; gap: var(--space-3); }
.lesson-head .spacer { flex: 1; }
.lesson-title { min-width: 0; display: flex; flex-direction: column; gap: 0.15rem; }
.lesson-title-main { color: var(--text); font-size: var(--fs-lg); font-weight: var(--fw-bold); line-height: 1.2; overflow-wrap: anywhere; }
.lesson-head .hud-stat { font-size: 0.7rem; padding-top: 0.2rem; }
/* SAVED is a whisper; only an error speaks up */
.lesson-head { align-items: center; padding-bottom: var(--space-2); border-bottom: 1px solid var(--border); }
.lesson-head .save-state { padding-top: 0; font-size: 0.6rem; color: var(--text-muted); opacity: 0.6; }
.lesson-head .save-state[data-state="error"] { color: var(--danger); opacity: 1; font-size: 0.7rem; }
.lesson-tools { flex: none; display: flex; gap: var(--space-2); flex-wrap: wrap; }
.lesson-body { flex: 1; min-height: 0; overflow-y: auto; scrollbar-width: thin; display: flex; flex-direction: column; gap: 0.55rem; padding: 0.1rem 0.3rem var(--space-3) 0; }
.lesson-body[hidden], .lesson-empty[hidden], .lp-field[hidden] { display: none; }
.lp-field { display: flex; flex-direction: column; gap: 0.1rem; }
.lp-field h4 { display: flex; align-items: center; gap: 0.5rem; margin: 0 0 0 0.5rem; color: var(--label-ink); font-size: 0.68rem; font-weight: var(--fw-semibold); text-transform: uppercase; letter-spacing: 0.12em; }
.lp-hint { color: var(--text-muted); font-size: 0.72rem; font-weight: var(--fw-regular); text-transform: none; letter-spacing: 0.01em; }
.lp-text {
  width: 100%; min-height: 2rem; resize: none; overflow: hidden;
  padding: 0.3rem 0.5rem;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  background: transparent; color: var(--text); font-size: var(--fs-md); line-height: 1.4;
}
.lp-text:hover { border-color: var(--border); background: var(--card-2); }
.lp-text:focus { border-color: var(--accent); background: var(--card-2); outline: none; }
.lp-objective { font-size: var(--fs-lg); font-weight: var(--fw-semibold); }
/* notes is the main box: always a visible, writable surface */
.lp-text.lp-notes { min-height: 3.6rem; border-color: var(--border); background: var(--card-2); }
/* fill the panel: Notes takes every spare pixel below the plan */
/* Agenda and Notes share the spare height; Materials and Attachments take what they need */
.lp-agenda-field { flex: 1 0 auto; min-height: 8rem; }
.lp-agenda-field h4 { justify-content: space-between; }
.lp-agenda-box { flex: 1 1 auto; display: flex; flex-direction: column; gap: 0.1rem; padding: 0.3rem 0.25rem; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--card-2); }
.lp-agenda-box .lp-item:hover { background: var(--card-3); }
.lp-agenda-empty { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.2rem; min-height: 5rem; border: 1px dashed var(--border-strong); background: transparent; color: var(--text-muted); font-weight: var(--fw-regular); }
.lp-agenda-empty b { color: var(--text-2); font-weight: var(--fw-semibold); }
.lp-agenda-empty span { color: var(--accent-ink); font-size: var(--fs-sm); }
.lp-agenda-empty:hover { border-color: var(--accent); background: var(--accent-fill-soft); }
.lp-agenda-empty[hidden], .lp-add-item[hidden] { display: none; }
/* Materials: the Notes box, shorter */
.lp-text.lp-box { min-height: 3rem; border-color: var(--border); background: var(--card-2); }
.lp-notes-field { flex: 1 0 auto; min-height: 8rem; }
.lp-notes-field .lp-text.lp-notes { flex: 1 1 auto; height: auto; overflow-y: auto; }
/* agenda footer: + Add activity left, the time budget right */
.lp-budget { color: var(--text-muted); font-size: 0.72rem; letter-spacing: 0.04em; text-transform: none; white-space: nowrap; }
.lp-budget[hidden] { display: none; }
.lp-budget[data-state="over"] { color: var(--att-tardy); }
.lp-text::placeholder { color: var(--text-muted); opacity: 0.7; }
/* empty fields read as a faint line, not a form box */
.lp-text:placeholder-shown:not(.lp-notes):not(:focus):not(:hover) { border-bottom-color: var(--border); }
.lp-cols { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }

.lp-agenda { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.05rem; }
.lp-agenda:empty { display: none; }
.lp-item { display: grid; grid-template-columns: 1.4rem minmax(0, 1fr) 4.2rem 1.8rem; align-items: center; gap: 0.4rem; padding: 0 0.3rem; border-radius: var(--radius-sm); }
.lp-item:hover { background: var(--card-2); }
.lp-num { color: var(--text-muted); font-family: var(--font-mono); font-size: 0.75rem; text-align: right; }
.lp-item input.lp-item-text, .lp-item input.lp-min { min-height: 1.85rem; padding: 0.2rem 0.45rem; border-color: transparent; background: transparent; font-size: var(--fs-md); }
.lp-item input.lp-item-text:hover, .lp-item input.lp-min:hover { border-color: var(--border); }
.lp-item input.lp-item-text:focus, .lp-item input.lp-min:focus { border-color: var(--accent); background: var(--card-2); }
.lp-item input.lp-min { width: 100%; font-family: var(--font-mono); font-size: 0.8rem; }
.lp-remove { width: 1.8rem; height: 1.8rem; min-height: 0; padding: 0; border-color: transparent; background: transparent; color: var(--text-muted); opacity: 0; }
.lp-item:hover .lp-remove, .lp-remove:focus-visible { opacity: 1; }
.lp-remove:hover { color: var(--danger); background: transparent; }
.lp-add-item { align-self: flex-start; min-height: 0; padding: 0.15rem 0.3rem; font-size: var(--fs-sm); }

/* attachments */
.lp-att-count { min-width: 1.3rem; padding: 0 0.35rem; border-radius: 999px; background: var(--card-3); color: var(--text); font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0; text-align: center; }
.lp-att-count:empty { display: none; }
.lp-att-actions { margin-left: auto; display: inline-flex; gap: 0.35rem; }
.lp-mini {
  display: inline-flex; align-items: center; gap: 0.3rem;
  min-height: 1.85rem; padding: 0.15rem 0.6rem;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--card-2); color: var(--text-2);
  font-family: var(--font-body); font-size: var(--fs-xs); font-weight: var(--fw-semibold); text-decoration: none; text-transform: none; letter-spacing: 0.02em;
}
.lp-mini:hover { color: var(--text); border-color: var(--border-hover); background: var(--card-3); }
.lp-mini svg { width: 0.95rem; height: 0.95rem; }
.lp-mini[hidden] { display: none; }
.lp-link-form { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) auto auto; gap: 0.4rem; }
.lp-link-form[hidden] { display: none; }
.lp-link-form input { min-height: 2.1rem; font-size: var(--fs-sm); }
.lp-link-form button { min-height: 2.1rem; padding: 0.2rem 0.8rem; }
.lp-att-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.3rem; }
.lp-att-list:empty { display: none; }
.lp-att {
  position: relative;
  display: grid; grid-template-columns: 2rem minmax(0, 1fr) auto auto; align-items: center; gap: 0.6rem;
  padding: 0.35rem 0.4rem 0.35rem 0.45rem;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--card-2);
  overflow: hidden;
}
.lp-att:hover { border-color: var(--border-strong); background: var(--card-3); }
.lp-att.flash { animation: lp-flash 1.6s ease-out; }
@keyframes lp-flash { 0%, 35% { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft), 0 0 18px var(--accent-glow); } 100% { box-shadow: none; } }
.lp-att-icon { display: inline-flex; align-items: center; justify-content: center; width: 2rem; height: 2rem; border-radius: var(--radius-sm); background: var(--card); }
.lp-att-icon svg { width: 1.15rem; height: 1.15rem; }
/* one colour per kind, all from tokens */
.k-pdf, .k-video { color: var(--danger); }
.k-slides { color: var(--att-tardy); }
.k-doc, .k-audio { color: var(--lavender); }
.k-sheet { color: var(--ok); }
.k-image { color: var(--accent-ink); }
.k-link, .k-file { color: var(--text-2); }
.lp-att-name { display: block; min-width: 0; min-height: 0; padding: 0; border: 0; background: none; color: var(--text); font-size: var(--fs-md); font-weight: var(--fw-semibold); text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; }
button.lp-att-name:hover { color: var(--accent-ink); background: none; text-decoration: underline; text-underline-offset: 3px; }
.lp-att-meta { color: var(--text-muted); font-family: var(--font-mono); font-size: 0.72rem; white-space: nowrap; }
.lp-att-tools { display: inline-flex; gap: 0.15rem; }
.lp-att-tools .icon-btn { width: 1.9rem; height: 1.9rem; min-height: 0; padding: 0; display: inline-flex; align-items: center; justify-content: center; border-color: transparent; background: transparent; }
.lp-att-tools .icon-btn:hover { border-color: var(--border-strong); background: var(--card); color: var(--text); }
.lp-att-tools .icon-btn.danger:hover { color: var(--danger); }
.lp-att-tools svg { width: 1rem; height: 1rem; }
.lp-att.uploading { border-style: dashed; }
.lp-att.failed { border-color: var(--danger); }
.lp-att.failed .lp-att-meta { color: var(--danger); white-space: normal; }
.lp-att-bar { position: absolute; left: 0; bottom: 0; height: 2px; width: calc(var(--p) * 100%); background: var(--gradient-bar); transition: width 120ms linear; }
.lp-att-missing { padding: var(--space-3); border: 1px dashed var(--border-strong); border-radius: var(--radius-sm); color: var(--text-muted); font-size: var(--fs-sm); }
.lp-drop {
  display: flex; align-items: center; justify-content: center; gap: 0.6rem;
  width: 100%; min-height: 0; padding: 0.3rem var(--space-3);
  border: 1px dashed var(--border-strong); border-radius: var(--radius-sm);
  background: transparent; color: var(--text-muted); font-size: var(--fs-sm); font-weight: var(--fw-regular);
}
.lp-drop:hover { color: var(--text); border-color: var(--accent); background: var(--card-2); }
.lp-drop svg { flex: none; width: 1rem; height: 1rem; }
.lp-drop[hidden] { display: none; }
/* the whole editor is a drop target */
.lp-dropping { display: none; }
.lesson-panel.drop-on .lp-dropping {
  position: absolute; inset: 6px; z-index: 5;
  display: flex; align-items: center; justify-content: center;
  border: 2px dashed var(--accent); border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--card) 88%, transparent);
  pointer-events: none;
}
.lp-dropping span { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; color: var(--text); font-size: var(--fs-lg); text-align: center; }
.lp-dropping svg { width: 2.4rem; height: 2.4rem; color: var(--accent-ink); }

/* empty state */
.lesson-empty { flex: 1; min-height: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-2); padding: var(--space-5); text-align: center; }
.lesson-empty-title { margin: 0; color: var(--text); font-size: var(--fs-xl); font-weight: var(--fw-bold); }
.lesson-empty-sub { margin: 0 0 var(--space-3); color: var(--text-muted); }
.lesson-empty-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-2); }
.lesson-empty-drop { display: flex; align-items: center; gap: 0.4rem; margin: var(--space-4) 0 0; color: var(--text-muted); font-size: var(--fs-sm); }
.lesson-empty-drop svg { width: 1rem; height: 1rem; }

/* copy-from dialog */
.lesson-dialog { width: min(34rem, 92vw); max-height: 82vh; padding: 0; border: 1px solid var(--border-strong); border-radius: var(--radius-lg); background: var(--card); color: var(--text); box-shadow: var(--shadow-pop); }
.lesson-dialog::backdrop { background: rgba(8, 6, 14, 0.65); backdrop-filter: blur(4px); }
.lesson-dialog-form { display: flex; flex-direction: column; gap: var(--space-3); max-height: 82vh; padding: var(--space-4) var(--space-5) var(--space-5); }
.ld-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.ld-head h3 { margin: 0; font-size: 0.9rem; }
.ld-head .corner-btn, .lpp-head .corner-btn { display: inline-flex; align-items: center; justify-content: center; }
.ld-head .corner-btn svg, .lpp-head .corner-btn svg { width: 1rem; height: 1rem; }
.ld-pick { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.ld-pick[hidden] { display: none; }
.lesson-dialog-list { display: flex; flex-direction: column; gap: 0.35rem; min-height: 0; overflow-y: auto; scrollbar-width: thin; }
.lesson-dialog-row { display: flex; flex-direction: column; align-items: flex-start; gap: 0.15rem; width: 100%; padding: 0.55rem 0.75rem; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--card-2); color: var(--text); text-align: left; }
.lesson-dialog-row:hover:not(:disabled) { border-color: var(--accent); background: var(--card-3); }
.lesson-dialog-row:disabled { opacity: 0.5; cursor: default; }
.ld-name { font-weight: var(--fw-semibold); }
.ld-sub { display: flex; align-items: center; gap: 0.5rem; color: var(--text-muted); font-size: var(--fs-sm); font-weight: var(--fw-regular); }
.ld-clip { display: inline-flex; align-items: center; gap: 0.15rem; font-family: var(--font-mono); font-size: 0.72rem; }
.ld-clip svg { width: 0.85rem; height: 0.85rem; }
.ld-att { display: flex; align-items: center; gap: 0.5rem; color: var(--text-2); font-size: var(--fs-sm); }
.ld-att input { accent-color: var(--accent); width: 1rem; height: 1rem; margin: 0; }

/* preview: PDFs, images and YouTube inside the app */
.lp-preview { width: 90vw; height: 90vh; max-width: none; max-height: none; padding: 0; border: 1px solid var(--border-strong); border-radius: var(--radius-lg); background: var(--card); color: var(--text); box-shadow: var(--shadow-pop); overflow: hidden; }
.lp-preview::backdrop { background: rgba(8, 6, 14, 0.75); backdrop-filter: blur(6px); }
.lpp-inner { display: flex; flex-direction: column; height: 100%; }
.lpp-head { flex: none; display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border); }
.lpp-head .spacer { flex: 1; }
.lpp-icon { display: inline-flex; width: 1.6rem; height: 1.6rem; align-items: center; justify-content: center; }
.lpp-icon svg { width: 1.3rem; height: 1.3rem; }
.lpp-title { min-width: 0; display: flex; flex-direction: column; }
.lpp-title b { font-size: var(--fs-md); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lpp-title span { color: var(--text-muted); font-family: var(--font-mono); font-size: 0.72rem; }
.lpp-body { flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; background: var(--card-2); }
.lpp-body iframe { width: 100%; height: 100%; border: 0; background: var(--card-2); }
.lpp-body img { max-width: 100%; max-height: 100%; object-fit: contain; }
.lpp-loading { color: var(--text-muted); }

/* ---- courses: row labels, splits, the Courses button ---- */
.wk-period { gap: 0.2rem; }
.wk-period .wk-pname { font-size: var(--fs-md); font-weight: var(--fw-bold); }
.wk-period .wk-pnum { font-size: 0.7rem; letter-spacing: 0.08em; color: var(--text-muted); }
.wk-period.course .wk-pnum { color: var(--accent-ink); }
.wk-corner { display: flex; align-items: center; justify-content: center; }
.wk-courses {
  display: inline-flex; align-items: center; gap: 0.3rem;
  min-height: 0; padding: 0.25rem 0.55rem;
  border: 1px dashed var(--border-strong); border-radius: var(--radius-sm);
  background: transparent; color: var(--text-muted); font-size: var(--fs-xs); font-weight: var(--fw-semibold);
}
.wk-courses:hover { color: var(--text); border-color: var(--accent); background: var(--card-2); }
.wk-courses svg { width: 0.9rem; height: 0.9rem; }
.wk-meta .cm-split { color: var(--lavender); }
.mo-dots { flex-direction: column; align-items: stretch; width: 100%; }
.mo-dots i { justify-content: flex-start; min-width: 0; max-width: 100%; padding: 0 0.3rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; line-height: 1rem; font-family: var(--font-body); font-size: 0.68rem; letter-spacing: 0.01em; }
.dc-head .wk-pnum { font-size: 0.7rem; color: var(--accent-ink); }
.dc-split { display: flex; gap: 0.4rem; margin: 0; padding-top: 0.35rem; border-top: 1px dashed var(--border-strong); color: var(--text-2); font-size: var(--fs-sm); }
.dc-split svg { flex: none; width: 0.95rem; height: 0.95rem; margin-top: 0.15rem; color: var(--lavender); }
.dc-split b { color: var(--lavender); font-weight: var(--fw-semibold); }

/* editor: the course's periods under the title; the split controls stay quiet */
.lesson-title-sub { color: var(--text-muted); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; }
.lesson-title-sub:empty { display: none; }
.lesson-tools { align-items: center; }
.lesson-tools .spacer { flex: 1; }
.lp-quiet {
  display: inline-flex; align-items: center; gap: 0.3rem;
  min-height: 0; padding: 0.2rem 0.45rem;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  background: transparent; color: var(--text-muted);
  font-size: var(--fs-xs); font-weight: var(--fw-semibold);
}
.lp-quiet:hover { color: var(--text); border-color: var(--border-strong); background: var(--card-2); }
.lp-quiet svg { width: 0.9rem; height: 0.9rem; }
.lp-split-wrap[hidden], .lp-more-wrap[hidden], .lp-addfield-wrap[hidden], .lp-more-main[hidden], .lp-split-list[hidden] { display: none; }
.lp-more-wrap, .lp-addfield-wrap { position: relative; flex: none; }
.lp-addfield-wrap { align-self: flex-start; }
.lp-more-btn { padding: 0.2rem 0.35rem; }
.lp-more-btn svg { width: 1.1rem; height: 1.1rem; }
.lp-more-main, .lp-split-list { display: flex; flex-direction: column; gap: 0.15rem; }
.lp-more-menu { width: 15rem; }
.lp-more-menu button svg { flex: none; width: 0.95rem; height: 0.95rem; align-self: center; color: var(--text-muted); }
.lp-split-menu button em { font-style: normal; font-family: var(--font-body); font-size: var(--fs-sm); color: var(--text); }
.lp-addfield-menu { left: 0; right: auto; top: auto; bottom: calc(100% + 0.35rem); width: 11rem; }
.lp-attach-wrap { position: relative; align-self: flex-start; }
.lp-attach-menu { left: 0; right: auto; width: 14rem; }
.lp-attach-menu button svg { flex: none; width: 0.95rem; height: 0.95rem; align-self: center; color: var(--text-muted); }
.lp-att-field { gap: 0.3rem; }
.lp-att-field .lp-att-list:empty + .lp-link-form[hidden] + .lp-attach-wrap { margin-top: -0.3rem; }
.lp-split-menu {
  position: absolute; right: 0; top: calc(100% + 0.35rem); z-index: 20;
  display: flex; flex-direction: column; gap: 0.25rem;
  width: 17rem; padding: var(--space-2);
  border: 1px solid var(--border-strong); border-radius: var(--radius-md);
  background: var(--card); box-shadow: var(--shadow-pop);
}
.lp-split-menu[hidden] { display: none; }
.lp-split-note { padding: 0.2rem 0.35rem 0.4rem; color: var(--text-muted); font-size: var(--fs-xs); line-height: 1.35; }
.lp-split-menu button { display: flex; align-items: baseline; gap: 0.55rem; width: 100%; min-height: 0; padding: 0.4rem 0.55rem; border: 1px solid transparent; background: transparent; color: var(--text); text-align: left; font-family: var(--font-mono); font-size: 0.8rem; justify-content: flex-start; }
.lp-split-menu button span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-muted); font-family: var(--font-body); font-size: var(--fs-xs); }
.lp-split-menu button:hover { background: var(--card-3); border-color: var(--border-strong); }
.lp-scope { flex: none; display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap; padding: 0.35rem; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--card-2); }
.lp-scope[hidden] { display: none; }
.lp-scope .spacer { flex: 1; }
.lp-scope-chip {
  display: inline-flex; align-items: center; gap: 0.35rem;
  min-height: 0; padding: 0.25rem 0.6rem;
  border: 1px solid var(--border-strong); border-radius: 999px;
  background: var(--card); color: var(--text-2); font-size: var(--fs-xs); font-weight: var(--fw-semibold);
}
.lp-scope-chip span { color: var(--text-muted); font-family: var(--font-mono); font-size: 0.68rem; font-weight: normal; }
.lp-scope-chip svg { width: 0.85rem; height: 0.85rem; }
.lp-scope-chip.own { color: var(--lavender); }
.lp-scope-chip.on { border-color: var(--accent); color: var(--text); box-shadow: inset 0 0 0 1px var(--accent); }
.lp-scope-chip.own.on { border-color: var(--lavender); box-shadow: inset 0 0 0 1px var(--lavender); }

/* settings menu → Courses… */
.hud-menu-btn { display: flex; flex-direction: column; align-items: flex-start; gap: 0.05rem; width: 100%; min-height: 0; margin-top: 0.15rem; border: 0; border-top: 1px solid var(--border); border-radius: 0 0 var(--radius-sm) var(--radius-sm); background: transparent; color: var(--text); text-align: left; font-weight: var(--fw-semibold); }
.hud-menu-btn span { color: var(--text-muted); font-size: var(--fs-xs); font-weight: var(--fw-regular); }
.hud-menu-btn:hover { background: var(--card-2); }

/* the Courses dialog (nav.js) */
.courses-dialog { width: min(56rem, 94vw); max-height: 88vh; padding: 0; border: 1px solid var(--border-strong); border-radius: var(--radius-lg); background: var(--card); color: var(--text); box-shadow: var(--shadow-pop); }
.courses-dialog::backdrop { background: rgba(8, 6, 14, 0.65); backdrop-filter: blur(4px); }
.cd-inner { display: flex; flex-direction: column; max-height: 88vh; }
.cd-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding: var(--space-4) var(--space-5) 0; }
.cd-head h2 { margin: 0; font-size: 0.95rem; }
.cd-head .corner-btn { display: inline-flex; align-items: center; justify-content: center; }
.cd-head .corner-btn svg { width: 1rem; height: 1rem; }
.cd-note { margin: var(--space-2) var(--space-5) 0; color: var(--text-muted); font-size: var(--fs-sm); }
.cd-body { flex: 1; min-height: 0; overflow-y: auto; display: grid; grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.3fr); gap: var(--space-5); padding: var(--space-4) var(--space-5) var(--space-5); }
.cd-body > .hint, .cd-error { grid-column: 1 / -1; }
.cd-error { margin: 0; color: var(--danger); }
.cd-error span { display: block; margin-top: 0.3rem; color: var(--text-muted); font-size: var(--fs-xs); }
.cd-sec h3 { margin: 0 0 var(--space-2); color: var(--label-ink); font-size: var(--fs-xs); font-weight: var(--fw-semibold); text-transform: uppercase; letter-spacing: 0.12em; }
.cd-courses, .cd-periods { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.35rem; }
.cd-courses li { display: grid; grid-template-columns: minmax(0, 1fr) 5.6rem 2rem; align-items: center; gap: 0.5rem; }
.cd-courses input { min-height: 2.2rem; font-weight: var(--fw-semibold); }
.cd-in { color: var(--accent-ink); font-family: var(--font-mono); font-size: 0.72rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cd-in.none { color: var(--text-muted); }
.cd-courses .icon-btn { width: 2rem; height: 2rem; min-height: 0; padding: 0; display: inline-flex; align-items: center; justify-content: center; border-color: transparent; background: transparent; }
.cd-courses .icon-btn:hover { color: var(--danger); border-color: var(--border-strong); }
.cd-courses .icon-btn svg { width: 1rem; height: 1rem; }
.cd-none { color: var(--text-muted); font-size: var(--fs-sm); }
.cd-add { display: flex; gap: 0.4rem; margin-top: var(--space-3); }
.cd-add input { flex: 1; min-width: 0; min-height: 2.2rem; }
.cd-periods li { display: grid; grid-template-columns: 2rem minmax(0, 1fr) 9.5rem; align-items: center; gap: 0.5rem; padding: 0.3rem 0.4rem; border-radius: var(--radius-sm); }
.cd-periods li:hover { background: var(--card-2); }
.cd-periods li.not-taught { opacity: 0.6; }
.cd-pnum { color: var(--accent-ink); font-family: var(--font-mono); font-size: 0.8rem; }
.cd-pname { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-sm); }
.cd-pname em { color: var(--text-muted); font-size: var(--fs-xs); }
.cd-sec { min-width: 0; }
.cd-periods select { width: 100%; min-width: 0; min-height: 2.1rem; font-size: var(--fs-sm); }
@media (max-width: 640px) {
  .cd-body { grid-template-columns: 1fr; }
  .cd-periods li { grid-template-columns: 2.2rem minmax(0, 1fr); }
  .cd-periods select { grid-column: 1 / -1; }
}

/* ---- full screen + smaller screens ---- */
body.present .lessons-layout { grid-template-columns: minmax(0, 1.3fr) minmax(26rem, 1fr); }
body.present .wk-text { font-size: var(--fs-md); }
@media (max-width: 1150px) {
  .lessons-page { overflow: auto; }
  .lessons-layout { grid-template-columns: 1fr; grid-template-rows: minmax(30rem, 62vh) auto; }
  .lesson-panel { min-height: 26rem; overflow: visible; }
  .lesson-body { overflow: visible; }   /* the page scrolls here, not the editor */
  .lp-notes-field { min-height: 12rem; }
}
@media (max-width: 640px) {
  .cal-box { padding: var(--space-3); }
  .cal-label { min-width: 0; font-size: var(--fs-md); }
  .cal-search { flex-basis: 100%; max-width: none; order: 10; margin-left: 0; }
  .cal-views { order: 9; }
  .wk-grid { grid-template-columns: 4.2rem repeat(5, minmax(6.5rem, 1fr)); }
  .wk-pname { display: none; }
  .mo-dots i { min-width: 0.6rem; height: 0.6rem; padding: 0; font-size: 0; }
  .lp-cols { grid-template-columns: 1fr; }
  .lp-link-form { grid-template-columns: 1fr 1fr; }
  .lp-att { grid-template-columns: 2rem minmax(0, 1fr) auto; }
  .lp-att-meta { display: none; }
  .lp-preview { width: 100vw; height: 100dvh; border-radius: 0; }
}

/* ==========================================================================
   Setup wizard (setup.html) — the first-run steps after a self sign-up:
   periods taught → class names → rosters → theme. One centred card.
   ========================================================================== */
main.setup-page { display: grid; justify-items: center; align-content: start; padding: var(--space-6) var(--page-pad); }
.setup-card {
  position: relative;
  width: min(44rem, 100%);
  display: grid;
  gap: var(--space-4);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-5) var(--space-6) var(--space-5);
}
.setup-card::before {
  content: "";
  position: absolute; inset: 0 var(--space-5) auto;
  height: 1px;
  background: var(--gradient-glow-top);
}
.sw-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.sw-steps { display: flex; gap: var(--space-2); list-style: none; margin: 0; padding: 0; counter-reset: sw; flex-wrap: wrap; }
.sw-steps li {
  counter-increment: sw;
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-family: var(--font-display);
  font-size: 0.62rem;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 0.3rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
}
.sw-steps li::before { content: counter(sw); font-family: var(--font-mono); }
.sw-steps li.done { color: var(--text-2); border-color: var(--border-strong); }
.sw-steps li[aria-current="step"] { color: var(--text); border-color: var(--accent); background: var(--gradient-accent); }
.sw-skip { font-size: var(--fs-xs); }

.sw-panel { display: grid; gap: var(--space-3); }
.sw-panel[hidden] { display: none; }
.sw-kicker {
  margin: 0;
  font-family: var(--font-display);
  font-size: 0.7rem;
  letter-spacing: var(--track-display);
  text-transform: uppercase;
  color: var(--accent-ink);
}
.sw-title { margin: 0; font-family: var(--font-body); font-weight: var(--fw-bold); font-size: var(--fs-xl); line-height: 1.15; color: var(--text); }
.sw-lead { margin: 0; color: var(--text-2); line-height: 1.5; }

.sw-periods { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-3); }
@media (max-width: 520px) { .sw-periods { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.sw-period { flex-direction: column; min-height: 4.6rem; gap: 0.1rem; }
.sw-period-n { font-family: var(--font-mono); font-size: var(--fs-xl); line-height: 1; }
.sw-period-l { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-muted); }

.sw-rows, .sw-rosters { display: grid; gap: var(--space-3); }
.sw-rosters { grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr)); max-height: 52vh; overflow-y: auto; padding-right: var(--space-1); }
.sw-row { display: grid; grid-template-columns: 6.5rem minmax(0, 1fr); align-items: center; gap: var(--space-3); }
.sw-row input { width: 100%; min-height: 2.5rem; font-size: var(--fs-md); }
.sw-roster { display: grid; gap: var(--space-2); }
.sw-roster textarea { width: 100%; resize: vertical; min-height: 6rem; font-size: var(--fs-md); }
.sw-row-k {
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: var(--fs-xs);
  color: var(--label-ink);
}
.sw-count { font-family: var(--font-mono); text-transform: none; letter-spacing: 0; color: var(--accent-ink); margin-left: 0.4rem; }

.sw-themes { display: grid; grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr)); gap: var(--space-3); }
.sw-theme { min-height: 3.2rem; }
.sw-theme[aria-checked="true"] { background: var(--gradient-accent); border-color: var(--accent); box-shadow: var(--shadow-pressed); color: var(--text); }

.sw-msg { min-height: 1.3rem; font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--danger); }
.sw-foot { display: flex; justify-content: space-between; gap: var(--space-3); border-top: 1px solid var(--border); padding-top: var(--space-4); }
.sw-foot .primary { min-width: 9rem; margin-left: auto; }
@media (max-width: 520px) { .setup-card { padding: var(--space-4); } .sw-row { grid-template-columns: 1fr; gap: var(--space-1); } }
