/* ============================================================
   SCIENCE LAB X — "Saturday-Morning Laboratory" Design System
   Playful, not childish. Bold colour, chunky sticker shapes,
   motion with meaning — Duolingo/Kurzgesagt energy for a 15-year-old.

   Rounded/Sans/Mono Triad: Baloo 2 · Manrope · Fira Code
   Sticker layering: hard offset shadows instead of diffuse tonal ones.
   Big radii for friendly geometry.
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Baloo+2:wght@500;600;700;800&family=Manrope:wght@300;400;500;600;700;800&family=Fira+Code:wght@400;500;600;700&display=swap');

/* ── Design Tokens ────────────────────────────────────────── */
:root {
    color-scheme: light;

    /* ─ Editorial Foundation ─ */
    --surface:                 #f9f9f9;
    --surface-container-low:   #f3f3f3;
    --surface-container-lowest:#ffffff;
    --surface-container-high:  #e8e8e8;
    --on-surface:              #1a1a2e;
    --on-surface-variant:      #4d5461;
    --outline:                 #737687;
    --outline-variant:         #c3c5d8;
    --primary:                 #0049db;
    --primary-container:       #2962ff;
    --on-primary:              #ffffff;
    --cell-input-bg:           #fefefd;

    /* ─ Diagrammatic Syntax ─ */
    --secondary-container:     #fdd400;
    --tertiary-container:      #bf00df;
    --primary-fixed-dim:       #b6c4ff;
    --secondary:               #705d00;

    /* ─ Strand identities — overridden per body[data-strand] ─ */
    --accent:        var(--primary);
    --accent-deep:   var(--primary-container);
    --accent-soft:   #eef1ff;
    --accent-glow:   rgba(0, 73, 219, 0.18);
    --accent-ink:    #002a7f;
    /* two-stop candy gradient, per strand below; this is the fallback
       used on pages with no body[data-strand] (the hub) */
    --accent-grad:   linear-gradient(135deg, var(--primary), #22D3EE);

    /* ─ Fixed brand / chapter card colors ─ */
    --hue-violet:  #6C4BF4;
    --hue-blue:    #1B7BF5;
    --hue-cyan:    #06B6D4;
    --hue-mint:    #10B981;
    --hue-lime:    #84CC16;
    --hue-amber:   #F59E0B;
    --hue-orange:  #FB6D3A;
    --hue-rose:    #F43F5E;
    --hue-pink:    #EC4899;

    /* ─ Backward-compat aliases (used by existing HTML) ─ */
    --canvas:       var(--surface);
    --surface-2:    var(--surface-container-high);
    --surface-sink: var(--surface-container-low);
    --card:         var(--surface-container-lowest);
    --hairline:     var(--outline-variant);
    --hairline-2:   var(--outline);
    --ink:          var(--on-surface);
    --ink-2:        var(--on-surface-variant);
    --ink-3:        var(--outline);
    --ink-4:        var(--outline-variant);
    --on-accent:    var(--on-primary);

    /* ─ Semantics ─ */
    --ok:      #059669;
    --ok-bg:   #edf7f1;
    --warn:    #D97706;
    --warn-bg: #fdf6e9;
    --bad:     #c9303e;
    --bad-bg:  #fcedef;

    /* ─ Diagram palette ─ */
    --dia-bg:     var(--surface-container-lowest);
    --dia-grid:   var(--surface-container-low);
    --dia-axis:   var(--outline-variant);
    --dia-ink:    var(--on-surface);
    --dia-muted:  var(--on-surface-variant);
    --dia-1:      var(--primary);
    --dia-2:      #fb6d3a;
    --dia-3:      #10b981;
    --dia-4:      #6c4bf4;
    --dia-5:      #f59e0b;
    --dia-6:      #ec4899;

    /* ─ Shape — friendly rounded geometry ─ */
    --r-sm: 8px;
    --r-md: 12px;
    --r-lg: 20px;
    --r-xl: 28px;
    --r-pill: 999px;

    /* ─ The Academic Shadow: diffused, primary-tinted, 4% opacity ─ */
    --shadow-sm: 0 1px 3px rgba(0, 73, 219, 0.04), 0 2px 10px rgba(0, 73, 219, 0.03);
    --shadow-md: 0 4px 16px rgba(0, 73, 219, 0.04), 0 12px 36px rgba(0, 73, 219, 0.03);
    --shadow-lg: 0 12px 32px rgba(0, 73, 219, 0.04), 0 28px 72px rgba(0, 73, 219, 0.04);

    --ease: cubic-bezier(0.22, 1, 0.36, 1);
    --ease-snap: cubic-bezier(0.16, 1, 0.3, 1);
    /* a real spring: overshoots past 1 then settles — sticker-pop motion */
    --ease-pop: cubic-bezier(0.34, 1.56, 0.64, 1);

    /* ─ Instrument glyphs — inline SVG masks, tinted by background-color
         so every icon follows the theme and the strand accent ─ */
    --i-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20 6.5 9.2 17.3 4 12.1' fill='none' stroke='%23000' stroke-width='3.2' stroke-linecap='square'/%3E%3C/svg%3E");
    --i-cross: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6.5 6.5l11 11M17.5 6.5l-11 11' fill='none' stroke='%23000' stroke-width='3.2' stroke-linecap='square'/%3E%3C/svg%3E");
    --i-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M3 12h17M14 6l6 6-6 6' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='square'/%3E%3C/svg%3E");
    --i-drag:  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2v20M2 12h20M12 2 8.6 5.4M12 2l3.4 3.4M12 22l-3.4-3.4M12 22l3.4-3.4M2 12l3.4-3.4M2 12l3.4 3.4M22 12l-3.4-3.4M22 12l-3.4 3.4' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='square'/%3E%3C/svg%3E");
    --i-grid:  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cpath d='M0 .5h24M.5 0v24' stroke='%23000' stroke-width='1'/%3E%3C/svg%3E");
    --i-ruler: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12'%3E%3Cpath d='M.5 0v12' stroke='%23000' stroke-width='1'/%3E%3C/svg%3E");

    /* ─ Doodle layer — one repeating tile (flask · bolt · atom), tinted by
         background-color so it re-inks per theme/strand like the icons above.
         Kept to very low opacity wherever it's used. ─ */
    --i-doodle: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='80' height='80' viewBox='0 0 80 80'%3E%3Cg fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 8h8M16 8v7l-6 11a3.6 3.6 0 0 0 3.2 5.3h9.6A3.6 3.6 0 0 0 26 26l-6-11V8'/%3E%3Ccircle cx='58' cy='16' r='2.4'/%3E%3Cellipse cx='58' cy='16' rx='11' ry='4.4'/%3E%3Cellipse cx='58' cy='16' rx='11' ry='4.4' transform='rotate(60 58 16)'/%3E%3Cellipse cx='58' cy='16' rx='11' ry='4.4' transform='rotate(120 58 16)'/%3E%3Cpath d='M20 46l6-11h6l-4 8h7l-12 16 2-10h-6z'/%3E%3C/g%3E%3C/svg%3E");

    /* ─ Big single-glyph emblems — one per strand, used floating in the
         chapter hero. Same tinting technique. ─ */
    --i-flask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M9 3h6M10 3v6.2L4.6 18a2.2 2.2 0 0 0 1.9 3.3h11a2.2 2.2 0 0 0 1.9-3.3L14 9.2V3M8 15h8' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    --i-leaf:  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M4.5 19.5C4.5 10 11 3.5 20 3.5c0 9-6.5 16-16 16Zm0 0C8 15.5 12 11.5 18 8' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    --i-atom:  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='2.1' fill='%23000' stroke='none'/%3E%3Cellipse cx='12' cy='12' rx='10.5' ry='4.4' fill='none' stroke='%23000' stroke-width='1.6'/%3E%3Cellipse cx='12' cy='12' rx='10.5' ry='4.4' transform='rotate(60 12 12)' fill='none' stroke='%23000' stroke-width='1.6'/%3E%3Cellipse cx='12' cy='12' rx='10.5' ry='4.4' transform='rotate(120 12 12)' fill='none' stroke='%23000' stroke-width='1.6'/%3E%3C/svg%3E");
    --i-globe: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='9' fill='none' stroke='%23000' stroke-width='1.7'/%3E%3Cellipse cx='12' cy='12' rx='4' ry='9' fill='none' stroke='%23000' stroke-width='1.5'/%3E%3Cpath d='M3 12h18M4.6 7.5h14.8M4.6 16.5h14.8' fill='none' stroke='%23000' stroke-width='1.5'/%3E%3C/svg%3E");

    /* ─ Typography Triad ─ */
    --font:         'Manrope', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
    --font-display: 'Baloo 2', ui-rounded, 'SF Pro Rounded', 'Nunito', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --font-mono:    'SLX Notation', 'Fira Code', ui-monospace, 'SF Mono', 'Cascadia Code', monospace;

    --shell: 1200px;

    /* Ghost border */
    --ghost-border: rgba(195, 197, 216, 0.15);
}

/* ── Strand accents — saturated, candy-with-discipline ───────
   Each strand also carries a two-stop --accent-grad used on heroes,
   chips, progress fills and quiz gauges. ─────────────────────── */
body[data-strand="chemistry"] {
    --accent:#8A2BE8; --accent-deep:#6421C4; --accent-soft:#f3ecff; --accent-glow:rgba(138,43,232,.20); --accent-ink:#3B1780;
    --accent-grad: linear-gradient(135deg, #8A2BE8, #E339E0);
}
body[data-strand="biology"] {
    --accent:#0BBF77; --accent-deep:#067A4E; --accent-soft:#e8faf1; --accent-glow:rgba(11,191,119,.20); --accent-ink:#0A5138;
    --accent-grad: linear-gradient(135deg, #0BBF77, #A3D916);
}
body[data-strand="physics"] {
    --accent:#2B7CFF; --accent-deep:#124FCB; --accent-soft:#e9f1ff; --accent-glow:rgba(43,124,255,.20); --accent-ink:#12356F;
    --accent-grad: linear-gradient(135deg, #2B7CFF, #22D3EE);
}
body[data-strand="environment"] {
    --accent:#F2650A; --accent-deep:#B84E06; --accent-soft:#fef0e2; --accent-glow:rgba(242,101,10,.20); --accent-ink:#6E3405;
    --accent-grad: linear-gradient(135deg, #F2650A, #FFC93C);
}


/* ── Dark Mode ────────────────────────────────────────────── */
:root[data-theme="dark"] {
    color-scheme: dark;

    --surface:                 #141519;
    --surface-container-low:   #1c1d24;
    --surface-container-lowest:#0e0f13;
    --surface-container-high:  #252931;
    --on-surface:              #e8e8ec;
    --on-surface-variant:      #9b9ea7;
    --outline:                 #5c5f6e;
    --outline-variant:         #3a3d4a;
    --primary:                 #7eaaff;
    --primary-container:       #1a4bcf;
    --on-primary:              #001a4d;
    --cell-input-bg:           #1e2028;

    --secondary-container:     #c4a600;
    --tertiary-container:      #9b00b3;
    --primary-fixed-dim:       #3d5a99;
    --secondary:               #c4a600;

    --accent-soft:   #1a2040;
    --accent-glow:   rgba(126, 170, 255, 0.18);
    --accent-ink:    #c7deff;
    /* --accent-grad is a fill token (hero/chip/progress backgrounds), so —
       unlike --accent — it stays vivid/mid-tone in dark mode too, not a
       light pastel: it needs to keep working under white foreground text. */
    --accent-grad:   linear-gradient(135deg, #2563EB, #0891B2);

    --ok:  #34D399; --ok-bg:  #0d2a1f;
    --warn:#FBBF24; --warn-bg:#2a2008;
    --bad: #FB7185; --bad-bg: #2a0f1a;

    --shadow-sm: 0 1px 3px rgba(0,0,0,.3);
    --shadow-md: 0 6px 20px rgba(0,0,0,.35);
    --shadow-lg: 0 16px 48px rgba(0,0,0,.4);

    --ghost-border: rgba(58, 61, 74, 0.20);
}

:root[data-theme="dark"] body[data-strand="chemistry"]   { --accent:#A78BFA; --accent-deep:#C4B5FD; --accent-soft:#211838; --accent-ink:#DDD3FF; --accent-glow:rgba(167,139,250,.2); --accent-grad: linear-gradient(135deg, #7C3AED, #C026D3); }
:root[data-theme="dark"] body[data-strand="biology"]     { --accent:#34D399; --accent-deep:#6EE7B7; --accent-soft:#0c2a20; --accent-ink:#B6F5DC; --accent-glow:rgba(52,211,153,.2); --accent-grad: linear-gradient(135deg, #059669, #65A30D); }
:root[data-theme="dark"] body[data-strand="physics"]     { --accent:#60A5FA; --accent-deep:#93C5FD; --accent-soft:#0f2240; --accent-ink:#C7DEFF; --accent-glow:rgba(96,165,250,.2); --accent-grad: linear-gradient(135deg, #2563EB, #0891B2); }
:root[data-theme="dark"] body[data-strand="environment"] { --accent:#FBA94C; --accent-deep:#FCD34D; --accent-soft:#2a1c08; --accent-ink:#FFE0B5; --accent-glow:rgba(251,169,76,.2); --accent-grad: linear-gradient(135deg, #EA580C, #D97706); }


/* ── Reset ────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html { scroll-behavior: smooth; scroll-padding-top: 86px; }

body {
    font-family: var(--font);
    background: var(--surface);
    color: var(--on-surface);
    font-size: 16px;
    line-height: 1.7;
    font-weight: 400;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    overflow-x: hidden;
    transition: background-color .3s var(--ease), color .3s var(--ease);
}

img, svg, canvas { max-width: 100%; display: block; }
button, input, select, textarea { font: inherit; color: inherit; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

::selection { background: var(--primary); color: var(--on-primary); }
:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }

/* ── Layout ───────────────────────────────────────────────── */
.shell { width: 100%; max-width: var(--shell); margin: 0 auto; padding: 0 40px; }
.shell-wide { max-width: 1420px; }
.stack-lg > * + * { margin-top: 28px; }

/* ── Nav — Glassmorphism ──────────────────────────────────── */
.nav {
    position: sticky; top: 0; z-index: 900;
    background: rgba(249, 249, 249, 0.80);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--ghost-border);
}
:root[data-theme="dark"] .nav {
    background: rgba(20, 21, 25, 0.70);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
}
.nav-inner {
    max-width: 1420px; margin: 0 auto; padding: 0 40px;
    height: 64px; display: flex; align-items: center; gap: 20px;
}
.brand { display: flex; align-items: center; gap: 12px; text-decoration: none; color: inherit; }
.brand:hover { text-decoration: none; }
.brand-mark {
    width: 36px; height: 36px; border-radius: var(--r-sm);
    display: grid; place-items: center;
    font-family: var(--font-display); font-size: 1.1rem; font-weight: 700;
    font-style: italic;
    background: linear-gradient(135deg, var(--hue-violet), var(--hue-blue) 50%, var(--hue-mint));
    color: #fff;
    box-shadow: 0 3px 10px var(--accent-glow);
}
.brand-name { font-family: var(--font-display); font-weight: 600; font-size: 1.05rem; letter-spacing: -.01em; white-space: nowrap; }
.brand-name span { color: var(--on-surface-variant); font-weight: 400; }
.nav-spacer { flex: 1; }
.nav-links { display: flex; gap: 2px; list-style: none; align-items: center; }
.nav-links a {
    color: var(--on-surface-variant); font-size: .88rem; font-weight: 600;
    padding: 8px 14px; border-radius: var(--r-pill); letter-spacing: .01em;
    transition: color .16s var(--ease), background .16s var(--ease);
}
.nav-links a:hover { color: var(--on-surface); text-decoration: none; background: var(--surface-container-low); }
.nav-links a.is-active { color: var(--primary); background: var(--accent-soft); }

/* ── Buttons — Manuscript Geometry (0px radius) ──────────── */
.btn {
    --btn-bg: var(--primary);
    --btn-fg: var(--on-primary);
    /* the ink the hover wipe lays down — darkened own-colour, so the label
       keeps its contrast direction on every variant */
    --btn-wipe: color-mix(in srgb, var(--btn-bg) 58%, var(--on-surface));
    position: relative; overflow: hidden; isolation: isolate;
    display: inline-flex; align-items: center; justify-content: center; gap: 9px;
    font-family: var(--font); font-size: .88rem; font-weight: 700;
    letter-spacing: .02em; text-transform: uppercase;
    padding: 12px 24px; border-radius: var(--r-sm);
    background: var(--btn-bg); color: var(--btn-fg);
    border: none; cursor: pointer; text-decoration: none; white-space: nowrap;
    transition: transform .18s var(--ease-snap), box-shadow .18s var(--ease), color .18s var(--ease);
}
/* the ink wipes across from the left — a press of the plate, not a glow */
.btn::before {
    content: ''; position: absolute; inset: 0; z-index: -1;
    background: var(--btn-wipe);
    transform: scaleX(0); transform-origin: left center;
    transition: transform .32s var(--ease-snap);
}
.btn:hover::before { transform: scaleX(1); }
.btn:hover { transform: translateY(-2px); text-decoration: none; box-shadow: var(--shadow-md); }
.btn:active { transform: translateY(0); box-shadow: none; }
.btn:active::before { transition-duration: .08s; }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .45; cursor: not-allowed; transform: none; }
.btn:disabled::before, .btn[aria-disabled="true"]::before { display: none; }
.btn-lg { font-size: .92rem; padding: 15px 32px; }
.btn-sm { font-size: .78rem; padding: 8px 16px; gap: 6px; }
/* trailing rule that extends on hover — editorial "read on" cue */
.btn .rule { display: inline-block; width: 14px; height: 1.5px; background: currentColor; transition: width .3s var(--ease-snap); }
.btn:hover .rule { width: 26px; }

/* Tertiary: text-only with primary underline on hover */
.btn-ghost {
    --btn-bg: transparent; --btn-fg: var(--on-surface);
    border: 1px solid var(--outline-variant); box-shadow: none;
}
.btn-ghost { --btn-wipe: var(--primary); }
.btn-ghost:hover { box-shadow: none; border-color: var(--primary); color: var(--on-primary); }
.btn-ghost:active { box-shadow: none; }

.btn-soft { --btn-bg: var(--accent-soft); --btn-fg: var(--accent-ink); --btn-wipe: var(--primary); box-shadow: none; }
.btn-soft:hover { color: var(--on-primary); box-shadow: var(--shadow-sm); }
.btn-soft:active { box-shadow: none; }

.btn-mint   { --btn-bg: var(--hue-mint);   }
/* amber carries dark ink, so its wipe goes lighter, not darker */
.btn-amber  { --btn-bg: var(--hue-amber);  --btn-fg:#3B2600; --btn-wipe: color-mix(in srgb, var(--btn-bg) 58%, #fff); }
.btn-rose   { --btn-bg: var(--hue-rose);   }
.btn-blue   { --btn-bg: var(--hue-blue);   }
.btn-violet { --btn-bg: var(--hue-violet); }

/* Segmented toggle group — the index tabs of the manuscript */
.seg {
    position: relative; display: inline-flex; gap: 0; padding: 3px;
    background: var(--surface-container-low);
    border: 1px solid var(--outline-variant);
    border-radius: var(--r-pill); flex-wrap: wrap;
}
.seg button {
    position: relative; overflow: hidden;
    border: none; background: transparent; color: var(--on-surface-variant);
    font-size: .8rem; font-weight: 700; padding: 9px 18px;
    letter-spacing: .02em; text-transform: uppercase; white-space: nowrap; border-radius: var(--r-pill);
    cursor: pointer; transition: color .16s var(--ease), background .16s var(--ease);
}
.seg button + button { border-left: 1px solid var(--ghost-border); }
/* rule that grows out of the baseline on hover */
.seg button::after {
    content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
    background: var(--primary); transform: scaleX(0); transform-origin: center;
    transition: transform .24s var(--ease-snap);
}
.seg button:hover { color: var(--on-surface); background: var(--surface-container-high); }
.seg button:hover::after { transform: scaleX(1); }
.seg button[aria-pressed="true"], .seg button.is-on {
    background: var(--primary); color: var(--on-primary);
    box-shadow: var(--shadow-sm);
    animation: seg-set .26s var(--ease-snap);
}
.seg button[aria-pressed="true"]::after, .seg button.is-on::after { background: var(--on-primary); transform: scaleX(1); }
@keyframes seg-set { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0); } }

.icon-btn {
    position: relative; overflow: hidden;
    width: 38px; height: 38px; display: grid; place-items: center;
    border-radius: var(--r-pill);
    border: 1px solid var(--outline-variant);
    background: var(--surface-container-low); color: var(--on-surface-variant); cursor: pointer;
    font-size: 1rem; transition: color .16s var(--ease), border-color .16s var(--ease), background .16s var(--ease);
}
.icon-btn:hover { background: var(--surface-container-high); color: var(--primary); border-color: var(--primary); }
.icon-btn:active { background: var(--primary); color: var(--on-primary); }
.icon-btn svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: square; }
.icon-btn svg .solid { fill: currentColor; stroke: none; }
.icon-btn svg { animation: glyph-set .34s var(--ease-snap); }
@keyframes glyph-set { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* ── Chips, badges ────────────────────────────────────────── */
.chip {
    display: inline-flex; align-items: center; gap: 7px;
    font-family: var(--font); font-size: .72rem; font-weight: 700;
    letter-spacing: .06em; text-transform: uppercase;
    padding: 5px 14px; border-radius: var(--r-pill);
    background: var(--accent-soft); color: var(--accent-ink);
}
.chip-outline { background: transparent; border: 1px solid var(--outline-variant); color: var(--on-surface-variant); border-radius: var(--r-pill); }
.badge-num {
    display: inline-grid; place-items: center;
    width: 30px; height: 30px; border-radius: var(--r-sm);
    background: var(--primary); color: var(--on-primary);
    font-family: var(--font-mono); font-size: .82rem; font-weight: 600; flex-shrink: 0;
}

/* ── Cards & Grids — Tonal Layering ──────────────────────── */
/* Cards lift via background shift, not shadows or heavy borders */
.card {
    background: var(--surface-container-lowest);
    border: 1px solid var(--ghost-border);
    border-radius: var(--r-lg); padding: 28px;
    box-shadow: var(--shadow-sm);
    transition: background-color .2s var(--ease);
}
.card-pad-sm { padding: 18px; }

.grid { display: grid; gap: 24px; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }

/* ── Typography: The Serif/Sans/Mono Triad ────────────────── */

/* Display & Headlines — Lora serif */
.display {
    font-family: var(--font-display); font-weight: 700;
    font-size: clamp(2.6rem, 6vw, 3.5rem); /* display-lg: 3.5rem */
    line-height: 1.08; letter-spacing: -.02em;
}
.h1 {
    font-family: var(--font-display); font-weight: 700;
    font-size: clamp(2rem, 4.4vw, 2.8rem);
    line-height: 1.12; letter-spacing: -.015em;
}
.h2 {
    font-family: var(--font-display); font-weight: 700;
    font-size: clamp(1.5rem, 3vw, 2rem);
    line-height: 1.2; letter-spacing: -.01em;
}
.h3 {
    font-family: var(--font-display); font-weight: 600;
    font-size: 1.2rem; line-height: 1.32; letter-spacing: -.005em;
}
.h4 { font-weight: 700; font-size: 1rem; letter-spacing: -.01em; }

/* Body Text — Manrope */
.lede { font-size: 1.08rem; color: var(--on-surface-variant); line-height: 1.72; font-weight: 400; }
.muted { color: var(--outline); }
.small { font-size: .88rem; }
.mono  { font-family: var(--font-mono); font-feature-settings: "liga" 1; }

/* Eyebrow / Technical labels — Manrope 600, label-md */
.eyebrow {
    font-family: var(--font); font-size: .72rem; font-weight: 700;
    letter-spacing: .14em; text-transform: uppercase;
    color: var(--primary);
}

/* Gradient text — vibrant multi-hue spectrum */
.grad-text {
    background: linear-gradient(105deg, var(--hue-violet), var(--hue-blue) 35%, var(--hue-cyan) 60%, var(--hue-mint) 85%);
    -webkit-background-clip: text; background-clip: text; color: transparent;
}

p + p { margin-top: 14px; }

/* ── Tabular numbers — Manrope 600 for all measurements ──── */
.num, .readout dd, .ctrl-value, .stat b {
    font-family: var(--font); font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* ── Section Scaffolding — No-Line Rule ──────────────────── */
/* Sectioning through background shifts, not borders */
.section { padding: 64px 0; }
.section + .section {
    /* No 1px solid border. Shift background instead. */
    border-top: none;
    background: var(--surface-container-low);
}
.section + .section + .section {
    background: var(--surface);
}
.section + .section + .section + .section {
    background: var(--surface-container-low);
}
.section-head { max-width: 760px; margin-bottom: 36px; }
.section-head .h2 { margin: 10px 0 14px; }

/* ── Lab — Interactive simulation container ──────────────── */
/* Treated as a plate in a technical manuscript: ruled frame, ticked
   corners, a caption bar and an instrument panel down the side. */
.lab {
    position: relative;
    background: var(--surface-container-lowest);
    border: 1px solid var(--outline-variant);
    border-radius: var(--r-xl); overflow: hidden;
    transition: border-color .24s var(--ease), box-shadow .24s var(--ease);
}
.lab:hover, .lab:focus-within { border-color: var(--primary); box-shadow: var(--shadow-md); }
/* register mark that inks in when the plate is in use */
.lab::after {
    content: ''; position: absolute; top: 0; left: 0; height: 2px; right: 0;
    background: var(--primary); transform: scaleX(0); transform-origin: left center;
    transition: transform .5s var(--ease-snap); pointer-events: none; z-index: 3;
}
.lab:hover::after, .lab:focus-within::after { transform: scaleX(1); }
.lab-head {
    position: relative;
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    padding: 16px 24px;
    border-bottom: 1px solid var(--outline-variant);
    background: var(--surface-container-low);
}
.lab-head .h3 { flex: 1; min-width: 180px; display: flex; align-items: center; gap: 10px; }
.lab-body { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(260px, 1fr); }
@media (max-width: 900px) { .lab-body { grid-template-columns: 1fr; } }
.lab-stage {
    position: relative; isolation: isolate;
    background: var(--surface-container-low);
    padding: 20px; min-height: 300px; min-width: 0;
    display: flex; align-items: center; justify-content: center;
}
/* graph-paper lattice behind the plate — SVG mask, so it re-inks per theme */
.lab-stage::before {
    content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
    background-color: var(--outline-variant); opacity: .28;
    -webkit-mask-image: var(--i-grid); mask-image: var(--i-grid);
    -webkit-mask-size: 24px 24px; mask-size: 24px 24px;
}
.lab-stage > * { width: 100%; position: relative; z-index: 1; }
.lab-stage > .drag-cue { width: auto; }
.lab-stage canvas, .lab-stage svg {
    width: 100%; height: auto;
    background: var(--cell-input-bg);
    border-radius: var(--r-md);
    box-shadow: 0 0 0 1px var(--outline-variant);
}
/* canvases you can grab say so, once, in the margin of the plate */
.lab-stage canvas.is-draggable { cursor: grab; }
.lab-stage canvas.is-draggable:active { cursor: grabbing; }
.lab-stage .drag-cue {
    position: absolute; z-index: 2; top: 28px; right: 28px;
    display: inline-flex; align-items: center; gap: 7px;
    padding: 5px 10px; border-radius: var(--r-sm);
    background: var(--primary); color: var(--on-primary);
    font-family: var(--font); font-size: .66rem; font-weight: 700;
    letter-spacing: .1em; text-transform: uppercase;
    pointer-events: none;
    transition: opacity .3s var(--ease), transform .3s var(--ease);
}
.lab-stage .drag-cue::before {
    content: ''; width: 13px; height: 13px; background-color: currentColor;
    -webkit-mask: var(--i-drag) center/contain no-repeat; mask: var(--i-drag) center/contain no-repeat;
    animation: cue-nudge 2.8s var(--ease) infinite;
}
@keyframes cue-nudge { 0%, 70%, 100% { transform: none; } 80% { transform: translate(2px, -2px); } 90% { transform: translate(-1px, 1px); } }
.lab-stage.is-grabbed .drag-cue { opacity: 0; transform: translateY(-4px); }
.lab-panel {
    padding: 24px;
    background: var(--surface-container-low);
    display: flex; flex-direction: column; gap: 18px; min-width: 0;
}
/* The rule that separates panel from stage follows the layout, not the
   source order: a stacked panel is ruled along whichever edge faces the
   stage, a panel in the margin down its left. A panel that *is* the
   body needs no rule, and no empty second column either. */
.lab-body > .lab-panel:not(:only-child),
.lab > .lab-panel { border-top: 1px solid var(--outline-variant); }
/* authored panel-first, so it stacks above the stage */
.lab-body > .lab-panel:has(~ .lab-stage) {
    border-top: none;
    border-bottom: 1px solid var(--outline-variant);
}
.lab-body > .lab-panel:only-child,
.lab-body > .lab-stage:only-child { grid-column: 1 / -1; }
@media (min-width: 901px) {
    .lab-body > .lab-panel:not(:only-child) {
        border-top: none; border-bottom: none;
        border-left: 1px solid var(--outline-variant);
    }
}
/* Panel-width grids must never out-measure the column they sit in. */
.lab-panel .grid-2,
.lab-panel .grid-3,
.lab-panel .grid-4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); }
.lab-foot {
    position: relative; padding: 14px 24px 14px 26px;
    border-top: 1px solid var(--outline-variant);
    background: var(--surface-container-low);
    font-size: .92rem; color: var(--on-surface-variant);
}
.lab-foot::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--primary); }
.lab-foot strong { color: var(--on-surface); }
/* Full-bleed plates once ran the stage edge to edge and stacked the
   instrument panel underneath, which pushed the taller experiments well
   past a screenful. Keep the panel in the margin on wide screens: the
   canvas is sized from its column width, so narrowing the stage shortens
   the whole plate. Stack only when the margin would be too narrow to
   hold a slider. */
.lab-full .lab-body { grid-template-columns: 1fr; }
@media (min-width: 901px) {
    .lab-full .lab-body { grid-template-columns: minmax(0, 2fr) minmax(300px, 1fr); }
    body[data-strand="chemistry"] .lab-full .lab-body {
        grid-template-columns: minmax(0, 1.8fr) minmax(360px, 1fr);
    }
    /* A handful of plates are authored panel-first. Pin by role rather
       than by source order so the stage always gets the wide column. */
    .lab-full .lab-body > .lab-stage:not(:only-child) { grid-column: 1; grid-row: 1; }
    .lab-full .lab-body > .lab-panel:not(:only-child) { grid-column: 2; grid-row: 1; }
}

/* ── Controls ─────────────────────────────────────────────── */
.ctrl { display: flex; flex-direction: column; gap: 8px; }
.ctrl-label {
    display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
    font-size: .82rem; font-weight: 600; color: var(--on-surface-variant);
    letter-spacing: .01em;
}
.ctrl-value {
    font-family: var(--font-mono); font-size: .82rem; font-weight: 600;
    color: var(--accent-ink); background: var(--accent-soft);
    border-bottom: 2px solid transparent;
    padding: 2px 10px; border-radius: var(--r-sm); white-space: nowrap;
    font-feature-settings: "liga" 1;
    transition: border-color .2s var(--ease), color .2s var(--ease);
}
.ctrl:focus-within .ctrl-value, .ctrl-value.is-live { border-bottom-color: var(--primary); }

/* Sliders — a calibrated rail with a caliper cursor */
input[type="range"] {
    -webkit-appearance: none; appearance: none;
    width: 100%; height: 30px; background: transparent; cursor: pointer;
    --fill: 50%;
}
input[type="range"]::-webkit-slider-runnable-track {
    height: 6px; border-radius: var(--r-pill);
    border: 1px solid var(--outline-variant);
    background:
        linear-gradient(var(--primary), var(--primary)) left/var(--fill) 100% no-repeat,
        var(--surface-container-high);
}
input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none; appearance: none;
    width: 18px; height: 18px; margin-top: -7px;
    border-radius: 50%;
    background: var(--primary);
    border: 3px solid var(--surface);
    box-shadow: 0 2px 6px var(--accent-glow);
    transition: transform .16s var(--ease-snap), box-shadow .16s var(--ease);
}
input[type="range"]:hover::-webkit-slider-thumb { transform: scale(1.14); }
input[type="range"]:active::-webkit-slider-thumb { transform: scale(1.24); box-shadow: 0 0 0 3px var(--cell-input-bg), 0 0 0 6px var(--accent-glow); }
input[type="range"]:focus-visible { outline: none; }
input[type="range"]:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 3px var(--cell-input-bg), 0 0 0 6px var(--accent-glow); }
input[type="range"]::-moz-range-track { height: 6px; border-radius: var(--r-pill); background: var(--surface-container-high); border: 1px solid var(--outline-variant); }
input[type="range"]::-moz-range-progress { height: 6px; border-radius: var(--r-pill); background: var(--primary); }
input[type="range"]::-moz-range-thumb { width: 16px; height: 16px; background: var(--primary); border: 3px solid var(--surface); border-radius: 50%; }
/* the ruler under the rail — every slider reads like a measuring instrument.
   Applied by structure, so no chapter has to add markup for it. */
.ctrl:has(> input[type="range"])::after,
.ctrl-rail::after {
    content: ''; display: block; height: 5px; margin: -3px 2px 0;
    background-color: var(--outline-variant);
    -webkit-mask: var(--i-ruler) left top / 12px 5px repeat-x; mask: var(--i-ruler) left top / 12px 5px repeat-x;
    pointer-events: none;
}

/* Input fields — bottom border only */
select, input[type="text"], input[type="number"] {
    width: 100%; padding: 10px 0;
    border: none; border-bottom: 0.5px solid var(--outline-variant);
    background: transparent; color: var(--on-surface);
    font-size: .93rem; font-weight: 500; cursor: pointer;
    transition: border-color .18s var(--ease);
}
select:focus, input:focus { border-bottom-color: var(--primary); outline: none; }
select:hover, input[type="text"]:hover, input[type="number"]:hover { border-bottom-color: var(--outline); }

.switch { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; font-size: .88rem; font-weight: 600; color: var(--on-surface-variant); }
.switch:hover { color: var(--on-surface); }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-track {
    width: 46px; height: 24px; border-radius: var(--r-pill); position: relative; flex-shrink: 0;
    background: var(--surface-container-high);
    border: 1px solid var(--outline-variant);
    transition: background .2s var(--ease), border-color .2s var(--ease);
}
.switch-track::after {
    content: ''; position: absolute; top: 2px; left: 2px;
    width: 18px; height: 18px; border-radius: 50%; background: var(--on-surface-variant);
    transition: transform .24s var(--ease-snap), background .2s var(--ease);
}
/* the tick strikes through the knob once it is thrown */
.switch-track::before {
    content: ''; position: absolute; top: 6px; left: 6px; width: 10px; height: 10px; z-index: 1;
    border-radius: 50%;
    background-color: var(--primary); opacity: 0;
    -webkit-mask: var(--i-check) center/contain no-repeat; mask: var(--i-check) center/contain no-repeat;
    transition: transform .24s var(--ease-snap), opacity .16s var(--ease);
}
.switch input:checked + .switch-track { background: var(--primary); border-color: var(--primary); }
.switch input:checked + .switch-track::after { transform: translateX(22px); background: var(--on-primary); }
.switch input:checked + .switch-track::before { transform: translateX(22px); opacity: 1; }
.switch input:focus-visible + .switch-track { outline: 2px solid var(--primary); outline-offset: 2px; }

/* ── Readout tiles ────────────────────────────────────────── */
.readouts { display: grid; grid-template-columns: repeat(auto-fit, minmax(104px, 1fr)); gap: 10px; }
.readout {
    position: relative; overflow: hidden;
    background: var(--surface-container-lowest);
    border: 1px solid var(--ghost-border);
    border-left: 2px solid var(--outline-variant);
    border-radius: var(--r-md); padding: 12px 14px;
    transition: border-color .22s var(--ease), background .22s var(--ease);
}
.readout:hover { background: var(--cell-input-bg); border-left-color: var(--outline); }
.readout dt { font-size: .68rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--outline); }
.readout dd { font-family: var(--font-mono); font-size: 1.14rem; font-weight: 600; color: var(--on-surface); margin-top: 4px; font-variant-numeric: tabular-nums; font-feature-settings: "liga" 1; }
.readout dd small { font-size: .68em; font-weight: 400; color: var(--outline); }
.readout.is-hot dd { color: var(--bad); } .readout.is-hot { border-left-color: var(--bad); }
.readout.is-good dd { color: var(--ok); } .readout.is-good { border-left-color: var(--ok); }
/* the tile marks itself whenever the number underneath actually changes */
.readout.is-live { border-left-color: var(--primary); }
.readout.is-live dd { animation: value-set .45s var(--ease-snap); }
@keyframes value-set { from { opacity: .35; transform: translateY(3px); } to { opacity: 1; transform: none; } }

/* ── Legend ────────────────────────────────────────────────── */
.legend { display: flex; flex-wrap: wrap; gap: 8px 18px; font-size: .82rem; color: var(--on-surface-variant); }
.legend span { display: inline-flex; align-items: center; gap: 7px; }
.legend i { width: 16px; height: 3px; background: var(--sw, var(--primary)); flex-shrink: 0; font-style: normal; }
.legend i.dot { width: 10px; height: 10px; }

/* ══════════════════════════════════════════════════════════
   INTERACTION KIT — for labs that are not slider-shaped.
   Dial, stepper, transport, pick-and-place, tap boards.
   ══════════════════════════════════════════════════════════ */

/* Rotary dial — a gauge you drag round or arrow-key. SLX.dial() drives it. */
.dial {
    --deg: 0deg; --sweep: 0deg;
    display: grid; place-items: center; gap: 8px;
    width: 136px; padding: 0; border: none; background: none;
    cursor: grab; touch-action: none; user-select: none;
}
.dial:active { cursor: grabbing; }
.dial-face {
    position: relative; width: 118px; height: 118px; border-radius: 50%;
    background:
        conic-gradient(from 210deg, var(--primary) var(--sweep), transparent var(--sweep)),
        var(--cell-input-bg);
    border: 1px solid var(--outline-variant);
    transition: border-color .2s var(--ease);
}
.dial:hover .dial-face, .dial:focus-visible .dial-face { border-color: var(--primary); }
.dial-face::before {
    content: ''; position: absolute; inset: 11px; border-radius: 50%;
    background: var(--surface-container-lowest);
    border: 1px solid var(--ghost-border);
}
/* calibration ticks, struck around the bezel only — the conic ruling is
   intersected with a ring so it never spokes into the middle */
.dial-face::after {
    content: ''; position: absolute; inset: 0; border-radius: 50%;
    background-color: var(--outline);
    -webkit-mask:
        repeating-conic-gradient(from 210deg, #000 0deg 1.2deg, transparent 1.2deg 30deg),
        radial-gradient(closest-side, transparent 82%, #000 83%);
    mask:
        repeating-conic-gradient(from 210deg, #000 0deg 1.2deg, transparent 1.2deg 30deg),
        radial-gradient(closest-side, transparent 82%, #000 83%);
    -webkit-mask-composite: source-in; mask-composite: intersect;
    opacity: .6; pointer-events: none;
}
.dial-needle {
    position: absolute; left: 50%; top: 50%; width: 3px; height: 44px; border-radius: var(--r-pill);
    margin: -44px 0 0 -1.5px; background: var(--on-surface);
    transform-origin: 50% 100%; transform: rotate(var(--deg)); z-index: 2;
}
.dial-cap { position: absolute; left: 50%; top: 50%; width: 12px; height: 12px; margin: -6px 0 0 -6px; border-radius: 50%; background: var(--primary); z-index: 3; }
.dial-read { font-family: var(--font-mono); font-size: .95rem; font-weight: 600; color: var(--on-surface); font-variant-numeric: tabular-nums; }
.dial-name { font-size: .68rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--outline); }

/* Stepper — thumb-sized increments where a slider would be fiddly */
.stepper { display: inline-flex; align-items: stretch; border: 1px solid var(--outline-variant); border-radius: var(--r-md); background: var(--surface-container-lowest); overflow: hidden; }
.stepper button {
    width: 40px; border: none; cursor: pointer;
    background: var(--surface-container-low); color: var(--primary);
    font-family: var(--font-mono); font-size: 1.1rem; font-weight: 700; line-height: 1;
    transition: background .16s var(--ease), color .16s var(--ease);
}
.stepper button:hover { background: var(--primary); color: var(--on-primary); }
.stepper button:active { background: var(--primary-container); }
.stepper button:disabled { opacity: .3; cursor: not-allowed; background: var(--surface-container-low); color: var(--outline); }
.stepper button:first-child { border-right: 1px solid var(--outline-variant); }
.stepper button:last-child { border-left: 1px solid var(--outline-variant); }
.stepper output { min-width: 82px; display: grid; place-items: center; padding: 8px 6px; font-family: var(--font-mono); font-weight: 600; font-size: .95rem; color: var(--on-surface); font-variant-numeric: tabular-nums; }
.stepper output.is-live { animation: value-set .4s var(--ease-snap); }

/* Transport — play / step / reset for animated plates */
.transport { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.transport button {
    display: grid; place-items: center; width: 40px; height: 40px; border-radius: 50%;
    border: 1px solid var(--outline-variant); background: var(--surface-container-lowest);
    color: var(--on-surface-variant); cursor: pointer;
    transition: background .16s var(--ease), color .16s var(--ease), border-color .16s var(--ease);
}
.transport button:hover { background: var(--surface-container-high); color: var(--primary); border-color: var(--primary); }
.transport button:active { background: var(--primary); color: var(--on-primary); }
.transport button svg { width: 15px; height: 15px; fill: currentColor; }
.transport button.is-primary {
    width: 48px; height: 48px; border-radius: 50%; border-color: var(--primary);
    background: var(--primary); color: var(--on-primary);
}
.transport button.is-primary svg { width: 18px; height: 18px; }
.transport button.is-primary:hover { background: var(--primary-container); color: var(--on-primary); }
/* while it runs, the frame counts a slow beat */
.transport button.is-primary.is-playing { animation: run-beat 1.6s steps(1, end) infinite; }
@keyframes run-beat { 0%, 60% { box-shadow: 0 0 0 3px var(--accent-glow); } 61%, 100% { box-shadow: 0 0 0 0 transparent; } }
.transport-label { font-family: var(--font-mono); font-size: .78rem; font-weight: 600; color: var(--outline); letter-spacing: .04em; }

/* Pick-and-place — drag tiles into slots, or tap-then-tap on touch */
.tray {
    display: flex; flex-wrap: wrap; gap: 8px; padding: 12px; min-height: 64px; border-radius: var(--r-lg);
    background: var(--surface-container-low); border: 1px dashed var(--outline-variant);
}
.tile {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 9px 15px; cursor: grab; border-radius: var(--r-sm);
    background: var(--surface-container-lowest); border: 1px solid var(--outline-variant);
    color: var(--on-surface); font-size: .86rem; font-weight: 600;
    touch-action: none; user-select: none;
    transition: transform .18s var(--ease-snap), border-color .18s var(--ease), background .18s var(--ease), box-shadow .18s var(--ease);
}
.tile:hover { border-color: var(--primary); transform: translateY(-2px); box-shadow: var(--shadow-sm); }
.tile:active, .tile.is-dragging { cursor: grabbing; border-color: var(--primary); box-shadow: var(--shadow-md); z-index: 5; }
.tile.is-picked { border-color: var(--primary); box-shadow: 0 0 0 3px var(--accent-glow); }
.tile.is-placed { cursor: default; }
.tile.is-right { border-color: var(--ok); background: var(--ok-bg); color: var(--ok); }
.tile.is-wrong { border-color: var(--bad); background: var(--bad-bg); color: var(--bad); animation: strike .38s var(--ease); }
@keyframes strike { 0%,100% { transform: translateX(0); } 25% { transform: translateX(-6px); } 50% { transform: translateX(5px); } 75% { transform: translateX(-3px); } }

.slot {
    display: flex; align-items: center; justify-content: center; gap: 8px;
    min-height: 56px; padding: 10px 14px; border-radius: var(--r-md);
    border: 1px dashed var(--outline-variant); background: var(--cell-input-bg);
    color: var(--outline); font-size: .84rem; font-weight: 600; text-align: center;
    transition: border-color .2s var(--ease), background .2s var(--ease), color .2s var(--ease);
}
.slot.is-target { border-style: solid; border-color: var(--primary); background: var(--accent-soft); color: var(--accent-ink); box-shadow: inset 0 0 0 2px var(--accent-glow); }
.slot.is-filled { border-style: solid; border-color: var(--outline-variant); background: var(--surface-container-lowest); color: var(--on-surface); }
.slot.is-right { border-color: var(--ok); background: var(--ok-bg); color: var(--ok); }
.slot.is-wrong { border-color: var(--bad); background: var(--bad-bg); color: var(--bad); animation: strike .38s var(--ease); }
.slot-label { display: block; font-size: .66rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--outline); margin-bottom: 6px; }

/* Tap boards — choose-one, hotspots, build-it palettes */
.taps { display: flex; flex-wrap: wrap; gap: 10px; }
.tap {
    position: relative; overflow: hidden;
    display: inline-flex; align-items: center; gap: 9px;
    padding: 12px 18px; cursor: pointer; border-radius: var(--r-md);
    background: var(--surface-container-lowest); border: 1px solid var(--outline-variant);
    color: var(--on-surface); font-size: .88rem; font-weight: 600; text-align: left;
    transition: transform .18s var(--ease-snap), border-color .18s var(--ease), color .18s var(--ease);
}
.tap::before {
    content: ''; position: absolute; inset: 0; background: var(--accent-soft);
    transform: scaleY(0); transform-origin: bottom; transition: transform .24s var(--ease-snap); z-index: 0;
}
.tap > * { position: relative; z-index: 1; }
.tap:hover { border-color: var(--primary); transform: translateY(-2px); }
.tap:hover::before { transform: scaleY(1); }
.tap:active { transform: translateY(0); }
.tap[aria-pressed="true"], .tap.is-on { border-color: var(--primary); color: var(--accent-ink); }
.tap[aria-pressed="true"]::before, .tap.is-on::before { transform: scaleY(1); }
.tap[aria-pressed="true"]::after, .tap.is-on::after {
    content: ''; position: absolute; right: 0; top: 0; bottom: 0; width: 3px; background: var(--primary); z-index: 2;
}
.tap.is-right { border-color: var(--ok); color: var(--ok); } .tap.is-right::before { background: var(--ok-bg); transform: scaleY(1); }
.tap.is-wrong { border-color: var(--bad); color: var(--bad); animation: strike .38s var(--ease); } .tap.is-wrong::before { background: var(--bad-bg); transform: scaleY(1); }
.tap-emoji { font-size: 1.2rem; line-height: 1; }
.tap-key {
    display: grid; place-items: center; width: 22px; height: 22px; border-radius: var(--r-sm); flex-shrink: 0;
    background: var(--surface-container-high); color: var(--on-surface-variant);
    font-family: var(--font-mono); font-size: .72rem; font-weight: 700;
}

/* ── Callouts ─────────────────────────────────────────────── */
.note {
    border-left: 3px solid var(--primary);
    background: var(--surface-container-low);
    border-radius: 0 var(--r-md) var(--r-md) 0;
    padding: 16px 20px;
    color: var(--on-surface); font-size: .94rem;
}
.note strong { font-weight: 800; }
.note-ok   { border-color: var(--ok);   background: var(--ok-bg);   color: var(--ok); }
.note-warn { border-color: var(--warn); background: var(--warn-bg); color: var(--warn); }
.note-bad  { border-color: var(--bad);  background: var(--bad-bg);  color: var(--bad); }
.note-ok strong, .note-warn strong, .note-bad strong { color: inherit; }

.formula {
    font-family: var(--font-mono); font-size: 1.04rem; font-weight: 600;
    background: var(--cell-input-bg);
    border: 1px solid var(--ghost-border);
    border-radius: var(--r-md); padding: 16px 20px; text-align: center;
    color: var(--on-surface); overflow-x: auto;
    font-feature-settings: "liga" 1;
}
.formula em { color: var(--primary); font-style: normal; }

/* KaTeX typesets the equations. Its display wrapper carries its own 1em
   margins and its own colour, both of which fight the .formula box, so
   hand the box back its padding and let the maths inherit the theme. */
.formula .katex-display { margin: 0; }
.formula .katex, .katex { color: inherit; }
.katex { font-size: 1.08em; }
.formula .katex { font-size: 1.18em; }
/* Long reactions scroll inside the box rather than widening the page. */
.formula .katex-display > .katex { white-space: normal; }

/* ── Quiz — the examination sheet ─────────────────────────── */
.quiz {
    display: flex; flex-direction: column; gap: 18px;
    padding: 28px; border-radius: var(--r-lg);
    background: var(--surface-container-lowest);
    border: 1px solid var(--outline-variant);
    border-top: 3px solid var(--primary);
}
.quiz-q { font-family: var(--font-display); font-weight: 600; font-size: 1.12rem; line-height: 1.45; }
.quiz-opts { display: grid; gap: 8px; }
.quiz-opt {
    position: relative; overflow: hidden;
    display: flex; align-items: center; gap: 13px; width: 100%; text-align: left;
    padding: 14px 18px; border-radius: var(--r-md);
    border: 1px solid var(--outline-variant);
    background: var(--surface-container-lowest); color: var(--on-surface);
    font-size: .94rem; font-weight: 500; cursor: pointer;
    transition: transform .18s var(--ease-snap), border-color .18s var(--ease), background .18s var(--ease);
    animation: opt-set .34s var(--ease-snap) both;
    animation-delay: calc(var(--i, 0) * 50ms);
}
@keyframes opt-set { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }
/* a rule inks in down the left edge as you consider an option */
.quiz-opt::before {
    content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
    background: var(--primary); transform: scaleY(0); transform-origin: top;
    transition: transform .24s var(--ease-snap);
}
.quiz-opt:hover:not([disabled]) { border-color: var(--primary); transform: translateX(4px); background: var(--surface-container-low); }
.quiz-opt:hover:not([disabled])::before { transform: scaleY(1); }
.quiz-opt .key {
    position: relative; display: grid; place-items: center; flex-shrink: 0;
    width: 28px; height: 28px; border-radius: var(--r-sm);
    background: var(--surface-container-high); color: var(--on-surface-variant);
    font-family: var(--font-mono); font-size: .78rem; font-weight: 600;
    transition: background .18s var(--ease), color .18s var(--ease);
}
.quiz-opt:hover:not([disabled]) .key { background: var(--primary); color: var(--on-primary); }
/* once answered the letter is struck out and replaced by an SVG mark */
.quiz-opt.is-right .key, .quiz-opt.is-wrong .key { color: transparent; }
.quiz-opt.is-right .key::after, .quiz-opt.is-wrong .key::after {
    content: ''; position: absolute; inset: 6px;
    background-color: var(--surface-container-lowest);   /* reads on the mark in both themes */
    animation: mark-set .3s var(--ease-snap) both;
}
@keyframes mark-set { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0); } }
.quiz-opt.is-right { border-color: var(--ok); background: var(--ok-bg); color: var(--ok); }
.quiz-opt.is-right::before { background: var(--ok); transform: scaleY(1); }
.quiz-opt.is-right .key { background: var(--ok); }
.quiz-opt.is-right .key::after { -webkit-mask: var(--i-check) center/contain no-repeat; mask: var(--i-check) center/contain no-repeat; }
.quiz-opt.is-wrong { border-color: var(--bad); background: var(--bad-bg); color: var(--bad); animation: strike .38s var(--ease); }
.quiz-opt.is-wrong::before { background: var(--bad); transform: scaleY(1); }
.quiz-opt.is-wrong .key { background: var(--bad); }
.quiz-opt.is-wrong .key::after { -webkit-mask: var(--i-cross) center/contain no-repeat; mask: var(--i-cross) center/contain no-repeat; }
.quiz-opt[disabled] { cursor: default; }
.quiz-opt[disabled]:not(.is-right):not(.is-wrong) { opacity: .45; }
.quiz-why {
    font-size: .92rem; padding: 15px 18px;
    background: var(--surface-container-low); color: var(--on-surface-variant);
    border-left: 3px solid var(--primary); border-radius: 0 var(--r-md) var(--r-md) 0;
    animation: opt-set .34s var(--ease-snap) both;
}
.quiz-bar { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.quiz-dots { display: flex; gap: 5px; align-items: center; }
.quiz-dots i {
    width: 16px; height: 4px; border-radius: var(--r-pill); background: var(--outline-variant);
    transition: background .2s var(--ease), transform .2s var(--ease-snap);
}
.quiz-dots i.done { background: var(--ok); }
.quiz-dots i.miss { background: var(--bad); }
.quiz-dots i.now  { background: var(--primary); transform: scaleY(2.2); }

/* Result plate — SVG gauge, drawn by the runtime */
.quiz-score {
    display: flex; align-items: center; gap: 24px; flex-wrap: wrap;
    padding: 22px; background: var(--surface-container-low);
    border: 1px solid var(--outline-variant);
}
.quiz-score svg { width: 104px; height: 104px; flex-shrink: 0; overflow: visible; }
.quiz-score .ring-track { fill: none; stroke: var(--outline-variant); stroke-width: 6; }
.quiz-score .ring-fill {
    fill: none; stroke: var(--primary); stroke-width: 6; stroke-linecap: butt;
    transform: rotate(-90deg); transform-origin: 50% 50%;
    animation: ring-draw 1s var(--ease-snap) both;
}
@keyframes ring-draw { from { stroke-dashoffset: var(--ring-len); } }
.quiz-score .ring-ticks { stroke: var(--outline); stroke-width: 1; opacity: .5; }
.quiz-score .ring-num { font-family: var(--font-display); font-size: 26px; font-weight: 700; fill: var(--on-surface); text-anchor: middle; }
.quiz-score .ring-of { font-family: var(--font); font-size: 10px; font-weight: 700; fill: var(--outline); text-anchor: middle; letter-spacing: .12em; }
.quiz-score-copy { flex: 1; min-width: 190px; }
.quiz-score-copy b { font-family: var(--font-display); font-size: 1.25rem; display: block; margin-bottom: 4px; }
.quiz-score-copy span { color: var(--on-surface-variant); font-size: .92rem; }

/* ── Progress ─────────────────────────────────────────────── */
.progress { height: 6px; border-radius: var(--r-pill); background: var(--surface-container-high); overflow: hidden; }
.progress i { display: block; height: 100%; border-radius: var(--r-pill); background: linear-gradient(90deg, var(--primary), var(--primary-container)); transition: width .5s var(--ease); }
.read-bar {
    position: fixed; top: 0; left: 0; height: 2px; z-index: 1000;
    background: linear-gradient(90deg, var(--primary), var(--primary-container));
    width: 0; transition: width .1s linear;
}

/* ── Tables ───────────────────────────────────────────────── */
.table-wrap { overflow-x: auto; border: 1px solid var(--ghost-border); border-radius: var(--r-lg); }
table.data { width: 100%; border-collapse: collapse; font-size: .92rem; min-width: 440px; }
table.data th, table.data td { padding: 12px 16px; text-align: left; border-bottom: 1px solid var(--ghost-border); }
table.data th {
    background: var(--surface-container-low);
    font-weight: 700; font-size: .72rem; letter-spacing: .08em; text-transform: uppercase; color: var(--on-surface-variant);
}
table.data tbody tr:last-child td { border-bottom: none; }
table.data tbody tr:hover { background: var(--surface-container-low); }
table.data td.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* ── Chapter hub cards ────────────────────────────────────── */
.ch-card {
    position: relative; display: flex; flex-direction: column; gap: 14px;
    padding: 28px; overflow: hidden; border-radius: var(--r-lg);
    background: var(--surface-container-lowest);
    border: 1px solid var(--ghost-border);
    box-shadow: var(--shadow-sm);
    color: inherit; text-decoration: none;
    transition: transform .2s var(--ease), box-shadow .2s var(--ease), border-color .2s var(--ease);
}
.ch-card::before {
    content: ''; position: absolute; inset: 0 auto auto 0;
    width: 100%; height: 3px;
    background: var(--ch, var(--primary));
}
.ch-card:hover { transform: translateY(-4px); text-decoration: none; box-shadow: var(--shadow-lg); border-color: var(--ch, var(--primary)); }
.ch-card-image {
    width: calc(100% + 56px);
    max-width: none;
    height: 150px;
    margin: -28px -28px 2px;
    object-fit: cover;
    border-bottom: 1px solid var(--ghost-border);
}
.ch-card-top { display: flex; align-items: center; gap: 14px; }
.ch-emoji {
    width: 44px; height: 44px; border-radius: var(--r-md);
    display: grid; place-items: center;
    font-size: 1.4rem;
    background: color-mix(in srgb, var(--ch, var(--primary)) 12%, transparent); flex-shrink: 0;
}
.ch-no {
    font-family: var(--font-mono); font-size: .72rem; font-weight: 600;
    color: var(--ch, var(--primary)); letter-spacing: .08em;
    font-feature-settings: "liga" 1;
}
.ch-title { font-family: var(--font-display); font-weight: 700; font-size: 1.08rem; line-height: 1.26; letter-spacing: -.01em; }
.ch-blurb { font-size: .9rem; color: var(--on-surface-variant); line-height: 1.58; flex: 1; }
.ch-meta { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; font-size: .76rem; color: var(--outline); }
.ch-meta .tag {
    padding: 3px 10px; border-radius: var(--r-pill);
    font-weight: 700; font-size: .68rem; letter-spacing: .04em; text-transform: uppercase;
    background: color-mix(in srgb, var(--ch, var(--primary)) 10%, transparent); color: var(--ch, var(--primary));
}

/* ── Chapter page hero ────────────────────────────────────── */
.ch-hero {
    position: relative; overflow: hidden; padding: 64px 0 56px;
    background:
        radial-gradient(800px 350px at 15% -8%, var(--accent-glow), transparent 60%),
        radial-gradient(600px 300px at 90% 5%, color-mix(in srgb, var(--hue-cyan) 18%, transparent), transparent 58%),
        var(--surface-container-low);
    border-bottom: 1px solid var(--ghost-border);
}
.ch-hero .h1 { margin: 16px 0 18px; max-width: 18ch; }
.ch-hero .lede { max-width: 62ch; }
.ch-hero-tags { display: flex; gap: 9px; flex-wrap: wrap; margin-top: 24px; }
.ch-hero-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.05fr) minmax(320px, .95fr);
    gap: clamp(28px, 5vw, 72px);
    align-items: center;
}
.ch-hero-copy { min-width: 0; }
.ch-hero-visual {
    margin: 0;
    overflow: hidden;
    border: 1px solid rgba(255, 255, 255, .32);
    border-radius: var(--r-xl);
    background: rgba(0, 0, 0, .12);
    box-shadow: 0 24px 56px rgba(0, 0, 0, .25);
}
.ch-hero-visual img {
    display: block;
    width: 100%;
    aspect-ratio: 3 / 2;
    object-fit: cover;
}
.ch-hero-visual figcaption {
    padding: 9px 14px;
    color: rgba(255, 255, 255, .82);
    font-size: .7rem;
    line-height: 1.35;
}
@media (max-width: 820px) {
    .ch-hero-grid { grid-template-columns: 1fr; }
    .ch-hero-visual { max-width: 640px; }
}

/* ── Jump links ───────────────────────────────────────────── */
.jump { display: flex; gap: 8px; flex-wrap: wrap; }
.jump a {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 8px 16px; border-radius: var(--r-pill);
    background: var(--surface-container-lowest); color: var(--on-surface-variant);
    font-size: .82rem; font-weight: 600; text-decoration: none;
    border: 1px solid var(--ghost-border); transition: all .16s var(--ease);
}
.jump a:hover { background: var(--accent-soft); color: var(--accent-ink); border-color: var(--primary); text-decoration: none; }
.jump a.is-active { background: var(--accent-soft); color: var(--accent-ink); border-color: var(--primary); }

/* ── Footer + chapter pager ───────────────────────────────── */
.pager { display: flex; gap: 14px; justify-content: space-between; flex-wrap: wrap; padding: 48px 40px; }
.pager a {
    flex: 1 1 240px; display: flex; flex-direction: column; gap: 4px;
    padding: 20px 24px; border-radius: var(--r-lg);
    background: var(--surface-container-low);
    border: 1px solid var(--ghost-border);
    color: inherit; text-decoration: none; transition: all .18s var(--ease);
}
.pager a:hover { background: var(--accent-soft); border-color: var(--primary); text-decoration: none; }
.pager .dir { font-size: .72rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--outline); }
.pager .name { font-family: var(--font-display); font-weight: 600; font-size: 1rem; letter-spacing: -.01em; }
.pager a.next { text-align: right; align-items: flex-end; }

.foot {
    border-top: 1px solid var(--ghost-border);
    background: var(--surface-container-low);
    padding: 48px 0; color: var(--on-surface-variant); font-size: .88rem;
}
.foot-inner { display: flex; gap: 24px; justify-content: space-between; flex-wrap: wrap; align-items: center; }

/* ── Utilities ────────────────────────────────────────────── */
.row { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.row-tight { gap: 8px; }
.between { justify-content: space-between; }
.center { text-align: center; }
.wrap-center { margin-left: auto; margin-right: auto; }
.hide { display: none !important; }
.scroll-x { overflow-x: auto; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }

/* ── Animations — subtle, academic ────────────────────────── */
@keyframes pulse-soft { 0%,100% { opacity:1 } 50% { opacity:.55 } }
.pulse { animation: pulse-soft 1.8s ease-in-out infinite; }

@keyframes rise { from { opacity:0; transform: translateY(12px) } to { opacity:1; transform:none } }
.rise { animation: rise .45s var(--ease) both; }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important; animation-iteration-count: 1 !important;
        transition-duration: .001ms !important; scroll-behavior: auto !important;
    }
}

/* ── Responsive ───────────────────────────────────────────── */
@media (max-width: 720px) {
    body { font-size: 15px; }
    .section { padding: 44px 0; }
    .nav-links { display: none; }
    .card { padding: 22px; }
    .lab-stage { padding: 12px; }
    .lab-panel { padding: 18px; }
    .shell { padding: 0 24px; }
    .pager { padding: 32px 24px; }
}

@media print { .nav, .read-bar, .lab-panel, .pager { display: none !important; } }

/* ── The Margin Callout — Signature layout pattern ────────── */
/* Scholarly marginalia positioned in the far side margin */
.marginalia {
    position: relative;
}
.margin-note {
    font-family: var(--font); font-size: .72rem; font-weight: 600;
    letter-spacing: .04em; text-transform: uppercase;
    color: var(--outline);
    line-height: 1.5;
}
@media (min-width: 1200px) {
    .margin-note {
        position: absolute; right: -180px; top: 0;
        width: 160px; text-align: left;
    }
}

/* ── Diagram exhibit spacing ──────────────────────────────── */
/* Diagrams as first-class citizens with 64px margin */
.diagram-exhibit {
    margin: 64px 0;
}

/* ── 2026 visual refinement ─────────────────────────────────
   A calmer, more legible layer shared by the hub and chapters.
   The existing component API stays intact; this only improves
   hierarchy, rhythm, contrast and responsive behaviour. */
:root {
    --surface: #f7f8fc;
    --surface-container-low: #eef1f7;
    --surface-container-lowest: #ffffff;
    --surface-container-high: #e4e8f1;
    --on-surface: #121827;
    --on-surface-variant: #535d70;
    --outline: #70798b;
    --outline-variant: #d6dbe5;
    --primary: #3157d5;
    --primary-container: #2446bb;
    --accent-soft: #e9eeff;
    --accent-ink: #183582;
    --shell: 1160px;
    --shadow-sm: 0 1px 2px rgba(18, 24, 39, .04), 0 8px 24px rgba(43, 57, 95, .06);
    --shadow-md: 0 2px 4px rgba(18, 24, 39, .05), 0 16px 40px rgba(43, 57, 95, .10);
    --shadow-lg: 0 4px 8px rgba(18, 24, 39, .05), 0 24px 64px rgba(43, 57, 95, .14);
}

:root[data-theme="dark"] {
    --surface: #0f1219;
    --surface-container-low: #171b25;
    --surface-container-lowest: #1c212d;
    --surface-container-high: #272d3a;
    --on-surface: #f3f5fa;
    --on-surface-variant: #b7bfce;
    --outline: #929bad;
    --outline-variant: #343c4d;
    --primary: #86a5ff;
    --primary-container: #6689ef;
    --accent-soft: #222d50;
    --accent-ink: #dce5ff;
}

body {
    line-height: 1.65;
    background:
        radial-gradient(900px 480px at 50% -280px, var(--accent-glow), transparent 72%),
        var(--surface);
}

.shell { padding-inline: clamp(20px, 4vw, 48px); }

.nav {
    background: color-mix(in srgb, var(--surface) 88%, transparent);
    border-bottom-color: color-mix(in srgb, var(--outline-variant) 72%, transparent);
}
.nav-inner {
    height: 72px;
    padding-inline: clamp(20px, 4vw, 48px);
}
.brand-mark {
    width: 38px;
    height: 38px;
    border-radius: 11px;
    box-shadow: 0 8px 20px rgba(49, 87, 213, .20);
}
.brand-name { font-family: var(--font); font-weight: 800; letter-spacing: -.035em; }
.nav-links { gap: 6px; }
.nav-links a { padding: 9px 14px; }
.nav-links a:hover { background: var(--surface-container-lowest); }

.display {
    font-family: var(--font);
    font-size: clamp(2.8rem, 7vw, 5.2rem);
    font-weight: 800;
    line-height: .98;
    letter-spacing: -.065em;
}
.h1, .h2, .h3 {
    font-family: var(--font);
    letter-spacing: -.035em;
}
.h1 { font-size: clamp(2.2rem, 5vw, 3.7rem); line-height: 1.04; }
.h2 { font-size: clamp(1.75rem, 3.5vw, 2.65rem); line-height: 1.08; }
.h3 { line-height: 1.25; }
.lede { line-height: 1.7; }
.eyebrow { letter-spacing: .16em; }

.btn {
    border-radius: 12px;
    letter-spacing: .01em;
    text-transform: none;
    box-shadow: 0 8px 22px rgba(49, 87, 213, .16);
}
.btn::before { display: none; }
.btn:hover {
    transform: translateY(-2px);
    filter: brightness(.96);
}
.btn-ghost { background: var(--surface-container-lowest); box-shadow: var(--shadow-sm); }
.btn-ghost:hover { background: var(--surface-container-low); color: var(--on-surface); }
.btn-soft { box-shadow: none; }

.section { padding-block: clamp(64px, 8vw, 104px); }
.section + .section,
.section + .section + .section,
.section + .section + .section + .section {
    background: transparent;
}
.section-head {
    max-width: 680px;
    margin-bottom: clamp(28px, 4vw, 44px);
}
.section-head .h2 { margin: 12px 0 16px; }

.card {
    padding: 26px;
    border-color: var(--outline-variant);
    box-shadow: none;
}
.card:hover { border-color: color-mix(in srgb, var(--primary) 45%, var(--outline-variant)); }
.grid { gap: 20px; }

.ch-card {
    min-height: 236px;
    min-width: 0;
    gap: 16px;
    padding: 26px;
    border-color: var(--outline-variant);
    box-shadow: none;
}
.ch-card-image {
    width: calc(100% + 52px);
    margin: -26px -26px 2px;
}
.ch-card::before {
    display: none;
}
.ch-card:hover {
    transform: translateY(-5px);
    border-color: color-mix(in srgb, var(--ch, var(--primary)) 55%, var(--outline-variant));
    box-shadow: var(--shadow-md);
}
.ch-emoji {
    width: 48px;
    height: 48px;
    border-radius: 14px;
}
.ch-title {
    font-family: var(--font);
    font-size: 1.05rem;
    font-weight: 800;
    letter-spacing: -.025em;
    overflow-wrap: anywhere;
}
.ch-card-top > div { min-width: 0; }
.ch-blurb { line-height: 1.65; }
.ch-meta { margin-top: auto; }

.seg {
    padding: 5px;
    background: var(--surface-container-lowest);
    box-shadow: var(--shadow-sm);
}
.seg button {
    padding: 10px 16px;
    text-transform: none;
    letter-spacing: 0;
}
.seg button + button { border-left: 0; }
.seg button::after { display: none; }
.seg button[aria-pressed="true"], .seg button.is-on { box-shadow: 0 5px 14px var(--accent-glow); }

.lab { box-shadow: var(--shadow-sm); }
.lab:hover, .lab:focus-within { box-shadow: var(--shadow-md); }
.lab-foot::before { display: none; }

/* Rounded surfaces use a complete outline. A single coloured edge makes
   the curve look clipped and visually heavier on one side. */
.readout {
    border: 1px solid var(--outline-variant);
}
.readout:hover { border-color: var(--outline); }
.readout.is-hot { border-color: color-mix(in srgb, var(--bad) 60%, var(--outline-variant)); }
.readout.is-good { border-color: color-mix(in srgb, var(--ok) 60%, var(--outline-variant)); }
.readout.is-live { border-color: color-mix(in srgb, var(--primary) 60%, var(--outline-variant)); }

.note {
    border: 1px solid var(--outline-variant);
    border-radius: var(--r-md);
}
.note-ok { border-color: color-mix(in srgb, var(--ok) 55%, var(--outline-variant)); }
.note-warn { border-color: color-mix(in srgb, var(--warn) 55%, var(--outline-variant)); }
.note-bad { border-color: color-mix(in srgb, var(--bad) 55%, var(--outline-variant)); }

.quiz {
    border: 1px solid var(--outline-variant);
}
.quiz-why {
    border: 1px solid color-mix(in srgb, var(--primary) 45%, var(--outline-variant));
    border-radius: var(--r-md);
}

.ch-hero {
    padding-block: clamp(56px, 8vw, 88px);
    border-bottom-color: var(--outline-variant);
}

.pager a { background: var(--surface-container-lowest); border-color: var(--outline-variant); }
.foot { background: var(--surface-container-low); border-top-color: var(--outline-variant); }

/* Hub-specific composition */
.hub-hero {
    min-height: 690px;
    display: grid;
    place-items: center;
    padding-block: clamp(76px, 10vw, 126px) !important;
    isolation: isolate;
}
.hub-hero::before {
    content: "";
    position: absolute;
    z-index: -1;
    inset: 34px max(18px, calc((100vw - 1400px) / 2));
    border: 1px solid color-mix(in srgb, var(--outline-variant) 72%, transparent);
    border-radius: 36px;
    background:
        linear-gradient(135deg, color-mix(in srgb, var(--surface-container-lowest) 86%, transparent), color-mix(in srgb, var(--surface) 70%, transparent));
    box-shadow: var(--shadow-sm);
}
.hub-hero .display { max-width: 13ch !important; margin-block: 24px 22px !important; }
.hub-hero .lede { max-width: 58ch !important; font-size: clamp(1.05rem, 1.8vw, 1.22rem); }
.hub-hero .row { gap: 14px; }
.stat-strip {
    max-width: 850px !important;
    gap: 0 !important;
    margin-top: 58px !important;
    border-top: 1px solid var(--outline-variant);
}
.stat {
    border: 0 !important;
    border-radius: 0 !important;
    background: transparent !important;
    box-shadow: none !important;
    padding: 22px 18px 0 !important;
}
.stat + .stat { border-left: 1px solid var(--outline-variant) !important; }
.stat b { font-size: 2rem !important; }
.stat span { color: var(--on-surface-variant) !important; letter-spacing: .08em; }
.filter-bar {
    position: sticky;
    top: 82px;
    z-index: 20;
    padding-block: 10px;
    background: color-mix(in srgb, var(--surface) 92%, transparent);
    backdrop-filter: blur(12px);
}

/* Keep long titles from widening grid tracks and pushing cards off-screen. */
#chapter-grid {
    width: 100%;
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

@media (max-width: 1020px) {
    #chapter-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 720px) {
    .nav-inner { height: 64px; }
    .brand-name { font-size: .98rem; }
    .display { font-size: clamp(2.55rem, 14vw, 3.7rem); }
    .hub-hero { min-height: auto; padding-block: 70px !important; }
    .hub-hero::before { inset: 16px 10px; border-radius: 24px; }
    .hub-hero .row { align-items: stretch !important; }
    .hub-hero .btn { flex: 1 1 100%; }
    .stat-strip { grid-template-columns: repeat(2, 1fr) !important; margin-top: 42px !important; }
    .stat { padding-top: 18px !important; }
    .stat:nth-child(3) { border-left: 0 !important; }
    .stat:nth-child(n + 3) { border-top: 1px solid var(--outline-variant) !important; }
    .filter-bar {
        top: 64px;
        margin-inline: -20px;
        padding-inline: 20px;
        overflow-x: auto;
        flex-wrap: nowrap !important;
        scrollbar-width: none;
    }
    .filter-bar::-webkit-scrollbar { display: none; }
    .filter-bar .seg { flex-wrap: nowrap; }
    .ch-card { min-height: 0; padding: 23px; }
    #chapter-grid { grid-template-columns: minmax(0, 1fr); }
    .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
    .foot-inner { align-items: flex-start; }
}

/* ── Interactive system refinement ────────────────────────── */
/* Focus is communicated by a complete outline, never an animated top rule. */
.lab::after { display: none; }
.lab:hover,
.lab:focus-within {
    border-color: color-mix(in srgb, var(--primary) 52%, var(--outline-variant));
}

.lab-head {
    min-height: 72px;
    padding: 16px 20px;
    background: var(--surface-container-lowest);
    border-bottom-color: var(--outline-variant);
}
.lab-head .h3 {
    font-family: var(--font);
    font-size: 1.05rem;
    font-weight: 800;
    letter-spacing: -.025em;
}
.lab-body { background: var(--surface-container-lowest); }
.lab-stage { background-color: var(--surface-container-low); }
.lab-panel {
    padding: 26px;
    background: color-mix(in srgb, var(--surface-container-low) 78%, var(--surface-container-lowest));
}

.ctrl { gap: 10px; }
.ctrl-label {
    font-size: .78rem;
    font-weight: 750;
    letter-spacing: .035em;
}
.ctrl-value {
    border: 1px solid var(--outline-variant);
    border-radius: var(--r-pill);
    background: var(--surface-container-lowest);
    padding: 4px 10px;
}
.ctrl:focus-within .ctrl-value,
.ctrl-value.is-live {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--accent-glow);
}

input[type="range"]::-webkit-slider-runnable-track {
    height: 8px;
    border: 0;
    box-shadow: inset 0 0 0 1px var(--outline-variant);
}
input[type="range"]::-webkit-slider-thumb {
    width: 20px;
    height: 20px;
    margin-top: -6px;
    border: 4px solid var(--surface-container-lowest);
    box-shadow: 0 1px 4px rgba(18, 24, 39, .22), 0 0 0 1px var(--primary);
}
.ctrl:has(> input[type="range"])::after,
.ctrl-rail::after { display: none; }

.seg {
    border-color: var(--outline-variant);
    border-radius: 14px;
    gap: 4px;
}
.seg button {
    min-height: 40px;
    border-radius: 10px;
    padding-inline: 15px;
}
.seg button:hover { background: var(--accent-soft); color: var(--accent-ink); }

.stepper {
    min-height: 44px;
    border-radius: 12px;
    box-shadow: var(--shadow-sm);
}
.stepper button {
    min-width: 44px;
    border: 0 !important;
    background: var(--surface-container-lowest);
    font-size: 1.05rem;
}
.stepper button + * { border-left: 1px solid var(--outline-variant) !important; }
.stepper output + button { border-left: 1px solid var(--outline-variant) !important; }

.tap,
.tile {
    min-height: 44px;
    border-color: var(--outline-variant);
    border-radius: 12px;
    background: var(--surface-container-lowest);
    box-shadow: 0 1px 2px rgba(18, 24, 39, .04);
}
.tap::before { display: none; }
.tap:hover,
.tile:hover {
    background: var(--accent-soft);
    border-color: color-mix(in srgb, var(--primary) 55%, var(--outline-variant));
    color: var(--accent-ink);
    box-shadow: var(--shadow-sm);
}
.tap[aria-pressed="true"],
.tap.is-on,
.tile.is-picked {
    background: var(--accent-soft);
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--accent-glow);
}

.switch-track {
    width: 48px;
    height: 26px;
    background: var(--surface-container-high);
}
.switch-track::after { width: 20px; height: 20px; }
.switch input:checked + .switch-track::after,
.switch input:checked + .switch-track::before { transform: translateX(22px); }

.transport button,
.icon-btn {
    box-shadow: 0 1px 2px rgba(18, 24, 39, .05);
}
.transport button:hover,
.icon-btn:hover { box-shadow: var(--shadow-sm); }

/* Chemistry labs share a subtle material identity without colouring
   every surface purple. */
body[data-strand="chemistry"] .lab-head {
    background:
        linear-gradient(90deg, color-mix(in srgb, var(--accent-soft) 92%, transparent), var(--surface-container-lowest) 64%),
        url("chemistry-pattern.png") center 42% / 560px auto,
        var(--surface-container-lowest);
    background-blend-mode: normal, multiply, normal;
}
body[data-strand="chemistry"] .lab-stage::before { opacity: .48; }
body[data-strand="chemistry"] .formula {
    border-color: color-mix(in srgb, var(--accent) 28%, var(--outline-variant));
    background: color-mix(in srgb, var(--accent-soft) 38%, var(--surface-container-lowest));
}

/* Chemistry control panels need enough scale to balance their canvases. */
body[data-strand="chemistry"] .lab:not(.lab-full) .lab-body {
    grid-template-columns: minmax(0, 1.35fr) minmax(390px, 1fr);
}
body[data-strand="chemistry"] .lab-panel {
    gap: 22px;
    padding: 30px;
}
body[data-strand="chemistry"] .lab-panel .ctrl { gap: 12px; }
body[data-strand="chemistry"] .lab-panel .ctrl-label {
    font-size: .88rem;
}
body[data-strand="chemistry"] .lab-panel .btn-sm {
    min-width: 52px;
    min-height: 48px;
    padding: 10px 16px;
    font-size: .95rem;
}
body[data-strand="chemistry"] .lab-panel .ctrl-value {
    min-width: 38px;
    min-height: 34px;
    display: inline-grid;
    place-items: center;
    font-size: .9rem;
}
body[data-strand="chemistry"] .lab-panel .readouts {
    grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
    gap: 12px;
}
body[data-strand="chemistry"] .lab-panel .readout {
    min-height: 92px;
    padding: 16px 18px;
}
body[data-strand="chemistry"] .lab-panel .readout dt { font-size: .74rem; }
body[data-strand="chemistry"] .lab-panel .readout dd {
    margin-top: 8px;
    font-size: 1.28rem;
}

/* Soft actions stay legible on hover. */
.btn-soft:hover {
    background: var(--primary);
    border-color: var(--primary);
    color: var(--on-primary);
    filter: none;
}
#rx-play {
    min-width: 104px;
    min-height: 46px;
    font-size: .9rem;
}
#rx-progress-v {
    max-width: min(280px, 38vw);
    min-height: 42px;
    padding: 8px 16px;
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--accent-ink);
    background: var(--accent-soft);
    border-color: color-mix(in srgb, var(--accent) 40%, var(--outline-variant));
    box-shadow: none;
    white-space: nowrap;
}

/* Keep supporting information visually attached to the experiment. */
.lab-full > .legend {
    min-height: 58px;
    padding: 14px 22px !important;
    background: var(--surface-container-lowest);
    border-top: 1px solid var(--outline-variant);
}
.lab-full > .legend i {
    width: 18px;
    height: 4px;
    border-radius: var(--r-pill);
}
.lab-foot {
    padding: 16px 22px;
    background: color-mix(in srgb, var(--accent-soft) 44%, var(--surface-container-lowest));
    color: var(--on-surface-variant);
    font-size: .88rem;
    line-height: 1.65;
}
.lab-foot strong { font-weight: 800; }

@media (max-width: 720px) {
    .lab-head { align-items: flex-start; }
    .lab-head .seg {
        width: 100%;
        overflow-x: auto;
        flex-wrap: nowrap;
        justify-content: flex-start;
    }
    .lab-panel { padding: 20px; }
}

@media (max-width: 1000px) {
    body[data-strand="chemistry"] .lab:not(.lab-full) .lab-body {
        grid-template-columns: 1fr;
    }
}

/* ── Instrument readout tiles ───────────────────────────────
   Readouts should feel like live measurements, not plain text cards.
   Accent colours rotate without implying a good/bad state. */
.readouts {
    gap: 12px;
}
.readout {
    --tile-accent: var(--primary);
    isolation: isolate;
    min-height: 112px;
    padding: 18px 18px 16px;
    border: 1px solid color-mix(in srgb, var(--tile-accent) 24%, var(--outline-variant));
    border-radius: 16px;
    background:
        linear-gradient(145deg,
            color-mix(in srgb, var(--tile-accent) 9%, var(--surface-container-lowest)),
            var(--surface-container-lowest) 58%);
    box-shadow:
        0 1px 2px rgba(18, 24, 39, .04),
        0 10px 24px color-mix(in srgb, var(--tile-accent) 7%, transparent);
    transition:
        transform .2s var(--ease-snap),
        border-color .2s var(--ease),
        box-shadow .2s var(--ease),
        background .2s var(--ease);
}
.readout:nth-child(6n + 1) { --tile-accent: var(--dia-1); }
.readout:nth-child(6n + 2) { --tile-accent: var(--dia-4); }
.readout:nth-child(6n + 3) { --tile-accent: var(--dia-3); }
.readout:nth-child(6n + 4) { --tile-accent: var(--dia-5); }
.readout:nth-child(6n + 5) { --tile-accent: var(--dia-2); }
.readout:nth-child(6n + 6) { --tile-accent: var(--dia-6); }

.readout::before {
    content: "";
    position: absolute;
    z-index: -1;
    top: -24px;
    right: -20px;
    width: 88px;
    height: 88px;
    border-radius: 50%;
    background: color-mix(in srgb, var(--tile-accent) 13%, transparent);
    box-shadow: 0 0 0 12px color-mix(in srgb, var(--tile-accent) 5%, transparent);
    transition: transform .28s var(--ease-snap);
}
.readout::after {
    content: "";
    position: absolute;
    right: 16px;
    bottom: 14px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--tile-accent);
    box-shadow: 0 0 0 5px color-mix(in srgb, var(--tile-accent) 12%, transparent);
    opacity: .8;
}
.readout:hover {
    transform: translateY(-3px);
    border-color: color-mix(in srgb, var(--tile-accent) 58%, var(--outline-variant));
    background:
        linear-gradient(145deg,
            color-mix(in srgb, var(--tile-accent) 14%, var(--surface-container-lowest)),
            var(--surface-container-lowest) 62%);
    box-shadow:
        0 2px 5px rgba(18, 24, 39, .06),
        0 16px 34px color-mix(in srgb, var(--tile-accent) 13%, transparent);
}
.readout:hover::before { transform: scale(1.14); }
.readout dt {
    position: relative;
    z-index: 1;
    max-width: calc(100% - 38px);
    color: color-mix(in srgb, var(--tile-accent) 62%, var(--on-surface-variant));
    font-size: .7rem;
    font-weight: 800;
    letter-spacing: .11em;
}
.readout dd {
    position: relative;
    z-index: 1;
    max-width: calc(100% - 16px);
    margin-top: 12px;
    color: var(--on-surface);
    font-family: var(--font);
    font-size: clamp(1.08rem, 1.6vw, 1.35rem);
    font-weight: 750;
    line-height: 1.3;
    letter-spacing: -.025em;
    overflow-wrap: anywhere;
}
.readout dd small { letter-spacing: 0; }

.readout.is-good {
    --tile-accent: var(--ok);
    border-color: color-mix(in srgb, var(--ok) 55%, var(--outline-variant));
    background: linear-gradient(145deg, var(--ok-bg), var(--surface-container-lowest) 70%);
}
.readout.is-hot {
    --tile-accent: var(--bad);
    border-color: color-mix(in srgb, var(--bad) 55%, var(--outline-variant));
    background: linear-gradient(145deg, var(--bad-bg), var(--surface-container-lowest) 70%);
}
.readout.is-live {
    border-color: color-mix(in srgb, var(--tile-accent) 65%, var(--outline-variant));
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--tile-accent) 10%, transparent);
}

@media (max-width: 720px) {
    .readouts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .readout { min-height: 104px; padding: 16px; }
}

@media (max-width: 420px) {
    .readouts { grid-template-columns: minmax(0, 1fr); }
    /* Chemistry's wider, more specific dashboard rule otherwise wins here,
       squeezing measurement words into letter-by-letter columns on phones. */
    body[data-strand="chemistry"] .lab-panel .readouts { grid-template-columns: minmax(0, 1fr); }
}

/* Semantic readings: the visual treatment now reflects the data itself. */
.readout.has-reading-color {
    --tile-accent: var(--reading-color);
    background:
        linear-gradient(135deg,
            color-mix(in srgb, var(--reading-color) 22%, var(--surface-container-lowest)),
            color-mix(in srgb, var(--reading-color) 7%, var(--surface-container-lowest)) 68%);
    border-color: color-mix(in srgb, var(--reading-color) 48%, var(--outline-variant));
}
.readout.has-reading-color dd {
    display: flex;
    align-items: center;
    gap: 11px;
}
.readout.has-reading-color dd::after {
    content: "";
    order: -1;
    width: 24px;
    height: 24px;
    flex: 0 0 24px;
    border-radius: 50%;
    background: var(--reading-color);
    border: 3px solid color-mix(in srgb, var(--reading-color) 24%, var(--surface-container-lowest));
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--reading-color) 54%, var(--outline-variant));
}
.readout.is-colourless {
    --tile-accent: var(--outline);
    background:
        linear-gradient(135deg, color-mix(in srgb, var(--surface-container-high) 52%, transparent), transparent 55%),
        var(--surface-container-lowest);
    border-color: var(--outline-variant);
}
.readout.is-colourless dd::after {
    background:
        linear-gradient(45deg, var(--surface-container-high) 25%, transparent 25% 75%, var(--surface-container-high) 75%),
        linear-gradient(45deg, var(--surface-container-high) 25%, var(--surface-container-lowest) 25% 75%, var(--surface-container-high) 75%);
    background-position: 0 0, 4px 4px;
    background-size: 8px 8px;
    border-color: var(--surface-container-lowest);
}

.readout.has-meter dd {
    padding-bottom: 17px;
}
.readout.has-meter dd::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: 1px;
    width: var(--meter-width);
    height: 7px;
    border-radius: var(--r-pill);
    background: linear-gradient(90deg,
        color-mix(in srgb, var(--tile-accent) 72%, var(--primary)),
        var(--tile-accent));
    box-shadow:
        0 0 0 1px color-mix(in srgb, var(--tile-accent) 18%, var(--outline-variant)),
        0 3px 8px color-mix(in srgb, var(--tile-accent) 18%, transparent);
    transition: width .38s var(--ease-snap);
}
.readout.has-meter dd::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 1px;
    height: 7px;
    border-radius: var(--r-pill);
    background: var(--surface-container-high);
}

/* Equation balancer: molecule controls and a bookkeeping-style atom ledger. */
.bal-controls {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 14px;
    margin-top: 8px;
}
.bal-controls .ctrl {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 12px;
    min-height: 106px;
    padding: 12px;
    border: 1px solid var(--outline-variant);
    border-radius: 18px;
    background: color-mix(in srgb, var(--surface) 86%, var(--primary-container));
}
.bal-species {
    min-width: 0;
    display: grid;
    justify-items: center;
    gap: 3px;
}
.bal-molecule {
    width: 100%;
    height: 58px;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: visible;
}
.bal-copy {
    width: min(68px, 82%);
    height: 58px;
    flex: 0 1 68px;
    filter: drop-shadow(0 3px 3px rgba(15, 23, 42, .12));
    transition: transform .24s var(--ease-snap);
}
.bal-copy + .bal-copy { margin-left: -22px; }
.bal-copy:nth-child(2) { transform: translateY(-4px); }
.bal-copy:nth-child(3) { transform: translateY(4px); }
.bal-copy:nth-child(4) { transform: translateY(-2px); }
.bal-molecule svg { width: 100%; height: 100%; overflow: visible; }
.bal-copy-more {
    z-index: 2;
    display: grid;
    place-items: center;
    min-width: 30px;
    height: 30px;
    margin-left: -12px;
    border: 2px solid var(--surface);
    border-radius: 50%;
    background: var(--on-surface);
    color: var(--surface);
    font-family: var(--font-mono);
    font-size: .67rem;
    font-weight: 800;
}
.bal-molecule .bond {
    stroke: var(--outline);
    stroke-width: 3.5;
    stroke-linecap: round;
}
.bal-molecule .atom {
    stroke: color-mix(in srgb, var(--surface) 70%, transparent);
    stroke-width: 1.5;
}
.bal-molecule text {
    fill: var(--on-accent);
    font-family: var(--font);
    font-size: 11px;
    font-weight: 800;
    text-anchor: middle;
    dominant-baseline: central;
}
.bal-formula {
    font-family: var(--font-mono);
    font-size: .78rem;
    color: var(--on-surface-variant);
}
.bal-stepper {
    display: grid;
    justify-items: center;
    gap: 7px;
}
.bal-stepper-label {
    font-size: .57rem;
    font-weight: 800;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--outline);
}
.bal-stepper-buttons {
    display: grid;
    grid-template-columns: 32px 34px 32px;
    align-items: center;
    border: 1px solid var(--outline-variant);
    border-radius: 999px;
    overflow: hidden;
    background: var(--surface);
    box-shadow: 0 4px 12px rgba(15, 23, 42, .09);
}
.bal-stepper-buttons button {
    width: 32px;
    height: 38px;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--primary);
    font-size: 1.15rem;
    font-weight: 800;
    cursor: pointer;
}
.bal-stepper-buttons button:hover { background: var(--primary-container); }
.bal-stepper-buttons button:focus-visible {
    outline: 3px solid color-mix(in srgb, var(--primary) 35%, transparent);
    outline-offset: -3px;
}
.bal-stepper-buttons output {
    display: grid;
    place-items: center;
    height: 38px;
    border-inline: 1px solid var(--outline-variant);
    color: var(--on-surface);
    font-family: var(--font-mono);
    font-size: 1rem;
    font-weight: 800;
}
.bal-equation-preview {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    align-items: center;
    gap: 10px;
    margin-top: 16px;
    padding: 12px 10px;
    border-radius: 14px;
    background:
        linear-gradient(90deg,
            color-mix(in srgb, var(--dia-1) 9%, transparent) 0 46%,
            transparent 46% 54%,
            color-mix(in srgb, var(--dia-5) 9%, transparent) 54% 100%),
        var(--surface-container-lowest);
    box-shadow: inset 0 0 0 1px var(--outline-variant);
}
.bal-equation-side {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-width: 0;
}
.bal-equation-term {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    min-width: 0;
    font-family: var(--font-mono);
    font-size: .76rem;
    font-weight: 700;
}
.bal-equation-term b {
    display: grid;
    place-items: center;
    width: 24px;
    height: 24px;
    flex: 0 0 24px;
    border-radius: 8px;
    background: var(--primary);
    color: var(--on-primary);
    font-size: .72rem;
}
.bal-equation-term.product b { background: var(--tertiary-container); }
.bal-equation-plus {
    color: var(--outline);
    font-weight: 900;
}
.bal-equation-arrow {
    color: var(--on-surface);
    font-size: 1.5rem;
    font-weight: 900;
}
.bal-ledger {
    margin-top: 16px;
    border-block: 1px solid var(--outline-variant);
}
.bal-ledger-head,
.bal-ledger-row {
    display: grid;
    grid-template-columns: minmax(62px, .75fr) minmax(88px, 1fr) 34px minmax(88px, 1fr);
    align-items: center;
    min-height: 54px;
    padding-inline: 10px;
}
.bal-ledger-head {
    min-height: 32px;
    color: var(--outline);
    font-size: .62rem;
    font-weight: 800;
    letter-spacing: .08em;
    text-transform: uppercase;
}
.bal-ledger-head span:not(:first-child),
.bal-ledger-count,
.bal-ledger-mark { text-align: center; }
.bal-ledger-row + .bal-ledger-row { border-top: 1px dashed var(--outline-variant); }
.bal-ledger-element {
    display: flex;
    align-items: center;
    gap: 8px;
    font-weight: 800;
}
.bal-ledger-dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: var(--element-color);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--element-color) 14%, transparent);
}
.bal-ledger-count {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 24px;
    align-items: center;
    gap: 5px;
    font-family: var(--font-mono);
    font-weight: 800;
}
.bal-ledger-count b { font-size: .92rem; }
.bal-atom-dots {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 3px;
    max-width: 54px;
    margin-inline: auto;
}
.bal-atom-dots i {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--element-color);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--element-color) 34%, transparent);
}
.bal-atom-dots em {
    color: var(--outline);
    font-size: .65rem;
    font-style: normal;
    font-weight: 900;
    line-height: 8px;
}
.bal-ledger-mark {
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--bad-bg);
    font-size: .95rem;
    font-weight: 900;
    color: var(--bad);
}
.bal-ledger-row.is-good .bal-ledger-mark {
    color: var(--ok);
    background: var(--ok-bg);
}

@media (max-width: 720px) {
    .bal-controls { grid-template-columns: minmax(0, 1fr); }
    .bal-controls .ctrl { min-height: 96px; }
    .bal-equation-preview { gap: 5px; padding-inline: 6px; }
    .bal-equation-side { gap: 3px; }
    .bal-equation-term { gap: 2px; font-size: .68rem; }
    .bal-equation-term b { width: 20px; height: 20px; flex-basis: 20px; }
    .bal-ledger-head,
    .bal-ledger-row {
        grid-template-columns: 58px minmax(76px, 1fr) 30px minmax(76px, 1fr);
        padding-inline: 4px;
    }
}

/* ══════════════════════════════════════════════════════════
   PHASE 1 RESKIN — "Saturday-Morning Laboratory"
   Sticker shapes, candy gradients, spring motion. Appended as a
   final override layer — same pattern as the "2026 visual
   refinement" block above — so it wins the cascade without
   touching any earlier rule, class name or token. Every colour
   here is a token or color-mix() of one, so both themes and all
   four strands stay first-class.
   ══════════════════════════════════════════════════════════ */

/* ── Typography — bigger, tighter, Baloo 2 ───────────────────
   The font itself now comes from --font-display (see :root).
   These sizes replace the Manrope-flavoured display sizing the
   2026 refinement layer set, so headlines actually use the
   rounded face again. */
.display {
    font-family: var(--font-display);
    font-weight: 800;
    font-size: clamp(3rem, 7.5vw, 5.6rem);
    line-height: .96;
    letter-spacing: -.02em;
}
.h1 {
    font-family: var(--font-display);
    font-weight: 800;
    font-size: clamp(2.3rem, 5.2vw, 3.9rem);
    line-height: 1.03;
    letter-spacing: -.015em;
}
.h2 {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: clamp(1.8rem, 3.6vw, 2.75rem);
    line-height: 1.08;
    letter-spacing: -.01em;
}

/* ── Buttons — pill-shaped, ink-wipe restored ────────────────
   The 2026 layer rounded these to 12px and switched off the
   hover ink-wipe. Bring the wipe back (it's still the same
   mechanic, just friendlier) and make them full pills. */
.btn {
    border-radius: var(--r-pill);
}
.btn::before {
    display: block;
}

/* ── Cards & labs — sticker energy ───────────────────────────
   2px accent-tinted border + a small hard offset shadow that
   compresses as the card is "pressed" on hover, instead of a
   diffuse elevation shadow. */
.card {
    border: 2px solid color-mix(in srgb, var(--accent) 38%, var(--outline-variant));
    box-shadow: 4px 4px 0 var(--accent-glow);
    transition: transform .16s var(--ease-snap), box-shadow .16s var(--ease-snap), border-color .2s var(--ease);
}
.card:hover {
    transform: translate(3px, 3px);
    box-shadow: 1px 1px 0 var(--accent-glow);
    border-color: var(--accent);
}

.lab {
    border: 2px solid color-mix(in srgb, var(--accent) 32%, var(--outline-variant));
    box-shadow: 4px 4px 0 var(--accent-glow);
    transition: transform .18s var(--ease-snap), box-shadow .18s var(--ease-snap), border-color .2s var(--ease);
}
.lab:hover, .lab:focus-within {
    transform: translate(3px, 3px);
    box-shadow: 1px 1px 0 var(--accent-glow);
    border-color: var(--accent);
}
/* note: .lab keeps its own overflow:hidden (for the rounded plate
   corners) — that clips descendant content, not the box's own
   box-shadow, so the offset shadow above still renders in full */

/* ── Chapter hub cards — the emoji becomes a real sticker ────
   Bigger, tilted, tinted with the card's own --ch colour;
   straightens and pops on hover. */
.ch-emoji {
    width: 56px; height: 56px;
    font-size: 1.9rem;
    border-radius: 16px;
    background: color-mix(in srgb, var(--ch, var(--accent)) 22%, var(--surface-container-lowest));
    box-shadow:
        0 0 0 2px color-mix(in srgb, var(--ch, var(--accent)) 48%, transparent),
        3px 3px 0 color-mix(in srgb, var(--ch, var(--accent)) 32%, transparent);
    transform: rotate(-7deg);
    transition: transform .3s var(--ease-pop), box-shadow .3s var(--ease);
}
.ch-card:hover .ch-emoji {
    transform: rotate(0deg) scale(1.1);
    box-shadow:
        0 0 0 2px color-mix(in srgb, var(--ch, var(--accent)) 65%, transparent),
        4px 4px 0 color-mix(in srgb, var(--ch, var(--accent)) 40%, transparent);
}

/* ── Chips, progress, quiz gauge — carry the strand gradient ─ */
.chip:not(.chip-outline) {
    background: var(--accent-grad);
    color: #fff;
}
.progress i, .read-bar {
    background: var(--accent-grad);
}
.quiz { border-top: 3px solid var(--accent); }
.quiz-score .ring-fill {
    stroke: var(--accent);
    filter: drop-shadow(0 0 6px var(--accent-glow));
}
.quiz-dots i.now { background: var(--accent); }

/* ── Section backgrounds — strand-tinted washes + doodle layer
   instead of flat grey alternation. The doodle is one repeating
   SVG mask (flask · bolt · atom) tinted by background-color, so
   it re-inks per theme exactly like the existing grid/ruler icons. ─ */
.section {
    position: relative;
    isolation: isolate;
}
.section::before {
    content: '';
    position: absolute; inset: 0; z-index: -1; pointer-events: none;
    background-color: var(--accent);
    opacity: .05;
    -webkit-mask-image: var(--i-doodle); mask-image: var(--i-doodle);
    -webkit-mask-size: 130px 130px; mask-size: 130px 130px;
    -webkit-mask-repeat: repeat; mask-repeat: repeat;
}
:root[data-theme="dark"] .section::before { opacity: .07; }
.section + .section {
    background: linear-gradient(180deg, color-mix(in srgb, var(--accent-soft) 75%, transparent), transparent 70%);
}
.section + .section + .section {
    background: transparent;
}
.section + .section + .section + .section {
    background: linear-gradient(180deg, color-mix(in srgb, var(--accent-soft) 55%, transparent), transparent 65%);
}

/* ── Chapter hero — full-bleed strand gradient ───────────────
   --accent-grad stays vivid/mid-tone in both themes (see the
   token definitions), so white foreground text works here
   without a light/dark split. A doodle layer and one big
   floating strand emblem (flask/leaf/atom/globe) sit behind the
   copy; the emblem drifts gently unless reduced-motion is set. */
.ch-hero {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    background: var(--accent-grad);
    color: #fff;
    border-bottom: none;
}
.ch-hero > .shell { position: relative; z-index: 1; }
.ch-hero .chip:not(.chip-outline) { background: rgba(255, 255, 255, .22); color: #fff; }
.ch-hero .chip-outline { border-color: rgba(255, 255, 255, .5); color: rgba(255, 255, 255, .95); }
.ch-hero .lede { color: rgba(255, 255, 255, .92); }
.ch-hero::before {
    content: '';
    position: absolute; inset: 0; z-index: 0; pointer-events: none;
    background-color: #fff;
    opacity: .1;
    -webkit-mask-image: var(--i-doodle); mask-image: var(--i-doodle);
    -webkit-mask-size: 140px 140px; mask-size: 140px 140px;
    -webkit-mask-repeat: repeat; mask-repeat: repeat;
}
.ch-hero::after {
    content: '';
    position: absolute; z-index: 0; pointer-events: none;
    top: 6%; right: 3%;
    width: clamp(120px, 20vw, 230px); height: clamp(120px, 20vw, 230px);
    background-color: rgba(255, 255, 255, .22);
    -webkit-mask: var(--i-doodle-emblem, var(--i-atom)) center / contain no-repeat;
    mask: var(--i-doodle-emblem, var(--i-atom)) center / contain no-repeat;
    animation: hero-float 6.5s var(--ease) infinite;
}
body[data-strand="chemistry"]   .ch-hero::after { --i-doodle-emblem: var(--i-flask); }
body[data-strand="biology"]     .ch-hero::after { --i-doodle-emblem: var(--i-leaf); }
body[data-strand="physics"]     .ch-hero::after { --i-doodle-emblem: var(--i-atom); }
body[data-strand="environment"] .ch-hero::after { --i-doodle-emblem: var(--i-globe); }
@keyframes hero-float {
    0%, 100% { transform: translateY(0) rotate(-4deg); }
    50%      { transform: translateY(-14px) rotate(3deg); }
}
@media (max-width: 720px) {
    .ch-hero::after { display: none; } /* keeps small screens readable, not cluttered */
}

/* ── Jump bar — chunky numbered "level strip" ────────────────
   Pure-CSS counters number each pill; :has() (already used
   elsewhere in this file, e.g. .ctrl:has(> input[type=range]))
   marks every pill before the active one as "passed" so the
   strip fills in as you scroll, with no app.js changes needed. */
.jump {
    counter-reset: level;
    gap: 10px;
}
.jump a {
    counter-increment: level;
    position: relative;
    padding: 8px 18px 8px 40px;
    border-width: 2px;
    font-weight: 800;
}
.jump a::before {
    content: counter(level, decimal-leading-zero);
    position: absolute; left: 6px; top: 50%; translate: 0 -50%;
    display: grid; place-items: center;
    width: 22px; height: 22px; border-radius: 50%;
    background: var(--accent-soft); color: var(--accent-ink);
    font-family: var(--font-mono); font-size: .64rem; font-weight: 700;
    transition: background .18s var(--ease), color .18s var(--ease);
}
.jump a.is-active,
.jump a:has(~ a.is-active) {
    background: color-mix(in srgb, var(--accent-soft) 65%, transparent);
    border-color: color-mix(in srgb, var(--accent) 45%, var(--outline-variant));
}
.jump a.is-active::before,
.jump a:has(~ a.is-active)::before {
    background: var(--accent-grad);
    color: #fff;
}
.ch-hero .jump a {
    background: rgba(255, 255, 255, .16);
    color: rgba(255, 255, 255, .95);
    border-color: rgba(255, 255, 255, .3);
}
.ch-hero .jump a::before { background: rgba(255, 255, 255, .3); color: #fff; }
.ch-hero .jump a:hover,
.ch-hero .jump a.is-active,
.ch-hero .jump a:has(~ a.is-active) {
    background: #fff; color: var(--accent-ink); border-color: #fff;
}
.ch-hero .jump a.is-active::before,
.ch-hero .jump a:has(~ a.is-active)::before { background: var(--accent-ink); color: #fff; }

/* ── Motion — spring pop on reveal ────────────────────────────
   app.js adds .rise to every [data-reveal] item once it enters
   view; upgrade the keyframe itself from a fade to a small
   overshooting pop, no JS change required. */
@keyframes rise {
    from { opacity: 0; transform: translateY(14px) scale(.96); }
    60%  { transform: translateY(-2px) scale(1.015); }
    to   { opacity: 1; transform: none; }
}
.rise { animation: rise .5s var(--ease-pop) both; }

/* ── Motion — quiz & tap/tile/slot right/wrong states ─────────
   Correct = a quick spring pop. Wrong keeps the existing shake
   and adds a colour flash so the miss actually registers. */
@keyframes mark-pop {
    0%   { transform: scale(1); }
    45%  { transform: scale(1.045); }
    100% { transform: scale(1); }
}
@keyframes flash-good {
    0%, 100% { background-color: var(--ok-bg); }
    35%      { background-color: color-mix(in srgb, var(--ok) 30%, var(--ok-bg)); }
}
@keyframes flash-bad {
    0%, 100% { background-color: var(--bad-bg); }
    35%      { background-color: color-mix(in srgb, var(--bad) 35%, var(--bad-bg)); }
}
.quiz-opt.is-right,
.tap.is-right, .tile.is-right, .slot.is-right {
    animation: mark-pop .42s var(--ease-pop) both, flash-good .5s var(--ease) both;
}
.quiz-opt.is-wrong,
.tap.is-wrong, .tile.is-wrong, .slot.is-wrong {
    animation: strike .38s var(--ease) both, flash-bad .5s var(--ease) both;
}

/* ── Reduced motion ───────────────────────────────────────────
   The single global block further up this file (universal
   selector, !important animation/transition durations) already
   flattens every animation added here — hero-float, rise,
   mark-pop, flash-good/bad, and the sticker hover transforms —
   with no further rules needed. */

/* ══════════════════════════════════════════════════════════
   PHASE 2 — delight-layer companions (assets/app.js + anime.js)
   Everything JS-driven (confetti, sfx toggle, number rolls,
   reveal staggers, idle nudges, the quiz gauge gradient) reads
   its colours live via SLX.css() and needs no CSS of its own —
   this section is only the two bits of static markup that ship
   with every chapter page from build_site.py: the chapter-hero
   emoji badge and the nav sound toggle.
   ══════════════════════════════════════════════════════════ */

/* The emoji badge replaces the abstract doodle emblem (the
   .ch-hero::after flask/leaf/atom/globe mask) when present —
   a real chapter emoji reads clearer than an abstract glyph.
   Reuses the existing hero-float keyframes so it drifts exactly
   like the emblem did; prefers-reduced-motion already flattens it. */
.ch-hero-emoji {
    position: absolute;
    z-index: 0;
    top: 4%;
    right: 4%;
    pointer-events: none;
    font-size: clamp(72px, 14vw, 168px);
    line-height: 1;
    filter: drop-shadow(0 10px 26px rgba(0, 0, 0, .22));
    animation: hero-float 6.5s var(--ease) infinite;
}
.ch-hero:has(.ch-hero-emoji)::after { display: none; }
@media (max-width: 720px) {
    .ch-hero-emoji { display: none; } /* keeps small screens readable, not cluttered */
}

/* Sound toggle sits beside the theme toggle and reuses .icon-btn
   as-is — app.js only swaps the emoji glyph and aria-label. */

/* ══════════════════════════════════════════════════════════
   PHASE 3 — progress & rewards (assets/app.js: SLX.progress,
   SLX.mission, badges, the Lab Notebook drawer, hub game-select).
   Everything JS-driven here is gated behind SLX.motionOK already
   in app.js; the global prefers-reduced-motion block further up
   this file also flattens any CSS keyframe/transition below.
   New colour tokens get an explicit dark-mode override since they
   are not derived from the strand accent (rings/pills need to
   read the same regardless of which strand's page they sit on).
   ══════════════════════════════════════════════════════════ */

:root {
    --slx-ring: var(--hue-violet);
    --slx-ring-track: var(--outline-variant);
}
:root[data-theme="dark"] {
    --slx-ring: #A78BFA;
}

/* ── Mission stamp — .lab-foot flips to a verified state ─────── */
.lab-foot.is-done {
    background: color-mix(in srgb, var(--ok) 16%, var(--surface-container-lowest));
    border-left: 3px solid var(--ok);
    padding-left: 19px;
}
.lab-foot-stamp {
    display: inline-grid; place-items: center; flex-shrink: 0;
    width: 22px; height: 22px; border-radius: 50%;
    background: var(--ok); color: #fff; font-size: .74rem;
    margin-right: 8px;
}

/* ── Toast — badge-earned popup, bottom-centre ────────────────── */
.slx-toast-host {
    position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%);
    z-index: 10000; display: flex; flex-direction: column-reverse; gap: 10px;
    align-items: center; pointer-events: none;
}
.slx-toast {
    pointer-events: auto;
    display: flex; align-items: center; gap: 12px;
    padding: 14px 20px; border-radius: var(--r-lg);
    background: var(--surface-container-lowest);
    border: 2px solid var(--accent, var(--primary));
    box-shadow: 4px 4px 0 var(--accent-glow), var(--shadow-lg);
    max-width: min(92vw, 380px);
}
.slx-toast-emoji { font-size: 1.8rem; line-height: 1; flex-shrink: 0; }
.slx-toast-copy { display: flex; flex-direction: column; gap: 2px; font-size: .86rem; text-align: left; }
.slx-toast-copy b { font-family: var(--font-display); font-size: .98rem; }
.slx-toast-copy span { color: var(--on-surface-variant); }

/* ── Lab Notebook drawer ──────────────────────────────────────── */
body.slx-notebook-locked { overflow: hidden; }
.slx-notebook-overlay {
    position: fixed; inset: 0; z-index: 9990;
    background: rgba(10, 12, 20, .44);
    opacity: 0; pointer-events: none;
    transition: opacity .25s var(--ease);
}
.slx-notebook-overlay.is-open { opacity: 1; pointer-events: auto; }
.slx-notebook {
    position: fixed; top: 0; right: 0; height: 100%; z-index: 9991;
    width: min(400px, 92vw);
    background: var(--surface-container-lowest);
    border-left: 1px solid var(--outline-variant);
    box-shadow: var(--shadow-lg);
    display: flex; flex-direction: column;
    transform: translateX(100%);
    transition: transform .32s var(--ease);
    overflow-y: auto;
}
.slx-notebook.is-open { transform: translateX(0); }
.slx-notebook-head {
    position: sticky; top: 0; z-index: 1;
    display: flex; align-items: center; justify-content: space-between;
    padding: 18px 22px; border-bottom: 1px solid var(--outline-variant);
    background: var(--surface-container-lowest);
}
.slx-notebook-body { padding: 22px; display: flex; flex-direction: column; gap: 28px; }
.slx-notebook-section h3 { margin-bottom: 14px; }
.slx-ring-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(50px, 1fr)); gap: 10px; }
.slx-ring { position: relative; width: 50px; height: 50px; margin: 0 auto; }
.slx-ring svg { width: 100%; height: 100%; }
.slx-ring-num {
    position: absolute; inset: 0; display: grid; place-items: center;
    font-family: var(--font-mono); font-size: .66rem; font-weight: 700; color: var(--on-surface-variant);
}
.slx-badge-shelf { display: grid; gap: 10px; }
.slx-badge {
    display: flex; align-items: center; gap: 12px;
    padding: 12px 14px; border-radius: var(--r-md);
    border: 1px solid var(--outline-variant);
    background: var(--surface-container-low);
}
.slx-badge.is-earned { border-color: var(--slx-ring); background: color-mix(in srgb, var(--slx-ring) 12%, var(--surface-container-low)); }
.slx-badge.is-locked { opacity: .5; filter: grayscale(.7); }
.slx-badge-emoji { font-size: 1.6rem; flex-shrink: 0; }
.slx-badge-copy { display: flex; flex-direction: column; gap: 2px; font-size: .84rem; text-align: left; }
.slx-badge-copy b { font-family: var(--font-display); font-size: .92rem; }
.slx-badge-copy span { color: var(--on-surface-variant); }
[data-notebook-reset] { align-self: flex-start; }
[data-notebook-reset].is-armed { color: var(--bad); border-color: var(--bad); }

/* Rings shared by the drawer and the hub cards: a plain track +
   an accent-consistent fill circle (see the --slx-ring token
   above) so every ring reads the same colour regardless of the
   host page's strand accent. */
.slx-ring .ring-track, .ch-ring .ring-track { fill: none; stroke: var(--slx-ring-track); stroke-width: 3; }
.slx-ring .ring-fill, .ch-ring .ring-fill {
    fill: none; stroke: var(--slx-ring); stroke-width: 3; stroke-linecap: round;
    transition: stroke-dashoffset .6s var(--ease);
}

/* ── Hub game-select — ring halo + Continue/Mastered pill ─────── */
.ch-card-top { position: relative; }
.ch-ring {
    position: absolute; top: -6px; left: -6px;
    width: 68px; height: 68px; z-index: 1;
    pointer-events: none;
}
.ch-pill {
    padding: 3px 10px; border-radius: var(--r-pill);
    font-weight: 700; font-size: .66rem; letter-spacing: .04em; text-transform: uppercase;
    background: var(--accent-grad); color: #fff;
}
.ch-pill-done { background: linear-gradient(135deg, var(--hue-mint), var(--hue-lime)); }
.ch-card.is-mastered { border-color: color-mix(in srgb, var(--hue-mint) 45%, var(--ghost-border)); }

/* "Jump back in" resume card — full-width, strand-gradient tint,
   always visible regardless of the strand filter (build_site.py's
   inline hub script skips .resume-card when filtering). */
.resume-card {
    grid-column: 1 / -1;
    background: var(--accent-grad);
    border-color: transparent;
}
.resume-card .ch-emoji { background: rgba(255, 255, 255, .22); }
.resume-card .ch-no { color: rgba(255, 255, 255, .82); }
.resume-card .ch-title, .resume-card .ch-blurb { color: #fff; }
.resume-card .ch-blurb { opacity: .92; }

/* ══════════════════════════════════════════════════════════
   FINAL PLAY PASS — atmosphere + stronger interaction cues
   ══════════════════════════════════════════════════════════ */

/* The hub gets one authored illustration instead of another abstract
   gradient. The dark veil keeps the centre quiet enough for copy while
   preserving the lively subject clusters around both edges. */
.hub-hero {
    color: #fff;
    background:
        linear-gradient(90deg, rgba(4, 12, 46, .66), rgba(9, 23, 77, .42) 50%, rgba(4, 12, 46, .66)),
        url("science-lab-hero.jpg") center / cover no-repeat;
}
.hub-hero::before {
    background:
        linear-gradient(135deg, rgba(10, 20, 65, .56), rgba(20, 43, 110, .30));
    border-color: rgba(255, 255, 255, .22);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .12), 0 30px 80px rgba(1, 8, 34, .32);
    backdrop-filter: blur(2px);
}
.hub-hero .shell { position: relative; z-index: 1; }
.hub-hero .display {
    background: linear-gradient(105deg, #fff 8%, #c7e6ff 42%, #9ff8da 76%, #fff);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    filter: drop-shadow(0 4px 22px rgba(0, 0, 0, .24));
}
.hub-hero .lede { color: rgba(255, 255, 255, .88); }
.hub-hero .lede em { color: #fff; font-style: normal; font-weight: 800; }
.hub-hero .chip {
    background: rgba(255, 255, 255, .15);
    color: #fff;
    border: 1px solid rgba(255, 255, 255, .24);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .12);
    backdrop-filter: blur(10px);
}
.hub-hero .btn {
    background: #fff;
    color: #16285e;
    box-shadow: 0 10px 0 rgba(3, 12, 46, .34);
}
.hub-hero .btn:hover { box-shadow: 0 5px 0 rgba(3, 12, 46, .34); }
.hub-hero .btn-ghost {
    background: rgba(8, 20, 62, .48);
    color: #fff;
    border: 1px solid rgba(255, 255, 255, .42);
}
.hub-hero .stat-strip { border-top-color: rgba(255, 255, 255, .28); }
.hub-hero .stat + .stat { border-left-color: rgba(255, 255, 255, .24) !important; }
.hub-hero .stat span { color: rgba(255, 255, 255, .72) !important; }

/* Every experiment announces itself as something to play, not merely a
   boxed figure. This applies to all authored labs without extra markup. */
.lab-head .h3::after {
    content: "LIVE LAB";
    display: inline-flex;
    align-items: center;
    min-height: 24px;
    margin-left: 8px;
    padding: 3px 9px;
    border: 1px solid color-mix(in srgb, var(--accent) 48%, var(--outline-variant));
    border-radius: var(--r-pill);
    background: color-mix(in srgb, var(--accent-soft) 84%, var(--surface-container-lowest));
    color: var(--accent-ink);
    font-family: var(--font);
    font-size: .57rem;
    font-weight: 800;
    letter-spacing: .12em;
    white-space: nowrap;
}
.lab-stage {
    background:
        radial-gradient(circle at 50% 20%, color-mix(in srgb, var(--accent) 10%, transparent), transparent 48%),
        var(--surface-container-low);
}
.lab-stage canvas,
.lab-stage svg {
    border-radius: 18px;
    box-shadow:
        0 0 0 1px color-mix(in srgb, var(--accent) 22%, var(--outline-variant)),
        0 14px 34px rgba(18, 24, 39, .10);
    transition: box-shadow .2s var(--ease), transform .2s var(--ease-snap);
}
.lab:focus-within .lab-stage canvas,
.lab:focus-within .lab-stage svg {
    box-shadow:
        0 0 0 3px var(--accent-glow),
        0 14px 34px rgba(18, 24, 39, .12);
}
.lab-panel > .ctrl {
    padding: 13px 14px;
    border: 1px solid var(--outline-variant);
    border-radius: 14px;
    background: color-mix(in srgb, var(--surface-container-lowest) 82%, transparent);
    transition: border-color .18s var(--ease), transform .18s var(--ease-snap), box-shadow .18s var(--ease);
}
.lab-panel > .ctrl:focus-within {
    transform: translateY(-2px);
    border-color: var(--accent);
    box-shadow: 0 6px 0 var(--accent-glow);
}
.lab-foot {
    display: flex;
    align-items: flex-start;
    gap: 12px;
}
.lab-foot::before {
    content: "⚡ MISSION";
    position: static;
    display: inline-flex;
    flex: 0 0 auto;
    width: auto;
    height: auto;
    padding: 4px 9px;
    border-radius: var(--r-pill);
    background: var(--accent-grad);
    color: #fff;
    font-family: var(--font);
    font-size: .58rem;
    font-weight: 800;
    letter-spacing: .1em;
    line-height: 1.4;
}

/* Cards on the chapter picker feel like selectable game levels. */
.ch-card {
    border-width: 2px;
    box-shadow: 4px 4px 0 color-mix(in srgb, var(--ch, var(--accent)) 16%, transparent);
}
.ch-card:hover {
    transform: translate(2px, 2px);
    box-shadow: 2px 2px 0 color-mix(in srgb, var(--ch, var(--accent)) 24%, transparent);
}
.ch-no::before { content: "LEVEL "; }

@media (max-width: 720px) {
    .hub-hero {
        background-position: 58% center;
    }
    .hub-hero::before { backdrop-filter: none; }
    .hub-hero .stat:nth-child(n + 3) { border-top-color: rgba(255, 255, 255, .24) !important; }
    .lab-head .h3::after { font-size: .52rem; }
    .lab-foot { flex-direction: column; }
}

/* ══════════════════════════════════════════════════════════
   PHASE 4 — smarter interactions (assets/app.js: SLX.predict,
   race mode for SLX.taps/SLX.pick, the review flashcard deck).
   Everything JS-driven here is already gated behind SLX.motionOK
   in app.js; the existing global prefers-reduced-motion block
   further up this file also flattens any animation below. All
   colours are tokens — no hex, dark mode needs nothing extra
   beyond what the tokens already provide.
   ══════════════════════════════════════════════════════════ */

/* ── Predict-then-play gate ─────────────────────────────────── */
.predict {
    display: flex; flex-direction: column; gap: 12px;
    padding: 16px 18px; border-radius: var(--r-lg);
    background: var(--surface-container-low);
    border: 1px solid var(--outline-variant);
}
.predict-prompt { font-weight: 700; color: var(--on-surface); }
.predict-opts { display: flex; flex-wrap: wrap; gap: 10px; }
.predict-opt {
    flex: 1 1 180px;
    padding: 10px 16px; border-radius: var(--r-md);
    border: 1px solid var(--outline-variant);
    background: var(--surface-container-lowest);
    color: var(--on-surface); font-size: .86rem; font-weight: 600;
    cursor: pointer;
    transition: transform .18s var(--ease-snap), border-color .18s var(--ease),
                background .18s var(--ease), opacity .18s var(--ease);
}
.predict-opt:hover:not(:disabled) { border-color: var(--primary); transform: translateY(-2px); }
.predict-opt:disabled { cursor: default; }
.predict-opt.is-chosen { border-color: var(--primary); box-shadow: 0 0 0 3px var(--accent-glow); }
.predict-opt.is-dimmed { opacity: .5; }
.predict-opt.is-right {
    border-color: var(--ok); background: var(--ok-bg); color: var(--ok); opacity: 1;
    animation: mark-pop .42s var(--ease-pop) both, flash-good .5s var(--ease) both;
}
.predict-opt.is-wrong {
    border-color: var(--bad); background: var(--bad-bg); color: var(--bad); opacity: 1;
    animation: strike .38s var(--ease) both, flash-bad .5s var(--ease) both;
}
.predict-status { font-size: .82rem; font-weight: 600; color: var(--on-surface-variant); }

/* ── Race mode — shared header for SLX.taps / SLX.pick ────────── */
.race-bar {
    display: flex; align-items: center; flex-wrap: wrap; gap: 10px 14px;
    margin-bottom: 12px; width: 100%;
}
/* .taps is a flex row of buttons — force the race-bar onto its own
   full-width row above them rather than becoming another tap tile. */
.taps.has-race > .race-bar { flex: 1 0 100%; }
.race-best { font-family: var(--font-mono); font-size: .78rem; font-weight: 700; color: var(--accent-ink); }
.race-timer {
    flex: 1 1 140px; min-width: 100px; height: 8px; border-radius: var(--r-pill);
    background: var(--surface-container-lowest); border: 1px solid var(--outline-variant);
    overflow: hidden;
}
.race-timer-fill { height: 100%; width: 100%; background: var(--accent-grad); border-radius: inherit; }
.race-result { font-size: .82rem; font-weight: 700; color: var(--on-surface); }

/* ── Lab Notebook — Review section rows ───────────────────────── */
.slx-review-list { display: grid; gap: 8px; }

/* ══════════════════════════════════════════════════════════
   CHAPTER WORLDS — each lesson has its own visual signature
   The strand still supplies the broad subject identity. These
   chapter tokens give every lesson a recognisable game-board
   colour pairing and motif without adding bitmap decoration to
   responsive scientific canvases.
   ══════════════════════════════════════════════════════════ */
body[data-chapter] {
    --accent-deep: color-mix(in srgb, var(--accent) 74%, var(--on-surface));
    --accent-soft: color-mix(in srgb, var(--accent) 12%, var(--surface-container-lowest));
    --accent-glow: color-mix(in srgb, var(--accent) 22%, transparent);
    --accent-ink: color-mix(in srgb, var(--accent) 58%, var(--on-surface));
    --accent-grad: linear-gradient(135deg, var(--accent), var(--chapter-accent-2));
    counter-reset: chapter-lab;
}
body[data-chapter="ch01"] { --accent:#7C3AED; --chapter-accent-2:#EC4899; --chapter-mark:"⚗️"; }
body[data-chapter="ch02"] { --accent:#8B5CF6; --chapter-accent-2:#06B6D4; --chapter-mark:"🧪"; }
body[data-chapter="ch03"] { --accent:#A855F7; --chapter-accent-2:#F59E0B; --chapter-mark:"⛏️"; }
body[data-chapter="ch04"] { --accent:#C026D3; --chapter-accent-2:#22D3EE; --chapter-mark:"💎"; }
body[data-chapter="ch05"] { --accent:#DB2777; --chapter-accent-2:#FBBF24; --chapter-mark:"📊"; }
body[data-chapter="ch06"] { --accent:#0EA36B; --chapter-accent-2:#84CC16; --chapter-mark:"🫁"; }
body[data-chapter="ch07"] { --accent:#10B981; --chapter-accent-2:#38BDF8; --chapter-mark:"🧠"; }
body[data-chapter="ch08"] { --accent:#14B8A6; --chapter-accent-2:#F59E0B; --chapter-mark:"🌱"; }
body[data-chapter="ch09"] { --accent:#06B6D4; --chapter-accent-2:#A78BFA; --chapter-mark:"🧬"; }
body[data-chapter="ch10"] { --accent:#1B6FF5; --chapter-accent-2:#22D3EE; --chapter-mark:"🔭"; }
body[data-chapter="ch11"] { --accent:#3B82F6; --chapter-accent-2:#F59E0B; --chapter-mark:"👁️"; }
body[data-chapter="ch12"] { --accent:#2563EB; --chapter-accent-2:#F43F5E; --chapter-mark:"⚡"; }
body[data-chapter="ch13"] { --accent:#6366F1; --chapter-accent-2:#EC4899; --chapter-mark:"🧲"; }
body[data-chapter="ch14"] { --accent:#0284C7; --chapter-accent-2:#FBBF24; --chapter-mark:"🔋"; }
body[data-chapter="ch15"] { --accent:#E06A16; --chapter-accent-2:#84CC16; --chapter-mark:"♻️"; }
body[data-chapter="ch16"] { --accent:#EA580C; --chapter-accent-2:#10B981; --chapter-mark:"🌍"; }

body[data-chapter] .lab { counter-increment: chapter-lab; }
body[data-chapter] .lab-head {
    background:
        linear-gradient(100deg,
            color-mix(in srgb, var(--accent) 15%, var(--surface-container-lowest)),
            color-mix(in srgb, var(--chapter-accent-2) 8%, var(--surface-container-lowest)) 70%);
}
body[data-chapter] .lab-head .h3::after {
    content: "LAB " counter(chapter-lab, decimal-leading-zero) "  •  LIVE";
    background: var(--accent-grad);
    border-color: transparent;
    color: #fff;
    box-shadow: 0 4px 0 var(--accent-glow);
}
body[data-chapter] .lab-stage {
    overflow: hidden;
    background:
        radial-gradient(circle at 18% 16%, color-mix(in srgb, var(--accent) 15%, transparent), transparent 38%),
        radial-gradient(circle at 84% 82%, color-mix(in srgb, var(--chapter-accent-2) 12%, transparent), transparent 42%),
        var(--surface-container-low);
}
body[data-chapter] .lab-stage::after {
    content: var(--chapter-mark);
    position: absolute;
    z-index: 0;
    right: -10px;
    bottom: -22px;
    font-size: clamp(76px, 10vw, 142px);
    line-height: 1;
    opacity: .055;
    filter: grayscale(.15);
    transform: rotate(-9deg);
    pointer-events: none;
}
body[data-chapter] .lab-stage canvas,
body[data-chapter] .lab-stage svg {
    z-index: 1;
}
body[data-chapter] input[type="range"]::-webkit-slider-runnable-track {
    background:
        linear-gradient(90deg,
            color-mix(in srgb, var(--accent) 28%, var(--surface-container-high)),
            color-mix(in srgb, var(--chapter-accent-2) 22%, var(--surface-container-high)));
}
body[data-chapter] .predict {
    border: 2px solid color-mix(in srgb, var(--accent) 35%, var(--outline-variant));
    background:
        linear-gradient(135deg,
            color-mix(in srgb, var(--accent) 10%, var(--surface-container-lowest)),
            color-mix(in srgb, var(--chapter-accent-2) 7%, var(--surface-container-lowest)));
    box-shadow: 4px 4px 0 var(--accent-glow);
}
body[data-chapter] .predict-prompt::before {
    content: "PREDICT  ";
    color: var(--accent);
    font-size: .68rem;
    font-weight: 900;
    letter-spacing: .12em;
}

@keyframes lab-react {
    0%   { transform: translateY(0) scale(1); }
    48%  { transform: translateY(-2px) scale(1.004); }
    100% { transform: translateY(0) scale(1); }
}
.lab.is-reacting {
    border-color: var(--accent);
    box-shadow: 3px 3px 0 var(--accent-glow), 0 0 0 4px color-mix(in srgb, var(--accent) 7%, transparent);
}
.lab-stage.is-reacting canvas,
.lab-stage.is-reacting svg {
    animation: lab-react .34s var(--ease-pop) both;
}
.lab.is-reacting .lab-head {
    background:
        linear-gradient(100deg,
            color-mix(in srgb, var(--accent) 21%, var(--surface-container-lowest)),
            color-mix(in srgb, var(--chapter-accent-2, var(--accent)) 12%, var(--surface-container-lowest)) 70%);
}

.concept-bridge {
    width: min(100%, 520px);
    margin: clamp(34px, 5vw, 58px) 0 0 auto;
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-areas:
        "kicker arrow"
        "name arrow";
    align-items: center;
    column-gap: 18px;
    padding: 15px 18px 15px 20px;
    border: 2px solid color-mix(in srgb, var(--accent) 34%, var(--outline-variant));
    border-radius: 18px;
    background:
        linear-gradient(120deg,
            color-mix(in srgb, var(--accent) 10%, var(--surface-container-lowest)),
            color-mix(in srgb, var(--chapter-accent-2, var(--accent)) 7%, var(--surface-container-lowest)));
    color: var(--on-surface);
    box-shadow: 4px 4px 0 var(--accent-glow);
    text-decoration: none;
    transition: transform .18s var(--ease-snap), box-shadow .18s var(--ease-snap), border-color .18s var(--ease);
}
.concept-bridge:hover {
    transform: translate(3px, 3px);
    border-color: var(--accent);
    box-shadow: 1px 1px 0 var(--accent-glow);
    text-decoration: none;
}
.concept-bridge-kicker {
    grid-area: kicker;
    color: var(--accent);
    font-size: .62rem;
    font-weight: 900;
    letter-spacing: .14em;
}
.concept-bridge-name {
    grid-area: name;
    font-family: var(--font-display);
    font-size: 1.02rem;
    font-weight: 750;
}
.concept-bridge-arrow {
    grid-area: arrow;
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: var(--accent-grad);
    color: #fff;
    font-size: 1.2rem;
    transition: transform .22s var(--ease-pop);
}
.concept-bridge:hover .concept-bridge-arrow { transform: translateX(4px); }

/* ══════════════════════════════════════════════════════════
   SHARED CLASSROOM MODE
   No learner profile, achievement badges, levels or progress UI.
   Immediate experiment feedback stays because it supports teaching.
   ══════════════════════════════════════════════════════════ */
.slx-notebook,
.slx-notebook-overlay,
.slx-toast-host,
.slx-review-chip,
.slx-flashcard,
.ch-ring,
.ch-pill,
.resume-card {
    display: none !important;
}
.lab-head .h3::after,
.lab-foot-stamp {
    display: none !important;
}
.lab-foot::before {
    content: "TRY THIS";
}
.lab-foot {
    /* Keep the classroom prompt as one readable sentence on a phone. The old
       column layout stranded the label on its own line and doubled the height
       of every experiment footer. */
    display: block;
}
.lab-foot::before {
    display: inline-flex;
    margin: 0 10px 0 0;
    vertical-align: middle;
}
.ch-no::before {
    content: none;
}
.slx-review-row {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 10px 14px; border-radius: var(--r-md);
    border: 1px solid var(--outline-variant);
    background: var(--surface-container-low);
    color: var(--on-surface); text-decoration: none; font-size: .84rem; font-weight: 600;
    transition: border-color .18s var(--ease), transform .18s var(--ease-snap);
}
.slx-review-row:hover { border-color: var(--primary); transform: translateX(2px); }
.slx-review-count {
    padding: 2px 9px; border-radius: var(--r-pill);
    background: var(--accent-grad); color: #fff;
    font-size: .68rem; font-weight: 800; letter-spacing: .02em;
}

/* ── Review chip — dismissible "N-card warm-up?" prompt ───────── */
.slx-review-chip {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    width: calc(100% - 80px); max-width: var(--shell); margin: 14px auto 0; padding: 12px 16px;
    border-radius: var(--r-lg);
    background: var(--surface-container-low);
    border: 1px solid var(--outline-variant);
    box-shadow: 4px 4px 0 var(--accent-glow);
}
@media (max-width: 720px) {
    .slx-review-chip { width: calc(100% - 48px); }
}
.slx-review-chip-open {
    background: none; border: none; padding: 0; margin: 0;
    font-family: var(--font); font-size: .9rem; font-weight: 700;
    color: var(--accent-ink); cursor: pointer; text-align: left;
}
.slx-review-chip-x {
    flex-shrink: 0; width: 26px; height: 26px; display: grid; place-items: center;
    border-radius: 50%; border: 1px solid var(--outline-variant); background: transparent;
    color: var(--outline); cursor: pointer;
}
.slx-review-chip-x:hover { color: var(--bad); border-color: var(--bad); }
.slx-review-chip { animation: rise .45s var(--ease-pop) both; }

/* ── Review flashcard modal — centred, reuses the notebook overlay ── */
.slx-flash-overlay { z-index: 9992; }
.slx-flashcard {
    position: fixed; z-index: 9993;
    left: 50%; top: 50%; transform: translate(-50%, -50%) scale(.94);
    width: min(420px, 92vw);
    background: var(--surface-container-lowest);
    border: 1px solid var(--outline-variant);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-lg);
    padding: 20px; display: flex; flex-direction: column; gap: 16px;
    opacity: 0; pointer-events: none;
    transition: opacity .22s var(--ease), transform .22s var(--ease-pop);
}
.slx-flashcard.is-open { opacity: 1; pointer-events: auto; transform: translate(-50%, -50%) scale(1); }
.slx-flash-head { display: flex; align-items: center; justify-content: space-between; }
.slx-flash-progress { font-family: var(--font-mono); font-size: .76rem; font-weight: 700; color: var(--outline); }
.slx-flash-card {
    min-height: 160px; display: flex; flex-direction: column; align-items: center;
    justify-content: center; gap: 12px; text-align: center; padding: 18px;
    border-radius: var(--r-md); border: 1px dashed var(--outline-variant);
    background: var(--surface-container-low);
    cursor: pointer;
}
.slx-flash-card:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }
.slx-flash-q { font-weight: 700; font-size: 1rem; color: var(--on-surface); }
.slx-flash-back { display: flex; flex-direction: column; gap: 6px; }
.slx-flash-a { font-weight: 700; color: var(--ok); }
.slx-flash-why { font-size: .84rem; color: var(--on-surface-variant); }
.slx-flash-hint { font-size: .72rem; letter-spacing: .04em; text-transform: uppercase; color: var(--outline); }
.slx-flash-actions { display: flex; gap: 10px; justify-content: flex-end; }

/* ══════════════════════════════════════════════════════════
   READING LAYER — enlarge-a-card + keyword cards
   Two modal surfaces that share one overlay, one z-index band
   and one scroll lock (body.slx-notebook-locked, already set
   by the runtime). Both are built by assets/app.js; chapters
   never write markup for them.
   ══════════════════════════════════════════════════════════ */

.slx-modal-overlay {
    position: fixed; inset: 0; z-index: 9994;
    background: color-mix(in srgb, var(--on-surface) 46%, transparent);
    backdrop-filter: blur(3px);
    opacity: 0; pointer-events: none;
    transition: opacity .2s var(--ease);
}
.slx-modal-overlay.is-open { opacity: 1; pointer-events: auto; }
.slx-kw-overlay { z-index: 9996; }

/* ── The ⤢ affordance on every card ───────────────────────── */
.card { position: relative; }
.card-zoom {
    position: absolute; top: 10px; right: 10px; z-index: 3;
    width: 34px; height: 34px;
    display: inline-flex; align-items: center; justify-content: center;
    padding: 0; border-radius: var(--r-pill);
    border: 2px solid color-mix(in srgb, var(--accent) 45%, var(--outline-variant));
    background: var(--surface-container-lowest);
    color: var(--accent-ink, var(--on-surface));
    cursor: pointer;
    opacity: 0; transform: scale(.86);
    transition: opacity .16s var(--ease), transform .16s var(--ease-snap),
                background-color .16s var(--ease), color .16s var(--ease);
}
.card-zoom svg {
    width: 16px; height: 16px;
    fill: none; stroke: currentColor; stroke-width: 2.1;
    stroke-linecap: round; stroke-linejoin: round;
}
.card:hover > .card-zoom,
.card:focus-within > .card-zoom,
.card-zoom:focus-visible { opacity: 1; transform: scale(1); }
.card-zoom:hover { background: var(--accent); color: var(--on-accent, #fff); border-color: var(--accent); }
.card-zoom:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }
/* Touch has no hover — never hide the control behind a gesture that
   device cannot make. */
@media (hover: none) {
    .card-zoom { opacity: .78; transform: scale(1); }
}

/* The placeholder that holds the card's slot in the grid while the
   card itself is away in the modal. */
.slx-zoom-slot { border-radius: var(--r-lg); background: transparent; }

/* The modal is deliberately a *fixed* full-height sheet, not a box that
   grows with its card. A short card and a long one open to the same
   frame, so the close button and the text-size controls never move
   between one card and the next — the reader's hand learns one target.
   Overflow is the body's job; the head and foot stay put. */
.slx-zoom {
    position: fixed; z-index: 9995;
    left: 50%; top: 50%;
    transform: translate(-50%, -50%) scale(.96);
    width: min(980px, 94vw);
    height: calc(100vh - 2 * var(--slx-zoom-inset, 34px));
    height: calc(100dvh - 2 * var(--slx-zoom-inset, 34px));
    max-height: none;
    display: flex; flex-direction: column;
    background: var(--surface-container-lowest);
    border: 2px solid var(--accent);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-lg);
    opacity: 0; pointer-events: none;
    transition: opacity .22s var(--ease), transform .22s var(--ease-pop);
}
.slx-zoom.is-open { opacity: 1; pointer-events: auto; transform: translate(-50%, -50%) scale(1); }
.slx-zoom-head {
    flex: none;
    display: flex; align-items: center; gap: 14px;
    padding: 14px 16px 14px 22px;
    border-bottom: 1px solid var(--outline-variant);
    background: color-mix(in srgb, var(--accent) 12%, var(--surface-container-lowest));
    border-radius: calc(var(--r-lg) - 2px) calc(var(--r-lg) - 2px) 0 0;
}
.slx-zoom-title {
    flex: 1; min-width: 0;
    font-family: var(--font); font-weight: 800; font-size: .95rem;
    color: var(--on-surface);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.slx-zoom-font-controls {
    display: inline-flex; align-items: center; flex: none;
    padding: 3px; gap: 2px;
    border: 1px solid var(--outline-variant);
    border-radius: var(--r-pill);
    background: var(--surface-container-lowest);
}
.slx-zoom-font-controls button {
    min-width: 36px; height: 36px; padding: 0 8px;
    border: 0; border-radius: var(--r-pill);
    background: transparent; color: var(--on-surface);
    font-family: var(--font); font-size: .82rem; font-weight: 800;
    cursor: pointer;
}
.slx-zoom-font-controls button:hover { background: var(--surface-container); }
.slx-zoom-font-controls button:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
.slx-zoom-font-controls button:disabled { opacity: .35; cursor: default; }
.slx-zoom-body {
    flex: 1; min-height: 0;
    overflow-y: auto; overscroll-behavior: contain;
    padding: 28px 30px 40px;
    -webkit-overflow-scrolling: touch;
}
/* Most cards bring their own heading, so the bar is just a close target. */
.slx-zoom-head:has(.slx-zoom-title:empty) { padding: 10px 12px; }

/* A hairline that only appears once there is something scrolled past it,
   so a card that fits the sheet doesn't get a decorative line for nothing. */
.slx-zoom-head { box-shadow: none; transition: box-shadow .18s var(--ease); }
.slx-zoom.is-scrolled .slx-zoom-head { box-shadow: 0 6px 14px -10px var(--on-surface); }

/* The card itself, once it is in the modal: bigger type, no sticker
   border (the modal supplies the frame), no hover nudge. */
.slx-zoom-body > .card.is-zoomed {
    margin: 0; border: none; box-shadow: none; padding: 0;
    background: transparent; transform: none;
    font-size: var(--slx-zoom-font-size, 1.25rem); line-height: 1.75;
}
.slx-zoom-body > .card.is-zoomed:hover { transform: none; box-shadow: none; }
.slx-zoom-body > .card.is-zoomed > .card-zoom { display: none; }
.slx-zoom-body > .card.is-zoomed .h3 { font-size: 1.5rem; }
.slx-zoom-body > .card.is-zoomed p { font-size: 1em; }
.slx-zoom-body > .card.is-zoomed p.small { font-size: .92em; }
.slx-zoom-body > .card.is-zoomed li { font-size: 1em; line-height: 1.7; }
.slx-zoom-body > .card.is-zoomed .formula { font-size: 1.15em; }

/* Glossary-powered context is generated only for the enlarged reading
   view. It combines the book's own explanation with a relevant image when
   one is available, and falls back to the entry's emoji while offline. */
.slx-card-keywords { display: none; }
.slx-zoom-body > .card.is-zoomed .slx-card-keywords.has-images {
    display: block;
    margin-top: 34px;
    padding-top: 24px;
    border-top: 2px solid color-mix(in srgb, var(--accent) 30%, var(--outline-variant));
}
.slx-card-keywords-grid {
    display: grid; gap: 16px;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}
.slx-card-keyword {
    display: none;
    overflow: hidden;
    border: 1px solid var(--outline-variant);
    border-radius: var(--r-md);
    background: var(--surface-container-low);
}
.slx-card-keyword:has(.has-image) { display: block; }
.slx-card-keyword-visual {
    position: relative; min-height: 180px;
    background: color-mix(in srgb, var(--accent) 15%, var(--surface-container-high));
}
.slx-card-keyword-visual img {
    display: none;
    width: 100%; height: 100%; min-height: 180px; max-height: 280px;
    object-fit: contain;
    padding: 8px;
}
.slx-card-keyword-visual.has-image img { display: block; }

@media (max-width: 720px) {
    .slx-zoom { width: 96vw; --slx-zoom-inset: 14px; }
    .slx-zoom-body { padding: 18px 18px 30px; }
    .slx-zoom-head { gap: 8px; padding-left: 12px; }
    .slx-zoom-title { display: none; }
    .slx-zoom-font-controls { margin-right: auto; }
    .slx-card-keyword-visual,
    .slx-card-keyword-visual img { min-height: 150px; max-height: 210px; }
}

/* ── The deep half of a card ──────────────────────────────────
   A card in the grid is a *headline*: the topic, and just enough
   to know whether it is the thing you were looking for. Everything
   that makes it a lesson — the worked example, the diagram, the
   table, the caveats — is authored inside .card-more, which only
   exists once the card is in the modal.

   It is display:none rather than hidden-by-clip so that the grid
   never reserves height for it, and so images inside it are not
   fetched until the card is actually opened.
   ─────────────────────────────────────────────────────────── */
.card-more { display: none; }
.slx-zoom-body > .card.is-zoomed .card-more { display: block; }
.slx-zoom-body > .card.is-zoomed .card-more > * + * { margin-top: 16px; }
.slx-zoom-body > .card.is-zoomed .card-more > :is(h2, h3, h4, .h2, .h3, .h4) + * { margin-top: 10px; }
.slx-zoom-body > .card.is-zoomed .card-more > * + :is(h2, h3, h4, .h2, .h3, .h4) { margin-top: 28px; }

/* A subheading inside the deep half — smaller than the card's own
   title, so the modal reads as one article and not a stack of cards. */
.card-more .h4 { font-size: 1.05em; }

/* Cards that have a deep half say so, and behave like one big button. */
.card[data-has-more] { cursor: pointer; }
/* Child-combinator, not a bare class: the FLOW RHYTHM owl at the foot of
   this file matches `.card > * + *` at the same specificity and, being
   later, would otherwise overwrite this margin. */
.card > .card-more-hint {
    display: flex; align-items: center; gap: 7px;
    margin-top: 18px; padding-top: 14px;
    border-top: 1px dashed color-mix(in srgb, var(--accent) 40%, transparent);
    font-family: var(--font); font-size: .78rem; font-weight: 800;
    letter-spacing: .02em; color: var(--accent);
}
.card-more-hint::after {
    content: '→';
    transition: transform .18s var(--ease-snap);
}
.card[data-has-more]:hover .card-more-hint::after { transform: translateX(4px); }
/* Inside the modal the invitation has already been accepted. */
.card.is-zoomed .card-more-hint { display: none; }

/* ── Figures authored into a card's deep half ─────────────────
   Diagrams are inline SVG so they inherit the theme's colours and
   stay crisp at any text size; photographs use <img>. Both sit in
   the same frame so a chapter mixing the two still reads evenly. */
.card-fig {
    margin: 0;
    padding: 16px;
    border-radius: var(--r-md);
    background: var(--surface-container-high);
    border: 1px solid var(--outline-variant);
}
.card-fig > svg,
.card-fig > img {
    display: block;
    width: 100%; height: auto;
    max-height: 340px;
    /* `contain` — these are labelled diagrams, and cropping one loses
       exactly the labels that made it worth showing. */
    object-fit: contain;
}
.card-fig figcaption {
    margin-top: 10px;
    font-size: .78rem; line-height: 1.5;
    color: var(--outline);
}
/* Two diagrams that are meant to be compared, side by side. */
.card-fig-row {
    display: grid; gap: 14px;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

/* ── Keyword cards ────────────────────────────────────────── */
.kw {
    cursor: pointer;
    border-bottom: 2px dotted color-mix(in srgb, var(--accent) 78%, transparent);
    border-radius: 3px;
    padding: 0 1px;
    transition: background-color .15s var(--ease), border-color .15s var(--ease);
}
/* No trailing marker glyph: on a term that wraps, an ::after dot gets
   pushed onto the next line on its own and reads as a stray bullet. The
   accent-dotted underline carries the affordance instead, and it wraps
   with the words it belongs to. */
.kw:hover, .kw:focus-visible {
    background: color-mix(in srgb, var(--accent) 16%, transparent);
    border-bottom-color: var(--accent);
    border-bottom-style: solid;
}
.kw:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

.slx-kw {
    position: fixed; z-index: 9997;
    left: 50%; top: 50%;
    transform: translate(-50%, -50%) scale(.96);
    width: min(760px, 94vw);
    height: calc(100vh - 2 * var(--slx-kw-inset, 34px));
    height: calc(100dvh - 2 * var(--slx-kw-inset, 34px));
    display: flex; flex-direction: column;
    background: var(--surface-container-lowest);
    border: 2px solid var(--accent);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-lg);
    opacity: 0; pointer-events: none;
    transition: opacity .22s var(--ease), transform .22s var(--ease-pop);
}
.slx-kw.is-open { opacity: 1; pointer-events: auto; transform: translate(-50%, -50%) scale(1); }
.slx-kw-head {
    flex: none;
    display: flex; align-items: center; gap: 14px;
    padding: 16px 16px 16px 20px;
    border-bottom: 1px solid var(--outline-variant);
    background: color-mix(in srgb, var(--accent) 12%, var(--surface-container-lowest));
    border-radius: calc(var(--r-lg) - 2px) calc(var(--r-lg) - 2px) 0 0;
}
.slx-kw-emoji {
    flex: none;
    width: 44px; height: 44px;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 1.5rem; border-radius: 14px;
    background: color-mix(in srgb, var(--accent) 22%, var(--surface-container-lowest));
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 40%, transparent);
}
.slx-kw-titles { flex: 1; min-width: 0; }
.slx-kw-eyebrow {
    display: block;
    font-family: var(--font); font-size: .6rem; font-weight: 900;
    letter-spacing: .14em; color: var(--accent);
}
.slx-kw-titles .h3 { margin: 2px 0 0; font-size: 1.2rem; }
.slx-kw-body {
    flex: 1; min-height: 0;
    overflow-y: auto; overscroll-behavior: contain;
    padding: 28px 30px 40px;
    display: flex; flex-direction: column; gap: 18px;
    font-size: var(--slx-kw-font-size, 1.25rem);
    -webkit-overflow-scrolling: touch;
}
.slx-kw-figure { margin: 0; }
.slx-kw-figure img {
    /* `contain`, not `cover` — most of these are labelled diagrams, and a
       cropped diagram loses exactly the labels that made it worth showing. */
    display: block; width: 100%; max-height: 280px;
    object-fit: contain; border-radius: var(--r-md);
    background: var(--surface-container-high);
    padding: 6px;
}
.slx-kw-figure figcaption {
    margin-top: 6px;
    font-size: .74rem; color: var(--outline);
    text-transform: none;
}
.slx-kw-def { font-size: 1em; line-height: 1.75; color: var(--on-surface); }
.slx-kw-more {
    font-size: .92em; line-height: 1.7; color: var(--on-surface-variant);
    padding-left: 14px;
    border-left: 3px solid color-mix(in srgb, var(--accent) 45%, transparent);
}
.slx-kw-status { font-size: .74em; color: var(--outline); }
.slx-kw-status:empty { display: none; }

@media (max-width: 720px) {
    .slx-kw { width: 96vw; --slx-kw-inset: 14px; }
    .slx-kw-head { gap: 8px; padding: 12px; }
    .slx-kw-emoji { display: none; }
    .slx-kw-titles { display: none; }
    .slx-kw-head .slx-zoom-font-controls { margin-right: auto; }
    .slx-kw-body { padding: 18px 18px 30px; }
}

@media (prefers-reduced-motion: reduce) {
    .slx-modal-overlay, .slx-zoom, .slx-kw, .card-zoom { transition: none; }
    .slx-zoom, .slx-kw { transform: translate(-50%, -50%); }
    .slx-zoom.is-open, .slx-kw.is-open { transform: translate(-50%, -50%); }
}


/* ══════════════════════════════════════════════════════════
   FLOW RHYTHM inside cards and callouts

   The reset at the top of this file zeroes every margin, and
   nothing ever put vertical rhythm back inside a card — so a
   heading sat flush against its first line, and a paragraph ran
   straight into the formula below it. Chapters had been papering
   over that with one-off inline margins, which is why the spacing
   was inconsistent from card to card.

   One owl selector fixes it everywhere at once. Any inline margin
   an author already wrote still wins (inline beats a stylesheet),
   so nothing that was deliberately tuned moves.
   ══════════════════════════════════════════════════════════ */
.card > * + *,
.note > * + *,
.lab-foot > * + * { margin-top: 14px; }

/* A heading belongs to the text beneath it: tight below, roomy above. */
.card > :is(h2, h3, h4, .h2, .h3, .h4) + * { margin-top: 10px; }
.card > * + :is(h2, h3, h4, .h2, .h3, .h4) { margin-top: 26px; }

/* Blocks that carry their own frame need more air than a paragraph does. */
.card > * + :is(.formula, .note, .table-wrap, .predict, .legend, .seg),
.card > :is(.formula, .note, .table-wrap) + * { margin-top: 18px; }

/* The enlarge button is absolutely positioned — a stray margin-top from
   the rules above would shove it down off its own corner. */
.card > .card-zoom { margin: 0; }

/* Chapters often set out two or three equations as <br>-separated .mono
   spans inside a callout. Inline elements ignore margin-top, so give those
   spans their own line box — but only the ones that start a line, or mono
   used mid-sentence would gain leading that the rest of the paragraph lacks. */
.note br + .mono,
.card p br + .mono { display: inline-block; max-width: 100%; margin-top: 8px; }

/* Lists breathe too. */
.card :is(ul, ol) > li + li,
.note :is(ul, ol) > li + li { margin-top: 8px; }
.card > :is(ul, ol) { padding-left: 22px; }

/* Slightly more generous inside the enlarged card, where the whole point
   is comfortable reading. */
.slx-zoom-body > .card.is-zoomed > * + * { margin-top: 18px; }
.slx-zoom-body > .card.is-zoomed > :is(h2, h3, h4, .h2, .h3, .h4) + * { margin-top: 12px; }
.slx-zoom-body > .card.is-zoomed :is(ul, ol) > li + li { margin-top: 10px; }


/* ══════════════════════════════════════════════════════════
   CHEMICAL NOTATION + INSTRUMENT LABELS

   Two things this stylesheet never actually specified, and both
   show up hardest in the chemistry chapters:

   1. sub/sup were left at browser defaults, so every H₂O in a
      formula pushed its own line box taller than its neighbours
      and the subscripts sat at a different optical size from the
      mono digits around them.
   2. .formula was tinted so faintly it read as an empty rounded
      box rather than a specimen plate, and a long equation wrapped
      into one full line plus a centred stub.
   ══════════════════════════════════════════════════════════ */

sub, sup {
    font-size: .74em;
    line-height: 0;              /* never let a subscript stretch its line */
    position: relative;
    vertical-align: baseline;
    font-weight: inherit;
}
sub { bottom: -.26em; }
sup { top: -.46em; }

.formula {
    /* Same sticker language as .card and .lab: 2px border, tinted
       fill, small hard offset — so it reads as a plate holding a
       specimen, not as a gap in the card. */
    border: 2px solid color-mix(in srgb, var(--accent) 34%, var(--outline-variant));
    background: color-mix(in srgb, var(--accent) 9%, var(--surface-container-lowest));
    box-shadow: 3px 3px 0 var(--accent-glow);
    padding: 18px 22px;
    line-height: 2;
    letter-spacing: .01em;
    /* Long equations wrap at the " + " joins, which are real break points
       a chemist would accept. `text-wrap: balance` is deliberately NOT used
       here: it optimises for equal line lengths and happily orphans the
       condition label onto the start of the next line. */
    text-wrap: pretty;
    font-size: clamp(.92rem, .5vw + .8rem, 1.04rem);
}
body[data-strand="chemistry"] .formula {
    border-color: color-mix(in srgb, var(--accent) 42%, var(--outline-variant));
    background: color-mix(in srgb, var(--accent) 11%, var(--surface-container-lowest));
}

/* Reaction conditions written on the arrow — "—heat→", "—sunlight,
   chlorophyll→". Authored as a muted span, they used to collide with
   the formula either side and sat in the wrong typeface entirely. */
.formula :is(.small, .muted, .cond) {
    font-family: var(--font);
    font-size: .74em;
    font-weight: 700;
    font-style: normal;
    letter-spacing: .01em;
    color: var(--accent-ink, var(--accent));
    padding: 0 .3em;
    white-space: nowrap;
}
/* A condition set above the arrow is authored as <sup class="cond">. A bare
   <sup> inside a formula stays a genuine exponent (c² in E = ∆mc²), so it
   keeps the mono face and the surrounding ink colour. */
.formula sup.cond { padding-left: .15em; }

/* ── Instrument labels inside a lab panel ────────────────────
   The species name is what a student reads first and the unit
   caption is what they read second; both were set smaller and
   fainter than the numbers they describe. "COEFFICIENT" was
   rendering at about 9px. */
.bal-formula {
    font-family: var(--font-mono);
    font-size: .95rem;
    font-weight: 700;
    color: var(--on-surface);
    letter-spacing: .01em;
}
.bal-stepper-label {
    font-size: .66rem;
    font-weight: 800;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--on-surface-variant);
}

/* Column headers over a two-sided panel ("Reactants" / "Products"). */
.lab-panel > .row.between > .small.muted {
    font-size: .7rem;
    font-weight: 800;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--on-surface-variant);
}


/* ══════════════════════════════════════════════════════════
   UNICODE SUB/SUPERSCRIPTS IN A MONOSPACE CONTEXT

   Chapters write chemical formulae with real Unicode characters —
   O₂, H₂O, H⁺, OH⁻ — rather than <sub> tags (assets/app.js even has
   a helper that converts digits to them). In a monospace face every
   glyph gets one full cell, so "₂" was rendering exactly as wide as
   "O": measured 9.61px against 5.16px in the UI font. That is the
   gap you see in "O 2".

   Fix: hand just those code points to a proportional system face,
   in front of the mono stack. Purely a metrics correction — the
   digits, letters and operators stay in Fira Code, and no chapter
   markup changes.
   ══════════════════════════════════════════════════════════ */
@font-face {
    font-family: 'SLX Notation';
    src: local('Helvetica Neue'), local('Arial'), local('Segoe UI'),
         local('Roboto'), local('DejaVu Sans');
    unicode-range: U+00B2-00B3, U+00B9, U+2070-209C;
}


/* ══════════════════════════════════════════════════════════
   REFERENCE TABLES — the third exhibit type

   Every .table-wrap in the chapters is a lookup table: the first
   column is the thing you are looking up, the rest is what you
   get. The base rule only ever gave it a hairline frame with no
   background, so on a .section the doodle layer showed straight
   through the rows, and the frame itself (--ghost-border, 15%
   alpha) was effectively invisible next to the sticker-bordered
   .card and .lab beside it.

   This layer makes the table read as a peer of those two: opaque
   plate, 2px accent border, hard offset shadow. Structure comes
   from a faint zebra rather than from heavier rules, and the key
   column carries the weight. Appended as a final override layer,
   same as every block above — no chapter markup changes.
   ══════════════════════════════════════════════════════════ */
.table-wrap {
    background: var(--surface-container-lowest);
    border: 2px solid color-mix(in srgb, var(--accent) 28%, var(--outline-variant));
    box-shadow: 4px 4px 0 var(--accent-glow);
    /* a horizontal swipe on the table must not trigger back-navigation */
    overscroll-behavior-x: contain;
    scrollbar-width: thin;
    transition: border-color .2s var(--ease), box-shadow .16s var(--ease-snap);
}
.table-wrap:hover, .table-wrap:focus-visible { border-color: var(--accent); }
.table-wrap:focus-visible { outline: none; box-shadow: 4px 4px 0 var(--accent-glow), 0 0 0 3px var(--accent-glow); }

/* A table authored inside a card or lab is already sitting on a
   sticker plate — a second border and shadow would stack two
   frames. Inside, it drops back to a plain inset panel. */
.card .table-wrap, .lab .table-wrap, .slx-zoom-body .table-wrap {
    border-width: 1px;
    border-color: var(--outline-variant);
    box-shadow: none;
    background: var(--surface);
}
.card .table-wrap:hover, .lab .table-wrap:hover { border-color: var(--outline-variant); }

/* border-collapse:collapse drops collapsed borders behind sticky
   cells, so the sticky first column below needs separate borders.
   Only bottom edges are drawn, so nothing doubles up. */
table.data { border-collapse: separate; border-spacing: 0; }

table.data th, table.data td {
    padding: 13px 18px;
    border-bottom: 1px solid color-mix(in srgb, var(--outline-variant) 55%, transparent);
}
/* Cells wrap to two or three lines in the wordier tables; top
   alignment keeps every row's first line on one baseline. */
table.data td { vertical-align: top; line-height: 1.5; }

table.data thead th {
    background: color-mix(in srgb, var(--accent) 9%, var(--surface-container-lowest));
    color: var(--accent-ink);
    border-bottom: 2px solid color-mix(in srgb, var(--accent) 30%, var(--outline-variant));
    white-space: nowrap;
    vertical-align: middle;
}
/* The header is uppercased, which is wrong for a unit or a token
   whose case is part of its meaning — "pH" became "PH" and "Ω m"
   became "Ω M". Those opt out. */
table.data thead th.u, table.data thead th .u { text-transform: none; letter-spacing: .02em; }

/* Zebra carries the row structure; the rules above stay faint so
   the two never fight. Set on the <tr> so the sticky first cell
   can inherit an opaque background from it. */
table.data tbody tr { background: var(--surface-container-lowest); }
table.data tbody tr:nth-child(even) { background: color-mix(in srgb, var(--accent) 6%, var(--surface-container-lowest)); }
table.data tbody tr:hover { background: color-mix(in srgb, var(--accent) 14%, var(--surface-container-lowest)); }
.card .table-wrap table.data tbody tr,
.lab .table-wrap table.data tbody tr { background: var(--surface); }
.card .table-wrap table.data tbody tr:nth-child(even),
.lab .table-wrap table.data tbody tr:nth-child(even) { background: color-mix(in srgb, var(--accent) 5%, var(--surface)); }
.card .table-wrap table.data tbody tr:hover,
.lab .table-wrap table.data tbody tr:hover { background: color-mix(in srgb, var(--accent) 12%, var(--surface)); }

/* The lookup key. */
table.data tbody td:first-child { font-weight: 600; color: var(--on-surface); }

/* Chapters bold the one row that breaks the pattern — the virtual
   image, the odd ion out. Give that emphasis the accent. */
table.data tbody strong, table.data tbody b { color: var(--accent-ink); font-weight: 700; }

table.data td.num {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    letter-spacing: -.01em;
}

/* Below this width a five-column table always scrolls. Pinning the
   key column keeps every row labelled while you scroll, and is its
   own cue that there is more table to the right. */
@media (max-width: 900px) {
    table.data th:first-child, table.data td:first-child {
        position: sticky; left: 0; z-index: 1;
        border-right: 1px solid color-mix(in srgb, var(--outline-variant) 70%, transparent);
    }
    table.data tbody td:first-child { background: inherit; }
    table.data thead th:first-child { z-index: 2; }
}

@media (max-width: 640px) {
    table.data { font-size: .86rem; }
    table.data th, table.data td { padding: 10px 13px; }
    table.data thead th { font-size: .68rem; letter-spacing: .06em; }
}

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