@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;500;600;700;800;900&display=swap');

:root {
    --bg: #f5efe7;
    --card-bg: #fffdf9;
    --surface: rgba(250, 246, 241, 0.96);
    --surface-2: rgba(244, 238, 230, 0.92);
    --border: rgba(221, 213, 200, 0.95);
    --accent: #3660ff;
    --accent-dark: #2448d8;
    --text: #1e1b2e;
    --text-muted: #6b6580;
    --red-soft: #b14637;
    --green: #00c78d;
    --amber: #f4b447;
    --purple: #8657d1;
    --radius: 12px;
    --shadow: 0 10px 30px rgba(30, 27, 46, 0.06);
    --shadow-card: 0 18px 48px rgba(30, 27, 46, 0.08);

    /* Multiplier for the app's own text size, from Settings → Accessibility.
       Set on <html> by applyPrefs() in app.js; 1 here so nothing depends on
       the preference having loaded. */
    --text-scale: 1;
}

/* Scaling the root font size is what makes one setting move the whole
   interface: the app sizes text in rem almost everywhere, so this carries
   through without touching a single component rule. Anything hard-coded in px
   deliberately stays put — mostly icons and borders, which should not grow
   with the text. Browser zoom still composes on top of this. */
html { font-size: calc(100% * var(--text-scale, 1)); }

/* The explicit override, for a shared school machine whose OS settings nobody
   can change. The ~60 prefers-reduced-motion blocks elsewhere in the app stay
   as they are and still work on their own; this is a blanket on top, because
   retro-fitting a second condition onto every one of them would leave the next
   animation somebody adds outside both. Near-zero rather than `none` so
   transitionend and animationend listeners still fire. */
html[data-reduce-motion] *,
html[data-reduce-motion] *::before,
html[data-reduce-motion] *::after {
    animation-duration: 0.001ms !important;
    animation-delay: 0s !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0s !important;
}
html[data-reduce-motion] { scroll-behavior: auto !important; }

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

body {
    font-family: "Nunito", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    background: linear-gradient(160deg, #f5efe7 0%, #efe6db 46%, #f7f1ea 100%);
    background-attachment: fixed;
    color: var(--text);
    min-height: 100vh;
    position: relative;
}

body::before {
    content: '';
    position: fixed;
    inset: 0;
    background:
        radial-gradient(circle at 18% 22%, rgba(54, 96, 255, 0.055) 0%, transparent 40%),
        radial-gradient(circle at 82% 68%, rgba(244, 180, 71, 0.08) 0%, transparent 38%);
    pointer-events: none;
    z-index: 0;
}

#app { position: relative; z-index: 1; }

::-webkit-scrollbar-track { background: rgba(239, 231, 222, 0.82); }
::-webkit-scrollbar-thumb { background: rgba(168, 160, 180, 0.42); border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: rgba(168, 160, 180, 0.56); }

/* ── Layout ───────────────────────────────────────────── */
#app {
    max-width: 1100px;
    margin: 0 auto;
    padding: 32px 24px;
}

/* ── Nav ──────────────────────────────────────────────── */
#nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 40px;
    padding: 16px 0;
    border-bottom: 1px solid var(--border);
}

#nav .logo {
    font-size: 1.25rem;
    font-weight: 800;
    color: var(--accent);
    letter-spacing: -0.3px;
    cursor: pointer;
    user-select: none;
}

.nav-create-wrap {
    position: relative;
}

.nav-create-menu {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    background: var(--card-bg, #fff);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.12);
    min-width: 160px;
    z-index: 5000;
    overflow: hidden;
}

.nav-create-item {
    display: block;
    width: 100%;
    padding: 9px 14px;
    border: none;
    background: none;
    text-align: left;
    font-size: 0.88rem;
    font-family: inherit;
    color: var(--text);
    cursor: pointer;
    transition: background 0.1s;
}
.nav-create-item:hover { background: var(--surface, #f4f4f4); }

/* Shown only to accounts that do not have it, so the menu says what will
   happen before it happens rather than after three steps of a composer. */
.nav-create-pro {
    margin-left: auto;
    padding: 1px 6px;
    border-radius: 999px;
    background: var(--accent);
    color: #fff;
    font-size: 0.62rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

/* ── Blue Acorn app switcher ──────────────────────────── */
.ba-apps {
    position: relative;
    display: flex;
    align-items: center;
}

.ba-apps-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.65);
    color: var(--text-muted);
    cursor: pointer;
    transition: background 0.15s, color 0.15s, border-color 0.15s, box-shadow 0.15s;
}
.ba-apps-btn:hover,
.ba-apps-btn[aria-expanded="true"] {
    background: #fff;
    border-color: var(--accent, #3660ff);
    color: var(--accent, #3660ff);
    box-shadow: 0 3px 10px rgba(54, 96, 255, 0.14);
}

.ba-apps-btn svg { transition: transform 0.2s ease; }
.ba-apps-btn:hover svg { transform: scale(1.08); }

.ba-apps-menu {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    width: 290px;
    background: var(--card-bg, #fff);
    border: 1px solid var(--border);
    border-radius: 16px;
    box-shadow: 0 16px 40px rgba(0,0,0,0.16);
    padding: 8px;
    z-index: 5000;
}
@keyframes ba-menu-pop {
    from { opacity: 0; transform: scale(0.95) translateY(-5px); }
    to   { opacity: 1; transform: scale(1) translateY(0); }
}

.ba-apps-head {
    display: block;
    padding: 8px 12px 6px;
    font-size: 0.68rem;
    font-weight: 800;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.ba-app-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    border-radius: 11px;
    text-decoration: none;
    color: var(--text);
    transition: background 0.13s;
}
.ba-app-item:hover { background: var(--surface, rgba(0,0,0,0.05)); }

.ba-app-icon {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    border-radius: 12px;
}
.ba-app-icon.edu  { background: linear-gradient(135deg, #eaf7ef, #d9f0e2); box-shadow: inset 0 0 0 1px rgba(76,187,127,0.25); }
.ba-app-icon.prod { background: linear-gradient(135deg, #eaf0ff, #dce7ff); box-shadow: inset 0 0 0 1px rgba(91,139,255,0.25); }
.ba-app-icon svg { transition: transform 0.18s ease; }
.ba-app-item:hover .ba-app-icon svg { transform: translateY(-1px) rotate(-4deg); }

.ba-app-copy {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
}
.ba-app-copy strong {
    font-size: 0.92rem;
    font-weight: 700;
    letter-spacing: -0.2px;
    line-height: 1.25;
}
.ba-app-copy small {
    font-size: 0.74rem;
    color: var(--text-muted);
    line-height: 1.3;
}

.ba-app-pill {
    flex: none;
    font-size: 0.64rem;
    font-weight: 800;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--accent, #3660ff);
    background: rgba(54, 96, 255, 0.1);
    padding: 3px 8px;
    border-radius: 999px;
}

.ba-app-ext {
    flex: none;
    color: var(--text-muted);
    opacity: 0;
    transform: translateX(-3px);
    transition: opacity 0.15s, transform 0.15s;
}
.ba-app-item:hover .ba-app-ext { opacity: 1; transform: translateX(0); }

#nav .nav-right {
    display: flex;
    gap: 12px;
    align-items: center;
}
/* Nothing in the bar shrinks: a squeezed button wraps its label to two lines
   and grows taller than everything beside it. The bar gives up the wordmark
   instead – see the narrow-screen block below. */
#nav .nav-right > * { flex-shrink: 0; }
#nav .nav-right .btn { white-space: nowrap; }
/* One height and one shape for everything on the right: the link track,
   Upgrade/Pro and Create are pills, the app switcher and avatar are circles,
   and all of them are --nav-h tall so the row lines up edge to edge. */
#nav { --nav-h: 36px; }
#nav .nav-right .btn-sm { height: var(--nav-h); padding: 0 14px; border-radius: 999px; }
#nav .ba-apps-btn,
#nav .nav-avatar { width: var(--nav-h); height: var(--nav-h); border-radius: 50%; }
.btn-create { display: inline-flex; align-items: center; gap: 6px; }
.btn-create svg { flex: none; transition: transform 0.2s ease; }
.btn-create:hover svg,
.btn-create[aria-expanded="true"] svg { transform: rotate(90deg); }
/* Belt and braces: whatever is in the row, it stays inside the page. */
#nav { flex-wrap: nowrap; min-width: 0; }
#nav .nav-right { flex: 0 0 auto; }
#nav .logo { flex-shrink: 0; }

/* Tablets and below: Students and Lessons are places, not actions, and they are
   the two things in the bar that cost the most width. They move into the account
   menu (see .nav-menu-only) rather than being squeezed until the row overflows. */
@media (max-width: 720px) {
    #nav > .nav-link,
    #nav .nav-right > .nav-link,
    #nav .nav-links { display: none; }
}

/* Phones: the mark alone carries the brand so Upgrade, Create, the app switcher
   and the avatar all keep their full size on one row. */
@media (max-width: 560px) {
    #app { padding: 24px 16px; }
    #nav { gap: 10px; margin-bottom: 28px; }
    #nav .logo { font-size: 0; gap: 0; }
    #nav .nav-right { gap: 8px; }
    #nav .nav-right .btn-sm { padding: 0 11px; font-size: 0.8rem; }
}

/* Small phones: the last of the slack comes out of the paddings, so Upgrade and
   Create keep their labels all the way down to a 320px screen. */
@media (max-width: 430px) {
    #app { padding: 24px 12px; }
    #nav { gap: 8px; }
    #nav .nav-right { gap: 6px; }
    #nav { --nav-h: 32px; }
    #nav .nav-right .btn-sm { padding: 0 9px; font-size: 0.78rem; }
}

#nav .user-email {
    font-size: 0.9rem;
    color: var(--text-muted);
    font-weight: 500;
}

/* ── Cards / surfaces ─────────────────────────────────── */
.card {
    background: linear-gradient(180deg, rgba(250, 246, 241, 0.96), rgba(244, 238, 230, 0.92));
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 32px;
    box-shadow: var(--shadow);
    transition: border-color 0.2s, box-shadow 0.2s;
}

.card:hover {
    border-color: rgba(54, 96, 255, 0.12);
    box-shadow: var(--shadow-card);
}

/* ── Auth view ────────────────────────────────────────── */
#auth-view {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 70vh;
}

.auth-card {
    width: 100%;
    max-width: 420px;
}

.auth-card h2 {
    font-size: 1.5rem;
    font-weight: 700;
    margin-bottom: 24px;
}

.auth-tabs {
    display: flex;
    gap: 0;
    margin-bottom: 28px;
    border-bottom: 2px solid var(--border);
}

.auth-tab {
    padding: 8px 20px;
    font-size: 0.95rem;
    font-weight: 500;
    color: var(--text-muted);
    cursor: pointer;
    border: none;
    background: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -2px;
    transition: color 0.15s, border-color 0.15s;
}

.auth-tab.active {
    color: var(--accent);
    border-bottom-color: var(--accent);
    font-weight: 700;
}

.auth-divider {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 22px 0 18px;
    color: var(--text-muted);
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.auth-divider::before,
.auth-divider::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--border);
}

.btn-baedu-login {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    width: 100%;
    padding: 10px 20px;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: #fff;
    color: var(--text);
    font-size: 0.95rem;
    font-weight: 600;
    text-decoration: none;
    box-sizing: border-box;
    transition: background 0.15s, border-color 0.15s, transform 0.1s, box-shadow 0.15s;
}

.btn-baedu-login:hover {
    background: rgba(76, 187, 127, 0.08);
    border-color: rgba(76, 187, 127, 0.5);
}

.btn-baedu-login:active { transform: scale(0.98); }

.btn-baedu-login-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: #4cbb7f;
    flex-shrink: 0;
}

/* ── Forms ────────────────────────────────────────────── */
.form-group {
    margin-bottom: 18px;
}

/* Label with a trailing action, e.g. Password / Forgot your password? */
.form-label-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
}

.auth-inline-link {
    padding: 0;
    border: none;
    background: none;
    font-family: inherit;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--accent);
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 3px;
}
.auth-inline-link:hover { color: var(--accent-dark); }

.auth-back {
    display: block;
    width: 100%;
    margin-top: 14px;
    text-align: center;
}

/* Two fields that belong together on one line – first and last name. Wraps on
   narrow screens so neither gets squeezed below a usable width. */
.form-row {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
}
.form-row .form-group {
    flex: 1 1 140px;
    min-width: 0;
}

.auth-note {
    margin: 0 0 16px;
    font-size: 0.83rem;
    line-height: 1.55;
    color: var(--text-muted);
}
.auth-note-ok {
    padding: 11px 13px;
    border-radius: 10px;
    background: rgba(0, 199, 141, 0.1);
    border: 1px solid rgba(0, 199, 141, 0.28);
    color: #027a5b;
}

.form-group label {
    display: block;
    font-size: 0.875rem;
    font-weight: 500;
    margin-bottom: 6px;
    color: var(--text-muted);
}

input[type="text"],
input[type="email"],
input[type="password"],
textarea,
select {
    width: 100%;
    padding: 10px 14px;
    border: 1px solid var(--border);
    border-radius: 8px;
    font-size: 1rem;
    font-family: inherit;
    color: var(--text);
    background: rgba(255, 255, 255, 0.72);
    transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
    outline: none;
    box-shadow: var(--shadow);
}

input[type="text"]:focus,
input[type="email"]:focus,
input[type="password"]:focus,
textarea:focus,
select:focus {
    border-color: rgba(54, 96, 255, 0.22);
    background: rgba(255, 255, 255, 0.9);
    box-shadow: 0 0 0 4px rgba(54, 96, 255, 0.08);
}

select { cursor: pointer; }

textarea { resize: vertical; min-height: 80px; }

/* ── Subject combobox ────────────────────────────────── */
.sc-wrap {
    position: relative;
}
.sc-input {
    padding-right: 30px !important;
}
/* transitions.dev input-clear: the mirror and fake placeholder have to sit
   exactly where the real glyphs do, so they match the input's own padding,
   font and radius. The dropdown lives outside .t-clear because .t-clear
   clips overflow to its own box. */
.t-clear {
    border-radius: 8px;
}
.t-clear-mirror,
.t-clear-placeholder {
    padding: 10px 30px 10px 14px;
    font-size: 1rem;
    font-family: inherit;
    line-height: 1.2;
}
.t-clear-mirror { color: var(--text); }
.t-clear-placeholder { color: #9a94a8; }
/* The fake placeholder owns the empty state – the native one would
   double-render underneath it. */
.t-clear > input::placeholder { color: transparent; }
html[data-theme="dark"] .t-clear-glow { mix-blend-mode: screen; --glow-opacity: 0.85; }
.sc-clear {
    position: absolute;
    right: 9px;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    font-size: 1rem;
    color: var(--text-muted);
    cursor: pointer;
    padding: 2px 5px;
    line-height: 1;
    border-radius: 4px;
}
.sc-clear:hover { color: var(--text); background: rgba(0,0,0,0.06); }
.sc-dropdown {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 8px 28px rgba(0,0,0,0.13);
    max-height: 260px;
    overflow-y: auto;
    z-index: 200;
    padding: 4px 0;
}
.sc-group {
    padding: 8px 14px 3px;
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--text-muted);
    pointer-events: none;
    user-select: none;
}
.sc-option {
    padding: 7px 14px;
    font-size: 0.9rem;
    cursor: pointer;
    color: var(--text);
    border-radius: 6px;
    margin: 0 4px;
    transition: background 0.1s;
}
.sc-option:hover,
.sc-option--hl {
    background: rgba(54, 96, 255, 0.08);
    color: #3660ff;
}
.sc-option--selected {
    font-weight: 500;
    color: #3660ff;
}
.sc-option mark {
    background: transparent;
    color: #3660ff;
    font-weight: 700;
}
.sc-empty {
    padding: 14px;
    font-size: 0.875rem;
    color: var(--text-muted);
    text-align: center;
}

/* The badge now floats on the subject field via .t-badge; keep the pill
   styling, drop the inline flow position. */
.sc-wrap > .t-badge { top: -9px; right: 6px; z-index: 1; }
.auto-badge {
    display: inline-block;
    font-size: 0.65rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #3660ff;
    background: rgba(54, 96, 255, 0.1);
    border: 1px solid rgba(54, 96, 255, 0.25);
    border-radius: 4px;
    padding: 1px 5px;
    margin-left: 4px;
    vertical-align: middle;
}

/* Spec-coverage indicator: shown under the board/subject/level row when we
   hold the board's real published specification for that combination, so the
   teacher can tell grounded generation from generic guidance before they
   generate. Green states a fact about our data, not a quality promise. */
.spec-coverage {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin-top: 10px;
    padding: 8px 10px;
    border-radius: 8px;
    font-size: 0.8rem;
    line-height: 1.4;
    border: 1px solid var(--border);
    background: var(--surface-2);
    color: var(--text-muted);
}
.spec-coverage[data-state="verified"] {
    border-color: rgba(0, 199, 141, 0.35);
    background: rgba(0, 199, 141, 0.08);
    color: var(--text);
}
/* Aligned (National Curriculum) states a weaker fact than verified, so it
    wears the app's blue rather than the green that means "extracted from the
    board's own PDF and checked against its source page". */
.spec-coverage[data-state="aligned"] {
    border-color: rgba(54, 96, 255, 0.35);
    background: rgba(54, 96, 255, 0.08);
    color: var(--text);
}
.spec-coverage-icon { flex: none; line-height: 1.4; }
.spec-coverage-title { font-weight: 700; }
.spec-coverage-detail { color: var(--text-muted); }
.spec-coverage-toggle {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 6px;
    font-weight: 600;
    cursor: pointer;
}

/* ── Buttons ──────────────────────────────────────────── */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 10px 20px;
    border: none;
    border-radius: 8px;
    font-size: 0.95rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s, opacity 0.15s, transform 0.1s;
    font-family: inherit;
}

.btn:active { transform: scale(0.98); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

.btn-primary {
    background: linear-gradient(90deg, var(--accent), #60a5fa);
    color: #fff;
    box-shadow: 0 10px 24px rgba(54, 96, 255, 0.22);
}
.btn-primary:hover:not(:disabled) {
    background: linear-gradient(90deg, var(--accent-dark), #4f94e8);
    box-shadow: 0 14px 28px rgba(54, 96, 255, 0.28);
    transform: translateY(-1px);
}

.btn-secondary {
    background: rgba(255, 255, 255, 0.72);
    border: 1px solid var(--border);
    color: var(--text);
}
.btn-secondary:hover:not(:disabled) {
    background: rgba(255, 255, 255, 0.9);
    border-color: rgba(54, 96, 255, 0.2);
}

.btn-sm {
    padding: 6px 14px;
    font-size: 0.85rem;
}

.pres-load-more {
    display: flex;
    margin: 20px auto 0;
}

.btn-upgrade {
    background: rgba(54, 96, 255, 0.08);
    border: 1px solid rgba(54, 96, 255, 0.25);
    color: var(--accent);
    font-weight: 600;
}
.btn-upgrade:hover:not(:disabled) {
    background: rgba(54, 96, 255, 0.14);
    border-color: rgba(54, 96, 255, 0.4);
}

.btn-upgrade { display: inline-flex; align-items: center; gap: 6px; }
.btn-upgrade-icon { flex: none; }

.btn-ghost {
    background: transparent;
    color: var(--text-muted);
}
.btn-ghost:hover:not(:disabled) {
    color: var(--text);
    background: rgba(255, 255, 255, 0.58);
    border: 1px solid var(--border);
}

/* ── Error / success messages ─────────────────────────── */
.msg {
    padding: 10px 14px;
    border-radius: 8px;
    font-size: 0.9rem;
    margin-bottom: 16px;
}
.msg-error { background: rgba(177, 70, 55, 0.08); color: var(--red-soft); border: 1px solid rgba(177, 70, 55, 0.2); }
.msg-success { background: rgba(0, 199, 141, 0.08); color: #0c7a5b; border: 1px solid rgba(0, 199, 141, 0.2); }

/* The billing and admin cards mark their error slot with .error, which had no
   rule at all – a failed checkout rendered as plain body text. Same treatment
   as .msg-error so a failure reads as one. */
.error {
    padding: 10px 14px;
    margin-bottom: 16px;
    border-radius: 8px;
    font-size: 0.9rem;
    background: rgba(177, 70, 55, 0.08);
    color: var(--red-soft);
    border: 1px solid rgba(177, 70, 55, 0.2);
}
.error[hidden] { display: none; }

/* ── Generator view ───────────────────────────────────── */
#generator-view { display: flex; flex-direction: column; gap: 28px; }

#generate-form { display: flex; flex-direction: column; gap: 16px; }

#generate-form textarea#topic {
    min-height: 60px;
    font-size: 1.05rem;
    font-weight: 500;
}

/* ── Theme picker ─────────────────────────────────────── */
.theme-picker {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.theme-btn {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 14px;
    border: 2px solid var(--border);
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.72);
    color: var(--text);
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    font-family: inherit;
    transition: border-color 0.15s, background 0.15s;
}

.theme-btn:hover { border-color: rgba(54, 96, 255, 0.3); }
.theme-btn.active { border-color: var(--accent); background: rgba(54, 96, 255, 0.08); color: var(--accent); font-weight: 700; }

.theme-dot {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    flex-shrink: 0;
    display: inline-block;
}

.optional-toggle {
    font-size: 0.875rem;
    color: var(--accent);
    cursor: pointer;
    background: none;
    border: none;
    padding: 0;
    font-family: inherit;
    text-align: left;
}

/* Height is owned by the t-acc grid track; the wrap itself always renders. */
.opt-content-wrap { display: block; }

/* ── Progress ─────────────────────────────────────────── */
/* The card every long generation reports itself through – presentations and
   booklets both. A class rather than #progress-bar's own rules because two
   screens showing the same kind of wait should not drift apart, which is
   exactly what happened when the booklet grew its own tinted panel. */
.gen-progress {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 20px;
    background: linear-gradient(180deg, rgba(250, 246, 241, 0.96), rgba(244, 238, 230, 0.92));
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

/* Spinner ↔ done check share one slot (transitions.dev icon-swap). */
.progress-icon { flex-shrink: 0; width: 18px; height: 18px; align-items: center; justify-content: center; }
.progress-icon .t-icon { display: flex; align-items: center; justify-content: center; }
.progress-done { width: 18px; height: 18px; color: var(--green); }

.spinner {
    width: 18px;
    height: 18px;
    border: 2px solid var(--border);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin 0.7s linear infinite;
    flex-shrink: 0;
}
@keyframes spin { to { transform: rotate(360deg); } }

#progress-text { font-size: 0.9rem; color: var(--text-muted); font-weight: 500; }

/* ── Generating shimmer (used in slide panel thumbs) ──── */
.slide-skeleton {
    width: 100%;
    height: 100%;
    background: linear-gradient(90deg, #e8eaf0 25%, #f4f5fb 50%, #e8eaf0 75%);
    background-size: 200% 100%;
    animation: shimmer 1.2s infinite;
}
@keyframes shimmer { to { background-position: -200% 0; } }

/* ── PowerPoint editor ────────────────────────────────── */
#ppt-editor {
    display: flex;
    flex-direction: column;
    gap: 0;
    background: rgba(255, 255, 255, 0.75);
    border: 1px solid rgba(255, 255, 255, 0.9);
    border-radius: 18px;
    overflow: hidden;
    box-shadow: 0 30px 70px rgba(30, 27, 46, 0.16);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
}

/* Window chrome (mac-style title bar) */
#ppt-chrome {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 11px 16px;
    background: rgba(244, 238, 230, 0.72);
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}

.ppt-chrome-title {
    flex: 1;
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--text-muted);
    letter-spacing: -0.2px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Toolbar */
#ppt-toolbar {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 16px;
    background: rgba(250, 246, 241, 0.85);
    border-bottom: 1px solid var(--border);
    flex-wrap: wrap;
    min-height: 52px;
}

.ppt-mode-tabs {
    display: flex;
    gap: 2px;
    background: rgba(30, 27, 46, 0.05);
    border-radius: 8px;
    padding: 3px;
    flex-shrink: 0;
}

.ppt-mode-btn {
    padding: 5px 14px;
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--text-muted);
    cursor: pointer;
    border: none;
    background: none;
    border-radius: 6px;
    font-family: inherit;
    transition: color 0.15s, background 0.15s, box-shadow 0.15s;
    white-space: nowrap;
}

/* Direct edit bar */
#ppt-direct-bar {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-wrap: wrap;
}

/* Image keyword row */
#ppt-img-row {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding-top: 8px;
    border-top: 1px solid var(--border);
    margin-top: 4px;
}

#ppt-img-keyword {
    flex: 1;
    height: 32px;
    padding: 0 12px;
    border: 1px solid var(--border);
    border-radius: 8px;
    font-size: 0.85rem;
    font-family: inherit;
    color: var(--text);
    background: #fff;
    outline: none;
    min-width: 0;
}

#ppt-img-keyword::placeholder { color: var(--text-muted); }
#ppt-img-keyword:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(54,96,255,0.12); }

/* Selected element highlight (set via JS inline style – just the rule for reference) */
/* .ppt-selected-el { outline: 2px dashed rgba(99,179,237,0.9) !important; outline-offset: 2px; } */

/* AI edit bar */
#ppt-ai-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 1;
    min-width: 260px;
}

#ppt-ai-input {
    flex: 1;
    height: 34px;
    padding: 0 12px;
    border: 1px solid var(--border);
    border-radius: 8px;
    font-size: 0.9rem;
    font-family: inherit;
    color: var(--text);
    background: #fff;
    outline: none;
    min-width: 0;
}

#ppt-ai-input::placeholder { color: var(--text-muted); }
#ppt-ai-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(54,96,255,0.12); }

/* Main ppt layout */
#ppt-layout {
    display: flex;
    height: 580px;
}

/* Left thumbnail panel */
#slide-panel-wrap {
    /* Travel for the t-panel-slide reveal – a rail this narrow only needs a
       short rise for the cross-blur to read as a full open. */
    --panel-translate-y: 24px;
    width: 168px;
    flex-shrink: 0;
    background: rgba(244, 238, 230, 0.55);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    overflow-x: hidden;
}

#slide-panel {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 14px 12px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    scrollbar-width: thin;
    scrollbar-color: rgba(168,160,180,0.4) transparent;
}

.slide-panel-footer {
    padding: 10px 12px;
    border-top: 1px solid var(--border);
    flex-shrink: 0;
}

#btn-add-slide {
    width: 100%;
    padding: 8px 0;
    background: rgba(255, 255, 255, 0.6);
    border: 1px dashed rgba(54, 96, 255, 0.35);
    border-radius: 8px;
    color: var(--accent);
    font-size: 0.76rem;
    font-weight: 700;
    cursor: pointer;
    transition: all 0.15s;
    font-family: inherit;
}

#btn-add-slide:hover {
    background: #fff;
    border-color: var(--accent);
    box-shadow: 0 4px 12px rgba(54, 96, 255, 0.12);
}

#btn-duplicate-slide {
    width: 100%;
    margin-top: 6px;
    padding: 6px 0;
    background: none;
    border: none;
    border-radius: 8px;
    color: var(--text-muted);
    font-size: 0.74rem;
    font-weight: 700;
    font-family: inherit;
    cursor: pointer;
}

#btn-duplicate-slide:hover { color: var(--accent); background: rgba(255, 255, 255, 0.6); }

.toolbar-step-btn {
    font-size: 0.8rem;
    font-weight: 800;
    letter-spacing: -0.02em;
}

/* Slide thumbnails */
.slide-thumb {
    width: 144px;
    height: 81px;   /* 144 × 9/16 */
    position: relative;
    border-radius: 8px;
    overflow: hidden;
    border: 2px solid var(--border);
    cursor: pointer;
    flex-shrink: 0;
    transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s;
    background: #fff;
    box-shadow: 0 2px 8px rgba(30, 27, 46, 0.06);
}

.slide-thumb:hover { border-color: rgba(54, 96, 255, 0.4); transform: translateY(-1px); box-shadow: 0 6px 16px rgba(30, 27, 46, 0.1); }
.slide-thumb.active { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(54, 96, 255, 0.18); }
.slide-thumb.generating { border-color: var(--amber); }
.slide-thumb.error { border-color: var(--red-soft); }
.slide-thumb.drag-src { opacity: 0.35; }

/* Sized for the 960×540 slide coordinate space – .slide-thumb-inner is scaled
   down 0.15x to fit the thumbnail, so values here must be proportionally large
   to stay legible after that transform. */
.slide-thumb-error-msg {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 48px;
    font-size: 2.6rem;
    font-weight: 600;
    line-height: 1.3;
    color: var(--red-soft);
    background: rgba(177, 70, 55, 0.06);
}

.slide-thumb-inner {
    width: 960px;
    height: 540px;
    transform: scale(0.15);   /* 144/960 */
    transform-origin: top left;
    pointer-events: none;
    user-select: none;
    /* The skeleton keeps pulsing until the slide actually arrives. */
    --pulse-count: infinite;
}

.slide-thumb-num {
    position: absolute;
    bottom: 3px;
    right: 5px;
    background: rgba(0,0,0,0.55);
    color: #fff;
    font-size: 0.6rem;
    padding: 1px 5px;
    border-radius: 3px;
    pointer-events: none;
}

/* Center stage */
#ppt-stage {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 28px 32px 18px;
    background:
        radial-gradient(circle at 50% 0%, rgba(54,96,255,0.05), transparent 60%),
        linear-gradient(180deg, rgba(244,238,230,0.45), rgba(245,239,231,0.65));
    gap: 12px;
    min-width: 0;
}

#stage-canvas {
    width: 100%;
    position: relative;
    aspect-ratio: 16/9;
    overflow: hidden;
    border-radius: 12px;
    border: 1px solid var(--border);
    box-shadow: 0 24px 60px rgba(30, 27, 46, 0.22);
    cursor: default;
}

#stage-slide-inner {
    position: absolute;
    top: 0;
    left: 0;
    width: 960px;
    height: 540px;
    transform-origin: top left;
}

#stage-info {
    font-size: 0.78rem;
    color: var(--text-muted);
    align-self: center;
}

/* Unsplash attribution. Sits under the canvas as editor chrome, not on the
   slide, so it credits the photographer without touching the teacher's design
   or their exported deck. Quiet by default, legible on hover. */
.stage-credits {
    font-size: 0.75rem;
    color: var(--text-muted);
    align-self: center;
    text-align: center;
    margin-top: 6px;
    max-width: 100%;
}

.stage-credits a {
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
    text-decoration-thickness: 1px;
}

.stage-credits a:hover,
.stage-credits a:focus-visible {
    color: var(--text);
}

/* ── Presentation editor: small screens ───────────────────
   The desktop layout is a fixed-height 3-pane PowerPoint clone (side
   thumbnail rail + toolbar + canvas). Below 720px we stack the panes,
   turn the thumbnail rail and toolbar into horizontally-scrolling strips
   instead of letting them wrap into many rows, and shrink the thumbnails
   to match. Drag/resize on the canvas is still mouse-only (see app.js) –
   this pass only fixes layout, not touch editing. */
@media (max-width: 720px) {
    #ppt-chrome {
        flex-wrap: wrap;
        padding: 9px 12px;
        gap: 8px;
    }
    .ppt-chrome-title {
        flex-basis: 100%;
        order: -1;
        white-space: normal;
    }

    #ppt-toolbar {
        padding: 8px 10px;
        gap: 8px;
    }

    #ppt-direct-bar {
        flex-wrap: nowrap;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: thin;
        padding-bottom: 2px;
    }
    #ppt-direct-bar > * { flex-shrink: 0; }

    .toolbar-btn {
        min-width: 34px;
        height: 34px;
    }

    #ppt-ai-bar { min-width: 0; }

    #ppt-layout {
        flex-direction: column;
        height: auto;
    }

    /* Thumbnail rail becomes a horizontal strip above the stage */
    #slide-panel-wrap {
        width: 100%;
        border-right: none;
        border-bottom: 1px solid var(--border);
    }

    #slide-panel {
        flex-direction: row;
        overflow-x: auto;
        overflow-y: hidden;
        padding: 10px;
    }

    .slide-thumb {
        width: 96px;
        height: 54px;
    }

    .slide-thumb-inner {
        transform: scale(0.1); /* 96/960 */
    }

    .slide-panel-footer { padding: 8px 10px; }

    #ppt-stage {
        padding: 16px 12px;
    }
}

#stage-error {
    width: 100%;
    max-width: 600px;
    margin: 0;
}

/* ── Presentations list ───────────────────────────────── */
#presentations-list { display: flex; flex-direction: column; gap: 12px; }

.pres-item {
    background: rgba(255, 255, 255, 0.56);
    border: 1px solid rgba(221, 213, 200, 0.82);
    border-radius: var(--radius);
    padding: 18px 24px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    cursor: pointer;
    transition: border-color 0.2s, box-shadow 0.2s, background 0.2s, transform 0.15s;
    box-shadow: 0 8px 20px rgba(30, 27, 46, 0.04);
}
.pres-item:hover {
    border-color: rgba(54, 96, 255, 0.14);
    background: rgba(255, 255, 255, 0.76);
    box-shadow: 0 12px 26px rgba(30, 27, 46, 0.06);
    transform: translateY(-1px);
}

.pres-item-title { font-weight: 600; font-size: 1rem; }
.pres-item-meta { font-size: 0.8rem; color: var(--text-muted); margin-top: 2px; }

.pres-delete-btn {
    margin-left: 12px;
    padding: 5px 10px;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    font-size: 1rem;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.15s, background 0.15s, color 0.15s;
    flex-shrink: 0;
}
.pres-item:hover .pres-delete-btn { opacity: 1; }
/* Hover is not available to a keyboard or a touch screen, and delete was
   otherwise unreachable by either – the button was in the row all along,
   just never made visible. */
.pres-item:focus-within .pres-delete-btn,
.pres-delete-btn:focus-visible { opacity: 1; }
@media (hover: none) {
    .pres-item .pres-delete-btn { opacity: 1; }
}
.pres-delete-btn:hover { background: #ffebee; color: #c62828; }

.status-badge {
    font-size: 0.75rem;
    padding: 3px 10px;
    border-radius: 99px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}
.status-done { background: rgba(0, 199, 141, 0.12); color: #0c7a5b; border: 1px solid rgba(0, 199, 141, 0.2); }
.status-generating { background: rgba(244, 180, 71, 0.14); color: #b7791f; border: 1px solid rgba(244, 180, 71, 0.24); }
.status-error { background: rgba(177, 70, 55, 0.08); color: var(--red-soft); border: 1px solid rgba(177, 70, 55, 0.18); }
.status-pending { background: rgba(221, 213, 200, 0.5); color: var(--text-muted); border: 1px solid var(--border); }

/* ── Plan approval ────────────────────────────────────── */
#plan-view {
    background: linear-gradient(180deg, rgba(250, 246, 241, 0.96), rgba(244, 238, 230, 0.92));
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 28px 32px;
    box-shadow: var(--shadow);
}

#plan-title {
    font-size: 1.35rem;
    font-weight: 700;
    margin-bottom: 6px;
}

#plan-subtitle {
    font-size: 0.9rem;
    color: var(--text-muted);
    margin-bottom: 24px;
}

#plan-slides {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-bottom: 24px;
}

.plan-card {
    background: rgba(255, 255, 255, 0.56);
    border: 1px solid rgba(221, 213, 200, 0.82);
    border-radius: 8px;
    padding: 14px 18px;
}

.plan-card-header {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 6px;
}

.plan-card-num {
    width: 24px;
    height: 24px;
    background: var(--accent);
    color: #fff;
    border-radius: 50%;
    font-size: 0.75rem;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.plan-card-template {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 2px 8px;
    border-radius: 4px;
}
.tpl-title      { background: #e3f2fd; color: #1565c0; }
.tpl-content    { background: #e8f5e9; color: #2e7d32; }
.tpl-two-column { background: #fff3e0; color: #e65100; }
.tpl-quote      { background: #f3e5f5; color: #6a1b9a; }
.tpl-section    { background: #212121; color: #fff; }
.tpl-image-text { background: #fce4ec; color: #880e4f; }

.plan-card-slide-title {
    font-weight: 600;
    font-size: 0.95rem;
    flex: 1;
}

.plan-card-points {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.plan-card-points li {
    font-size: 0.8rem;
    color: var(--text-muted);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 2px 8px;
}

.plan-card-image-keyword {
    margin-top: 6px;
    font-size: 0.8rem;
    color: #880e4f;
}

.plan-card-controls {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-left: auto;
    flex-shrink: 0;
}

.plan-card-move {
    width: 26px;
    height: 26px;
    border: 1px solid var(--border);
    border-radius: 5px;
    background: var(--surface);
    color: var(--text-muted);
    font-size: 0.8rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    line-height: 1;
    transition: background 0.1s, color 0.1s;
}
.plan-card-move:hover:not(:disabled) { background: var(--accent); color: #fff; border-color: var(--accent); }
.plan-card-move:disabled { opacity: 0.3; cursor: not-allowed; }

.plan-card-notes-toggle {
    font-size: 0.75rem;
    color: var(--text-muted);
    background: none;
    border: none;
    cursor: pointer;
    padding: 4px 0 0;
    font-family: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.plan-card-notes-toggle:hover { color: var(--accent); }

.plan-card-notes {
    margin-top: 8px;
}

.plan-card-notes textarea {
    width: 100%;
    min-height: 52px;
    font-size: 0.85rem;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: 6px;
    font-family: inherit;
    color: var(--text);
    background: var(--surface);
    resize: none;
    outline: none;
}
.plan-card-notes textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(0,119,255,0.1); }

#plan-actions {
    display: flex;
    gap: 12px;
}

/* ── Edit mode tabs ───────────────────────────────────── */
.edit-mode-tabs {
    display: flex;
    gap: 0;
    border-bottom: 2px solid var(--border);
}

.edit-mode-tab {
    padding: 6px 16px;
    font-size: 0.9rem;
    font-weight: 500;
    color: var(--text-muted);
    cursor: pointer;
    border: none;
    background: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -2px;
    font-family: inherit;
    transition: color 0.15s, border-color 0.15s;
}

.edit-mode-tab.active {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

/* ── Direct edit toolbar ──────────────────────────────── */
#direct-edit-toolbar {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 10px 0 12px;
    flex-wrap: wrap;
}

.toolbar-group {
    display: flex;
    gap: 2px;
    padding: 3px;
    background: var(--surface);
    border-radius: 8px;
}

.toolbar-btn {
    min-width: 28px;
    height: 28px;
    padding: 0 6px;
    border: 1px solid transparent;
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    font-size: 0.875rem;
    font-family: inherit;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.12s, color 0.12s;
    line-height: 1;
}

.toolbar-btn:hover:not(:disabled) { background: var(--bg); color: var(--accent); }

.toolbar-btn svg {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

.toolbar-sep {
    width: 1px;
    height: 22px;
    background: var(--border);
    margin: 0 4px;
    align-self: center;
    flex-shrink: 0;
}

.toolbar-color-wrap {
    display: inline-flex;
    align-items: stretch;
    border: 1px solid var(--border);
    border-radius: 5px;
    overflow: hidden;
    height: 30px;
    flex-shrink: 0;
}

.toolbar-color-apply {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    padding: 3px 7px;
    background: var(--surface);
    border: none;
    cursor: pointer;
    color: var(--text);
    transition: background 0.1s;
}

.toolbar-color-apply:hover { background: var(--border); }

.toolbar-color-letter {
    font-size: 0.9rem;
    font-weight: 700;
    line-height: 1;
}

.toolbar-color-bar {
    display: block;
    width: 100%;
    height: 3px;
    border-radius: 1px;
    background: #e11d48;
}

.toolbar-color-picker-label {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 5px;
    border-left: 1px solid var(--border);
    background: var(--surface);
    cursor: pointer;
    color: var(--text-muted);
    font-size: 0.6rem;
    transition: background 0.1s;
    position: relative;
}

.toolbar-color-picker-label:hover { background: var(--border); color: var(--text); }

.toolbar-color-chevron { pointer-events: none; }

#text-color-picker {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

/* Highlight edit preview when in direct edit mode */
#edit-slide-preview-wrap.direct-edit-mode {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(0,119,255,0.15);
}

/* ── Stage overlay (selection box, handles, snap lines) ─ */
#stage-overlay {
    position: absolute;
    top: 0; left: 0;
    width: 960px;
    height: 540px;
    transform-origin: top left;
    pointer-events: none;
    z-index: 20;
}

#sel-box {
    position: absolute;
    border: 2px solid rgba(99,179,237,0.85);
    box-sizing: border-box;
    pointer-events: none;
}

/* Resize handles */
.rh {
    position: absolute;
    width: 10px;
    height: 10px;
    background: #fff;
    border: 2px solid rgba(99,179,237,0.9);
    border-radius: 2px;
    pointer-events: all;
    box-sizing: border-box;
}

.rh-nw { top: -5px;  left: -5px;              cursor: nwse-resize; }
.rh-ne { top: -5px;  right: -5px;             cursor: nesw-resize; }
.rh-sw { bottom: -5px; left: -5px;            cursor: nesw-resize; }
.rh-se { bottom: -5px; right: -5px;           cursor: nwse-resize; }
.rh-n  { top: -5px;  left: calc(50% - 5px);  cursor: n-resize; }
.rh-s  { bottom: -5px; left: calc(50% - 5px);cursor: s-resize; }
.rh-w  { left: -5px; top: calc(50% - 5px);   cursor: w-resize; }
.rh-e  { right: -5px; top: calc(50% - 5px);  cursor: e-resize; }

/* Snap guide lines */
#snap-line-v {
    position: absolute;
    top: 0; width: 1px; height: 540px;
    background: rgba(0, 200, 255, 0.75);
    pointer-events: none;
}

#snap-line-h {
    position: absolute;
    left: 0; height: 1px; width: 960px;
    background: rgba(0, 200, 255, 0.75);
    pointer-events: none;
}

/* The order parts of a slide arrive in when presenting. Editor chrome: drawn
   in the overlay, in slide units, and never part of the slide. */
.reveal-badge {
    position: absolute;
    min-width: 18px;
    height: 18px;
    padding: 0 4px;
    box-sizing: border-box;
    border-radius: 9px;
    background: var(--accent);
    color: #fff;
    font-size: 11px;
    font-weight: 800;
    line-height: 18px;
    text-align: center;
    box-shadow: 0 1px 3px rgba(0,0,0,0.3);
    /* The overlay it sits in lets clicks through; the badge itself takes them. */
    pointer-events: auto;
    cursor: pointer;
}

.reveal-badge:hover,
.reveal-badge:focus-visible {
    background: var(--accent-dark);
    outline: 2px solid #fff;
}

/* ── Cropping a picture ───────────────────────────────── */
/* Drawn in the stage overlay, in slide units. The layer covers the slide so
   nothing underneath can be clicked while a crop is open; the frame's huge
   shadow is what dims everything outside it. */
#crop-layer {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: auto;
}

.crop-rect {
    position: absolute;
    box-sizing: border-box;
    border: 2px solid #fff;
    box-shadow: 0 0 0 2000px rgba(15, 15, 30, 0.55);
    cursor: move;
}

.crop-h {
    position: absolute;
    width: 14px;
    height: 14px;
    background: #fff;
    border: 2px solid var(--accent);
    border-radius: 3px;
    box-sizing: border-box;
}

.crop-h-nw { left: -8px;  top: -8px;    cursor: nwse-resize; }
.crop-h-n  { left: calc(50% - 7px); top: -8px;    cursor: ns-resize; }
.crop-h-ne { right: -8px; top: -8px;    cursor: nesw-resize; }
.crop-h-e  { right: -8px; top: calc(50% - 7px);  cursor: ew-resize; }
.crop-h-se { right: -8px; bottom: -8px; cursor: nwse-resize; }
.crop-h-s  { left: calc(50% - 7px); bottom: -8px; cursor: ns-resize; }
.crop-h-sw { left: -8px;  bottom: -8px; cursor: nesw-resize; }
.crop-h-w  { left: -8px;  top: calc(50% - 7px);  cursor: ew-resize; }

.crop-bar {
    position: absolute;
    display: flex;
    gap: 6px;
    white-space: nowrap;
}

/* ── Context menu ─────────────────────────────────────── */
#thumb-ctx-menu,
#slide-bg-ctx-menu,
#reveal-badge-menu,
#slide-ctx-menu {
    position: fixed;
    background: #1a1a2e;
    border: 1px solid rgba(255,255,255,0.14);
    border-radius: 8px;
    padding: 4px 0;
    min-width: 160px;
    /* Above the modal layer (10000): the template designer puts the slide
       inside a modal, and a context menu that opens behind it is a menu the
       teacher cannot use at all. */
    z-index: 10100;
    box-shadow: 0 8px 32px rgba(0,0,0,0.5);
    user-select: none;
}

.ctx-item {
    display: block;
    width: 100%;
    padding: 7px 16px;
    font-size: 0.83rem;
    font-family: inherit;
    color: rgba(255,255,255,0.82);
    background: none;
    border: none;
    cursor: pointer;
    text-align: left;
    transition: background 0.1s;
    white-space: nowrap;
}

.ctx-item:hover { background: rgba(255,255,255,0.1); color: #fff; }
.ctx-item.ctx-danger { color: #fc8181; }
.ctx-item.ctx-danger:hover { background: rgba(252,129,129,0.1); }

.ctx-sep {
    height: 1px;
    background: rgba(255,255,255,0.1);
    margin: 4px 0;
}

/* ── BlueAcorn Education export ───────────────────────── */
.btn-baedu {
    background: #0ea5e9;
    color: #fff;
    white-space: nowrap;
}
.btn-baedu:hover:not(:disabled) { background: #0284c7; }

/* Modal overlay */
#send-modal,
#settings-modal {
    position: fixed;
    inset: 0;
    z-index: 10000;
}

/* Fullscreen presentation mode */
#present-overlay {
    position: fixed;
    inset: 0;
    /* `inset: 0` on a fixed element sizes to the *static* layout viewport.
       On mobile, while the browser's address bar/toolbar is showing, that
       static viewport is taller than what's actually visible, so the
       bottom of the overlay renders behind the browser chrome – cut off.
       `dvh`/`dvw` track the real, dynamic visible area instead. */
    height: 100dvh;
    width: 100dvw;
    z-index: 100000;
    background: #000;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}

#present-stage {
    width: 960px;
    height: 540px;
    /* #present-overlay is a flex container; without this, a flex item
       narrower than its content's hypothetical size (960px) gets shrunk to
       fit – on any screen under 960px wide, that's every phone. Only the
       width shrinks (flex-shrink acts on the main axis), so the box becomes
       narrower than 540px-tall while still full height, and JS's
       transform: scale() then scales that already-squashed box, clipping
       slide content that assumed a true 960-wide layout. */
    flex-shrink: 0;
    transform-origin: center center;
    overflow: hidden;
    box-shadow: 0 0 60px rgba(0, 0, 0, 0.5);
    cursor: default;
}

/* A part of the slide that has not been clicked in yet. Hidden rather than
   removed, so the rest of the slide is already where it will end up and nothing
   moves as each part arrives. Only ever set here: the editor, the exports and
   the pupil's read page show every slide whole. */
#present-stage .is-unrevealed {
    visibility: hidden;
}

#present-stage .slide {
    width: 960px;
    height: 540px;
}

.present-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 48px;
    height: 48px;
    border-radius: 50%;
    border: none;
    background: rgba(255, 255, 255, 0.1);
    color: #fff;
    font-size: 1.1rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s, opacity 0.15s;
    opacity: 0;
}

#present-overlay:hover .present-nav,
#present-overlay:hover .present-exit,
#present-overlay:hover #present-counter {
    opacity: 1;
}

.present-nav:hover { background: rgba(255, 255, 255, 0.22); }
.present-nav:disabled { opacity: 0.15 !important; cursor: default; }

.present-nav-prev { left: calc(24px + env(safe-area-inset-left, 0px)); }
.present-nav-next { right: calc(24px + env(safe-area-inset-right, 0px)); }

.present-exit {
    position: absolute;
    top: calc(20px + env(safe-area-inset-top, 0px));
    right: calc(24px + env(safe-area-inset-right, 0px));
    transform: none;
    width: 40px;
    height: 40px;
    font-size: 0.95rem;
}

#present-counter {
    position: absolute;
    bottom: calc(20px + env(safe-area-inset-bottom, 0px));
    left: 50%;
    transform: translateX(-50%);
    color: rgba(255, 255, 255, 0.75);
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    opacity: 0;
    transition: opacity 0.15s;
}

/* Unsplash credit during a presentation. Unlike the nav chrome this stays
   visible rather than revealing on hover: it is an attribution requirement,
   and one hidden behind a mouse move is not an attribution. Tucked into the
   bottom-left corner and kept faint so it reads as a caption, not as part of
   the slide – the teacher's design is untouched. */
#present-credits {
    position: absolute;
    bottom: calc(14px + env(safe-area-inset-bottom, 0px));
    left: calc(16px + env(safe-area-inset-left, 0px));
    max-width: 45vw;
    color: rgba(255, 255, 255, 0.45);
    font-size: 0.7rem;
    letter-spacing: 0.01em;
    pointer-events: none;
}

/* The nav arrows, exit button and slide counter only reveal on
   #present-overlay:hover – on a touch screen there is no hover, so they'd
   stay invisible for the entire presentation with no visible way to go
   back or exit. Keep them shown permanently on touch devices instead. */
@media (hover: none) {
    .present-nav,
    #present-counter {
        opacity: 1;
    }
}

/* ── Send to BlueAcorn Education ──────────────────────
   The settings modal shows the bridge between the two apps; this is the
   moment something crosses it. The item rides the wire while it uploads
   and lands on the far side, so the wait has somewhere to go. */

#send-modal {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
}

.send-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(30, 27, 46, 0.4);
    backdrop-filter: blur(5px);
    animation: set-fade 0.18s ease-out;
}

.send-card {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 452px;
    max-height: 100%;
    overflow-y: auto;
    border-radius: 18px;
    border: 1px solid var(--border);
    background: var(--card-bg);
    box-shadow: 0 32px 80px rgba(30, 27, 46, 0.22);
}

.send-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 20px 20px 0 26px;
}

.send-title {
    font-size: 1.02rem;
    font-weight: 800;
    letter-spacing: -0.015em;
    color: var(--text);
}

.send-close {
    width: 30px;
    height: 30px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 8px;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
    transition: background 0.14s, color 0.14s;
}
.send-close:hover { background: rgba(30, 27, 46, 0.06); color: var(--text); }

.send-body { padding: 4px 26px 24px; }

/* ── The stage ────────────────────────────────────────
   The work itself, as a sheet on a small stack. Waiting for the upload
   is a scan line reading down the page and a meter running underneath;
   arrival is a stamp. phase – waiting | ready | sending | done | blocked */
.send-stage {
    padding: 22px 0 4px;
    /* Worksheets and quizzes are pages; a deck is a slide. */
    --sheet-w: 132px;
    --sheet-h: 164px;
}
.send-stage[data-kind="presentation"] {
    --sheet-w: 186px;
    --sheet-h: 116px;
}

.send-sheet {
    position: relative;
    width: var(--sheet-w);
    height: var(--sheet-h);
    margin: 0 auto 20px;
    transition: transform 0.45s cubic-bezier(0.34, 1.4, 0.64, 1);
}
.send-stage[data-phase="done"]    .send-sheet { transform: translateY(-2px) scale(1.02); }
.send-stage[data-phase="blocked"] .send-sheet { opacity: 0.45; filter: grayscale(1); }

/* the two sheets behind, fanned */
.send-sheet-back {
    position: absolute;
    inset: 0;
    border-radius: 10px;
    background: var(--card-bg);
    border: 1px solid var(--border);
    transition: transform 0.5s cubic-bezier(0.34, 1.4, 0.64, 1);
}
.send-sheet-back.s1 { transform: translate(6px, 5px) rotate(2.2deg); }
.send-sheet-back.s2 { transform: translate(11px, 10px) rotate(4.4deg); }
.send-stage[data-phase="sending"] .send-sheet-back.s1 { transform: translate(3px, 2px) rotate(1deg); }
.send-stage[data-phase="sending"] .send-sheet-back.s2 { transform: translate(6px, 4px) rotate(2deg); }

.send-sheet-face {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    gap: 7px;
    padding: 16px 15px;
    border-radius: 10px;
    background: #fffefb;
    border: 1px solid var(--border);
    box-shadow: 0 12px 28px rgba(30, 27, 46, 0.12);
    overflow: hidden;
}

.send-sheet-kind {
    flex-shrink: 0;
    display: block;
    width: 22px;
    height: 22px;
    color: var(--accent);
    margin-bottom: 2px;
}
.send-sheet-kind svg { width: 22px; height: 22px; }

/* flex-shrink:0 matters – without it these collapse as the sheet animates. */
.send-sheet-head {
    flex-shrink: 0;
    height: 7px;
    width: 62%;
    border-radius: 3px;
    background: rgba(30, 27, 46, 0.34);
}

.send-sheet-line {
    flex-shrink: 0;
    height: 4px;
    border-radius: 2px;
    background: rgba(30, 27, 46, 0.13);
}

/* the scan line: reads the page while the upload is in flight */
.send-sheet-beam {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    height: 42px;
    opacity: 0;
    background: linear-gradient(180deg,
        rgba(54, 96, 255, 0) 0%,
        rgba(54, 96, 255, 0.14) 55%,
        rgba(54, 96, 255, 0.55) 96%,
        rgba(54, 96, 255, 0.9) 100%);
}
.send-stage[data-phase="sending"] .send-sheet-beam {
    opacity: 1;
    animation: send-scan 1.35s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}

/* Arrival: the page settles under a wash of green and the check draws on. */
.send-sheet-stamp {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 10px;
    background: rgba(236, 253, 247, 0.82);
    box-shadow: inset 0 0 0 2px var(--green);
    color: var(--green);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s ease, visibility 0s linear 0.3s;
}
.send-stage[data-phase="done"] .send-sheet-stamp {
    opacity: 1;
    visibility: visible;
    transition: opacity 0.3s ease, visibility 0s;
}

.send-sheet-stamp svg {
    width: 46px;
    height: 46px;
    stroke-dasharray: 26;
    stroke-dashoffset: 26;
}
.send-stage[data-phase="done"] .send-sheet-stamp svg {
    animation: send-check 0.45s cubic-bezier(0.65, 0, 0.35, 1) 0.16s forwards;
}

@keyframes send-check {
    to { stroke-dashoffset: 0; }
}

/* meter – indeterminate while it waits, full on arrival */
.send-meter {
    position: relative;
    height: 3px;
    width: var(--sheet-w);
    margin: 0 auto 16px;
    border-radius: 3px;
    background: rgba(30, 27, 46, 0.09);
    overflow: hidden;
    opacity: 0;
    transition: opacity 0.2s;
}
.send-stage[data-phase="sending"] .send-meter,
.send-stage[data-phase="done"] .send-meter { opacity: 1; }

.send-meter-fill {
    position: absolute;
    inset: 0;
    border-radius: 3px;
    background: var(--accent);
    transform: translateX(-100%);
}
.send-stage[data-phase="sending"] .send-meter-fill { animation: send-run 1.35s ease-in-out infinite; }
.send-stage[data-phase="done"] .send-meter-fill { transform: none; background: var(--green); }

@keyframes send-scan {
    0%   { transform: translateY(-42px); }
    100% { transform: translateY(var(--sheet-h)); }
}
@keyframes send-run {
    0%   { transform: translateX(-100%); }
    100% { transform: translateX(100%); }
}

/* What is being sent */
.send-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    text-align: center;
    padding-bottom: 18px;
    border-bottom: 1px solid var(--border);
    margin-bottom: 18px;
}
.send-item strong {
    font-size: 0.95rem;
    font-weight: 800;
    color: var(--text);
    letter-spacing: -0.01em;
}
.send-item span {
    font-size: 0.76rem;
    font-weight: 600;
    color: var(--text-muted);
}

.send-status {
    font-size: 0.85rem;
    line-height: 1.55;
    color: var(--text-muted);
    text-align: center;
    margin-bottom: 16px;
    text-wrap: balance;
}

.send-done {
    font-size: 0.9rem;
    font-weight: 800;
    color: #027a5b;
    text-align: center;
    margin-bottom: 16px;
}

.send-label {
    display: block;
    font-size: 0.68rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: var(--text-muted);
    margin-bottom: 7px;
}

.send-select {
    width: 100%;
    padding: 10px 12px;
    margin-bottom: 16px;
    border: 1px solid var(--border);
    border-radius: 10px;
    font-family: inherit;
    font-size: 0.88rem;
    font-weight: 600;
    background: #fff;
    color: var(--text);
    cursor: pointer;
}

.send-seg {
    display: flex;
    gap: 3px;
    padding: 3px;
    margin-bottom: 18px;
    background: rgba(30, 27, 46, 0.055);
    border-radius: 10px;
}

.send-seg-btn {
    flex: 1;
    padding: 8px 10px;
    border: none;
    border-radius: 8px;
    background: none;
    font-family: inherit;
    font-size: 0.83rem;
    font-weight: 700;
    color: var(--text-muted);
    cursor: pointer;
    transition: background 0.15s, color 0.12s, box-shadow 0.15s;
}
.send-seg-btn.is-on {
    background: var(--card-bg);
    color: var(--text);
    box-shadow: 0 1px 4px rgba(30, 27, 46, 0.14);
}

.send-go {
    display: block;
    width: 100%;
    text-align: center;
    text-decoration: none;
}
a.send-go { padding: 11px 16px; }

.send-quiet-btn {
    display: block;
    width: 100%;
    margin-top: 12px;
    padding: 0;
    border: none;
    background: none;
    font-family: inherit;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--text-muted);
    text-align: center;
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 3px;
}
.send-quiet-btn:hover { color: var(--text); }

#send-modal :focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 6px;
}

@media (max-width: 620px) {
    #send-modal { padding: 12px; }
    .send-head { padding: 16px 14px 0 20px; }
    .send-body { padding: 4px 20px 20px; }
}

@media (prefers-reduced-motion: reduce) {
    .send-card, .send-backdrop,
    .send-stage[data-phase="sending"] .send-sheet-beam,
    .send-stage[data-phase="sending"] .send-meter-fill { animation: none; }
    .send-sheet, .send-sheet-back, .send-sheet-stamp { transition: none; }
    .send-stage[data-phase="done"] .send-sheet-stamp svg { animation: none; stroke-dashoffset: 0; }
    /* Keep a visible in-progress cue without the sweep. */
    .send-stage[data-phase="sending"] .send-sheet-beam { opacity: 0.5; top: auto; bottom: 0; }
    .send-stage[data-phase="sending"] .send-meter-fill { transform: translateX(-40%); }
}

/* ── Worksheet and booklet preview ────────────────────
   The printed sheet, at printed proportions, in whichever accessible layout
   is selected. Tall and narrow on purpose: it is A4, and showing it in a
   landscape box would misrepresent how much fits on a page — which is the
   main thing a teacher is checking when they pick Large print.

   Both previews are the same surface and share every rule below. The booklet
   one was left out of this selector once and, with nothing positioning it,
   fell to the bottom of the page with a backdrop that covered only its own
   box instead of the screen. */

#ws-preview-modal,
#bk-preview-modal,
#lsn-set-modal,
#lsn-edit-modal {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
}

.wsp-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(30, 27, 46, 0.4);
    backdrop-filter: blur(5px);
}

.wsp-shell {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    width: 100%;
    max-width: 800px;
    height: 100%;
    max-height: 900px;
    border-radius: 18px;
    border: 1px solid var(--border);
    background: var(--card-bg);
    box-shadow: 0 32px 80px rgba(30, 27, 46, 0.22);
    overflow: hidden;
}

.wsp-head {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 16px 18px;
    border-bottom: 1px solid var(--border);
}

.wsp-title { font-size: 1rem; font-weight: 700; }

/* "Large print on cream" – names the layout being shown, so the preview is
   never ambiguous about which of the options produced it. */
.wsp-desc {
    flex: 1;
    min-width: 0;
    font-size: 0.82rem;
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.wsp-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    flex-shrink: 0;
    border: none;
    border-radius: 8px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
}
.wsp-close:hover { background: var(--surface-2); color: var(--text); }

.wsp-frame {
    flex: 1;
    width: 100%;
    border: 0;
    /* The document inside paints its own paper colour, including the tint. */
    background: #fff;
}

/* One booklet makes two documents, so its preview head carries a switch
   between them where the worksheet's carries a description. */
.bkp-kind-toggle {
    display: flex;
    gap: 2px;
    padding: 2px;
    border-radius: 9px;
    background: var(--surface-2);
}
.bkp-kind-btn { border-radius: 7px; }
/* The worksheet head has a flex:1 description to push the close button to the
   end; this one has a toggle that must not stretch, so the button claims the
   gap itself. */
#bk-preview-modal .wsp-close { margin-left: auto; }
.bkp-kind-btn.active {
    background: var(--card-bg);
    color: var(--text);
    box-shadow: 0 1px 3px rgba(30, 27, 46, 0.12);
}

/* Building a booklet preview means rendering the real PDF server-side, which
   takes a second or two – long enough that an empty frame reads as broken. */
.bkp-status {
    padding: 14px 18px;
    font-size: 0.9rem;
    color: var(--text-muted);
    text-align: center;
    border-bottom: 1px solid var(--border);
}

@media (max-width: 560px) {
    #ws-preview-modal,
    #bk-preview-modal { padding: 0; }
    .wsp-shell { max-height: none; border-radius: 0; border: 0; }
}

/* ── Settings ─────────────────────────────────────────
   A two-pane settings surface. The one loud element is the
   bridge in Connections: two app tiles and the wire between
   them, which is literally what this screen governs. */

#settings-modal {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    /* `inset: 0` sizes to the static layout viewport, which on mobile is
       taller than what's visible while the browser chrome is showing.
       `dvh` tracks the real visible area. (Over-constrained abspos: the
       explicit height wins and `bottom` is ignored.) */
    height: 100dvh;
}

.set-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(30, 27, 46, 0.4);
    backdrop-filter: blur(5px);
    animation: set-fade 0.18s ease-out;
}

.set-shell {
    position: relative;
    z-index: 1;
    display: grid;
    grid-template-columns: 212px minmax(0, 1fr);
    /* Let the single row shrink below its content's size so the panes'
       own scroll containers take over instead of the shell clipping them. */
    grid-template-rows: minmax(0, 1fr);
    width: 100%;
    max-width: 680px;
    max-height: 100%;
    border-radius: 18px;
    border: 1px solid var(--border);
    background: var(--card-bg);
    box-shadow: 0 32px 80px rgba(30, 27, 46, 0.22);
    overflow: hidden;
}

/* Rail – identity, sections, sign out */
.set-rail {
    display: flex;
    flex-direction: column;
    gap: 18px;
    min-height: 0;
    overflow-y: auto;
    padding: 22px 14px;
    background: linear-gradient(180deg, #f2ebe1, #ece4d8);
    border-right: 1px solid var(--border);
}

.set-id {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 0 6px;
    min-width: 0;
}

.set-id-avatar {
    width: 30px;
    height: 30px;
    flex-shrink: 0;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    font-size: 0.8rem;
    font-weight: 800;
    display: flex;
    align-items: center;
    justify-content: center;
}

.set-id-email {
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.set-rail-menu { display: flex; flex-direction: column; }
.set-nav { display: flex; flex-direction: column; gap: 2px; }

.set-nav-item {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    padding: 9px 11px;
    border: none;
    border-radius: 9px;
    background: none;
    font-family: inherit;
    font-size: 0.86rem;
    font-weight: 700;
    color: var(--text-muted);
    text-align: left;
    cursor: pointer;
    transition: background 0.14s, color 0.14s;
}
.set-nav-item svg { flex-shrink: 0; opacity: 0.75; }
.set-nav-item:hover { background: rgba(30, 27, 46, 0.05); color: var(--text); }
.set-nav-item.is-active {
    background: var(--card-bg);
    color: var(--text);
    box-shadow: 0 1px 3px rgba(30, 27, 46, 0.08);
}
.set-nav-item.is-active svg { opacity: 1; color: var(--accent); }

/* Sign out sits in the same column as the sections – it is one of
   the things you come to the rail to do – but reads a step quieter. */
.set-signout {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    margin-top: 6px;
    padding: 9px 11px;
    border: none;
    border-radius: 9px;
    background: none;
    font-family: inherit;
    font-size: 0.84rem;
    font-weight: 600;
    color: var(--text-muted);
    text-align: left;
    cursor: pointer;
    transition: background 0.14s, color 0.14s;
    position: relative;
}
.set-signout::before {
    content: '';
    position: absolute;
    top: -4px;
    left: 11px;
    right: 11px;
    border-top: 1px solid var(--border);
}
.set-signout svg { flex-shrink: 0; opacity: 0.7; }
.set-signout:hover { background: rgba(177, 70, 55, 0.08); color: var(--red-soft); }
.set-signout:hover svg { opacity: 1; }

/* Pane */
.set-pane {
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
}
/* The comfortable floor is an aesthetic minimum, not a requirement – only
   claim it when the viewport can actually spare the room. */
@media (min-height: 560px) {
    .set-pane { min-height: 372px; }
}

.set-pane-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 20px 20px 14px 26px;
}

.set-pane-title {
    font-size: 1.02rem;
    font-weight: 800;
    letter-spacing: -0.015em;
    color: var(--text);
}

.set-close {
    width: 30px;
    height: 30px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 8px;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
    transition: background 0.14s, color 0.14s;
}
.set-close:hover { background: rgba(30, 27, 46, 0.06); color: var(--text); }

.set-pane-body {
    flex: 1;
    min-height: 0;
    padding: 0 26px 24px;
    overflow-y: auto;
    overscroll-behavior: contain;
}

.set-view { animation: set-view-in 0.2s ease-out; }

/* Rows */
.set-group + .set-group { margin-top: 22px; }

.set-group-title {
    font-size: 0.68rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: var(--text-muted);
    margin-bottom: 8px;
}

.set-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding: 12px 0;
    border-top: 1px solid var(--border);
}
.set-row:first-child { border-top: none; }
.set-row-main { min-width: 0; }

.set-row-label {
    font-size: 0.88rem;
    font-weight: 700;
    color: var(--text);
}

.set-row-sub {
    font-size: 0.78rem;
    line-height: 1.45;
    color: var(--text-muted);
    margin-top: 2px;
}

.set-pill {
    flex-shrink: 0;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 0.7rem;
    font-weight: 800;
    letter-spacing: 0.02em;
    background: rgba(30, 27, 46, 0.07);
    color: var(--text-muted);
}
.set-pill.is-on { background: rgba(0, 199, 141, 0.14); color: #027a5b; }
.set-pill.is-pro { background: rgba(244, 180, 71, 0.2); color: #8a5c05; }

.set-linkbtn {
    flex-shrink: 0;
    padding: 0;
    border: none;
    background: none;
    font-family: inherit;
    font-size: 0.82rem;
    font-weight: 700;
    color: var(--accent);
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 3px;
}
.set-linkbtn:hover { color: var(--accent-dark); }

.set-quiet-btn {
    display: block;
    width: 100%;
    margin-top: 14px;
    padding: 0;
    border: none;
    background: none;
    font-family: inherit;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--text-muted);
    text-align: center;
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 3px;
}
.set-quiet-btn:hover { color: var(--red-soft); }
.set-quiet-btn:disabled { opacity: 0.45; cursor: not-allowed; text-decoration: none; }
.set-quiet-btn:disabled:hover { color: var(--text-muted); }

.set-note {
    font-size: 0.85rem;
    line-height: 1.55;
    color: var(--text-muted);
    text-align: center;
    margin: 0 auto 18px;
    max-width: 42ch;
    text-wrap: balance;
}

.set-alert {
    border-radius: 10px;
    padding: 10px 13px;
    font-size: 0.81rem;
    line-height: 1.45;
    background: rgba(244, 180, 71, 0.14);
    border: 1px solid rgba(217, 119, 6, 0.28);
    color: #8a5c05;
    margin-bottom: 16px;
}

.set-loading {
    font-size: 0.88rem;
    color: var(--text-muted);
    padding: 6px 0;
}

/* Set-a-password dialog – opened from both the billing panel and Settings,
   so it sits above the settings modal's z-index. */
#password-modal,
#reset-modal {
    position: fixed;
    inset: 0;
    z-index: 10002;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}
#password-modal[hidden],
#reset-modal[hidden] { display: none; }

.pw-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(30, 27, 46, 0.45);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    animation: set-fade 0.18s ease-out;
}

.pw-card {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 410px;
    max-height: 100%;
    overflow-y: auto;
    border-radius: 18px;
    border: 1px solid var(--border);
    background: var(--card-bg);
    box-shadow: 0 32px 80px rgba(30, 27, 46, 0.22);
}

.pw-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 1.05rem 1.25rem 0.85rem;
    border-bottom: 1px solid var(--border);
}
.pw-title {
    margin: 0;
    font-size: 1.02rem;
    font-weight: 800;
    letter-spacing: -0.01em;
    color: var(--text);
}

.pw-body {
    display: flex;
    flex-direction: column;
    padding: 1.15rem 1.25rem 1.25rem;
}

.pw-lede {
    margin: 0 0 1.1rem;
    font-size: 0.83rem;
    line-height: 1.55;
    color: var(--text-muted);
}
.pw-lede strong { color: var(--text); font-weight: 700; }

.pw-label {
    margin-bottom: 5px;
    font-size: 0.75rem;
    font-weight: 700;
    color: var(--text);
}

.pw-input {
    width: 100%;
    margin-bottom: 0.8rem;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--card-bg);
    font-family: inherit;
    font-size: 0.9rem;
    color: var(--text);
}
.pw-input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(54, 96, 255, 0.14);
}

.pw-hint {
    margin: -0.35rem 0 0;
    font-size: 0.73rem;
    color: var(--text-muted);
}

.pw-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.6rem;
    margin-top: 1.15rem;
}

/* Dead reset link – a terminal message with a single way forward, so it
   centres rather than borrowing the form's label-left / action-right rhythm. */
.pw-dead {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 2rem 1.6rem 1.75rem;
}

.pw-dead-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 46px;
    margin-bottom: 1rem;
    border-radius: 50%;
    background: rgba(244, 180, 71, 0.16);
    color: #8a5c05;
}

.pw-dead-title {
    margin: 0 0 0.5rem;
    font-size: 1.08rem;
    font-weight: 800;
    letter-spacing: -0.01em;
    color: var(--text);
}

.pw-dead-copy {
    margin: 0 0 1.5rem;
    font-size: 0.84rem;
    line-height: 1.55;
    color: var(--text-muted);
    text-wrap: balance;
}

.pw-dead-btn { min-width: 190px; }

@media (max-width: 480px) {
    .pw-actions { flex-direction: column-reverse; }
    .pw-actions .btn { width: 100%; }
}

/* ── The bridge ───────────────────────────────────────
   Two apps and the wire between them. Unlinked: the wire is
   a dashed gap and Education sits greyed out. Linked: the
   wire draws closed from both ends and the seal lands. */
.set-bridge {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0;
    padding: 26px 0 22px;
    --wire: var(--accent);
}
.set-bridge[data-state="attention"] { --wire: #d97706; }

.set-bridge-app {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    width: 96px;
}

.set-bridge-tile {
    width: 52px;
    height: 52px;
    border-radius: 15px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 0.86rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    box-shadow: 0 6px 16px rgba(30, 27, 46, 0.16);
    transition: filter 0.4s ease, opacity 0.4s ease, transform 0.4s ease;
}
.set-bridge-tile.is-prod { background: linear-gradient(150deg, #4d72ff, #2448d8); }
.set-bridge-tile.is-edu  { background: linear-gradient(150deg, #38bdf8, #0284c7); }

.set-bridge[data-state="unlinked"] .set-bridge-tile.is-edu {
    filter: grayscale(1);
    opacity: 0.42;
    transform: scale(0.94);
}

.set-bridge-name {
    font-size: 0.72rem;
    font-weight: 700;
    color: var(--text-muted);
    text-align: center;
    line-height: 1.3;
}

.set-wire {
    position: relative;
    flex: 1;
    max-width: 96px;
    height: 22px;
    margin-bottom: 26px;
}

/* dashed baseline, always there */
.set-wire::before {
    content: '';
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    height: 0;
    border-top: 2px dashed rgba(30, 27, 46, 0.18);
    transform: translateY(-50%);
}

/* the live wire, drawn from the centre outwards */
.set-wire-live {
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    height: 2px;
    background: var(--wire);
    border-radius: 2px;
    transform: translateY(-50%) scaleX(0);
    transition: transform 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}
.set-bridge[data-state="linked"] .set-wire-live,
.set-bridge[data-state="attention"] .set-wire-live { transform: translateY(-50%) scaleX(1); }

.set-wire-seal {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--wire);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.72rem;
    font-weight: 800;
    line-height: 1;
    box-shadow: 0 0 0 4px var(--card-bg);
    transform: translate(-50%, -50%) scale(0);
    transition: transform 0.28s cubic-bezier(0.34, 1.56, 0.64, 1) 0.3s;
}
.set-bridge[data-state="linked"] .set-wire-seal,
.set-bridge[data-state="attention"] .set-wire-seal { transform: translate(-50%, -50%) scale(1); }

.set-bridge-status {
    text-align: center;
    font-size: 0.8rem;
    font-weight: 700;
    color: var(--text-muted);
    margin: -6px 0 16px;
}

@keyframes set-fade { from { opacity: 0; } }
@keyframes set-rise {
    from { opacity: 0; transform: translateY(10px) scale(0.985); }
}
@keyframes set-view-in {
    from { opacity: 0; transform: translateY(4px); }
}

/* Focus */
#settings-modal :focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 6px;
}

@media (max-height: 560px) {
    #settings-modal { padding: 12px; }
    .set-pane-head { padding: 14px 14px 10px 20px; }
    .set-rail { padding: 14px 14px; gap: 12px; }
}

@media (max-width: 620px) {
    #settings-modal { padding: 12px; }
    .set-shell {
        grid-template-columns: minmax(0, 1fr);
        /* Stacked here: the rail is its own row and sizes to content, the
           pane takes the rest and scrolls. */
        grid-template-rows: auto minmax(0, 1fr);
    }
    .set-rail {
        flex-direction: row;
        align-items: center;
        gap: 12px;
        padding: 12px 14px;
        border-right: none;
        border-bottom: 1px solid var(--border);
        overflow-x: auto;
    }
    .set-id { display: none; }
    .set-rail-menu { flex-direction: row; align-items: center; gap: 6px; width: 100%; }
    .set-nav { flex-direction: row; }
    .set-signout {
        margin-top: 0;
        margin-left: auto;
        width: auto;
        white-space: nowrap;
    }
    .set-signout::before { content: none; }
    .set-pane { min-height: 0; }
    .set-pane-head { padding: 16px 14px 10px 20px; }
    .set-pane-body { padding: 0 20px 20px; }
}

@media (prefers-reduced-motion: reduce) {
    .set-shell, .set-backdrop, .set-view { animation: none; }
    .set-wire-live, .set-wire-seal, .set-bridge-tile { transition: none; }
}

.toggle-switch {
    position: relative;
    display: inline-block;
    width: 40px;
    height: 24px;
    flex-shrink: 0;
}
.toggle-switch input {
    position: absolute;
    opacity: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    cursor: pointer;
    z-index: 1;
}
.toggle-track {
    position: absolute;
    inset: 0;
    background: var(--border);
    border-radius: 999px;
    transition: background 0.15s;
}
.toggle-thumb {
    position: absolute;
    top: 3px;
    left: 3px;
    width: 18px;
    height: 18px;
    background: #fff;
    border-radius: 50%;
    box-shadow: 0 1px 3px rgba(0,0,0,0.25);
    transition: transform 0.15s;
}
.toggle-switch input:checked ~ .toggle-track { background: var(--accent); }
/* Travel is owned by the t-toggle recipe (translate + overshoot keyframes). */
.toggle-switch { --toggle-travel: 16px; }
.toggle-switch input:disabled ~ .toggle-track { opacity: 0.5; cursor: not-allowed; }

/* ── Utilities ────────────────────────────────────────── */
[hidden] { display: none !important; }

.flex-row { display: flex; align-items: center; gap: 12px; }
.ml-auto { margin-left: auto; }
.mt-16 { margin-top: 16px; }
.section-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 16px;
}
.section-header h2 { font-size: 1.2rem; font-weight: 700; }

/* ── Add Slide modal ──────────────────────────────────── */
/* Every modal built from .add-slide-backdrop/.add-slide-card needs this
   viewport-filling root: the backdrop is absolutely positioned, so without a
   fixed ancestor it sizes to the document flow and both the blur and the
   centred card land on the wrong box. */
#add-slide-modal,
#template-save-modal,
#template-manager-modal,
#gen-template-modal,
#template-designer-modal {
    position: fixed;
    inset: 0;
    z-index: 10000;
}

/* The save modal is opened ON TOP of the designer, which sits later in the
   document – at equal z-index the designer would win and hide the card the
   teacher is meant to fill in. Above the slot form (10200) too, since that is
   part of the layer it is covering. */
#template-save-modal { z-index: 10300; }

/* Centring is done by the flex container, not by a transform on the card.
   show() puts .is-open on the card itself, and .t-modal.is-open's
   `transform: scale(1)` outranks any `translate(-50%, -50%)` we could put on
   .add-slide-card – so a transform-based centring silently loses the moment
   the modal opens, dropping the card's top-left corner on the screen centre.
   Guarded with :not([hidden]) because an author `display` beats the UA's
   [hidden] { display: none } and would pin every modal permanently open. */
#add-slide-modal:not([hidden]),
#template-save-modal:not([hidden]),
#template-manager-modal:not([hidden]),
#gen-template-modal:not([hidden]),
#template-designer-modal:not([hidden]) {
    display: flex;
    align-items: center;
    justify-content: center;
}

.add-slide-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(15, 15, 30, 0.55);
    backdrop-filter: blur(3px);
}

.add-slide-card {
    position: relative;   /* stacks above the absolutely-positioned backdrop;
                             the flex container above does the centring */
    background: var(--card-bg, #fff);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 20px 60px rgba(15, 15, 30, 0.22);
    width: 100%;
    max-width: 460px;
    padding: 24px 28px 28px;
    z-index: 1;
    display: flex;
    flex-direction: column;
    gap: 18px;
}

.add-slide-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.add-slide-title-text {
    font-weight: 700;
    font-size: 1rem;
    color: var(--text);
}

.add-slide-close {
    width: 28px;
    height: 28px;
    background: none;
    border: none;
    color: var(--text-muted);
    font-size: 1.4rem;
    cursor: pointer;
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    line-height: 1;
    font-family: inherit;
    transition: background 0.12s;
}
.add-slide-close:hover { background: var(--border); }

.add-slide-blank-btn {
    width: 100%;
    padding: 10px;
    border: 1.5px dashed var(--border);
    border-radius: 8px;
    background: var(--surface, #f8f9fa);
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--text-muted);
    cursor: pointer;
    font-family: inherit;
    transition: all 0.15s;
    text-align: center;
}
.add-slide-blank-btn:hover {
    border-color: var(--accent);
    color: var(--accent);
    background: rgba(0,119,255,0.04);
}

/* Starter activity: which earlier lessons to recall. Shared by the Add Slide dialog
   and the quiz composer. */
.do-now-panel {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.do-now-help,
.recall-empty {
    margin: 0;
    font-size: 0.82rem;
    color: var(--text-muted);
}

.recall-list {
    display: flex;
    flex-direction: column;
    max-height: 190px;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 4px;
}

.recall-list:empty { display: none; }
.recall-list > .recall-empty { padding: 8px; }

.recall-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    border-radius: 6px;
    font-size: 0.86rem;
    cursor: pointer;
}

.recall-row:hover { background: var(--surface-2); }

.recall-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.recall-when {
    flex: none;
    font-size: 0.76rem;
    color: var(--text-muted);
}

.add-slide-or {
    font-size: 0.8rem;
    color: var(--text-muted);
    text-align: center;
    position: relative;
}
.add-slide-or::before,
.add-slide-or::after {
    content: '';
    position: absolute;
    top: 50%;
    width: 28%;
    height: 1px;
    background: var(--border);
}
.add-slide-or::before { left: 0; }
.add-slide-or::after  { right: 0; }

#add-slide-instruction {
    width: 100%;
    box-sizing: border-box;
    padding: 10px 12px;
    border: 1.5px solid var(--border);
    border-radius: 8px;
    font-size: 0.9rem;
    font-family: inherit;
    background: var(--surface, #f8f9fa);
    color: var(--text);
    resize: vertical;
    min-height: 72px;
    transition: border-color 0.15s;
}
#add-slide-instruction:focus {
    outline: none;
    border-color: var(--accent);
}

.add-slide-actions {
    display: flex;
    justify-content: flex-end;
}

/* ── Worksheet page ───────────────────────────────── */
#worksheet-view { display: flex; flex-direction: column; gap: 28px; }

.ws-page-card {
    display: flex;
    flex-direction: column;
    gap: 16px;
    padding: 28px 32px;
}

/* The composer owns its own vertical rhythm (wizard head, panel, footer), so
   the card contributes padding and nothing else. */
#ws-page-form,
#quiz-page-form,
#generate-card,
#marking-form-card {
    display: block;
    padding: 26px 32px 24px;
}

/* Marks is a count, not a sentence – full width would read as a text field. */
.ws-marks-field { width: 160px; font-weight: 700; }

/* ── Worksheet composer: wizard head ──────────────────
   A slim bar and a step count instead of a chip rail: the steps are read in
   order, and a jump target for a step whose fields are all optional is a
   decision the teacher does not need to be offered. */
.ws-wiz-head {
    display: flex;
    align-items: center;
    gap: 14px;
    padding-bottom: 20px;
    border-bottom: 1px solid var(--border);
}

.ws-wiz-kicker {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    flex: none;
    font-size: 0.82rem;
    font-weight: 800;
}
.ws-wiz-kicker-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 8px;
    background: rgba(0, 199, 141, 0.14);
    color: #067f5c;
}
.ws-wiz-kicker-icon svg { width: 15px; height: 15px; }

.ws-wiz-bar {
    position: relative;
    flex: 1;
    height: 5px;
    border-radius: 999px;
    background: var(--surface-2);
    overflow: hidden;
}
.ws-wiz-bar-fill {
    position: absolute;
    inset: 0 auto 0 0;
    width: var(--ws-progress, 33.333%);
    border-radius: inherit;
    background: linear-gradient(90deg, var(--accent), #60a5fa);
    transition: width 0.32s cubic-bezier(0.22, 1, 0.36, 1);
}
@media (prefers-reduced-motion: reduce) {
    .ws-wiz-bar-fill { transition: none; }
}

.ws-wiz-count {
    flex: none;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}
.ws-wiz-count b { color: var(--text); font-weight: 800; }

/* ── Composer: step panels ────────────────────────────── */
.ws-step-panel {
    display: flex;
    flex-direction: column;
    gap: 26px;
    padding: 30px 0 8px;
    /* Focused programmatically on every step change so the step is announced;
       a ring around the whole panel would read as an error, not a landmark. */
    outline: none;
    animation: ws-step-in 0.26s cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes ws-step-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .ws-step-panel { animation: none; }
}

.ws-hero { text-align: center; }
.ws-hero-title {
    font-size: 1.65rem;
    font-weight: 800;
    line-height: 1.2;
    letter-spacing: -0.02em;
}
.ws-hero-sub {
    max-width: 46ch;
    margin: 8px auto 0;
    font-size: 0.92rem;
    line-height: 1.55;
    color: var(--text-muted);
    text-wrap: pretty;
}

/* One measured column down the middle: the step asks a single question, so
   its controls should not run the full width of a 1100px card. */
.ws-stack {
    display: flex;
    flex-direction: column;
    gap: 22px;
    width: 100%;
    max-width: 560px;
    margin: 0 auto;
}
.ws-stack-wide { max-width: 780px; }

.ws-step-help {
    font-size: 0.78rem;
    line-height: 1.5;
    color: var(--text-muted);
}

/* The one required field, sized like it. */
.ws-topic-hero {
    padding: 15px 16px;
    font-size: 1.12rem;
    font-weight: 600;
}

.ws-context-group { padding-top: 14px; }

/* ── Composer: tiles ──────────────────────────────────
   Board and level are picked by recognising your own, not by opening a list
   and reading eight options. The select each was built from is still in the
   DOM, hidden, holding the value everything else reads. */
.ws-tiles {
    display: grid;
    gap: 8px;
    margin-top: 4px;
}
.ws-tiles-board { grid-template-columns: repeat(3, 1fr); }
.ws-tiles-level { grid-template-columns: repeat(4, 1fr); }

/* <optgroup> labels rendered by buildWsTiles as full-width headings, so the
   primary years read as a family inside the level tile wall. */
.ws-tiles-group {
    grid-column: 1 / -1;
    margin-top: 8px;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--text-muted);
}
.ws-tiles-group:first-child { margin-top: 0; }

/* The National Curriculum narrowing hides level tiles it cannot answer for,
   and a folded group hides its own. `.ws-tile` sets display:flex, which
   outranks the user-agent's [hidden] { display: none } — without this the
   "hidden" tiles stay on screen and only the filtering appears broken. */
.ws-tile[hidden],
.ws-tiles-group[hidden],
.ws-tiles-toggle[hidden] { display: none; }

/* The chip that folds a group of tiles away. Sized and coloured as a quiet
   control rather than a tile: it must not read as one of the options it
   hides, or the count beside it looks like part of a selection. */
.ws-tiles-toggle {
    grid-column: 1 / -1;
    justify-self: start;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    width: fit-content;
    margin: 10px 0 2px;
    padding: 5px 11px 5px 8px;
    border: 1px dashed var(--border);
    border-radius: 999px;
    background: transparent;
    color: var(--text-muted, #6b7280);
    font: inherit;
    font-size: 0.83rem;
    cursor: pointer;
    transition: border-color .15s ease, color .15s ease, background .15s ease;
}
.ws-tiles-toggle:hover {
    border-color: var(--accent);
    color: var(--text);
}
.ws-tiles-toggle:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.ws-tiles-toggle[aria-expanded="true"] {
    border-style: solid;
    color: var(--text);
}
.ws-tiles-toggle-chev {
    width: 0; height: 0;
    border-left: 4.5px solid currentColor;
    border-top: 3.5px solid transparent;
    border-bottom: 3.5px solid transparent;
    transition: transform .15s ease;
}
.ws-tiles-toggle[aria-expanded="true"] .ws-tiles-toggle-chev { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) {
    .ws-tiles-toggle,
    .ws-tiles-toggle-chev { transition: none; }
}

/* While a primary level is set, every board tile except the National
   Curriculum is disabled rather than hidden — the select stays the value of
   record, and the note says why the rest are dead to the pointer. */
.ws-tile:has(input:disabled) {
    opacity: 0.45;
    cursor: not-allowed;
}
.ws-tile input[type="radio"]:disabled { cursor: not-allowed; }
.ws-tiles-note {
    margin-top: 6px;
    font-size: 0.78rem;
    line-height: 1.4;
    color: var(--text-muted);
}

.ws-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 7px;
    min-height: 78px;
    padding: 12px 10px;
    border: 1.5px solid var(--border);
    border-radius: 10px;
    background: var(--card-bg);
    font-size: 0.86rem;
    font-weight: 600;
    line-height: 1.25;
    text-align: center;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s, color 0.15s, box-shadow 0.15s;
}
.ws-tile:hover { border-color: var(--accent); }
.ws-tile input[type="radio"] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}
.ws-tile:has(input:checked) {
    border-color: var(--accent);
    background: rgba(54, 96, 255, 0.1);
    color: var(--accent-dark);
    box-shadow: 0 4px 14px rgba(54, 96, 255, 0.13), inset 0 0 0 1px rgba(54, 96, 255, 0.25);
}
.ws-tile:has(input:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* ── Composer: segmented controls ─────────────────────── */
.ws-seg-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 18px;
}
/* Length carries a second line per option and Difficulty does not, so the two
   controls would otherwise sit at different heights. The grid already makes
   the two groups equal; this lets the control inside each one take whatever
   height that leaves, rather than pinning a number that breaks the moment an
   option gains or loses its sub-label. */
.ws-seg-row > .ws-form-group > .ws-seg { flex: 1; }

.ws-seg {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 4px;
    margin-top: 4px;
    padding: 4px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface-2);
}

.ws-seg-opt {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 5px;
    padding: 10px 6px;
    border-radius: 7px;
    font-size: 0.84rem;
    font-weight: 600;
    line-height: 1.2;
    text-align: center;
    color: var(--text-muted);
    cursor: pointer;
    transition: background 0.15s, color 0.15s, box-shadow 0.15s;
}
.ws-seg-opt input[type="radio"] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}
.ws-seg-opt:hover { color: var(--text); }
.ws-seg-opt:has(input:checked) {
    background: var(--card-bg);
    color: var(--text);
    box-shadow: 0 2px 8px rgba(30, 27, 46, 0.08);
}
.ws-seg-opt:has(input:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}
.ws-tile-sub {
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--text-muted);
}

/* ── Composer: question-type tiles ────────────────────── */
.ws-type-tiles {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
    margin-top: 4px;
}

.ws-type-tile {
    position: relative;
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 12px;
    border: 1.5px solid var(--border);
    border-radius: 10px;
    background: var(--card-bg);
    font-size: 0.86rem;
    font-weight: 600;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}
.ws-type-tile:hover { border-color: var(--accent); }
.ws-type-tile input[type="checkbox"] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}
.ws-type-tile:has(input:checked) {
    border-color: var(--accent);
    background: rgba(54, 96, 255, 0.06);
    box-shadow: 0 4px 14px rgba(54, 96, 255, 0.09);
}
.ws-type-tile:has(input:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
/* Each type keeps its own tone whether or not it is picked: the grid is
   easier to scan when a type is the same colour every time you see it, and
   the selected state is already carried by the tile's border and tick. */
.ws-tile-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    flex: none;
    border-radius: 9px;
    background: var(--tone-bg, var(--surface-2));
    color: var(--tone, var(--text-muted));
    transition: transform 0.15s cubic-bezier(0.22, 1, 0.36, 1);
}
.ws-tile-icon svg { width: 16px; height: 16px; }
.ws-type-tile:has(input:checked) .ws-tile-icon { transform: scale(1.06); }
@media (prefers-reduced-motion: reduce) {
    .ws-tile-icon { transition: none; }
}
.ws-type-tile .ws-tile-name { flex: 1; min-width: 0; }
.ws-tile-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    flex: none;
    border-radius: 50%;
    background: var(--accent);
    opacity: 0;
    transform: scale(0.7);
    transition: opacity 0.15s, transform 0.15s cubic-bezier(0.22, 1, 0.36, 1);
}
.ws-tile-mark svg {
    width: 9px;
    height: 9px;
    fill: none;
    stroke: #fff;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.ws-type-tile:has(input:checked) .ws-tile-mark { opacity: 1; transform: scale(1); }
@media (prefers-reduced-motion: reduce) {
    .ws-tile-mark { transition: none; }
}

/* ── Composer: icon tones ─────────────────────────────
   One pair of custom properties per tone, set on the element that carries the
   glyph. Every tile still names itself in text – the colour is a second way
   to find your own board, not the only way. */
.tone-blue   { --tone: var(--accent);      --tone-bg: rgba(54, 96, 255, 0.12); }
.tone-green  { --tone: #067f5c;            --tone-bg: rgba(0, 199, 141, 0.16); }
.tone-amber  { --tone: #8f620d;            --tone-bg: rgba(244, 180, 71, 0.24); }
.tone-purple { --tone: #6f43b8;            --tone-bg: rgba(134, 87, 209, 0.15); }
.tone-red    { --tone: var(--red-soft);    --tone-bg: rgba(177, 70, 55, 0.12); }
.tone-teal   { --tone: #0c7178;            --tone-bg: rgba(13, 127, 134, 0.13); }
.tone-indigo { --tone: #3b3fa8;            --tone-bg: rgba(59, 63, 168, 0.13); }
.tone-slate  { --tone: var(--text-muted);  --tone-bg: var(--surface-2); }

/* ── Composer: step hero icon ─────────────────────────── */
.ws-hero-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 52px;
    height: 52px;
    margin-bottom: 14px;
    border-radius: 16px;
    background: var(--tone-bg);
    color: var(--tone);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tone) 18%, transparent);
}
.ws-hero-icon svg { width: 26px; height: 26px; }

/* ── Composer: fields with a leading glyph ────────────── */
.ws-field-icon { position: relative; }
.ws-field-icon-glyph {
    position: absolute;
    top: 50%;
    left: 15px;
    display: inline-flex;
    color: var(--text-muted);
    transform: translateY(-50%);
    pointer-events: none;
}
.ws-field-icon-glyph svg { width: 19px; height: 19px; }
.ws-field-icon .ws-topic-input { padding-left: 46px; }
.ws-field-icon .ws-select-icon { padding-left: 44px; }
/* The subject combobox draws its own mirror and placeholder on top of the
   input, so all three have to clear the glyph or the text lands under it.
   Scoped to the composers – the publish dialog's picker has no glyph. */
.ws-subject-group .sc-input,
.ws-subject-group .t-clear-mirror,
.ws-subject-group .t-clear-placeholder { padding-left: 44px; }

/* Sized like the topic field on step 1: it is the one thing on this step you
   type rather than pick, and it now opens the step. The mirror has to track
   every one of these or the value jumps when it renders. */
.ws-subject-group .sc-input,
.ws-subject-group .t-clear-mirror,
.ws-subject-group .t-clear-placeholder {
    padding-top: 14px;
    padding-bottom: 14px;
    font-size: 1.08rem;
}
.ws-subject-group .sc-input,
.ws-subject-group .t-clear-mirror { font-weight: 600; }
.ws-subject-group .t-clear { border-radius: 8px; }

/* Inline on the label instead of pinned to the field's corner. The shared
   .t-badge is absolute by default, so this instance opts out. */
.ws-subject-group .ws-field-label {
    display: flex;
    align-items: center;
    gap: 6px;
}
.ws-subject-group .ws-field-label .t-badge { position: static; }
.ws-subject-group .auto-badge { margin-left: 0; }
/* The glyph belongs to the field, so it takes the field's focus colour. */
.ws-field-icon:focus-within .ws-field-icon-glyph { color: var(--accent); }

/* ── Composer: tile + segment glyphs ──────────────────── */
.ws-tile-glyph {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    flex: none;
    border-radius: 9px;
    background: var(--tone-bg);
    color: var(--tone);
    transition: transform 0.15s cubic-bezier(0.22, 1, 0.36, 1);
}
.ws-tile-glyph svg { width: 17px; height: 17px; }
.ws-tile:has(input:checked) .ws-tile-glyph,
.ws-seg-opt:has(input:checked) .ws-tile-glyph { transform: scale(1.06); }
@media (prefers-reduced-motion: reduce) {
    .ws-tile-glyph { transition: none; }
}

/* ── Composer: support option icons ───────────────────── */
.ws-support-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    flex: none;
    margin-top: -3px;
    border-radius: 9px;
    background: var(--tone-bg);
    color: var(--tone);
}
.ws-support-icon svg { width: 17px; height: 17px; }

/* ── Composer: question-style cards ───────────────────
   Two side-by-side choices instead of a checkbox: exam-style generation
   changes what the whole sheet is, so it should not read as a modifier of
   the type list it actually replaces. */
.ws-mode-cards {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
    margin-top: 4px;
}

.ws-mode-card {
    position: relative;
    display: flex;
    gap: 10px;
    padding: 14px 14px 14px 16px;
    border: 1.5px solid var(--border);
    border-radius: 12px;
    background: var(--card-bg);
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s, box-shadow 0.15s, transform 0.12s;
}
.ws-mode-card:hover { border-color: var(--accent); }
.ws-mode-card:active { transform: scale(0.995); }
.ws-mode-card input[type="radio"] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}
.ws-mode-card:has(input:checked) {
    border-color: var(--accent);
    background: rgba(54, 96, 255, 0.055);
    box-shadow: 0 6px 20px rgba(54, 96, 255, 0.12);
}
.ws-mode-card:has(input:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
/* Not "off" – not offered yet. Kept legible so the card still explains what
   it would give you and how to unlock it. */
.ws-mode-card:has(input:disabled) {
    cursor: default;
    opacity: 0.72;
    background: var(--surface-2);
}
.ws-mode-card:has(input:disabled):hover { border-color: var(--border); }

.ws-mode-body {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}
.ws-mode-top {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}
.ws-mode-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    flex: none;
    border-radius: 7px;
    background: rgba(54, 96, 255, 0.1);
    color: var(--accent);
}
.ws-mode-icon svg { width: 15px; height: 15px; }
.ws-mode-exam[data-available="true"] .ws-mode-icon {
    background: rgba(0, 199, 141, 0.14);
    color: var(--green);
}
.ws-mode-name {
    font-size: 0.95rem;
    font-weight: 700;
    line-height: 1.2;
}
.ws-mode-badge {
    padding: 2px 7px;
    border-radius: 999px;
    background: rgba(0, 199, 141, 0.14);
    color: #067f5c;
    font-size: 0.66rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}
.ws-mode-desc {
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--text-muted);
}
.ws-mode-status {
    font-size: 0.76rem;
    line-height: 1.45;
    color: var(--text-muted);
}
.ws-mode-exam[data-available="true"] .ws-mode-status {
    color: #067f5c;
    font-weight: 700;
}

/* The tick sits in the corner rather than in a column of its own: with two
   cards the selected one is already obvious from its fill, and the corner
   mark confirms it without stealing a line of width from the copy. */
.ws-mode-mark {
    position: absolute;
    top: 10px;
    right: 10px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--accent);
    opacity: 0;
    transform: scale(0.7);
    transition: opacity 0.15s, transform 0.15s cubic-bezier(0.22, 1, 0.36, 1);
}
.ws-mode-mark svg {
    width: 10px;
    height: 10px;
    fill: none;
    stroke: #fff;
    stroke-width: 1.7;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.ws-mode-card:has(input:checked) .ws-mode-mark {
    opacity: 1;
    transform: scale(1);
}
@media (prefers-reduced-motion: reduce) {
    .ws-mode-mark { transition: none; }
}

/* The theme step has one control and a hero that already names it, so the
   label is for screen readers only. */
.ws-label-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* ── Composer: theme and slide-count tiles ────────────
   The buttons and their JS are unchanged – these two containers just retire
   the pill look inside the composer so the deck step matches the tiles on the
   steps before it. */
.ws-theme-tiles {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 8px;
    margin-top: 4px;
}
.ws-theme-tiles .theme-btn {
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
    padding: 10px;
    border-width: 1.5px;
    border-radius: 10px;
    background: var(--card-bg);
    font-size: 0.84rem;
    font-weight: 600;
    text-align: center;
}
/* The swatch does the work at this size, so give it real estate. */
.ws-theme-tiles .theme-dot {
    width: 100%;
    height: 34px;
    border-radius: 7px;
    box-shadow: inset 0 0 0 1px rgba(30, 27, 46, 0.1);
}
.ws-theme-tiles .theme-btn.active { background: rgba(54, 96, 255, 0.08); }

.ws-count-tiles {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 8px;
    margin-top: 4px;
}
.ws-count-tiles .theme-btn {
    justify-content: center;
    min-height: 44px;
    padding: 8px;
    border-width: 1.5px;
    border-radius: 10px;
    background: var(--card-bg);
    font-weight: 700;
}

/* ── Composer: "also make" extras ─────────────────────── */
.pack-opt-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    flex: none;
    border-radius: 9px;
    background: var(--tone-bg);
    color: var(--tone);
}
.pack-opt-icon svg { width: 17px; height: 17px; }
/* The whole row is the target, so give it the tile's affordances. */
.ws-step-panel .pack-opt {
    align-items: center;
    padding: 10px 12px;
    border: 1.5px solid transparent;
    border-radius: 10px;
    transition: border-color 0.15s, background 0.15s;
}
.ws-step-panel .pack-opt:hover { border-color: var(--border); background: var(--card-bg); }
.ws-step-panel .pack-opt:has(input:checked) {
    border-color: var(--accent);
    background: rgba(54, 96, 255, 0.05);
}

/* Layouts: until layouts are chosen the hint is empty, and a flex-grow on it
   pushed the button out to the far edge of an otherwise blank row. */
.ws-step-panel .gen-template-summary {
    justify-content: flex-start;
    flex-wrap: wrap;
    margin-top: 4px;
}
.ws-step-panel .gen-template-hint { flex: 0 1 auto; margin-top: 0; }
.ws-step-panel .gen-template-hint:empty { display: none; }

@media (max-width: 720px) {
    .ws-theme-tiles { grid-template-columns: repeat(2, 1fr); }
    .ws-count-tiles { grid-template-columns: repeat(3, 1fr); }
}

/* ── Composer: inline hints ───────────────────────────
   Raised by a choice that is probably a slip (an international board with a
   UK-only level), so it sits under the control that caused it and offers the
   fix rather than only naming the problem. */
.ws-hint {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin-top: 10px;
    padding: 12px 14px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface-2);
    animation: ws-hint-in 0.22s cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes ws-hint-in {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .ws-hint { animation: none; }
}
/* Firm: we hold no specifications at all for that combination. */
.ws-hint[data-state="firm"] {
    border-color: rgba(244, 180, 71, 0.5);
    background: rgba(244, 180, 71, 0.12);
}
.ws-hint-icon {
    display: inline-flex;
    flex: none;
    margin-top: 1px;
    color: var(--text-muted);
}
.ws-hint[data-state="firm"] .ws-hint-icon { color: #8f620d; }
.ws-hint-icon svg { width: 18px; height: 18px; }
.ws-hint-text {
    font-size: 0.84rem;
    line-height: 1.5;
    color: var(--text);
    text-wrap: pretty;
}
.ws-hint-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 10px;
}

/* ── Composer: wizard footer ──────────────────────────── */
.ws-wizard-actions {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 4px;
    padding-top: 18px;
    border-top: 1px solid var(--border);
}
.ws-wizard-hint {
    flex: 1;
    min-width: 0;
    font-size: 0.78rem;
    line-height: 1.4;
    color: var(--text-muted);
}
.ws-wizard-go {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: none;
}

@media (max-width: 720px) {
    .ws-hero-title { font-size: 1.35rem; }
    .ws-step-panel { gap: 22px; padding-top: 24px; }
    .ws-wiz-kicker span:not(.ws-wiz-kicker-icon) { display: none; }
    .ws-mode-cards { grid-template-columns: 1fr; }
    .ws-tiles-board { grid-template-columns: repeat(2, 1fr); }
    .ws-tiles-level { grid-template-columns: repeat(3, 1fr); }
    .ws-type-tiles { grid-template-columns: 1fr 1fr; }
    .ws-seg-row { grid-template-columns: 1fr; gap: 22px; }
    .ws-wizard-actions { flex-wrap: wrap; }
    .ws-wizard-hint { order: 3; flex-basis: 100%; }
    .ws-wizard-go { flex: 1; }
    .ws-wizard-go .btn { flex: 1; }
}

.ws-field-label {
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
}

.ws-topic-input {
    width: 100%;
    box-sizing: border-box;
    padding: 10px 12px;
    border: 1.5px solid var(--border);
    border-radius: 8px;
    font-size: 1rem;
    font-family: inherit;
    background: var(--surface, #f8f9fa);
    color: var(--text);
    transition: border-color 0.15s;
}
.ws-topic-input:focus {
    outline: none;
    border-color: var(--accent);
}

.ws-form-group {
    display: flex;
    flex-direction: column;
    gap: 5px;
    flex: 1;
}

.ws-form-row {
    display: flex;
    gap: 12px;
}

.ws-select {
    width: 100%;
    box-sizing: border-box;
    padding: 8px 10px;
    border: 1.5px solid var(--border);
    border-radius: 8px;
    font-size: 0.9rem;
    font-family: inherit;
    background: var(--surface, #f8f9fa);
    color: var(--text);
    transition: border-color 0.15s;
}
.ws-select:focus {
    outline: none;
    border-color: var(--accent);
}

.ws-optional {
    font-weight: 400;
    color: var(--text-muted);
}

.ws-type-checks {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 20px;
    margin-top: 2px;
}

.ws-type-check {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 0.9rem;
    color: var(--text);
    cursor: pointer;
    user-select: none;
}

/* The native input still owns state + keyboard; the drawn .t-check box is
   what the user sees, so the input itself is only a hit target. */
.ws-type-check input[type="checkbox"] {
    position: absolute;
    width: 16px;
    height: 16px;
    opacity: 0;
    margin: 0;
    cursor: pointer;
}
.ws-type-check .t-check {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    border-radius: 4px;
    background: transparent;
    box-shadow: inset 0 0 0 1.5px var(--border);
}
.ws-type-check input:checked ~ .t-check {
    background: var(--accent);
    box-shadow: inset 0 0 0 1.5px var(--accent);
}
.ws-type-check input:focus-visible ~ .t-check {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.ws-type-check .t-check svg {
    width: 10px;
    height: 10px;
    fill: none;
    stroke: #fff;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* Additional support.
   Collapsed behind a summary button rather than sat open like Question Types:
   most sheets use none of it, and five two-line options permanently open would
   dominate a form whose main job is still "type a topic". The summary line
   names what is on, so the state is never hidden by the collapse. */
.ws-supports-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    width: 100%;
    margin-top: 2px;
    padding: 9px 12px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 0.9rem;
    text-align: left;
    cursor: pointer;
}
.ws-supports-toggle:hover { border-color: var(--accent); }
.ws-supports-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    flex: none;
    margin-right: -2px;
    border-radius: 9px;
    background: var(--tone-bg);
    color: var(--tone);
}
.ws-supports-icon svg { width: 16px; height: 16px; }
/* The summary is the label; the caret must stay pinned right. */
.ws-supports-toggle #ws-supports-summary { flex: 1; text-align: left; }
.ws-supports-toggle[aria-expanded="true"] .ws-supports-caret { transform: rotate(180deg); }
.ws-supports-caret {
    color: var(--text-muted);
    transition: transform 0.15s;
}
@media (prefers-reduced-motion: reduce) {
    .ws-supports-caret { transition: none; }
}

.ws-support-checks {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-top: 10px;
    padding: 14px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface-2);
}

.ws-support-intro {
    font-size: 0.8rem;
    line-height: 1.5;
    color: var(--text-muted);
}

/* Two lines per option, so align the box to the first line rather than to the
   centre of the whole block. */
.ws-support-check { align-items: flex-start; }
.ws-support-check .t-check { margin-top: 2px; }

/* The home-language picker closes the supports panel, separated from the
   checkboxes above it by a rule: it is the same axis, but it is a choice of
   one rather than another thing to tick. */
.ws-support-lang {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 10px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
}
.ws-support-lang select { width: 100%; }
.ws-support-lang-note { margin: 0; }

.ws-support-text { display: flex; flex-direction: column; gap: 2px; }
.ws-support-text b { font-weight: 600; }
.ws-support-text em {
    font-style: normal;
    font-size: 0.8rem;
    line-height: 1.45;
    color: var(--text-muted);
}

.ws-generate-actions {
    display: flex;
    justify-content: flex-end;
}

.ws-preview-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 16px;
}

.ws-preview-area {
    background: var(--card-bg, #fff);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 36px 40px;
    font-family: Georgia, 'Times New Roman', serif;
    font-size: 0.9rem;
    color: #111;
    line-height: 1.6;
}

/* Worksheet content styles */
.ws-doc-title {
    font-size: 1.25rem;
    font-weight: 700;
    text-align: center;
    margin: 0 0 8px;
    font-family: inherit;
}

.ws-meta-row {
    display: flex;
    gap: 24px;
    font-size: 0.85rem;
    border-top: 1px solid #ccc;
    border-bottom: 1px solid #ccc;
    padding: 6px 0;
    margin-bottom: 20px;
}

.ws-section {
    margin-bottom: 24px;
}

.ws-section-title {
    font-size: 0.95rem;
    font-weight: 700;
    border-bottom: 1px solid #aaa;
    padding-bottom: 4px;
    margin: 0 0 4px;
    font-family: Arial, sans-serif;
}

.ws-instruction {
    font-size: 0.8rem;
    font-style: italic;
    color: #555;
    margin: 0 0 10px;
    font-family: Arial, sans-serif;
}

.ws-question {
    margin-bottom: 14px;
}

.ws-q-text {
    margin: 0 0 4px;
}

.ws-options {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 2px 20px;
    padding-left: 16px;
    font-size: 0.88rem;
}

.ws-tf-label {
    display: inline-block;
    border: 1px solid #aaa;
    border-radius: 4px;
    padding: 1px 8px;
    font-size: 0.8rem;
    margin-right: 8px;
    font-family: Arial, sans-serif;
}

.ws-answer-lines {
    margin-top: 4px;
    padding-left: 8px;
}

.ws-answer-line {
    border-bottom: 1px solid #bbb;
    height: 22px;
    margin-bottom: 4px;
}

/* Worksheet preview toolbar right side */
.ws-toolbar-right {
    display: flex;
    align-items: center;
    gap: 12px;
}

/* Export dropdown menu */
.export-menu-wrap {
    position: relative;
    display: inline-block;
}

.export-caret {
    font-size: 0.7em;
    margin-left: 2px;
}

.export-menu {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    min-width: 220px;
    background: var(--card-bg, #fff);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
    padding: 6px;
    z-index: 60;
    display: flex;
    flex-direction: column;
}

.export-menu-left {
    left: 0;
    right: auto;
}

.export-menu-item {
    display: block;
    width: 100%;
    text-align: left;
    padding: 8px 10px;
    border: none;
    background: none;
    border-radius: 6px;
    font-size: 0.85rem;
    color: var(--text, #1a1a2e);
    cursor: pointer;
    white-space: nowrap;
}

.export-menu-item:hover {
    background: var(--hover-bg, rgba(0, 0, 0, 0.05));
}

.export-menu-item:disabled {
    opacity: 0.5;
    cursor: default;
    background: none;
}

.export-menu-sep {
    height: 1px;
    background: var(--border);
    margin: 4px 6px;
}

.export-menu-label {
    display: block;
    padding: 4px 10px 2px;
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted, #777);
}

.export-menu-select {
    width: calc(100% - 12px);
    margin: 0 6px 4px;
    padding: 6px 8px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--card-bg, #fff);
    color: var(--text, #1a1a2e);
    font-size: 0.85rem;
}

/* Who the chosen layout is for. Sits under the two selects and changes as
   they do, so a teacher picking "Low density" out of curiosity finds out what
   it does without leaving the menu. Empty for the standard layout. */
.export-menu-note {
    margin: 0 8px 6px;
    max-width: 22em;
    font-size: 0.74rem;
    line-height: 1.45;
    color: var(--text-muted, #777);
}
.export-menu-note:empty { display: none; }

.theme-switch-menu {
    min-width: 380px;
    padding: 10px;
}

.theme-switch-menu .theme-picker {
    gap: 6px;
}

.theme-switch-menu .theme-btn {
    padding: 5px 10px;
    font-size: 0.82rem;
}

/* Background colour, under the themes it sits on top of. */
.slide-bg-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--border);
}

.slide-bg-label {
    font-size: 0.82rem;
    font-weight: 700;
    color: var(--text);
}

#slide-bg-picker {
    width: 34px;
    height: 28px;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: none;
    cursor: pointer;
}

.slide-bg-clear {
    flex-basis: 100%;
    font-size: 0.78rem;
    color: var(--text-muted);
}

.slide-bg-link {
    padding: 0;
    border: none;
    background: none;
    font: inherit;
    color: var(--accent);
    text-decoration: underline;
    cursor: pointer;
}

/* Dropdown menus (Theme / Download / etc.) are absolutely positioned off
   their trigger button and hard-coded to a min-width (380px for the theme
   picker) – on a narrow phone that runs straight off the right edge of the
   screen with no way to reach the cut-off items. Below 720px, detach them
   from the button and center them as a fixed panel that always fits.
   This must come after the base .export-menu/.theme-switch-menu rules
   above so it wins the cascade at equal specificity. */
@media (max-width: 720px) {
    .export-menu {
        position: fixed;
        left: 50%;
        right: auto;
        top: auto;
        bottom: 16px;
        transform: translateX(-50%);
        width: calc(100vw - 32px);
        max-width: 380px;
        min-width: 0;
        max-height: 60vh;
        overflow-y: auto;
    }
    .theme-switch-menu {
        min-width: 0;
    }
}

.ws-save-status {
    font-size: 0.8rem;
    color: var(--text-muted);
    min-width: 56px;
    text-align: right;
}

/* ── Worksheet sheet editor ────────────────────────────────
   The sheet itself is styled by worksheet-print.css + worksheet-edit.css
   inside #ws-sheet-frame. Everything here is the frame around it: the
   scrolling canvas the sheet is scaled into, and the inspector beside it. */

.ws-sheet-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 320px;
    gap: 18px;
    align-items: start;
}

.ws-sheet-canvas {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 18px;
    overflow: hidden;
}

/* The sheet renders at its true A4 width and is scaled down by the editor
   (see renderWsSheet in app.js), exactly as the slide stage scales a 960px
   slide. The scaler carries the post-scale height so the page scrolls as one
   long sheet instead of nesting a second scrollbar inside the frame. */
/* The scaler is a plain box carrying the POST-scale size, so the page grows
   and scrolls correctly; the frame inside it is the thing actually scaled.
   Scaling the scaler itself would apply the factor twice - once to the box
   whose height we had just set, and again to its contents. */
.ws-sheet-scaler {
    margin: 0 auto;
}

.ws-sheet-frame {
    display: block;
    width: 794px;          /* 210mm at 96dpi */
    transform-origin: top left;
    border: none;
    background: #fff;
    box-shadow: var(--shadow-card);
    border-radius: 4px;
}

.ws-inspector {
    position: sticky;
    top: 16px;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
}

.ws-inspector-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 12px 14px;
    border-bottom: 1px solid var(--border);
}

.ws-inspector-title {
    font-size: 0.8rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
}

.ws-inspector-close {
    border: none;
    background: transparent;
    color: var(--text-muted);
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
    padding: 2px 6px;
    border-radius: 6px;
}
.ws-inspector-close:hover { color: var(--text); background: var(--surface-2); }

.ws-inspector-body:empty { display: none; }

.ws-inspector-empty {
    padding: 16px 14px 20px;
    font-size: 0.85rem;
    color: var(--text-muted);
}

/* The inspector reuses the form editor's own field rows (buildAnswerFields),
   so it inherits their styling and only needs the padding around them. */
.ws-inspector-body .ws-edit-q-row {
    padding: 14px;
    border-bottom: none;
}

/* Creating the first step or sentence starter. Once one exists its text is
   edited on the sheet, so these disappear. */
.ws-inspector-supports {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    padding: 0 14px 14px;
}
.ws-inspector-supports:empty { display: none; }

@media (max-width: 1100px) {
    .ws-sheet-layout { grid-template-columns: minmax(0, 1fr); }
    .ws-inspector { position: static; }
}

/* Worksheet editor outer card */
.ws-editor-outer {
    padding: 0;
    overflow: hidden;
}

.ws-title-input {
    display: block;
    width: 100%;
    padding: 18px 24px 14px;
    border: none;
    border-bottom: 1px solid var(--border);
    background: transparent;
    font-family: inherit;
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--text);
    outline: none;
}
.ws-title-input::placeholder { color: var(--text-muted); font-weight: 400; }

/* Section editor */
.ws-editor {
    display: flex;
    flex-direction: column;
    gap: 0;
}

.ws-edit-card {
    border-bottom: 1px solid var(--border);
    padding: 18px 22px 20px;
    transition: background 0.12s;
}
.ws-edit-card:last-child { border-bottom: none; }
.ws-edit-card.ws-drag-active {
    opacity: 0.45;
    background: rgba(54, 96, 255, 0.04);
}

.ws-edit-card-header {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 14px;
}

/* Key words. Sits above the sections, matching where it prints, and tinted so
   it does not read as a seventh question type. */
.ws-gloss-card { background: rgba(54, 96, 255, 0.035); }
.ws-gloss-card-title {
    font-size: 0.9rem;
    font-weight: 700;
    color: var(--text);
}
.ws-gloss-card-note {
    font-size: 0.75rem;
    color: var(--text-muted);
}
.ws-gloss-rows { display: flex; flex-direction: column; gap: 7px; }
.ws-gloss-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 2fr) auto;
    align-items: center;
    gap: 8px;
}
/* Three inputs once the sheet has a home language, matching the three columns
   the printed key-words box grows. See public/js/home-languages.js. */
.ws-gloss-card-bilingual .ws-gloss-row {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.8fr) minmax(0, 1fr) auto;
}

.ws-gloss-term-input,
.ws-gloss-def-input,
.ws-gloss-l1-input {
    width: 100%;
    min-width: 0;
    padding: 7px 10px;
    border: 1px solid var(--border);
    border-radius: 7px;
    background: var(--card-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 0.85rem;
    outline: none;
}
.ws-gloss-term-input { font-weight: 700; }
/* Named ahead of the UI stack, not after it: a Latin face with a partial
   Arabic range renders the line badly rather than deferring to the face that
   covers it. Line-height for Nastaliq, whose descenders need the room. */
.ws-gloss-l1-input {
    font-family: 'Noto Nastaliq Urdu', 'Noto Sans Arabic', 'Noto Sans Gurmukhi',
                 'Noto Sans Bengali', 'Noto Sans Tamil', 'Noto Sans CJK SC',
                 'Noto Sans SC', inherit;
    line-height: 1.7;
}
.ws-gloss-term-input:focus-visible,
.ws-gloss-def-input:focus-visible,
.ws-gloss-l1-input:focus-visible {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(54, 96, 255, 0.12);
}
.ws-gloss-add { align-self: flex-start; margin-top: 10px; }

@media (max-width: 620px) {
    .ws-gloss-row,
    .ws-gloss-card-bilingual .ws-gloss-row { grid-template-columns: minmax(0, 1fr) auto; }
    .ws-gloss-def-input,
    .ws-gloss-l1-input { grid-column: 1 / -1; }
}

/* Section controls stay out of the way until the section is in play */
.ws-drag-handle,
.ws-remove-section,
.ws-del-q,
.ws-add-q-btn {
    opacity: 0;
    transition: opacity 0.14s, color 0.12s, background 0.12s, border-color 0.12s;
}
.ws-edit-card:hover .ws-drag-handle,
.ws-edit-card:hover .ws-remove-section,
.ws-edit-card:hover .ws-add-q-btn,
.ws-edit-card:focus-within .ws-drag-handle,
.ws-edit-card:focus-within .ws-remove-section,
.ws-edit-card:focus-within .ws-add-q-btn { opacity: 1; }
.ws-edit-q-row:hover .ws-del-q,
.ws-edit-q-row:focus-within .ws-del-q { opacity: 1; }

.ws-drag-handle {
    font-size: 1.1rem;
    color: var(--text-muted);
    cursor: grab;
    user-select: none;
    padding: 2px 4px;
    border-radius: 4px;
    line-height: 1;
}
.ws-drag-handle:active { cursor: grabbing; }

/* The section's type, as a chip – same shape the rest of the app uses */
.ws-card-label {
    flex: 0 0 auto;
    margin-right: auto;
    padding: 3px 10px;
    border-radius: 999px;
    background: rgba(54, 96, 255, 0.08);
    font-size: 0.7rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--accent);
}

.ws-remove-section {
    background: none;
    border: none;
    cursor: pointer;
    color: var(--text-muted);
    font-size: 0.85rem;
    padding: 2px 6px;
    border-radius: 4px;
    line-height: 1;
}
.ws-remove-section:hover { color: var(--red-soft); background: rgba(177, 70, 55, 0.08); }

/* Question rows */
.ws-edit-questions {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-bottom: 10px;
}

.ws-edit-q-row {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 6px 6px 6px 0;
    border-radius: 8px;
    transition: background 0.12s;
}
.ws-edit-q-row:hover { background: rgba(30, 27, 46, 0.022); }

.ws-q-num {
    min-width: 22px;
    height: 22px;
    margin-top: 7px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
    background: rgba(30, 27, 46, 0.05);
    font-size: 0.75rem;
    font-weight: 800;
    color: var(--text-muted);
    flex-shrink: 0;
}

.ws-edit-q-fields {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}

.ws-edit-input {
    width: 100%;
    padding: 6px 10px;
    border: 1.5px solid var(--border);
    border-radius: 6px;
    font-family: inherit;
    font-size: 0.88rem;
    color: var(--text);
    background: var(--surface);
    outline: none;
    transition: border-color 0.12s;
}
.ws-edit-input:focus { border-color: var(--accent); }

.ws-q-main { font-size: 0.9rem; }

/* A question field holding maths. The input keeps the LaTeX source - that is
   what gets typed and saved - and the overlay shows it rendered whenever the
   field is not the one being edited (see attachMathPreview in app.js). The
   overlay sits inside the input's own border, so the box, its focus ring and
   its background are still the input's. */
.ws-math-field {
    position: relative;
    display: block;
    flex: 1;
    min-width: 0;
}
.ws-math-preview {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    padding: 6px 10px;
    border: 1.5px solid transparent;
    border-radius: 6px;
    overflow: hidden;
    color: var(--text);
    pointer-events: none;
}
.ws-math-preview-line { white-space: nowrap; }
/* Block form, for the textareas (indicative and model answers): the overlay
   starts at the top rather than centring, and the text wraps the way the
   textarea underneath would have wrapped it. */
.ws-math-preview-block { align-items: flex-start; }
.ws-math-preview-block .ws-math-preview-line {
    white-space: pre-wrap;
    overflow-wrap: break-word;
    width: 100%;
}
/* Kills the inline descender gap under the field, so the wrapper is exactly
   as tall as the box it wraps - including after a textarea is dragged taller. */
.ws-math-field > .ws-edit-input { display: block; }
/* The source is still in the box and still selectable - it is just not drawn,
   because the overlay is drawing it properly a layer up. */
.ws-edit-input.ws-math-masked { color: transparent; }

.ws-mc-options {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 4px 8px;
}

.ws-mc-opt-label {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 0.82rem;
    color: var(--text-muted);
}
.ws-mc-opt-label .ws-edit-input { flex: 1; }

/* Teacher-only fields are keyed green, so the answer to a question is
   scannable without reading every field. */
.ws-answer-label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.78rem;
    font-weight: 700;
    color: #0d8a66;
}

.ws-answer-select {
    padding: 4px 8px;
    border: 1.5px solid rgba(0, 199, 141, 0.32);
    border-radius: 6px;
    font-family: inherit;
    font-size: 0.85rem;
    background: rgba(0, 199, 141, 0.05);
    color: var(--text);
    outline: none;
    transition: border-color 0.12s;
}
.ws-answer-select:focus { border-color: var(--green); }

.ws-edit-input.ws-inline-answer,
.ws-edit-input.ws-marks-input,
.ws-edit-input.ws-model-answer,
.ws-edit-input.ws-part-answer {
    border-color: rgba(0, 199, 141, 0.32);
    background: rgba(0, 199, 141, 0.05);
}
.ws-edit-input.ws-inline-answer:focus,
.ws-edit-input.ws-marks-input:focus,
.ws-edit-input.ws-model-answer:focus,
.ws-edit-input.ws-part-answer:focus { border-color: var(--green); }

.ws-inline-answer {
    flex: 1;
}

/* Matching editor */
.ws-match-pairs {
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.ws-match-pair-row {
    display: flex;
    align-items: center;
    gap: 6px;
}
.ws-match-pair-row input {
    flex: 1;
}
.ws-match-arrow {
    color: var(--text-muted);
    flex: 0 0 auto;
}
.ws-add-pair-btn {
    align-self: flex-start;
    background: none;
    border: 1px dashed var(--border);
    border-radius: 6px;
    padding: 3px 10px;
    font-size: 0.8rem;
    color: var(--text-muted);
    cursor: pointer;
}
.ws-add-pair-btn:hover {
    color: var(--text);
    border-color: var(--text-muted);
}

/* Multi-part (short_answer / extended_response) editor */
.ws-parts {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.ws-part-row {
    display: grid;
    grid-template-columns: auto 1fr auto auto;
    grid-template-areas:
        "letter text   marks del"
        "table  table  table  table"
        "answer answer answer answer";
    align-items: start;
    gap: 4px 6px;
    padding: 6px 8px;
    border: 1px solid var(--border);
    border-radius: 6px;
}
.ws-part-letter {
    grid-area: letter;
    color: var(--text-muted);
    font-size: 0.82rem;
    padding-top: 6px;
}
.ws-part-text  { grid-area: text; }
.ws-part-row .ws-answer-label { grid-area: marks; white-space: nowrap; }
.ws-part-row .ws-marks-input  { width: 56px; }
.ws-part-row .ws-del-q        { grid-area: del; margin-top: 0; }
.ws-part-answer {
    grid-area: answer;
    resize: vertical;
    font-family: inherit;
    font-size: 0.85rem;
}
.ws-add-part-btn {
    align-self: flex-start;
    background: none;
    border: 1px dashed var(--border);
    border-radius: 6px;
    padding: 3px 10px;
    font-size: 0.8rem;
    color: var(--text-muted);
    cursor: pointer;
}
.ws-add-part-btn:hover {
    color: var(--text);
    border-color: var(--text-muted);
}

/* Data table editor, nested inside a part row (exam-style generation only) */
.ws-part-table-editor {
    grid-area: table;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.ws-table-grid {
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.ws-table-row {
    display: grid;
    grid-template-columns: repeat(var(--ws-table-cols), 1fr) auto;
    gap: 4px;
    align-items: center;
}
.ws-table-cell {
    font-size: 0.82rem;
    padding: 4px 6px;
}
.ws-table-row .ws-del-q {
    margin-top: 0;
    padding: 4px 6px;
}
.ws-table-controls {
    display: flex;
    gap: 8px;
}

/* Graph / reaction-profile / electron-shells / source editors, nested inside
   a part row (exam-style generation only). Share the table editor's grid
   slot - a part has at most one of these, never more than one at a time. */
.ws-part-graph-editor,
.ws-part-profile-editor,
.ws-part-shells-editor,
.ws-part-source-editor,
.ws-part-code-editor {
    grid-area: table;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.ws-graph-fields {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 14px;
}
.ws-graph-field {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 0.78rem;
    color: var(--text-muted);
    white-space: nowrap;
}
.ws-graph-field .ws-edit-input { width: 90px; }
.ws-graph-points {
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.ws-graph-point-row {
    display: grid;
    grid-template-columns: 1fr 1fr auto;
    gap: 4px;
    align-items: center;
    max-width: 220px;
}
.ws-graph-point-row.ws-shell-row {
    grid-template-columns: 1fr auto;
    max-width: 140px;
}
.ws-part-source-editor textarea {
    resize: vertical;
    font-family: inherit;
    font-size: 0.85rem;
}
.ws-code-editor-text {
    resize: vertical;
    font-family: 'SF Mono', 'Consolas', 'Menlo', ui-monospace, monospace;
    font-size: 0.82rem;
    line-height: 1.4;
    white-space: pre;
    overflow-x: auto;
    tab-size: 4;
}

/* Extended response editor */
.ws-er-meta {
    display: flex;
    gap: 12px;
    align-items: center;
}
.ws-marks-input {
    width: 64px;
    flex: 0 0 auto;
}
.ws-model-answer {
    resize: vertical;
    font-family: inherit;
    font-size: 0.85rem;
}

.ws-del-q {
    background: none;
    border: none;
    cursor: pointer;
    color: var(--text-muted);
    font-size: 0.8rem;
    padding: 6px 6px;
    border-radius: 4px;
    flex-shrink: 0;
    margin-top: 2px;
    line-height: 1;
}
.ws-del-q:hover { color: var(--red-soft); background: rgba(177, 70, 55, 0.08); }

.ws-add-q-btn {
    background: none;
    border: 1px dashed var(--border);
    border-radius: 6px;
    padding: 5px 12px;
    font-family: inherit;
    font-size: 0.82rem;
    color: var(--text-muted);
    cursor: pointer;
    text-align: left;
    margin-left: 30px;
}
.ws-add-q-btn:hover { border-color: var(--accent); color: var(--accent); }

/* Add section bar */
.ws-add-section-bar {
    padding: 14px 22px;
    border-top: 1px solid var(--border);
    position: relative;
    display: flex;
    align-items: center;
}

.ws-section-picker {
    position: absolute;
    bottom: calc(100% + 4px);
    left: 22px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: var(--shadow-card);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    z-index: 100;
    min-width: 190px;
    padding: 4px;
}

.ws-section-pick-btn,
.quiz-section-pick-btn {
    background: none;
    border: none;
    padding: 8px 12px;
    border-radius: 7px;
    font-family: inherit;
    font-size: 0.88rem;
    color: var(--text);
    cursor: pointer;
    text-align: left;
    transition: background 0.1s;
}
.ws-section-pick-btn:hover,
.quiz-section-pick-btn:hover { background: rgba(54, 96, 255, 0.07); color: var(--accent); }

@media (hover: none) {
    .ws-drag-handle,
    .ws-remove-section,
    .ws-del-q,
    .ws-add-q-btn { opacity: 1; }
}

/* ── Quick access panel ───────────────────────────────── */
.quick-access {
    display: flex;
    gap: 12px;
    margin-bottom: 32px;
}

.qa-card {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    padding: 16px 18px;
    background: var(--surface);
    border: 1.5px solid var(--border);
    border-radius: var(--radius);
    cursor: pointer;
    text-align: left;
    transition: border-color 0.15s, box-shadow 0.15s, transform 0.1s;
}

.qa-card:hover {
    border-color: var(--accent);
    box-shadow: 0 4px 16px rgba(54, 96, 255, 0.1);
    transform: translateY(-1px);
}

.qa-icon {
    font-size: 1.1rem;
    color: var(--accent);
    margin-bottom: 4px;
    line-height: 1;
}

.qa-label {
    font-size: 0.95rem;
    font-weight: 700;
    color: var(--text);
}

.qa-desc {
    font-size: 0.8rem;
    color: var(--text-muted);
}

/* ── Dashboard tabs ───────────────────────────────────── */
/* The rail belongs to the row rather than the bar, so it still runs the full
   width once the bar becomes a sideways scroller on narrow screens. */
.dash-tabs-row {
    display: flex;
    align-items: flex-end;
    margin-bottom: 24px;
    border-bottom: 2px solid var(--border);
}
.dashboard-tabs {
    display: flex;
    gap: 0;
}

.mkt-modes {
    display: flex;
    gap: 4px;
    padding: 3px;
    border-radius: 999px;
    background: var(--surface-2);
    border: 1px solid var(--border);
}
.mkt-mode {
    padding: 6px 14px;
    border-radius: 999px;
    border: 1px solid transparent;
    background: none;
    color: var(--text-muted);
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.15s, color 0.15s, box-shadow 0.15s;
}
.mkt-mode:hover:not(.active) { color: var(--text); }
.mkt-mode.active {
    background: var(--card-bg);
    border-color: var(--border);
    color: var(--accent);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.07);
}
.mkt-mode:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

/* Status pill on your own listings – "live", "in review", "rejected". */
.mkt-status {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 700;
}
.mkt-status-published { background: rgba(0, 199, 141, 0.12); color: #0c7a5b; }
.mkt-status-flagged_for_review { background: rgba(245, 158, 11, 0.14); color: #92620a; }
.mkt-status-rejected { background: rgba(220, 38, 38, 0.12); color: #b91c1c; }

/* ── Admin moderation queue ─────────────────────── */
.mod-row {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 12px;
    padding: 12px 0;
    border-bottom: 1px solid var(--border);
}
.mod-row:last-child { border-bottom: 0; }
.mod-thumb { grid-row: 1; }
.mod-row-body { min-width: 0; }
.mod-row-actions { display: flex; gap: 6px; flex-shrink: 0; }
.mod-reason {
    margin-top: 4px;
    font-size: 0.82rem;
    color: var(--red-soft);
}
/* A reported listing has no thumbnail column, so its body spans the slot the
   moderation row leaves for one. */
.rep-row { grid-template-columns: 1fr auto; }
/* Reporter notes are user-written and can be long; kept quiet and wrapped
   rather than clamped, since the detail is the point of reading them. */
.rep-notes {
    margin-top: 6px;
    padding: 7px 9px;
    border-radius: 8px;
    background: var(--surface-2);
    font-size: 0.78rem;
    line-height: 1.5;
    color: var(--text-muted);
    white-space: pre-wrap;
}

.mod-slides {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 12px;
    padding: 12px 0 4px;
}
.mod-slide {
    border: 1px solid var(--border);
    border-radius: 8px;
    aspect-ratio: 16 / 9;
    overflow: hidden;
}
.btn-mod-approve:hover { background: rgba(0, 199, 141, 0.12); border-color: rgba(0, 199, 141, 0.35); color: #0d8a66; }
.btn-mod-reject:hover { background: rgba(177, 70, 55, 0.1); border-color: rgba(177, 70, 55, 0.35); color: var(--red-soft); }

@media (max-width: 640px) {
    .mod-row { grid-template-columns: auto 1fr; }
    .mod-row-actions { grid-column: 1 / -1; }
}

/* Marketplace card styling lives at the end of this file – it overrides
   .pres-card-item / .pres-item-btn, which are declared further down. */

.dash-tab {
    padding: 10px 22px;
    font-size: 0.95rem;
    font-weight: 500;
    color: var(--text-muted);
    cursor: pointer;
    border: none;
    background: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -2px;
    transition: color 0.15s, border-color 0.15s;
}

.dash-tab.active {
    color: var(--accent);
    border-bottom-color: var(--accent);
    font-weight: 700;
}

.dash-tab:hover:not(.active) { color: var(--text); }

/* ── Quiz share inline result ─────────────────────────── */
.quiz-share-wrap {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 6px;
}

.quiz-share-result {
    display: flex;
    align-items: center;
    gap: 8px;
    background: rgba(54, 96, 255, 0.07);
    border: 1px solid rgba(54, 96, 255, 0.2);
    border-radius: 6px;
    padding: 6px 8px 6px 12px;
    font-size: 0.82rem;
    color: var(--text);
    max-width: 380px;
}

/* The link itself gives way first, on one line. It used to be `word-break:
   break-all` on the row, which also applied to the buttons – so "Copy" wrapped
   to "Cop/y" as soon as the toolbar got tight. */
#quiz-share-url {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-family: monospace;
}

.quiz-share-result .btn {
    flex: none;
    white-space: nowrap;
}

/* ── Quiz results view ────────────────────────────────── */
#quiz-results-view {
    padding: 24px var(--page-pad);
}

.results-toolbar {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-bottom: 24px;
}

.results-toolbar h2 {
    font-size: 1.2rem;
    font-weight: 700;
}

.results-count {
    font-size: 0.8rem;
    color: var(--text-muted);
    white-space: nowrap;
}

.results-search {
    padding: 5px 10px;
    border: 1px solid var(--border);
    border-radius: 6px;
    font-size: 0.82rem;
    background: var(--surface);
    color: var(--text);
    width: 200px;
    outline: none;
    transition: border-color 0.15s;
}

.results-search:focus {
    border-color: var(--accent);
}

.results-search::-webkit-search-cancel-button {
    -webkit-appearance: none;
    height: 14px;
    width: 14px;
    background: var(--text-muted);
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='white' d='M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z'/%3E%3C/svg%3E") center/contain no-repeat;
    cursor: pointer;
    opacity: 0.5;
    transition: opacity 0.15s;
}

.results-search::-webkit-search-cancel-button:hover {
    opacity: 1;
}

.results-body {
    padding: 24px;
}

.result-card {
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 16px 20px;
    margin-bottom: 12px;
    background: var(--surface);
}

.result-header {
    display: flex;
    align-items: center;
    gap: 12px;
    cursor: pointer;
    user-select: none;
    margin-bottom: 10px;
}

.result-name { font-weight: 600; font-size: 1rem; }

.result-time {
    font-size: 0.8rem;
    color: var(--text-muted);
    margin-right: auto;
}

/* The same tinted pill as "Responses" on a quiz row: an action that makes
   something, so it reads as one rather than as a line of muted text. */
.btn-followup {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    padding: 5px 10px 5px 9px;
    border: 1px solid rgba(54, 96, 255, 0.2);
    border-radius: 999px;
    background: rgba(54, 96, 255, 0.08);
    color: var(--accent);
    font: inherit;
    font-size: 0.8rem;
    font-weight: 600;
    line-height: 1.2;
    white-space: nowrap;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
}
.btn-followup:hover,
.btn-followup[aria-expanded="true"] {
    background: rgba(54, 96, 255, 0.15);
    border-color: rgba(54, 96, 255, 0.35);
}
.btn-followup:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.btn-followup svg { width: 14px; height: 14px; flex: none; }
.btn-followup .btn-followup-caret { width: 10px; height: 10px; opacity: 0.7; }

.stu-mark-followup { margin-top: 10px; }

.collapse-icon {
    font-size: 0.6rem;
    color: var(--text-muted);
    flex-shrink: 0;
}

/* The chevron flip lives in the t-acc-chevron rule. */

/* Collapsed height is owned by the t-acc grid track. */

.btn-delete-response {
    background: none;
    border: none;
    cursor: pointer;
    color: var(--text-muted);
    font-size: 1.1rem;
    line-height: 1;
    padding: 2px 6px;
    border-radius: 4px;
    transition: color 0.15s, background 0.15s;
    flex-shrink: 0;
}

.btn-delete-response:hover {
    color: #c41e3a;
    background: rgba(196, 30, 58, 0.08);
}

.result-questions {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.result-q {
    padding: 8px 10px;
    background: rgba(0,0,0,0.02);
    border-radius: 6px;
}

.result-q-text {
    font-size: 0.85rem;
    color: var(--text);
    margin-bottom: 4px;
    line-height: 1.4;
}

.result-q-row {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.82rem;
}

.result-q-label {
    color: var(--text-muted);
    flex-shrink: 0;
}

.result-q-value {
    font-weight: 600;
    color: var(--text);
}

.result-q-value.correct {
    color: #1a8a3f;
}

.result-q-value.incorrect {
    color: #c41e3a;
}

.result-q-badge {
    font-size: 0.85rem;
    font-weight: 700;
    flex-shrink: 0;
}

.result-q-badge.correct {
    color: #1a8a3f;
}

.result-q-badge.incorrect {
    color: #c41e3a;
}

.result-q-correct {
    display: block;
    font-size: 0.78rem;
    color: var(--text-muted);
    margin-top: 2px;
}

.result-q-img {
    display: block;
    max-width: 320px;
    width: 100%;
    border: 1px solid var(--border, #e0e0e0);
    border-radius: 8px;
    background: #fff;
    cursor: zoom-in;
    margin: 4px 0;
}

.result-q-img.expanded {
    max-width: 100%;
    cursor: zoom-out;
}

.result-q-blank {
    font-weight: 400;
    color: var(--text-muted);
    font-style: italic;
}

/* ── Quiz list Responses button ───────────────────────── */
.pres-item-btn {
    margin-left: 8px;
    padding: 5px 12px;
    border-radius: 6px;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    border: 1px solid transparent;
    transition: background 0.15s, border-color 0.15s;
    flex-shrink: 0;
    white-space: nowrap;
}

.btn-responses {
    background: rgba(54, 96, 255, 0.08);
    border-color: rgba(54, 96, 255, 0.2);
    color: var(--accent);
    opacity: 0;
    transition: opacity 0.15s, background 0.15s;
}

.pres-item:hover .btn-responses { opacity: 1; }
.btn-responses:hover { background: rgba(54, 96, 255, 0.15); }

/* ═══════════════════════════════════════════════════════════
   Polish & motion layer
   Appended last so it wins the cascade. Subtle, springy,
   consistent – the in-app counterpart of the landing's energy.
   ═══════════════════════════════════════════════════════════ */

/* ── Shared keyframes ─────────────────────────────────── */
@keyframes viewIn {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: translateY(0); }
}
@keyframes itemIn {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}
@keyframes cardPop {
    from { opacity: 0; transform: translate(-50%, -50%) translateY(14px) scale(0.96); }
    to   { opacity: 1; transform: translate(-50%, -50%) translateY(0) scale(1); }
}
@keyframes menuIn {
    from { opacity: 0; transform: translateY(-6px) scale(0.97); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes fadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}
@keyframes thumbIn {
    from { opacity: 0; transform: scale(0.85); }
    to   { opacity: 1; transform: scale(1); }
}
@keyframes statusPulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.55; }
}
@keyframes indeterminate {
    from { transform: translateX(-110%); }
    to   { transform: translateX(360%); }
}

/* ── View transitions ─────────────────────────────────── */
#generator-view:not([hidden]),
#presentations-list-view:not([hidden]),
#worksheet-view:not([hidden]),
#quiz-view:not([hidden]),
#quiz-results-view:not([hidden]),
#plan-view:not([hidden]),
#ppt-editor:not([hidden]),
#ws-page-preview:not([hidden]),
#quiz-page-preview:not([hidden]) {
    animation: viewIn 0.35s cubic-bezier(0.22, 0.8, 0.36, 1) both;
}

/* ── Staggered list entrances ─────────────────────────── */
.pres-item,
.plan-card {
    animation: itemIn 0.32s cubic-bezier(0.22, 0.8, 0.36, 1) both;
}
.pres-item:nth-child(1), .plan-card:nth-child(1) { animation-delay: 0.02s; }
.pres-item:nth-child(2), .plan-card:nth-child(2) { animation-delay: 0.06s; }
.pres-item:nth-child(3), .plan-card:nth-child(3) { animation-delay: 0.10s; }
.pres-item:nth-child(4), .plan-card:nth-child(4) { animation-delay: 0.14s; }
.pres-item:nth-child(5), .plan-card:nth-child(5) { animation-delay: 0.18s; }
.pres-item:nth-child(6), .plan-card:nth-child(6) { animation-delay: 0.22s; }
.pres-item:nth-child(7), .plan-card:nth-child(7) { animation-delay: 0.26s; }
.pres-item:nth-child(n+8), .plan-card:nth-child(n+8) { animation-delay: 0.3s; }

/* Plan cards feel inspectable */
.plan-card {
    transition: border-color 0.2s, box-shadow 0.2s, transform 0.2s;
}
.plan-card:hover {
    border-color: rgba(54, 96, 255, 0.28);
    box-shadow: 0 8px 20px rgba(30, 27, 46, 0.07);
    transform: translateX(3px);
}

/* ── Nav ──────────────────────────────────────────────── */
#nav .logo { transition: opacity 0.15s; }
#nav .logo:hover { opacity: 0.75; }

/* ── Quick-access cards: colour-coded, livelier ───────── */
.qa-card {
    position: relative;
    overflow: hidden;
    padding: 18px 20px;
    transition: border-color 0.2s, box-shadow 0.2s, transform 0.2s;
}
.qa-card::after {
    content: '\2192';
    position: absolute;
    right: 18px;
    top: 50%;
    transform: translate(8px, -50%);
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--accent);
    opacity: 0;
    transition: opacity 0.2s ease, transform 0.2s ease;
}
.qa-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 12px 28px rgba(30, 27, 46, 0.1);
}
.qa-card:hover::after {
    opacity: 1;
    transform: translate(0, -50%);
}

.qa-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 38px;
    height: 38px;
    border-radius: 11px;
    font-size: 1rem;
    margin-bottom: 8px;
    transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.qa-card:hover .qa-icon { transform: rotate(-6deg) scale(1.12); }

.qa-card[data-action="presentation"] .qa-icon {
    background: rgba(54, 96, 255, 0.12);
    color: var(--accent);
}
.qa-card[data-action="worksheet"] .qa-icon {
    background: rgba(0, 199, 141, 0.14);
    color: #0d8a66;
}
.qa-card[data-action="quiz"] .qa-icon {
    background: rgba(244, 180, 71, 0.22);
    color: #b7791f;
}
.qa-card[data-action="marking"] .qa-icon {
    background: rgba(134, 87, 209, 0.14);
    color: var(--purple);
}
/* Rose, which the marketplace band used to carry. With blue, green, amber and
   purple already spoken for there is no sixth hue here that is not a near-miss
   of one of them – teal reads as the worksheet green it sits next to. Hue is
   doing real work in this row (five things you make, told apart at a glance)
   and none in the band, which is told apart by being full width and the only
   solid button on the page, so the row gets the last one. */
.qa-card[data-action="booklet"] .qa-icon {
    background: rgba(214, 71, 122, 0.13);
    color: #c03a6c;
}
.qa-card[data-action="presentation"]:hover { border-color: var(--accent); }
.qa-card[data-action="worksheet"]:hover    { border-color: var(--green); }
.qa-card[data-action="quiz"]:hover         { border-color: var(--amber); }
.qa-card[data-action="marking"]:hover      { border-color: var(--purple); }
.qa-card[data-action="booklet"]:hover      { border-color: #c03a6c; }

/* ── Dashboard tabs: sliding underline ────────────────── */
.dash-tab {
    position: relative;
    border-bottom-color: transparent;
    border-radius: 8px 8px 0 0;
    transition: color 0.15s, background 0.15s;
}
.dash-tab.active { border-bottom-color: transparent; }
.dash-tab:hover:not(.active) { background: rgba(30, 27, 46, 0.035); }

/* ── Generation progress: indeterminate track ─────────── */
#progress-bar {
    position: relative;
    overflow: hidden;
}
#progress-bar::after {
    content: '';
    position: absolute;
    left: 0;
    bottom: 0;
    height: 3px;
    width: 38%;
    border-radius: 2px;
    background: linear-gradient(90deg, transparent, var(--accent), transparent);
    animation: indeterminate 1.5s ease-in-out infinite;
}
#progress-text { animation: statusPulse 2s ease-in-out infinite; }

.status-generating { animation: statusPulse 1.6s ease-in-out infinite; }

/* ── Slide thumbnails: pop in ─────────────────────────── */
.slide-thumb { animation: thumbIn 0.28s cubic-bezier(0.34, 1.4, 0.64, 1) both; }

/* ── Toolbar buttons: tactile press ───────────────────── */
.toolbar-btn {
    border-radius: 6px;
    transition: background 0.15s, border-color 0.15s, color 0.15s, transform 0.12s, box-shadow 0.15s;
}
.toolbar-btn:hover:not(:disabled) {
    transform: translateY(-1px);
    box-shadow: 0 3px 8px rgba(30, 27, 46, 0.08);
}
.toolbar-btn:active:not(:disabled) { transform: translateY(0); box-shadow: none; }
.toolbar-btn:disabled { opacity: 0.4; cursor: not-allowed; }

/* ── Theme picker: lift + ring ────────────────────────── */
.theme-btn {
    transition: border-color 0.15s, background 0.15s, transform 0.15s, box-shadow 0.15s;
}
.theme-btn:hover { transform: translateY(-1px); }
.theme-btn.active { box-shadow: 0 0 0 3px rgba(54, 96, 255, 0.14); }

/* ── Modals: fade backdrop, springy card ──────────────── */
.add-slide-backdrop { animation: fadeIn 0.2s ease both; }

/* ── Context menus ────────────────────────────────────── */

/* ── Section pickers (worksheet / quiz "+ Add section") ─ */

/* ── Subject dropdown ─────────────────────────────────── */

/* ── Consistent keyboard focus ────────────────────────── */
.btn:focus-visible,
.toolbar-btn:focus-visible,
.theme-btn:focus-visible,
.qa-card:focus-visible,
.dash-tab:focus-visible,
.ppt-mode-btn:focus-visible,
.auth-tab:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* ═══════════════════════════════════════════════════════════
   Teacher-friendly layer
   Warm identity, page headers, empty states & list polish –
   shares the landing page's personality inside the product.
   ═══════════════════════════════════════════════════════════ */

/* ── Nav identity ─────────────────────────────────────── */
#nav .logo {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    color: var(--text);
}

.logo-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: 10px;
    background: linear-gradient(135deg, var(--accent), #60a5fa);
    color: #fff;
    font-size: 0.76rem;
    font-weight: 900;
    box-shadow: 0 5px 14px rgba(54, 96, 255, 0.32);
    transform: rotate(-4deg);
    transition: transform 0.2s ease;
    flex-shrink: 0;
}
#nav .logo:hover .logo-mark { transform: rotate(4deg) scale(1.06); }

/* ── The organisation's crest ──────────────────────────
   Sits next to our mark, divided from it by a hairline: co-branding, not a
   replacement. #nav is space-between and this is its middle child, so the auto
   right margin is what keeps it tucked against the logo instead of drifting
   into the centre of the bar.

   Sized by height so that a wide wordmark and a square badge both sit on the
   same baseline and neither changes the height of the nav. */
.nav-org {
    display: inline-flex;
    align-items: center;
    margin-right: auto;
    margin-left: 14px;
    padding: 2px 0 2px 14px;
    border: 0;
    border-left: 1px solid var(--border);
    background: none;
    cursor: pointer;
    min-width: 0;
    /* A school's crest can be any width, so it is the one part of the bar that
       gives way when the row runs out of room – everything else keeps its size
       and the logo would wrap its wordmark if squeezed. */
    flex: 0 1 auto;
    overflow: hidden;
    /* Quieter than our own mark until you go looking for it: it says whose
       account this is, it is not a second thing competing to be clicked. */
    opacity: 0.85;
    transition: opacity 0.15s;
}
.nav-org:hover { opacity: 1; }
.nav-org:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

.nav-org-logo {
    display: block;
    height: 28px;
    width: auto;
    max-width: 140px;
    min-width: 0;
    object-fit: contain;
}

/* On a phone or a tablet the nav is already fighting for room, and our own mark
   plus the create button are what a teacher actually needs there. Dropped rather
   than shrunk: a crest squeezed to a sliver of colour says less than no crest. */
@media (max-width: 820px) {
    .nav-org { display: none; }
}

/* ── Account avatar + menu ────────────────────────────── */
.nav-user { position: relative; display: flex; align-items: center; }

.nav-avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: linear-gradient(135deg, #4cbb7f, #2f9d6a);
    color: #fff;
    font-family: inherit;
    font-size: 0.82rem;
    font-weight: 800;
    text-transform: uppercase;
    cursor: pointer;
    box-shadow: 0 3px 10px rgba(47, 157, 106, 0.28);
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.nav-avatar { position: relative; }
/* Pro members: a blue star on the avatar's corner, ringed in the page colour so
   it reads as sitting on top of the circle rather than cut into it. */
.nav-avatar-pro {
    position: absolute;
    right: -3px;
    bottom: -3px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: linear-gradient(135deg, #60a5fa, var(--accent));
    color: #fff;
    box-shadow: 0 0 0 2px var(--bg, #f1ebe3);
}
.nav-avatar-pro[hidden] { display: none; }
.nav-avatar:hover,
.nav-avatar[aria-expanded="true"] {
    transform: scale(1.06);
    box-shadow: 0 5px 14px rgba(47, 157, 106, 0.38);
}

.nav-user-menu {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    min-width: 220px;
    background: var(--card-bg, #fff);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: 0 12px 32px rgba(0,0,0,0.14);
    padding: 6px;
    z-index: 5000;
}

.nav-user-info {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 8px 10px 10px;
    border-bottom: 1px solid var(--border);
    margin-bottom: 6px;
}
.nav-user-label {
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    color: var(--text-muted);
}
.nav-user-email {
    max-width: 240px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text);
}

.nav-user-item {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    padding: 8px 10px;
    border: none;
    border-radius: 8px;
    background: none;
    text-align: left;
    font-size: 0.86rem;
    font-weight: 600;
    font-family: inherit;
    color: var(--text);
    cursor: pointer;
    transition: background 0.1s, color 0.1s;
}
.nav-user-item:hover { background: var(--surface, rgba(0,0,0,0.05)); color: #a4453c; }
.nav-user-item[hidden] { display: none; }

/* The account-menu copies of Students and Lessons. Only on the widths where the
   bar has dropped them, so the same destination is never offered twice. */
.nav-menu-only { display: none; }
@media (max-width: 720px) {
    .nav-menu-only:not([hidden]) { display: flex; }
}
.nav-user-item-badge { margin-left: auto; }
.nav-user-item[aria-current="page"] { background: rgba(54, 96, 255, 0.1); color: var(--accent); }

.nav-create-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    margin-right: 9px;
    flex-shrink: 0;
    vertical-align: middle;
}
.dot-blue  { background: var(--accent); }
.dot-green { background: var(--green); }
.dot-amber { background: var(--amber); }
.nav-create-item { display: flex; align-items: center; padding: 10px 14px; font-weight: 600; }

/* ── Dashboard greeting ───────────────────────────────── */
.view-head { margin-bottom: 26px; }

.view-kicker {
    font-size: 0.78rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: var(--accent);
    margin-bottom: 4px;
}

.view-title {
    font-size: 1.7rem;
    font-weight: 900;
    letter-spacing: -0.02em;
    color: var(--text);
    line-height: 1.15;
}

/* ── Dashboard: slim command line ──────────────────────── */
.dash-bar {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 14px 16px;
    flex-wrap: wrap;
    margin-bottom: 22px;
}
.dash-hello {
    display: flex;
    flex-direction: column;
    gap: 3px;
}
.dash-kicker {
    font-size: 0.8rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: var(--accent);
}
.dash-title {
    font-size: 1.6rem;
    font-weight: 900;
    letter-spacing: -0.02em;
    line-height: 1.1;
    color: var(--text);
}

/* Time saved as a small ticket of reclaimed hours – the signature.
   A torn perforation splits the running total from the tally. */
.timetape {
    position: relative;
    z-index: 30;   /* keep the subject popovers above the create row below */
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 6px 13px;
    font-size: 0.85rem;
    color: var(--text);
    background: linear-gradient(180deg, #fffdf9, #f6efe4);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8), 0 2px 8px rgba(30, 27, 46, 0.05);
    cursor: default;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.timetape:hover {
    transform: translateY(-1px);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8), 0 7px 18px rgba(30, 27, 46, 0.1);
}
.tt-clock { color: var(--green); flex-shrink: 0; }
.tt-main {
    display: inline-flex;
    align-items: baseline;
    gap: 4px;
    font-weight: 600;
    color: var(--text-muted);
}
.tt-main b {
    font-size: 1.02rem;
    font-weight: 900;
    color: var(--text);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.01em;
}
.tt-main b::before {
    content: '\2248\00a0';
    font-weight: 800;
    color: var(--text-muted);
}
.tt-unit { font-weight: 800; color: var(--accent); margin-right: 3px; }
.tt-perf {
    width: 2px;
    align-self: stretch;
    margin: 3px 1px;
    background-image: repeating-linear-gradient(180deg, rgba(30, 27, 46, 0.3) 0 2px, transparent 2px 5px);
}
.tt-counts { display: inline-flex; align-items: center; gap: 10px; }
.tt-c {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-variant-numeric: tabular-nums;
    cursor: default;
    border-radius: 5px;
}
.tt-c:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tt-c b { font-weight: 800; color: var(--text); }
.tt-c::before {
    content: '';
    width: 7px;
    height: 7px;
    border-radius: 2px;
    flex-shrink: 0;
}
.tt-c-pres::before { background: var(--accent); }
.tt-c-ws::before   { background: var(--green); }
.tt-c-quiz::before { background: var(--amber); }
.tt-empty { color: var(--text-muted); font-size: 0.82rem; }

/* Per-subject breakdown popover on each count */
.tt-pop {
    position: absolute;
    top: calc(100% + 11px);
    right: 0;
    z-index: 60;
    min-width: 168px;
    max-width: 240px;
    padding: 11px 13px;
    text-align: left;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: 11px;
    box-shadow: var(--shadow-card);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-5px);
    transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;
    cursor: default;
}
/* invisible bridge so the hover survives the gap to the popover */
.tt-pop::before {
    content: '';
    position: absolute;
    top: -11px;
    left: 0;
    right: 0;
    height: 11px;
}
/* little pointer up toward the number */
.tt-pop::after {
    content: '';
    position: absolute;
    top: -5px;
    right: 12px;
    width: 9px;
    height: 9px;
    background: var(--card-bg);
    border-left: 1px solid var(--border);
    border-top: 1px solid var(--border);
    transform: rotate(45deg);
}
.tt-c:hover .tt-pop,
.tt-c:focus .tt-pop,
.tt-c:focus-visible .tt-pop {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}
.tt-pop-title {
    display: block;
    font-size: 0.64rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    margin-bottom: 8px;
}
.tt-pop-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.tt-pop-list li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 18px;
    font-size: 0.83rem;
    font-weight: 600;
    color: var(--text);
}
.tt-pop-list li span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.tt-pop-list li b {
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
    flex-shrink: 0;
}
.tt-pop-empty { font-size: 0.82rem; color: var(--text-muted); margin: 0; }

/* ── Make row: the five create tiles (primary action) ──── */
.make-row {
    display: grid;
    /* Five fixed columns, not auto-fit. At the app's 1052px of content auto-fit
       with a 210px minimum lands on four and drops the fifth onto a row of its
       own – exactly the "three plus a stray" the marketplace band below was
       kept out of the row to avoid. The step down is 5 → 3 → 2 → 1, never 4,
       for the same reason. */
    grid-template-columns: repeat(5, 1fr);
    gap: 12px;
    /* Matches the gap between the tiles, not the 30px that separates this whole
       block from the library below. The band underneath is another answer to
       "what are we making today?", so it belongs inside the group's rhythm
       rather than floating between the group and the next section. */
    margin-bottom: 12px;
}

/* Horizontal chip: icon, then label, with the + on the trailing edge. Five
   across at the app's 1052px cap leaves about 200px each, which is enough for
   an icon and a label and not enough for a description – so the description
   goes rather than being ellipsised into a stub, and the row keeps the height
   it had at four. It comes back below, where there is room for it. */
.qa-tile {
    flex-direction: row;
    align-items: center;
    gap: 12px;
    padding: 13px 14px;
    min-width: 0;
}
.qa-tile::after { content: none; }
.qa-tile .qa-icon { width: 34px; height: 34px; border-radius: 10px; margin-bottom: 0; }
.qa-tile .qa-icon svg { width: 18px; height: 18px; }
.qa-tile-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; text-align: left; }
.qa-tile .qa-label { font-size: 0.92rem; white-space: nowrap; }
.qa-tile .qa-desc { display: none; font-size: 0.78rem; line-height: 1.4; }
.qa-plus {
    margin-left: auto;
    font-size: 1.35rem;
    font-weight: 300;
    line-height: 1;
    color: var(--text-muted);
    opacity: 0.4;
    transition: opacity 0.2s ease, transform 0.2s ease, color 0.2s ease;
}
.qa-tile:hover .qa-plus { opacity: 1; transform: rotate(90deg); color: var(--accent); }

/* Below five across every chip is at least 340px wide, which is room for the
   description again – the line that says what a Booklet actually is. */
@media (max-width: 1000px) {
    .make-row { grid-template-columns: repeat(3, 1fr); }
    .qa-tile { padding: 13px 16px; }
    .qa-tile .qa-desc { display: block; }
}
@media (max-width: 720px)  { .make-row { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px)  { .make-row { grid-template-columns: 1fr; } }

/* ── Marketplace band ─────────────────────────────────────
   Sits between the create tiles and the library, because that is where the
   "make one or take one" decision actually gets made. Deliberately not styled
   as another .qa-card: those are white, colour-coded and open a form in place,
   and this is one solid slab of ink that leaves for another page. The
   difference in treatment is the difference in what happens.

   The same paper as the cards above it, not a tint, a gradient or a dark slab
   – on cream everything else is light, and a band that changes the background
   reads as a smudge or as a cookie bar. This one earns its prominence from
   being the full width of the row above, a size up on the icon and type, and
   the only solid button on the page. */
.mkt-band {
    display: flex;
    align-items: center;
    gap: 18px;
    margin-bottom: 30px;
    padding: 20px 22px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card-bg);
    color: inherit;
    text-decoration: none;
    transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
}
.mkt-band:hover {
    transform: translateY(-2px);
    border-color: var(--accent);
    box-shadow: 0 12px 28px rgba(30, 27, 46, 0.1);
}
.mkt-band:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* A size up on .qa-icon's 38px, and deliberately the only one of these that is
   not a colour. The five generators own blue, green, amber, purple and rose
   between them; taking a sixth hue here would either repeat one of theirs or
   land close enough to read as a near-miss, and this is not a sixth thing to
   make. Ink on paper says "same family, different job" more honestly than a
   colour nobody else has. */
.mkt-band-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 48px;
    height: 48px;
    border-radius: 13px;
    background: rgba(30, 27, 46, 0.07);
    color: var(--text);
    transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.mkt-band-icon svg { width: 25px; height: 25px; }
.mkt-band:hover .mkt-band-icon { transform: rotate(-6deg) scale(1.1); }

.mkt-band-txt { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.mkt-band-title { font-size: 1.06rem; font-weight: 800; letter-spacing: -0.015em; }
.mkt-band-sub {
    font-size: 0.84rem;
    line-height: 1.45;
    color: var(--text-muted);
}

.mkt-band-cta {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    flex-shrink: 0;
    margin-left: auto;
    padding: 10px 18px;
    border-radius: 999px;
    background: var(--accent);
    color: #fff;
    font-size: 0.88rem;
    font-weight: 800;
    white-space: nowrap;
    transition: background 0.2s ease;
}
.mkt-band:hover .mkt-band-cta { background: var(--accent-dark); }
.mkt-band-arrow { transition: transform 0.2s ease; }
.mkt-band:hover .mkt-band-arrow { transform: translateX(3px); }

/* Narrow: the band stacks and the button takes the full width, rather than the
   copy being squeezed to two words a line beside a pill that will not shrink. */
@media (max-width: 720px) {
    .mkt-band { flex-wrap: wrap; gap: 12px; }
    .mkt-band-txt { flex: 1 1 200px; }
    .mkt-band-cta { margin-left: 0; width: 100%; justify-content: center; }
    /* The copy wraps to four or five lines down here, and an icon centred
       against that block reads as floating rather than as labelling it. */
    .mkt-band-icon { align-self: flex-start; }
}

@media (prefers-reduced-motion: reduce) {
    .mkt-band, .mkt-band-icon, .mkt-band-arrow, .mkt-band-cta { transition: none; }
    .mkt-band:hover { transform: none; }
    .mkt-band:hover .mkt-band-icon { transform: none; }
    .mkt-band:hover .mkt-band-arrow { transform: none; }
}

/* ── Share nudge ──────────────────────────────────────────
   Raised at the moment a lesson is finished – after presenting it, or after
   taking it away to teach. Bottom-left, because the editor's own controls and
   the toast host both live on the right, and a prompt that covers the thing you
   just did is a prompt you dismiss without reading. */
.share-nudge {
    position: fixed;
    left: 20px;
    bottom: 20px;
    z-index: 120;
    width: 300px;
    max-width: calc(100vw - 40px);
    padding: 18px 20px 16px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--card-bg);
    box-shadow: 0 16px 40px rgba(30, 27, 46, 0.16);
    opacity: 0;
    transform: translateY(12px);
    transition: opacity var(--duration-medium) var(--ease-smooth-out),
                transform var(--duration-medium) var(--ease-smooth-out);
}
.share-nudge.is-open {
    opacity: 1;
    transform: translateY(0);
    transition: opacity var(--duration-slow) var(--ease-smooth-out),
                transform var(--duration-slow) var(--ease-smooth-out);
}
.share-nudge[hidden] { display: none; }

.share-nudge-close {
    position: absolute;
    top: 10px;
    right: 10px;
    display: inline-flex;
    padding: 5px;
    border: 0;
    border-radius: 7px;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
}
.share-nudge-close:hover { background: rgba(30, 27, 46, 0.06); color: var(--text); }
.share-nudge-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.share-nudge-title {
    margin-right: 22px;
    font-size: 0.98rem;
    font-weight: 800;
    letter-spacing: -0.01em;
}
.share-nudge-sub {
    margin-top: 4px;
    font-size: 0.83rem;
    line-height: 1.5;
    color: var(--text-muted);
}
.share-nudge-actions { display: flex; gap: 8px; margin-top: 14px; }

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

/* On a phone the editor is already tight, so it spans the width at the bottom
   rather than floating over a third of the slide. */
@media (max-width: 560px) {
    .share-nudge { left: 12px; right: 12px; bottom: 12px; width: auto; }
}

/* ── Library heading ──────────────────────────────────── */
.dash-section { margin-top: 0; }
.dash-eyebrow {
    font-size: 0.75rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: var(--text-muted);
    margin-bottom: 12px;
}
.dash-section .dashboard-tabs { margin-top: 0; }

@media (max-width: 620px) {
    .dash-bar { gap: 10px; }
    .timetape { width: 100%; justify-content: flex-start; }
}

/* ── Card headers (generator / worksheet / quiz forms) ── */
.card-head {
    display: flex;
    align-items: flex-start;
    gap: 16px;
    padding-bottom: 20px;
    margin-bottom: 20px;
    border-bottom: 1px dashed var(--border);
}

.card-head-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 46px;
    border-radius: 14px;
    flex-shrink: 0;
    transform: rotate(-4deg);
}
.card-head-icon svg { width: 24px; height: 24px; }
.ch-blue  { background: rgba(54, 96, 255, 0.12);  color: var(--accent); }
.ch-green { background: rgba(0, 199, 141, 0.14);  color: #0d8a66; }
.ch-amber { background: rgba(244, 180, 71, 0.22); color: #b7791f; }

.card-head-title {
    font-size: 1.35rem;
    font-weight: 900;
    letter-spacing: -0.02em;
    color: var(--text);
    margin-bottom: 3px;
}

.card-head-sub {
    font-size: 0.9rem;
    color: var(--text-muted);
    line-height: 1.5;
    max-width: 560px;
}

/* ── Quick-access icons (inline SVG) ──────────────────── */
.qa-icon svg { width: 20px; height: 20px; }

/* ── List items: type icons & tidier row ──────────────── */
.pres-item { gap: 14px; }

.pres-item-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border-radius: 11px;
    flex-shrink: 0;
    transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.pres-item-icon svg { width: 18px; height: 18px; }
.pres-item:hover .pres-item-icon { transform: rotate(-5deg) scale(1.08); }

.pi-presentation { background: rgba(54, 96, 255, 0.1);   color: var(--accent); }
.pi-worksheet    { background: rgba(0, 199, 141, 0.12);  color: #0d8a66; }
.pi-quiz         { background: rgba(244, 180, 71, 0.2);  color: #b7791f; }
.pi-marking      { background: rgba(134, 87, 209, 0.12); color: var(--purple); }
.pi-students     { background: rgba(54, 96, 255, 0.1);   color: var(--accent); }

.pres-delete-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.pres-delete-btn svg { display: block; }

/* ── Loading skeleton rows ────────────────────────────── */
.list-skel {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 18px 24px;
    border: 1px solid rgba(221, 213, 200, 0.6);
    border-radius: var(--radius);
    background: rgba(255, 255, 255, 0.4);
    margin-bottom: 12px;
    animation: itemIn 0.3s ease both;
}

.list-skel-icon,
.list-skel-lines i {
    display: block;
    background: linear-gradient(90deg, rgba(221, 213, 200, 0.55) 25%, rgba(238, 232, 224, 0.9) 50%, rgba(221, 213, 200, 0.55) 75%);
    background-size: 200% 100%;
    animation: shimmer 1.4s infinite;
    border-radius: 6px;
}

.list-skel-icon {
    width: 38px;
    height: 38px;
    border-radius: 11px;
    flex-shrink: 0;
}

.list-skel-lines {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.list-skel-lines i { height: 12px; }
.list-skel-lines i:last-child { height: 9px; opacity: 0.7; }

/* ── Friendly empty states ────────────────────────────── */
.list-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 52px 24px 56px;
    border: 1.5px dashed rgba(168, 160, 180, 0.4);
    border-radius: 16px;
    background: rgba(255, 255, 255, 0.35);
    animation: itemIn 0.35s cubic-bezier(0.22, 0.8, 0.36, 1) both;
}

.list-empty-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 62px;
    height: 62px;
    border-radius: 18px;
    margin-bottom: 16px;
    transform: rotate(-5deg);
}
.list-empty-icon svg { width: 30px; height: 30px; }
.le-presentation { background: rgba(54, 96, 255, 0.1);  color: var(--accent); }
.le-worksheet    { background: rgba(0, 199, 141, 0.12); color: #0d8a66; }
.le-quiz         { background: rgba(244, 180, 71, 0.2); color: #b7791f; }
.le-marking      { background: rgba(134, 87, 209, 0.12); color: var(--purple); }
.le-students     { background: rgba(54, 96, 255, 0.1);  color: var(--accent); }

.list-empty-title {
    font-size: 1.1rem;
    font-weight: 800;
    color: var(--text);
    margin-bottom: 6px;
}

.list-empty-sub {
    font-size: 0.9rem;
    color: var(--text-muted);
    line-height: 1.55;
    max-width: 380px;
    margin-bottom: 20px;
}

/* ── Reduced motion: sit still ────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    #generator-view, #presentations-list-view, #worksheet-view,
    #quiz-view, #quiz-results-view, #plan-view, #ppt-editor,
    #ws-page-preview, #quiz-page-preview,
    .pres-item, .plan-card, .slide-thumb,
    .nav-create-menu, .add-slide-card,
    .add-slide-backdrop,
    .quota-card, .quota-backdrop, .quota-meter-fill,
    #thumb-ctx-menu, #slide-ctx-menu, #slide-bg-ctx-menu,
    .ws-section-picker, .sc-dropdown,
    .list-skel, .list-empty {
        animation: none !important;
    }
    #progress-bar::after, #progress-text, .status-generating {
        animation: none !important;
    }
}

/* ── Presentation view toggle + preview grid ─────────────── */
.view-toggle {
    margin-left: auto;
    display: inline-flex;
    gap: 2px;
    padding: 3px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 10px;
}
.view-toggle-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px; height: 26px;
    border: none;
    border-radius: 7px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}
.view-toggle-btn svg { width: 15px; height: 15px; }
.view-toggle-btn:hover { color: var(--text); }
.view-toggle-btn.active {
    background: var(--card-bg);
    color: var(--accent);
    box-shadow: 0 1px 3px rgba(0,0,0,0.08);
}

.pres-grid,
.res-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 18px;
}

/* ── Worksheet & quiz cards ───────────────────────────
   These share .pres-card-item's shell and body, so all three kinds sit on
   the grid at the same size. Only the cover differs: a deck can show its
   first slide, a worksheet can't, so it states how much practice is inside
   and what kind. The bar carries proportion only – the line under it names
   the types, so identity never rests on colour. */
.res-worksheet { --res-hue: #0d8a66; --res-tint: rgba(0, 199, 141, 0.08); }
.res-quiz      { --res-hue: #b7791f; --res-tint: rgba(244, 180, 71, 0.13); }
.res-card:hover { border-color: var(--res-hue); }

/* matches .pres-preview's footprint exactly */
.res-cover {
    aspect-ratio: 16 / 9;
    display: flex;
    flex-direction: column;
    padding: 12px 14px;
    background: var(--res-tint);
    border-bottom: 1px solid var(--border);
}

.res-kind {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.66rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--res-hue);
}
.res-kind svg { width: 13px; height: 13px; }

.res-count {
    display: block;
    margin-top: auto;
    margin-bottom: 8px;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--text-muted);
    line-height: 1.1;
}
.res-count b {
    font-size: 1.45rem;
    font-weight: 800;
    letter-spacing: -0.03em;
    color: var(--text);
    margin-right: 3px;
}

/* one line each, so the body never grows past a presentation's */
.res-card .pres-item-meta {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Proportion bar: one hue, segments held apart by surface-coloured gaps. */
.res-mix {
    display: flex;
    gap: 2px;
    height: 7px;
    margin-bottom: 7px;
}
.res-mix-seg {
    min-width: 4px;
    border-radius: 2px;
    background: var(--res-hue);
    opacity: 0.85;
}
.res-mix-seg:nth-child(even) { opacity: 0.55; }
.res-mix-seg:nth-child(3n)   { opacity: 0.7; }

.res-mix-text {
    font-size: 0.72rem;
    line-height: 1.35;
    color: var(--text-muted);
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

.pres-card-item {
    position: relative;   /* anchors .card-menu-btn-float to the card's corner */
    display: flex;
    flex-direction: column;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: 14px;
    overflow: hidden;
    cursor: pointer;
    transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s;
}
.pres-card-item:hover {
    transform: translateY(-3px);
    box-shadow: var(--shadow-card);
    border-color: var(--accent);
}
.pres-preview {
    position: relative;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
}
.pres-preview-slide {
    position: absolute;
    top: 0; left: 0;
    width: 960px; height: 540px;
    transform-origin: top left;
    pointer-events: none;
    user-select: none;
}
.pres-preview-empty {
    display: flex;
    align-items: center;
    justify-content: center;
}
.pres-preview-empty span { color: var(--text-muted); opacity: 0.5; }
.pres-preview-empty svg { width: 32px; height: 32px; }
.pres-card-body {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 12px 14px 12px;
    min-width: 0;
}
.pres-card-body .pres-item-title {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.pres-card-actions {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 8px;
    opacity: 0;
    transition: opacity 0.15s;
}
.pres-card-item:hover .pres-card-actions,
.pres-card-item:focus-within .pres-card-actions { opacity: 1; }
.pres-card-actions .pres-delete-btn { margin-left: auto; opacity: 1; }
@media (hover: none) {
    .pres-card-actions { opacity: 1; }
}
@media (max-width: 640px) {
    .pres-grid { gap: 12px; }
}

.pres-thumb {
    flex: 0 0 76px;
    width: 76px;
    aspect-ratio: 16 / 9;
    border: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
    border-radius: 6px;
}

/* ── Alignment fixes: tabs toggle + list/card actions ──── */
.dashboard-tabs { align-items: center; }
.view-toggle {
    align-self: center;
    margin-bottom: 6px;
    flex-shrink: 0;
}

/* List rows: rely on flex gap, kill the old margin hacks */
.pres-item { gap: 12px; }
.pres-item-btn { margin-left: 0; }
.pres-item .pres-item-btn { align-self: center; }

/* Consistent, compact icon button for delete */
.pres-delete-btn,
.pres-card-actions .pres-delete-btn {
    margin-left: 0;
    width: 30px;
    height: 28px;
    padding: 0;
    border-radius: 7px;
}
.pres-item .pres-delete-btn { margin-left: 4px; }
.pres-card-actions .pres-delete-btn { margin-left: auto; }

/* Publish is an icon button so a fourth action still fits the card row.
   It takes over pushing the pair right, so delete stops doing it. */
.btn-publish-mkt {
    width: 30px;
    height: 28px;
    padding: 0;
    border-radius: 7px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 255, 255, 0.72);
    border-color: var(--border);
    color: var(--text-muted);
}
.btn-publish-mkt:hover {
    background: rgba(54, 96, 255, 0.12);
    border-color: rgba(54, 96, 255, 0.35);
    color: #2b4fd6;
}

/* Share to the org library – the same icon-button shell as Publish, which
   sits next to it on a presentation row. Both are "send this somewhere", so
   they should not be two different shapes; without this the button fell back
   to the browser's own grey and sat off the row's baseline.
   .pres-share-btn is the worksheet and quiz rows, .btn-share-org the
   presentation ones – same control, two call sites. */
.btn-share-org,
.pres-share-btn {
    width: 30px;
    height: 28px;
    padding: 0;
    border-radius: 7px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 255, 255, 0.72);
    border-color: var(--border);
    color: var(--text-muted);
}
.btn-share-org:hover,
.pres-share-btn:hover {
    background: rgba(54, 96, 255, 0.12);
    border-color: rgba(54, 96, 255, 0.35);
    color: #2b4fd6;
}
/* Cards are as narrow as 240px, so their actions live behind one overflow
   menu rather than four buttons fighting for the row. */
.card-menu-btn {
    margin-left: auto;
    width: 30px;
    height: 28px;
    padding: 0;
    border-radius: 7px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 255, 255, 0.72);
    border-color: var(--border);
    color: var(--text-muted);
}
.card-menu-btn:hover,
.card-menu-btn[aria-expanded="true"] {
    background: var(--surface-2);
    border-color: var(--border);
    color: var(--text);
}

/* Floating variant: pinned to the thumbnail's corner instead of taking a row of
   its own. It lands on top of an arbitrary slide, so it carries its own frosted
   backdrop rather than trusting whatever is behind it for contrast. */
.card-menu-btn-float {
    position: absolute;
    top: 8px;
    right: 8px;
    z-index: 2;
    margin-left: 0;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: rgba(255, 253, 249, 0.82);
    backdrop-filter: blur(8px) saturate(1.15);
    -webkit-backdrop-filter: blur(8px) saturate(1.15);
    border: 1px solid rgba(30, 27, 46, 0.1);
    box-shadow: 0 2px 6px rgba(30, 27, 46, 0.14);
    color: #4a4560;
    /* Revealed on hover like the other card actions, but scaled from slightly
       small so it grows in rather than blinking on. */
    opacity: 0;
    transform: scale(0.92);
    transition: opacity 0.15s ease, transform 0.15s ease,
                background 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}
.card-menu-btn-float svg { width: 16px; height: 16px; }

.pres-card-item:hover .card-menu-btn-float,
.pres-card-item:focus-within .card-menu-btn-float,
.card-menu-btn-float[aria-expanded="true"],
.card-menu-btn-float:focus-visible {
    opacity: 1;
    transform: scale(1);
}
.card-menu-btn-float:hover,
.card-menu-btn-float[aria-expanded="true"] {
    background: var(--card-bg);
    border-color: rgba(30, 27, 46, 0.16);
    box-shadow: 0 3px 10px rgba(30, 27, 46, 0.2);
    color: var(--text);
}
.card-menu-btn-float:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
/* Touch has no hover to reveal it, so it stays put. */
@media (hover: none) {
    .card-menu-btn-float { opacity: 1; transform: none; }
}

.card-menu {
    position: fixed;
    z-index: 200;
    min-width: 186px;
    padding: 5px;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: var(--shadow-card);
}
.card-menu-over-modal { z-index: 10001; }
.card-menu-item {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    padding: 8px 10px;
    border: 0;
    background: transparent;
    border-radius: 7px;
    font-size: 0.86rem;
    font-weight: 500;
    color: var(--text);
    text-align: left;
    cursor: pointer;
}
.card-menu-item:hover,
.card-menu-item:focus-visible { background: var(--surface-2); outline: none; }
.card-menu-item svg { width: 15px; height: 15px; flex-shrink: 0; }
.card-menu-danger { color: var(--red-soft); }
.card-menu-danger:hover,
.card-menu-danger:focus-visible { background: rgba(177, 70, 55, 0.09); }

/* Give the generate shortcuts a real style of their own */
.btn-gen-ws,
.btn-gen-quiz {
    background: rgba(255, 255, 255, 0.72);
    border-color: var(--border);
    color: var(--text-muted);
}
.btn-gen-ws:hover {
    background: rgba(0, 199, 141, 0.12);
    border-color: rgba(0, 199, 141, 0.35);
    color: #0d8a66;
}
.btn-gen-quiz:hover {
    background: rgba(244, 180, 71, 0.16);
    border-color: rgba(244, 180, 71, 0.45);
    color: #b7791f;
}

/* Card actions row: equal-height buttons, tidy spacing */
.pres-card-actions { gap: 8px; }
.pres-card-actions .pres-item-btn {
    padding: 4px 10px;
    height: 28px;
    display: inline-flex;
    align-items: center;
}
/* Except the icon ones, which are square. The rule above is the more specific
   of the two, so it would otherwise pad a 30px button in by 10 each side and
   crush the glyph. */
.pres-card-actions .pres-share-btn { width: 30px; padding: 0; justify-content: center; }

/* Share and delete pin to the right on every card, so Share does not sit hard
   left on a worksheet (no Responses button) and mid-row on a quiz. Whatever
   word-shaped buttons a card has stay left; the icon pair is always in the
   same corner. */
.pres-card-actions .pres-share-btn { margin-left: auto; }
.pres-card-actions .pres-share-btn + .pres-delete-btn { margin-left: 0; }

/* Reveals with the card. Its own rule keys off .pres-item, which is the list
   row – on a card the button was left at opacity 0, invisible but still
   taking up its space and still clickable. */
.pres-card-item:hover .btn-responses,
.pres-card-item:focus-within .btn-responses { opacity: 1; }

/* Five tabs never fit a phone on one row, and wrapping them broke the bar:
   the rail and the sliding underline are both pinned to the bottom of the
   whole element, so they ended up rows below the tab they belong to. One
   scrolling row puts tab, rail and underline back on the same line. */
@media (max-width: 760px) {
    .dashboard-tabs {
        flex-wrap: nowrap;
        min-width: 0;
        overflow-x: auto;
        overflow-y: hidden;
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
        /* A scroll container clips anything outside its padding box, so the
           underline moves inside it – and the box hangs 2px lower so that
           inside edge lands exactly on the row's rail rather than above it. */
        padding-bottom: 2px;
        margin-bottom: -2px;
    }
    .dashboard-tabs::-webkit-scrollbar { display: none; }
    /* Outweighs the shared `bottom: -2px` further down the file, which would
       put the underline outside the scroller and get it clipped away. */
    .dash-tabs-row .dashboard-tabs .t-tabs-pill { bottom: 0; }
    .dash-tab {
        flex-shrink: 0;
        margin-bottom: 0;
        padding: 10px 14px;
    }
}

@media (max-width: 560px) {
    /* Phones: the toggle moves up onto the heading line, so the tab strip gets
       the full width to scroll in. Lifted out of flow rather than reordered
       because the heading row has the height for it already. */
    .dash-section { position: relative; }
    .dash-eyebrow { display: flex; align-items: center; min-height: 32px; margin-bottom: 6px; }
    .view-toggle { position: absolute; top: 0; right: 0; margin: 0; }

    .pres-item .btn-gen-ws,
    .pres-item .btn-gen-quiz { display: none; }
}

/* ── Billing & usage ─────────────────────────────── */
.billing-card, .admin-card { max-width: 720px; margin: 0 auto; }
.admin-card { max-width: 900px; }
.admin-card + .admin-card { margin-top: 28px; }
.billing-loading { color: var(--text-muted); font-size: 0.95rem; padding: 0.5rem 0; }
.billing-plan-row {
    display: flex; justify-content: space-between; align-items: center;
    gap: 1rem 1.5rem; flex-wrap: wrap;
    padding: 1.1rem 1.25rem; margin-bottom: 1.75rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}
.billing-plan-row-edu {
    background: linear-gradient(120deg, rgba(244, 180, 71, 0.1), rgba(244, 180, 71, 0.03));
    border-color: rgba(244, 180, 71, 0.3);
}
.plan-info { display: flex; flex-direction: column; gap: 0.4rem; min-width: 0; }
.plan-line { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.plan-badge {
    display: inline-block; padding: 0.25rem 0.8rem; border-radius: 999px;
    font-size: 0.8rem; font-weight: 700; letter-spacing: 0.02em; line-height: 1.4;
}
.plan-free { background: var(--surface); border: 1px solid var(--border); color: var(--text-muted); }
.plan-pro { background: linear-gradient(90deg, var(--accent), #60a5fa); color: #fff; box-shadow: 0 2px 8px rgba(54, 96, 255, 0.25); }
.plan-edu { background: rgba(244, 180, 71, 0.16); border: 1px solid rgba(244, 180, 71, 0.5); color: #96650f; }
.plan-edu-status { font-size: 0.82rem; color: var(--text-muted); }
.plan-pending { background: rgba(244, 180, 71, 0.16); border: 1px solid rgba(244, 180, 71, 0.5); color: #96650f; }
.plan-meta-line { margin: 0; font-size: 0.85rem; color: var(--text-muted); line-height: 1.4; }

.sept-pending-panel {
    margin-bottom: 1.75rem;
    padding: 1.5rem 1.6rem;
    background: linear-gradient(120deg, rgba(244, 180, 71, 0.1), rgba(244, 180, 71, 0.03));
    border: 1px solid rgba(244, 180, 71, 0.3);
    border-radius: var(--radius);
}
.sept-pending-badge {
    display: inline-block;
    margin-bottom: 0.6rem;
    padding: 0.2rem 0.6rem;
    background: var(--amber);
    color: #4a3308;
    border-radius: 999px;
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.sept-pending-head { margin: 0 0 0.4rem; font-size: 1.05rem; }
.sept-pending-copy { margin: 0; font-size: 0.88rem; color: var(--text-muted); line-height: 1.5; }

.switch-yearly-row {
    display: flex; align-items: center; justify-content: space-between; gap: 1.25rem;
    margin-top: 1rem; padding: 1rem 1.25rem;
    background: linear-gradient(120deg, rgba(54, 96, 255, 0.06), rgba(96, 165, 250, 0.04));
    border: 1px solid rgba(54, 96, 255, 0.15);
    border-radius: 10px;
}
.switch-yearly-head { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.switch-yearly-head strong { font-size: 0.95rem; }
.switch-yearly-badge {
    padding: 0.1rem 0.55rem; border-radius: 999px;
    background: rgba(54, 96, 255, 0.12); color: var(--accent);
    font-size: 0.7rem; font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase;
    white-space: nowrap;
}
.switch-yearly-info p { margin: 0.3rem 0 0; font-size: 0.82rem; color: var(--text-muted); line-height: 1.4; }
.switch-yearly-cta { display: flex; align-items: center; gap: 0.9rem; flex-shrink: 0; }
.switch-yearly-price { font-size: 1.1rem; font-weight: 700; white-space: nowrap; }
.switch-yearly-price small { font-size: 0.75rem; font-weight: 500; color: var(--text-muted); }
.switch-yearly-cta .btn { white-space: nowrap; }
@media (max-width: 560px) {
    .switch-yearly-row { flex-direction: column; align-items: stretch; }
    .switch-yearly-cta { justify-content: space-between; }
}
.plan-cancelling {
    color: #8a6210; background: rgba(244, 180, 71, 0.18);
    border-radius: 999px; padding: 0.2rem 0.65rem;
    font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
}
.billing-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }

/* ── Billing sections ──────────────────────────────────
   Three genuinely different things live under here: an allowance that resets
   every month, a storage total that never does, and credit being earned. They
   used to be one flat run of identical bars under headings small enough to miss.
   A real heading with the section's headline fact opposite it separates them
   before any bar has to be read. */
.bill-sec { margin-top: 2rem; }
.bill-sec-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1rem;
    padding-bottom: 0.6rem;
    border-bottom: 1px solid var(--border);
}
.bill-sec-title { font-size: 1rem; font-weight: 800; letter-spacing: -0.01em; }
.bill-sec-meta {
    flex-shrink: 0;
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

/* Two across on anything but a phone. Five allowances stacked full-width put a
   screen's worth of space between each label and its own number; side by side
   they read as a set, and the count sits next to the thing it counts. */
.quota-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 1.1rem 1.75rem;
}
.quota-item { min-width: 0; }

.usage-row-top { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; margin-bottom: 0.4rem; }
.usage-label { font-size: 0.9rem; font-weight: 600; }
.usage-count { color: var(--text-muted); font-size: 0.85rem; font-weight: 500; font-variant-numeric: tabular-nums; white-space: nowrap; }
.usage-sep { opacity: 0.55; margin: 0 0.1em; }
.usage-track { height: 8px; border-radius: 999px; background: var(--surface-2); border: 1px solid var(--border); overflow: hidden; }
.usage-fill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--accent), #60a5fa); transition: width 0.4s ease; }
.usage-fill.warn { background: linear-gradient(90deg, var(--amber), #f6c35c); }
/* Marketplace credit. Green because this is the one bar on the page that fills
   towards something you get rather than a limit you run out of – the same
   shape, saying the opposite thing. */
.usage-fill.earn { background: linear-gradient(90deg, var(--green), #5ee0b8); }
.usage-fill.full { background: linear-gradient(90deg, var(--red-soft), #c9604f); }
.usage-note { margin: 0.45rem 0 0; font-size: 0.8rem; color: var(--text-muted); }
.usage-note-alert { color: var(--red-soft); font-weight: 600; }

/* The organisation bucket – one pool, with a ceiling on the priciest kinds
   inside it.

   Laid out as nesting rather than as a row of equal bars because the flat
   version was actively misleading: a total and two caps sitting side by side
   in one grid read as three separate allowances, so a member concluded they
   had the whole bucket AND a presentation budget AND an edit budget. The caps
   come out of the total. So the total is full width and first, and the capped
   kinds are indented under it behind a rule that says "these are part of the
   thing above" – the one job spacing alone can't do here, because the next
   section is also just spacing away. */
.bucket-total .usage-label { font-size: 1rem; }
.bucket-total .usage-track { height: 10px; }

.bucket-subs {
    margin-top: 1.4rem;
    padding-inline-start: 1rem;
    padding-block-end: 0.35rem;
    border-inline-start: 2px solid var(--border);
}
.bucket-subs-head {
    margin: 0 0 0.9rem;
    font-size: 0.8rem;
    line-height: 1.45;
    color: var(--text-muted);
}
/* Tighter than the standalone grid: these are subordinate rows, and matching
   the parent's spacing would undo the hierarchy the indent just established. */
.bucket-subs .quota-grid { gap: 0.9rem 1.5rem; }

/* Clear of the indented block above by more than that block's own row gap, or
   it reads as one more capped kind rather than a note about the whole bucket. */
.bucket-foot { margin-top: 1.9rem; }

/* Folded away unless storage is actually tight – see storageBreakdown. */
.storage-details { margin-top: 1rem; }
.storage-summary {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0;
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--text-muted);
    cursor: pointer;
    list-style: none;
}
.storage-summary::-webkit-details-marker { display: none; }
.storage-summary::before {
    content: '';
    width: 0.4rem;
    height: 0.4rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(-45deg);
    transition: transform 0.2s var(--ease-smooth-out);
}
.storage-details[open] .storage-summary::before { transform: rotate(45deg); }
.storage-summary:hover { color: var(--text); }
.storage-summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }
@media (prefers-reduced-motion: reduce) { .storage-summary::before { transition: none; } }

.storage-list { display: flex; flex-direction: column; gap: 0.3rem; margin: 0 -0.7rem; }
.storage-item { display: flex; align-items: center; gap: 0.9rem; width: 100%; padding: 0.55rem 0.7rem; border: 1px solid transparent; border-radius: 10px; background: none; color: inherit; font: inherit; text-align: left; text-decoration: none; cursor: pointer; transition: background 0.15s ease, border-color 0.15s ease; }
a.storage-item:hover { background: var(--surface-2); border-color: var(--border); }
a.storage-item:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The remainder row is a total, not a destination. */
.storage-item-rest { width: auto; margin: 0.3rem -0.7rem 0; cursor: default; }
.storage-item-rest .storage-item-title,
.storage-item-rest .storage-item-meta { color: var(--text-muted); }
.storage-item-rest .storage-item-fill { background: var(--border); }
.storage-item-main { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; flex: 1 1 auto; }
.storage-item-title { font-size: 0.88rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.storage-item-meta { font-size: 0.78rem; color: var(--text-muted); }
.storage-item-bar { flex: 0 1 120px; height: 6px; border-radius: 999px; background: var(--surface-2); border: 1px solid var(--border); overflow: hidden; }
.storage-item-fill { display: block; height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--accent), #60a5fa); }
.storage-item-size { flex: 0 0 auto; min-width: 4.5rem; text-align: right; font-size: 0.82rem; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--text-muted); }
@media (max-width: 560px) { .storage-item-bar { display: none; } }

/* ── Quota / upgrade modal ───────────────────────── */
#quota-modal {
    position: fixed;
    inset: 0;
    z-index: 10000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}

.quota-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(30, 27, 46, 0.45);
    backdrop-filter: blur(10px) saturate(1.1);
    -webkit-backdrop-filter: blur(10px) saturate(1.1);
    animation: fadeIn 0.25s ease both;
}

.quota-card {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 400px;
    max-height: calc(100vh - 2rem);
    overflow-y: auto;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: 20px;
    box-shadow: 0 30px 80px rgba(30, 27, 46, 0.28), 0 4px 16px rgba(30, 27, 46, 0.12);
    overflow: hidden;
}
@keyframes quotaPop {
    from { opacity: 0; transform: translateY(14px) scale(0.96); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}

.quota-close {
    position: absolute;
    top: 12px; right: 12px;
    z-index: 2;
    width: 30px; height: 30px;
    display: flex; align-items: center; justify-content: center;
    background: rgba(255, 255, 255, 0.18);
    border: none;
    border-radius: 50%;
    color: rgba(255, 255, 255, 0.85);
    font-size: 1.25rem;
    line-height: 1;
    cursor: pointer;
    font-family: inherit;
    transition: background 0.15s;
}
.quota-close:hover { background: rgba(255, 255, 255, 0.32); color: #fff; }

.quota-hero {
    position: relative;
    overflow: hidden;
    padding: 2.1rem 1.8rem 1.7rem;
    text-align: center;
    color: #fff;
    background: linear-gradient(135deg, #2448d8 0%, var(--accent) 48%, #6d8cff 100%);
}
.quota-hero-orb {
    position: absolute;
    border-radius: 50%;
    filter: blur(2px);
    pointer-events: none;
}
.quota-hero-orb-a {
    width: 190px; height: 190px;
    top: -90px; right: -60px;
    background: radial-gradient(circle, rgba(255, 255, 255, 0.22), transparent 70%);
}
.quota-hero-orb-b {
    width: 150px; height: 150px;
    bottom: -80px; left: -50px;
    background: radial-gradient(circle, rgba(255, 255, 255, 0.16), transparent 70%);
}
.quota-hero-icon {
    width: 54px; height: 54px;
    margin: 0 auto 0.9rem;
    display: flex; align-items: center; justify-content: center;
    background: rgba(255, 255, 255, 0.16);
    border: 1px solid rgba(255, 255, 255, 0.35);
    border-radius: 16px;
    color: #ffe9a8;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
}
.quota-hero h2 {
    font-size: 1.35rem;
    font-weight: 800;
    letter-spacing: -0.01em;
    margin-bottom: 0.4rem;
}
#quota-message {
    font-size: 0.88rem;
    line-height: 1.55;
    color: rgba(255, 255, 255, 0.88);
    max-width: 30ch;
    margin: 0 auto;
}

.quota-body { padding: 1.4rem 1.8rem 1.5rem; }

.quota-meter { margin-bottom: 1.2rem; }
.quota-meter-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-bottom: 0.45rem;
}
#quota-meter-label {
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
}
#quota-meter-count {
    font-size: 0.8rem;
    font-weight: 700;
    color: var(--red-soft);
    font-variant-numeric: tabular-nums;
}
.quota-meter-track {
    height: 9px;
    border-radius: 999px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    overflow: hidden;
}
.quota-meter-fill {
    width: 100%;
    height: 100%;
    border-radius: 999px;
    background: linear-gradient(90deg, var(--red-soft), #c9604f);
}
#quota-modal:not([hidden]) .quota-meter-fill { animation: quotaFill 0.7s 0.25s cubic-bezier(0.22, 1, 0.36, 1) both; }
@keyframes quotaFill { from { width: 0; } to { width: 100%; } }

.quota-perks {
    list-style: none;
    display: grid;
    gap: 0.55rem;
    margin-bottom: 1.3rem;
    padding: 0;
}
.quota-perks li {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-size: 0.88rem;
    color: var(--text);
}
.quota-perks b { font-weight: 800; }
.quota-perk-check {
    flex-shrink: 0;
    width: 20px; height: 20px;
    display: flex; align-items: center; justify-content: center;
    border-radius: 50%;
    background: rgba(0, 199, 141, 0.14);
    color: var(--green);
    font-size: 0.7rem;
    font-weight: 800;
}

.quota-plans {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.7rem;
    margin-bottom: 1rem;
}
.quota-plan {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.2rem;
    padding: 0.9rem 0.6rem 0.75rem;
    background: none;
    border: 1.5px solid var(--border);
    border-radius: 12px;
    font-family: inherit;
    color: var(--text);
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}
.quota-plan:hover { border-color: var(--text-muted); }
.quota-plan.is-selected {
    border-color: var(--accent);
    background: rgba(54, 96, 255, 0.06);
    box-shadow: inset 0 0 0 1px var(--accent);
}
.quota-plan-tag {
    position: absolute;
    top: -0.55rem;
    left: 50%;
    transform: translateX(-50%);
    padding: 0.14rem 0.5rem;
    background: var(--accent);
    color: #fff;
    border-radius: 999px;
    font-size: 0.62rem;
    font-weight: 800;
    letter-spacing: 0.03em;
    white-space: nowrap;
}
.quota-plan-name {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
}
.quota-plan.is-selected .quota-plan-name { color: var(--accent); }
.quota-plan-price {
    font-size: 1.2rem;
    font-weight: 800;
    letter-spacing: -0.01em;
}
.quota-plan-price small {
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--text-muted);
    margin-left: 0.15rem;
}
.quota-plan-price s {
    font-size: 0.8rem;
    font-weight: 500;
    color: var(--text-muted);
    margin-right: 0.15rem;
}
/* 'Free' standing where a price does, on a tile whose real price is struck
   through beside it. The darker green of .tone-green rather than --green
   itself: --green is a fill colour, and at this size it is a word to read. */
.quota-plan-free { color: #067f5c; }
/* The length has to stay glued to 'Free': the tiles are three across and at
   that width 'for 7 days' breaks after the 'for', orphaning 'days' on a line
   of its own. Unbreakable, so it drops whole to the second line instead. */
.quota-plan-for { white-space: nowrap; margin-left: 0.3rem; }
.quota-plan-note { font-size: 0.7rem; color: var(--text-muted); }

.quota-cta {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.6rem;
    padding: 0.85rem 1.2rem;
    background: linear-gradient(135deg, var(--accent), #5f7dff);
    color: #fff;
    border: none;
    border-radius: 12px;
    font-family: inherit;
    font-size: 0.98rem;
    font-weight: 800;
    letter-spacing: 0.01em;
    cursor: pointer;
    box-shadow: 0 10px 24px rgba(54, 96, 255, 0.32);
    transition: transform 0.15s, box-shadow 0.15s, filter 0.15s;
}
.quota-cta:hover {
    transform: translateY(-1px);
    filter: brightness(1.06);
    box-shadow: 0 14px 30px rgba(54, 96, 255, 0.4);
}
.quota-cta:active { transform: translateY(0); }

/* ---- Billing: upgrade plan picker ---- */
.upgrade-panel {
    margin-bottom: 1.75rem;
    padding: 1.25rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}
.upgrade-head h2 { margin: 0 0 0.25rem; font-size: 1rem; }
.upgrade-head p { margin: 0 0 1rem; font-size: 0.85rem; color: var(--text-muted); }

/* Reuses the quota-modal's .quota-plan tile + .quota-cta button so the
   billing page's plan picker looks and behaves like the upgrade modal –
   select a tile, then confirm with the one button below. */
.billing-plan-select { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 560px) {
    .billing-plan-select { grid-template-columns: 1fr; }
}

/* ---- Billing: school account – the ways into this account ----
   The panel is an inventory of who holds a key. The school holds one; the
   second slot is drawn genuinely empty, because that is the actual state of
   the account. That empty slot is the button, and hovering it completes the
   set – the interaction is the argument. */
.edu-panel {
    margin-bottom: 1.75rem;
    padding: 1.5rem 1.6rem 1.35rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card-bg);
    box-shadow: var(--shadow);
}

.edu-eyebrow {
    margin: 0 0 0.7rem;
    font-size: 0.67rem;
    font-weight: 800;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.edu-head {
    margin: 0 0 0.5rem;
    font-size: 1.35rem;
    font-weight: 800;
    line-height: 1.15;
    letter-spacing: -0.02em;
    color: var(--text);
}

.edu-copy {
    margin: 0 0 1.25rem;
    max-width: 46ch;
    font-size: 0.875rem;
    line-height: 1.55;
    color: var(--text-muted);
}

.edu-keys {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.edu-key {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    width: 100%;
    padding: 0.8rem 0.9rem;
    border-radius: 11px;
    /* Matches the open slot's 1.5px dashed border so both marks align. */
    border: 1.5px solid var(--border);
    background: var(--surface-2);
    text-align: left;
    font-family: inherit;
}

/* The key each account currently has. */
.edu-key-mark {
    position: relative;
    flex: none;
    width: 26px;
    height: 26px;
    border-radius: 8px;
}
.edu-key-mark::after {
    content: '';
    position: absolute;
    inset: 0;
    margin: auto;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--accent);
}
.edu-key-mark.is-held {
    background: rgba(54, 96, 255, 0.1);
    border: 1px solid rgba(54, 96, 255, 0.22);
}
/* Empty on purpose: nothing fills this slot until they act. */
.edu-key-mark.is-open {
    border: 1.5px dashed rgba(107, 101, 128, 0.5);
    background: none;
}
.edu-key-mark.is-open::after {
    transform: scale(0);
    transition: transform 0.22s cubic-bezier(0.34, 1.4, 0.64, 1);
}

.edu-key-text {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
    flex: 1;
}
.edu-key-label {
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--text);
}
.edu-key-sub {
    font-size: 0.75rem;
    line-height: 1.4;
    color: var(--text-muted);
}

.edu-key-pill {
    flex: none;
    padding: 3px 9px;
    border-radius: 999px;
    font-size: 0.66rem;
    font-weight: 800;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    background: rgba(30, 27, 46, 0.06);
    color: var(--text-muted);
}

/* The missing key – dashed and unfilled, and the only control here. */
.edu-key-open {
    border: 1.5px dashed rgba(107, 101, 128, 0.42);
    background: none;
    cursor: pointer;
    transition: border-color 0.2s ease, background-color 0.2s ease;
}
.edu-key-open:hover,
.edu-key-open:focus-visible {
    border-color: var(--accent);
    border-style: solid;
    background: rgba(54, 96, 255, 0.05);
}
.edu-key-open:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.edu-key-open:hover .edu-key-mark.is-open,
.edu-key-open:focus-visible .edu-key-mark.is-open {
    border-style: solid;
    border-color: rgba(54, 96, 255, 0.22);
    background: rgba(54, 96, 255, 0.1);
}
.edu-key-open:hover .edu-key-mark.is-open::after,
.edu-key-open:focus-visible .edu-key-mark.is-open::after {
    transform: scale(1);
}

.edu-key-go {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.8rem;
    font-weight: 700;
    color: var(--accent);
}
.edu-key-go::after {
    content: '→';
    transition: transform 0.2s ease;
}
.edu-key-open:hover .edu-key-go::after { transform: translateX(3px); }

.edu-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.7rem 1rem;
    margin-top: 1.15rem;
    padding-top: 1.1rem;
    border-top: 1px solid var(--border);
}
.edu-foot-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.edu-foot-line {
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--text);
}
.edu-foot-sub {
    font-size: 0.75rem;
    line-height: 1.4;
    color: var(--text-muted);
}

@media (prefers-reduced-motion: reduce) {
    .edu-key-mark.is-open::after,
    .edu-key-go::after,
    .edu-key-open { transition: none; }
}

@media (max-width: 520px) {
    .edu-panel { padding: 1.25rem 1.1rem 1.15rem; }
    .edu-key { flex-wrap: wrap; gap: 0.7rem; padding: 0.75rem 0.8rem; }
    .edu-key-sub { font-size: 0.72rem; }
    /* Too narrow for label and action side by side. Force a line break after
       the label so the action drops below it, indented to the text column
       rather than back under the mark. */
    .edu-key::after {
        content: '';
        order: 2;
        flex-basis: 100%;
        height: 0;
    }
    .edu-key-pill,
    .edu-key-go {
        order: 3;
        margin-left: calc(26px + 0.7rem);
    }
}

.edu-price {
    position: relative;
    flex: none;
    display: flex;
    align-items: baseline;
    gap: 0.12rem;
    padding: 0.5rem 0.8rem;
    background: rgba(54, 96, 255, 0.07);
    border: 1px solid rgba(54, 96, 255, 0.18);
    border-radius: 10px;
    color: var(--accent);
    cursor: default;
}
.edu-price-amt {
    position: relative;
    display: inline-block;
    font-size: 1.5rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    line-height: 1;
}
.edu-price-strike { position: relative; display: inline-block; transition: opacity 0.15s ease 0.3s; }
.edu-price-strike::after {
    content: '';
    position: absolute;
    left: 0;
    top: 50%;
    width: 100%;
    height: 2px;
    background: currentColor;
    transform: translateY(-50%) scaleX(0);
    transform-origin: left;
    transition: transform 0.35s cubic-bezier(0.65, 0, 0.35, 1);
}
.edu-price:hover .edu-price-strike::after { transform: translateY(-50%) scaleX(1); }
.edu-price:hover .edu-price-strike { opacity: 0; }
.edu-price-reveal {
    position: absolute;
    left: 0;
    top: 0;
    opacity: 0;
    white-space: nowrap;
    transition: opacity 0.25s ease 0.32s;
    pointer-events: none;
}
.edu-price:hover .edu-price-reveal { opacity: 1; }
.edu-price-per { font-size: 0.82rem; font-weight: 700; }
.edu-price-yearly-tag {
    max-width: 0;
    overflow: hidden;
    white-space: nowrap;
    font-size: 0.7rem;
    font-weight: 700;
    color: var(--text-muted);
    opacity: 0;
    transition: max-width 0.3s ease 0.15s, opacity 0.25s ease 0.2s;
}
.edu-price:hover .edu-price-yearly-tag {
    max-width: 6rem;
    opacity: 1;
    margin-left: 0.15rem;
}
/* Without a pointer there is no hover, so the £4.99 yearly price and its
   "billed yearly" qualifier would never be visible at all. Show the revealed
   state outright rather than hiding pricing behind an interaction. */
@media (hover: none) {
    .edu-price-strike { opacity: 0; }
    .edu-price-strike::after { transform: translateY(-50%) scaleX(1); }
    .edu-price-reveal { opacity: 1; }
    .edu-price-yearly-tag { max-width: 6rem; opacity: 1; margin-left: 0.15rem; }
}
@media (prefers-reduced-motion: reduce) {
    .edu-price-strike,
    .edu-price-strike::after,
    .edu-price-reveal,
    .edu-price-yearly-tag { transition: none; }
}

.quota-later {
    display: block;
    width: 100%;
    background: none;
    border: none;
    padding: 0.4rem;
    font-family: inherit;
    font-size: 0.84rem;
    font-weight: 600;
    color: var(--text-muted);
    cursor: pointer;
    border-radius: 8px;
    transition: color 0.15s, background 0.15s;
}
.quota-later:hover { color: var(--text); background: var(--surface-2); }

/* ── Admin dashboard ─────────────────────────────── */
.admin-summary { display: flex; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.75rem; }
.admin-stat {
    flex: 1; min-width: 150px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1rem 1.25rem;
}
.admin-stat-num { display: block; font-size: 1.6rem; font-weight: 800; letter-spacing: -0.01em; }
.admin-stat-label { font-size: 0.78rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); }
.admin-users { overflow-x: auto; }
.admin-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.admin-table th, .admin-table td { text-align: left; padding: 0.6rem 0.75rem; border-bottom: 1px solid var(--border); white-space: nowrap; }
.admin-table th { font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); }
.admin-table tbody tr:hover { background: var(--surface-2); }
.admin-table tbody tr:last-child td { border-bottom: none; }
.admin-email { font-weight: 600; }
.admin-tag {
    background: rgba(244, 180, 71, 0.18); color: #8a6210;
    border-radius: 999px; padding: 0.1rem 0.55rem;
    font-size: 0.7rem; font-weight: 700; margin-left: 0.35rem;
}

/* ── Marketplace demand (admin) ──────────────────────────── */
/* `.mkt-modes` carries no margin of its own – the moderation and reports cards
   get their spacing from the rows below it – so the range picker sat directly
   on the stat tiles. Scoped to this one control rather than added to
   `.mkt-modes`, which would shift the other two cards as a side effect. */
#mkt-stats-range { margin-bottom: 1.25rem; }

.mkt-stats-head {
    margin: 1.5rem 0 0.25rem;
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
}
.mkt-stats-note {
    margin: 0 0 0.75rem;
    font-size: 0.82rem;
    color: var(--text-muted);
}
/* Two lists that are read against each other – what people search for, and
   what they search for and do not find. Side by side where there is room. */
.mkt-stats-cols {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 0 2rem;
}
.mkt-stats-table { table-layout: fixed; }
/* The query is the long, variable part and it has to wrap. Written as `td` +
   class rather than the class alone because `.admin-table td` sets
   `white-space: nowrap` at the same specificity and would otherwise win on
   source order – long searches then ran straight through the count beside them
   and out past the edge of the card. */
.admin-table td.mkt-query {
    white-space: normal;
    overflow-wrap: anywhere;
    font-weight: 600;
}
/* Columns of numbers align on the digit, so tabular figures – unlike the stat
   tiles above, which are large standalone values and read better proportional. */
.mkt-num {
    text-align: right;
    font-variant-numeric: tabular-nums;
    width: 5rem;
}
.mkt-dim { color: var(--text-muted); }
/* Listing titles run to 500 characters, and `.admin-table td` holds everything
   on one line – so one long title widened the table past its scroll container
   and pushed the conversion percentage out of sight entirely, leaving a column
   of bars with no numbers against them. Wrapping instead keeps the table inside
   the card whatever a teacher called their deck. */
.admin-table td.mkt-listing {
    white-space: normal;
    max-width: 22rem;
    font-weight: 600;
}
.admin-table td.mkt-facets { white-space: normal; }

/* A meter, not a bar chart: one hue, with the unfilled track a lighter step of
   the same ramp so the whole bar reads as one scale. It exists to make the
   outliers findable in a column of similar-looking percentages – the number
   beside it is what is actually read, so the number is what gets the guaranteed
   room. The track is a fixed width rather than flexible: as `flex: 1` it grew
   to fill the column, pushed the percentage past the edge of the scrolling
   table, and left a row of bars with no values against them. */
.mkt-meter {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.mkt-meter-track {
    flex: none;
    width: 64px;
    height: 6px;
    border-radius: 999px;
    background: rgba(54, 96, 255, 0.15);
    overflow: hidden;
}
.mkt-meter-fill {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: var(--accent);
}
.mkt-meter-num {
    flex: none;
    min-width: 2.9rem;
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    font-size: 0.82rem;
}
.mkt-meter-none { color: var(--text-muted); }

@media (max-width: 640px) {
    .mkt-stats-cols { gap: 0; }
    .mkt-num { width: 4rem; }
    /* The bar is the first thing worth losing when space is short – the
       percentage next to it carries the same information exactly. */
    .mkt-meter-track { display: none; }
}

/* ── Referral discount, as the referred user sees it ─────── */
.referral-banner {
    display: flex; align-items: center; gap: 0.85rem;
    padding: 0.85rem 1rem;
    margin-bottom: 1.25rem;
    border: 1px solid rgba(54, 96, 255, 0.28);
    border-radius: var(--radius);
    background: rgba(54, 96, 255, 0.07);
}
.referral-banner-tag {
    flex: none;
    background: var(--accent); color: #fff;
    border-radius: 999px; padding: 0.3rem 0.7rem;
    font-size: 0.85rem; font-weight: 800; white-space: nowrap;
}
.referral-banner-text { display: flex; flex-direction: column; gap: 0.1rem; }
.referral-banner-text small { color: var(--text-muted); font-size: 0.8rem; }
.upgrade-option-price s { color: var(--text-muted); font-weight: 500; margin-right: 0.15rem; }

/* Sibling of the quota modal, deliberately: same card, same hero, same pop.
   The gradient is green rather than blue because this one is a gift, not a
   paywall – the shape says "modal you know", the colour says "good news". */
#referral-modal {
    position: fixed;
    inset: 0;
    z-index: 10001;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}
#referral-modal[hidden] { display: none; }

.ref-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(30, 27, 46, 0.45);
    backdrop-filter: blur(10px) saturate(1.1);
    -webkit-backdrop-filter: blur(10px) saturate(1.1);
    animation: fadeIn 0.25s ease both;
}

.ref-card {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 400px;
    max-height: calc(100vh - 2rem);
    overflow: hidden auto;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: 20px;
    box-shadow: 0 30px 80px rgba(30, 27, 46, 0.28), 0 4px 16px rgba(30, 27, 46, 0.12);
}
@keyframes refPop {
    from { opacity: 0; transform: translateY(14px) scale(0.96); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}
/* Focused programmatically on open purely to place the screen reader and the
   tab order – a ring around the whole card would just look like a mistake. */
.ref-card:focus { outline: none; }

.ref-close {
    position: absolute;
    top: 12px; right: 12px;
    z-index: 2;
    width: 30px; height: 30px;
    display: flex; align-items: center; justify-content: center;
    background: rgba(255, 255, 255, 0.18);
    border: none;
    border-radius: 50%;
    color: rgba(255, 255, 255, 0.85);
    font-size: 1.25rem;
    line-height: 1;
    font-family: inherit;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}
.ref-close:hover { background: rgba(255, 255, 255, 0.32); color: #fff; }

.ref-hero {
    position: relative;
    overflow: hidden;
    padding: 2.1rem 1.8rem 1.7rem;
    text-align: center;
    color: #fff;
    background: linear-gradient(135deg, #06976e 0%, var(--green) 52%, #4fe0b6 100%);
}
.ref-hero-orb {
    position: absolute;
    border-radius: 50%;
    filter: blur(2px);
    pointer-events: none;
}
.ref-hero-orb-a {
    width: 190px; height: 190px;
    top: -90px; right: -60px;
    background: radial-gradient(circle, rgba(255, 255, 255, 0.24), transparent 70%);
}
.ref-hero-orb-b {
    width: 150px; height: 150px;
    bottom: -80px; left: -50px;
    background: radial-gradient(circle, rgba(255, 255, 255, 0.16), transparent 70%);
}
.ref-hero-icon {
    width: 54px; height: 54px;
    margin: 0 auto 0.9rem;
    display: flex; align-items: center; justify-content: center;
    background: rgba(255, 255, 255, 0.16);
    border: 1px solid rgba(255, 255, 255, 0.35);
    border-radius: 16px;
    color: #fff;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
}
.ref-eyebrow {
    font-size: 0.7rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: rgba(255, 255, 255, 0.8);
    margin-bottom: 0.35rem;
}
#referral-modal-title {
    font-size: 1.35rem;
    font-weight: 800;
    letter-spacing: -0.01em;
    max-width: 20ch;
    margin: 0 auto;
}

.ref-body { padding: 1.5rem 1.8rem 1.5rem; }

/* 14px against the card's 20px: the tile sits inside 1.8rem of padding, so a
   matching 20px would read as pinched. */
.ref-offer {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.15rem;
    padding: 1.1rem 1rem;
    margin-bottom: 1.1rem;
    border: 1.5px dashed rgba(0, 199, 141, 0.5);
    border-radius: 14px;
    background: rgba(0, 199, 141, 0.08);
}
.ref-offer-amount {
    font-size: 2rem;
    font-weight: 900;
    letter-spacing: -0.03em;
    line-height: 1.1;
    color: #06976e;
}
.ref-offer-term {
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--text-muted);
}

.ref-note {
    font-size: 0.88rem;
    line-height: 1.55;
    color: var(--text-muted);
    text-align: center;
    max-width: 32ch;
    margin: 0 auto 1.2rem;
}

.ref-cta {
    width: 100%;
    padding: 0.85rem 1.2rem;
    background: linear-gradient(135deg, var(--accent), #5f7dff);
    color: #fff;
    border: none;
    border-radius: 12px;
    font-family: inherit;
    font-size: 0.98rem;
    font-weight: 800;
    letter-spacing: 0.01em;
    cursor: pointer;
    box-shadow: 0 10px 24px rgba(54, 96, 255, 0.32);
    transition: transform 0.15s, box-shadow 0.15s, filter 0.15s;
}
.ref-cta:hover {
    transform: translateY(-1px);
    filter: brightness(1.06);
    box-shadow: 0 14px 30px rgba(54, 96, 255, 0.4);
}
.ref-cta:active { transform: scale(0.96); }

.ref-later {
    display: block;
    width: 100%;
    margin-top: 0.5rem;
    padding: 0.4rem;
    background: none;
    border: none;
    border-radius: 8px;
    font-family: inherit;
    font-size: 0.84rem;
    font-weight: 600;
    color: var(--text-muted);
    cursor: pointer;
    transition: color 0.15s, background 0.15s;
}
.ref-later:hover { color: var(--text); background: var(--surface-2); }

/* Staged entrance. This modal is seen once in an account's lifetime, so the
   sequence is worth the attention it costs – the discount lands last. */
#referral-modal:not([hidden]) .ref-hero-icon,
#referral-modal:not([hidden]) .ref-eyebrow,
#referral-modal:not([hidden]) #referral-modal-title,
#referral-modal:not([hidden]) .ref-offer,
#referral-modal:not([hidden]) .ref-note,
#referral-modal:not([hidden]) .ref-cta {
    animation: refRise 0.4s cubic-bezier(0.22, 1, 0.36, 1) both;
}
#referral-modal:not([hidden]) .ref-hero-icon { animation-delay: 0.1s; }
#referral-modal:not([hidden]) .ref-eyebrow { animation-delay: 0.16s; }
#referral-modal:not([hidden]) #referral-modal-title { animation-delay: 0.22s; }
#referral-modal:not([hidden]) .ref-offer { animation-delay: 0.32s; }
#referral-modal:not([hidden]) .ref-note { animation-delay: 0.4s; }
#referral-modal:not([hidden]) .ref-cta { animation-delay: 0.46s; }
@keyframes refRise {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
    .ref-card, .ref-backdrop,
    #referral-modal:not([hidden]) .ref-hero-icon,
    #referral-modal:not([hidden]) .ref-eyebrow,
    #referral-modal:not([hidden]) #referral-modal-title,
    #referral-modal:not([hidden]) .ref-offer,
    #referral-modal:not([hidden]) .ref-note,
    #referral-modal:not([hidden]) .ref-cta { animation: none; }
    .ref-cta, .ref-later, .ref-close { transition: none; }
    .ref-cta:hover { transform: none; }
    .ref-cta:active { transform: none; }
}

/* ── What's new ──────────────────────────────────── */
/* The announcement dialog (showWhatsNew in app.js). Plain and short: a title,
   what is new, one button. */
#whatsnew-modal {
    position: fixed;
    inset: 0;
    z-index: 10001;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}
#whatsnew-modal[hidden] { display: none; }
.wn-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(30, 27, 46, 0.45);
    backdrop-filter: blur(10px) saturate(1.1);
    -webkit-backdrop-filter: blur(10px) saturate(1.1);
    animation: fadeIn 0.25s ease both;
}
.wn-card {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 420px;
    max-height: calc(100vh - 2rem);
    overflow: hidden auto;
    padding: 1.8rem 1.8rem 1.4rem;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: 20px;
    box-shadow: 0 30px 80px rgba(30, 27, 46, 0.28), 0 4px 16px rgba(30, 27, 46, 0.12);
}
.wn-card:focus { outline: none; }
.wn-close {
    position: absolute;
    top: 12px; right: 12px;
    width: 30px; height: 30px;
    display: flex; align-items: center; justify-content: center;
    background: rgba(30, 27, 46, 0.06);
    border: none;
    border-radius: 50%;
    color: var(--text-muted);
    font-size: 1.25rem;
    line-height: 1;
    font-family: inherit;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}
.wn-close { z-index: 1; }
.wn-close:hover { background: rgba(30, 27, 46, 0.12); color: var(--text); }
/* The picture across the top of the card. One eight-second loop that tells both
   features as a story: each earlier lesson sends a question across to the
   starter slide, each click (anywhere, as in Present) brings in the next hidden
   answer, the timer finishes. With no
   animation (reduced motion) it rests on the finished slide. */
.wn-hero { margin: -1.8rem -1.8rem 1.4rem; background: linear-gradient(135deg, rgba(54, 96, 255, 0.1), rgba(0, 199, 141, 0.1)); }
.wn-hero svg { display: block; width: 100%; height: auto; overflow: hidden; }
.wn-hero svg * { animation-duration: 8s; animation-iteration-count: infinite; }
.wn-a-c1 { --ink: var(--amber); }
.wn-a-c2 { --ink: var(--purple); }
.wn-a-c3 { --ink: var(--accent); }
.wn-a-ink { fill: var(--ink); }
.wn-a-line { fill: rgba(30, 27, 46, 0.14); }
.wn-a-paper { fill: #fff; stroke: rgba(30, 27, 46, 0.08); }
.wn-a-card { filter: drop-shadow(0 3px 5px rgba(30, 27, 46, 0.1)); animation: wnBob 2.6s ease-in-out infinite alternate; }
.wn-hero svg .wn-a-card { animation-duration: 2.6s; }
.wn-a-card.wn-a-c2 { animation-delay: -0.9s; }
.wn-a-card.wn-a-c3 { animation-delay: -1.7s; }
.wn-a-slide { fill: #fff; stroke: rgba(30, 27, 46, 0.06); filter: drop-shadow(0 8px 16px rgba(30, 27, 46, 0.12)); }
.wn-a-chip { fill: rgba(54, 96, 255, 0.12); }
.wn-hero text { font-size: 8.5px; font-weight: 800; text-anchor: middle; dominant-baseline: central; fill: #fff; }
.wn-hero .wn-a-chip-text { fill: var(--accent); }
.wn-hero .wn-a-q { font-size: 13px; text-anchor: start; fill: var(--text); }
.wn-hero .wn-a-hint { font-size: 11px; fill: rgba(30, 27, 46, 0.35); }
.wn-hero .wn-a-ans text { font-size: 13px; font-weight: 900; fill: var(--accent-dark); }
.wn-a-title { fill: var(--text); }
.wn-a-ring-track, .wn-a-ring { fill: none; stroke-width: 3; }
.wn-a-ring-track { stroke: rgba(54, 96, 255, 0.18); }
.wn-a-ring { stroke: var(--accent); stroke-linecap: round; stroke-dasharray: 1; stroke-dashoffset: 1; animation-name: wnRing; animation-timing-function: linear; }
.wn-a-done, .wn-a-burst, .wn-a-ans, .wn-a-ripple, .wn-a-cursor { transform-box: fill-box; transform-origin: center; }
.wn-a-done, .wn-a-ans { animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); }
.wn-a-done { animation-name: wnDone; }
.wn-a-done circle { fill: var(--green); }
.wn-a-done path { fill: none; stroke: #fff; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.wn-a-burst { fill: none; stroke: var(--green); stroke-width: 2; stroke-linecap: round; opacity: 0; animation-name: wnBurst; animation-timing-function: ease-out; }
.wn-a-row { animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); }
.wn-a-slot { fill: none; stroke: rgba(30, 27, 46, 0.2); stroke-dasharray: 3 3; }
/* Solid, and a touch larger than the dashed slot, so it covers it. */
.wn-a-ans rect { fill: #e3e9ff; stroke: #e3e9ff; stroke-width: 2; }
.wn-a-dot { opacity: 0; animation-timing-function: cubic-bezier(0.45, 0, 0.55, 1); }
.wn-a-pointer { transform: translate(390px, 158px); animation-name: wnPointer; animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1); }
.wn-a-ripple { fill: none; stroke: var(--accent); stroke-width: 2; opacity: 0; animation-name: wnRipple; animation-timing-function: ease-out; }
.wn-a-cursor { fill: var(--text); stroke: #fff; stroke-width: 1.5; stroke-linejoin: round; transform-origin: top left; animation-name: wnPress; }
.wn-a-r1 { animation-name: wnRow1; }
.wn-a-a1 { animation-name: wnAns1; }
.wn-a-d1 { animation-name: wnDot1; }
.wn-a-d1 circle { animation-name: wnArc1; }
.wn-a-r2 { animation-name: wnRow2; }
.wn-a-a2 { animation-name: wnAns2; }
.wn-a-d2 { animation-name: wnDot2; }
.wn-a-d2 circle { animation-name: wnArc2; }
.wn-a-r3 { animation-name: wnRow3; }
.wn-a-a3 { animation-name: wnAns3; }
.wn-a-d3 { animation-name: wnDot3; }
.wn-a-d3 circle { animation-name: wnArc3; }
@keyframes wnDot1 { 0%, 4% { opacity: 0; transform: translate(72px, 46px); } 5.5%, 12% { opacity: 1; } 13%, 100% { opacity: 0; transform: translate(176px, 78px); } }
@keyframes wnArc1 { 0%, 4% { transform: translateY(0); animation-timing-function: cubic-bezier(0, 0, 0.4, 1); } 8.5% { transform: translateY(-18px); animation-timing-function: cubic-bezier(0.6, 0, 1, 1); } 13%, 100% { transform: translateY(0); } }
@keyframes wnRow1 { 0%, 12% { opacity: 0; transform: translateX(-12px); } 18%, 92% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes wnAns1 { 0%, 39% { opacity: 0; transform: scale(0.5); } 44%, 92% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes wnDot2 { 0%, 11% { opacity: 0; transform: translate(60px, 92px); } 12.5%, 19% { opacity: 1; } 20%, 100% { opacity: 0; transform: translate(176px, 104px); } }
@keyframes wnArc2 { 0%, 11% { transform: translateY(0); animation-timing-function: cubic-bezier(0, 0, 0.4, 1); } 15.5% { transform: translateY(-18px); animation-timing-function: cubic-bezier(0.6, 0, 1, 1); } 20%, 100% { transform: translateY(0); } }
@keyframes wnRow2 { 0%, 19% { opacity: 0; transform: translateX(-12px); } 25%, 92% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes wnAns2 { 0%, 51% { opacity: 0; transform: scale(0.5); } 56%, 92% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes wnDot3 { 0%, 18% { opacity: 0; transform: translate(76px, 138px); } 19.5%, 26% { opacity: 1; } 27%, 100% { opacity: 0; transform: translate(176px, 130px); } }
@keyframes wnArc3 { 0%, 18% { transform: translateY(0); animation-timing-function: cubic-bezier(0, 0, 0.4, 1); } 22.5% { transform: translateY(-18px); animation-timing-function: cubic-bezier(0.6, 0, 1, 1); } 27%, 100% { transform: translateY(0); } }
@keyframes wnRow3 { 0%, 26% { opacity: 0; transform: translateX(-12px); } 32%, 92% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes wnAns3 { 0%, 63% { opacity: 0; transform: scale(0.5); } 68%, 92% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes wnPointer { 0%, 31% { transform: translate(390px, 158px); } 37%, 67% { transform: translate(326px, 108px); } 77%, 100% { transform: translate(390px, 158px); } }
@keyframes wnPress { 0%, 37%, 41.5%, 49%, 53.5%, 61%, 65.5%, 100% { transform: none; } 39%, 51%, 63% { transform: scale(0.8); } }
@keyframes wnRipple { 0%, 38.9%, 50.9%, 62.9% { opacity: 0; transform: scale(0.2); } 39%, 51%, 63% { opacity: 0.7; transform: scale(0.2); } 46%, 58%, 70%, 100% { opacity: 0; transform: scale(1); } }
@keyframes wnRing { 0%, 31% { stroke-dashoffset: 0; } 66%, 95% { stroke-dashoffset: 1; } 100% { stroke-dashoffset: 0; } }
@keyframes wnDone { 0%, 66.5% { opacity: 0; transform: scale(0.3); } 72%, 92% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: scale(0.6); } }
@keyframes wnBurst { 0%, 66.5% { opacity: 0; transform: scale(0.4); } 67.5% { opacity: 1; transform: scale(0.4); } 77%, 100% { opacity: 0; transform: scale(1.3); } }
@keyframes wnBob { from { transform: translateY(-2px); } to { transform: translateY(2px); } }
html[data-reduce-motion] .wn-hero svg * { animation: none; }
@media (prefers-reduced-motion: reduce) { .wn-hero svg * { animation: none; } }

.wn-pill {
    display: inline-block;
    padding: 0.2rem 0.6rem;
    border-radius: 999px;
    background: rgba(54, 96, 255, 0.1);
    color: var(--accent);
    font-size: 0.7rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.07em;
}
.wn-card h2 {
    margin: 0.7rem 0 1.2rem;
    font-size: 1.4rem;
    font-weight: 900;
    letter-spacing: -0.02em;
    text-wrap: balance;
}
.wn-list { list-style: none; margin: 0 0 1.4rem; padding: 0; display: grid; gap: 1.1rem; }
.wn-list li { display: flex; align-items: flex-start; gap: 0.9rem; }
.wn-icon {
    flex: none;
    width: 40px; height: 40px;
    display: flex; align-items: center; justify-content: center;
    border-radius: 12px;
    background: rgba(54, 96, 255, 0.1);
    color: var(--accent);
}
.wn-list li:nth-child(2) .wn-icon { background: rgba(0, 199, 141, 0.12); color: #06976e; }
.wn-text { font-size: 0.88rem; line-height: 1.5; color: var(--text-muted); }
.wn-text b { font-weight: 700; color: var(--text); }
.wn-name { display: block; margin-bottom: 0.15rem; font-size: 0.98rem; font-weight: 800; }
.wn-cta {
    width: 100%;
    padding: 0.8rem 1.2rem;
    background: var(--accent);
    color: #fff;
    border: none;
    border-radius: 12px;
    font-family: inherit;
    font-size: 0.95rem;
    font-weight: 800;
    cursor: pointer;
    transition: background 0.15s;
}
.wn-cta:hover { background: var(--accent-dark); }
.wn-later {
    display: block;
    width: 100%;
    margin-top: 0.5rem;
    padding: 0.4rem;
    background: none;
    border: none;
    border-radius: 8px;
    font-family: inherit;
    font-size: 0.84rem;
    font-weight: 600;
    color: var(--text-muted);
    cursor: pointer;
    transition: color 0.15s, background 0.15s;
}
.wn-later:hover { color: var(--text); background: var(--surface-2); }
.wn-cta:focus-visible, .wn-later:focus-visible, .wn-close:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
    .wn-backdrop { animation: none; }
    .wn-cta, .wn-later, .wn-close { transition: none; }
}

/* ── Referral links (admin) ──────────────────────── */
.referral-form { margin-bottom: 1.5rem; }
.referral-form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: 0.85rem;
    margin-bottom: 1rem;
}
.referral-form-grid label {
    display: flex; flex-direction: column; gap: 0.3rem;
    font-size: 0.78rem; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--text-muted);
}
.referral-form-grid input {
    font: inherit; text-transform: none; letter-spacing: normal;
    color: var(--text);
    padding: 0.5rem 0.65rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
}
.referral-form-grid input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.referral-optional { font-weight: 500; text-transform: none; letter-spacing: normal; opacity: 0.65; }

#referral-links, #referral-detail { overflow-x: auto; }
#referral-detail { margin-top: 1.75rem; padding-top: 1.25rem; border-top: 1px solid var(--border); }
.referral-detail-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.referral-detail-head h2 { font-size: 1.05rem; margin: 0; }
.referral-empty { color: var(--text-muted); font-size: 0.9rem; }
.referral-notes { font-size: 0.9rem; }
.referral-row-off { opacity: 0.55; }
.referral-url { display: flex; align-items: center; gap: 0.4rem; margin-top: 0.25rem; }
.referral-url code { font-size: 0.75rem; color: var(--text-muted); }
.referral-open, .referral-toggle, .referral-copy {
    font: inherit; background: none; border: none; padding: 0;
    color: var(--accent); font-weight: 600; cursor: pointer;
}
.referral-copy, .referral-toggle { font-size: 0.75rem; }
.referral-open:hover, .referral-toggle:hover, .referral-copy:hover { text-decoration: underline; }
.referral-open { font-weight: 700; }

/* ── Organisations ──────────────────────────────────────────────────────
   The org screen deliberately reuses the admin card, table and stat-tile
   styles: it is the same kind of page (a list of people and a few numbers)
   for a different reader, and a second visual language for it would be work
   to build and work to keep in step. Only what is genuinely new is here. */

.org-section { margin-top: 1.75rem; padding-top: 1.25rem; border-top: 1px solid var(--border); }
.org-section-head { font-size: 1.05rem; margin: 0 0 0.75rem; }
/* The export sits on the heading rather than under the table: it acts on the
   whole list, and below a hundred-row table nobody would ever find it. */
.org-section-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.org-section-link { font-size: 0.8125rem; font-weight: 500; color: var(--text-muted); text-decoration: none; }
.org-section-link:hover { color: var(--accent); text-decoration: underline; }

/* ── Per-person activity table ───────────────────────────────
   Counts align on the digit, so tabular figures: six columns of numbers read
   as columns only if the digits are the same width. */
.org-activity-table td.org-activity-num,
.org-activity-table th.org-activity-num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}
/* Someone who has made nothing this month. Dimmed rather than hidden – it is
   the row an admin came to this section to find, but it should not compete
   with the people who are using what the organisation bought. */
.org-activity-table tr.org-activity-idle td { color: var(--text-muted); }
.org-activity-share { display: flex; align-items: center; gap: 0.5rem; min-width: 7rem; }
.org-activity-bar {
    flex: 1;
    height: 0.375rem;
    border-radius: 999px;
    background: var(--border);
    position: relative;
    overflow: hidden;
}
.org-activity-bar::after {
    content: '';
    position: absolute;
    inset: 0 auto 0 0;
    width: var(--share, 0%);
    border-radius: inherit;
    background: var(--accent);
}
.org-activity-pct {
    font-size: 0.8125rem;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
    min-width: 2.5rem;
    text-align: right;
}
/* Leaving is deliberately the quietest thing on the page – findable, never
   sitting next to the controls an admin uses every day. */
.org-leave { margin-top: 2.5rem; }
.org-leave .org-note { margin: 0 0 0.75rem; }
.org-note { color: var(--text-muted); font-size: 0.875rem; line-height: 1.55; margin: 0.5rem 0 0; }

/* ── Branding panel ───────────────────────────────────────
   The preview is a fixed-height well rather than a box that resizes to the
   image, so a wide wordmark and a square crest both sit in the same frame and
   uploading a new logo does not make the page below it jump. The chequerboard
   is the usual signal for transparency: a school's PNG almost always has one,
   and against a plain white card it is impossible to tell whether it does. */

.org-brand-row { display: flex; align-items: flex-start; gap: 1.25rem; flex-wrap: wrap; }

.org-brand-preview {
    flex: 0 0 auto;
    display: flex; align-items: center; justify-content: center;
    width: 13rem; height: 6rem; padding: 0.6rem;
    border: 1px solid var(--border); border-radius: 10px;
    background-color: var(--surface);
    background-image:
        linear-gradient(45deg, rgba(128,128,128,0.13) 25%, transparent 25%, transparent 75%, rgba(128,128,128,0.13) 75%),
        linear-gradient(45deg, rgba(128,128,128,0.13) 25%, transparent 25%, transparent 75%, rgba(128,128,128,0.13) 75%);
    background-size: 14px 14px;
    background-position: 0 0, 7px 7px;
}
.org-brand-preview img { max-width: 100%; max-height: 100%; object-fit: contain; }
.org-brand-preview.is-empty {
    background-image: none;
    color: var(--text-muted); font-size: 0.8125rem;
    border-style: dashed;
}

.org-brand-controls { flex: 1 1 15rem; display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.5rem; }
.org-brand-controls .org-note { flex-basis: 100%; margin-top: 0.25rem; }

.org-brand-toggle { display: flex; align-items: center; gap: 0.55rem; margin-top: 1.25rem; font-size: 0.9rem; cursor: pointer; }
.org-brand-toggle input { width: 1rem; height: 1rem; accent-color: var(--accent); }

.org-brand-error { margin-top: 0.75rem; }

.org-seat-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.org-seat-row label { font-size: 0.8rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); }
.org-seat-row input {
    width: 6rem; padding: 0.5rem 0.6rem; font: inherit;
    border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--text);
}
.org-seat-row input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.org-invite-form { display: flex; gap: 0.6rem; flex-wrap: wrap; }
.org-invite-form input {
    flex: 1 1 16rem; padding: 0.55rem 0.7rem; font: inherit;
    border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--text);
}
.org-invite-form input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.org-bulk { margin-top: 1rem; }
.org-bulk summary { cursor: pointer; font-weight: 600; font-size: 0.9rem; color: var(--accent); }
.org-bulk textarea {
    width: 100%; margin-top: 0.6rem; padding: 0.6rem 0.7rem; font: inherit; font-size: 0.875rem;
    border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--text); resize: vertical;
}
.org-bulk textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.org-bulk-actions { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; margin-top: 0.6rem; }
.org-bulk-actions input[type="file"] { font-size: 0.8rem; color: var(--text-muted); }

.org-row-actions { white-space: nowrap; }
.org-row-actions .btn + .btn { margin-left: 0.35rem; }

/* A billing problem worth an org admin's attention – a failed card, a
   cancellation already scheduled, or a purchase order running out. A
   persistent strip above the nav, the same on every page, because none of
   these are things that should need a visit to the organisation page to be
   noticed. Full-bleed rather than confined to #app's column, so it reads as a
   site-level notice and not as one more thing on the page underneath it.
   Two severities: amber for "still working, act soon", red for "nobody here
   has Pro right now". The same warm/red pairing the admin-cap notes already
   use, not the referral-banner's accent blue, which reads as good news
   everywhere else in the product. */
/* Solid colour, not a tint. A 15%-opacity wash over a page background that is
   already warm reads as a rendering accident rather than a notice, and it
   leaves the text at whatever contrast the body colour happens to give. These
   two pairings are the ones the product already uses for exactly these two
   meanings – amber/#4a3308 from .sept-pending-badge, red/white from
   .admin-tag-capped – so the strip is loud without being a new colour. */
.org-alert-bar { width: 100%; }
.org-alert-bar-inner {
    max-width: 1100px; margin: 0 auto;
    display: flex; align-items: center; gap: 0.6rem;
    padding: 0.6rem 24px;
    font-size: 0.85rem; line-height: 1.4;
}
/* Colour alone can't carry severity – it isn't available to everyone, and on a
   strip this thin amber and red are not that far apart at a glance. */
.org-alert-bar-icon { flex: none; opacity: 0.85; }
.org-alert-bar-text { flex: 1; min-width: 0; }
#org-alert-bar-action { flex: none; }
/* Phones: the strip wraps to a second line rather than shouldering its own
   button off the edge. */
@media (max-width: 560px) {
    .org-alert-bar-inner { flex-wrap: wrap; padding: 0.6rem 16px; }
    /* Icon and message keep line one; the button drops beneath them, indented
       past the icon so it reads as belonging to the message. */
    .org-alert-bar-text { flex: 1 1 calc(100% - 3rem); }
    #org-alert-bar-action { margin-left: calc(17px + 0.6rem); }
}
.org-alert-bar-text strong { font-weight: 800; }

.org-alert-bar-warning { background: var(--amber); color: #4a3308; }
.org-alert-bar-critical { background: var(--red-soft); color: #fff; }

/* The default .btn is built for a light card and vanishes on either of those.
   Tinted from the strip's own text colour, so one rule covers both variants
   and neither needs a hardcoded background. */
.org-alert-bar .btn {
    flex: none;
    border: 1px solid currentColor;
    background: transparent;
    color: inherit;
    font-weight: 700;
}
.org-alert-bar .btn:hover { background: rgba(255, 255, 255, 0.22); }
.org-alert-bar-critical .btn:hover { background: rgba(255, 255, 255, 0.18); }

.org-seat-panel, .org-buy-panel {
    margin-top: 1.5rem; padding: 1.5rem;
    border: 1px solid var(--border); border-radius: 14px; background: var(--surface-2, var(--surface));
}

/* ── Buying seats ───────────────────────────────────────────────────────
   This is the only place a tutoring company converts, and it was styled as a
   settings section: a grey box, the pitch in muted small print, and a per-seat
   price the buyer had to multiply themselves. Lifted onto the same faint accent
   wash the referral banner uses for good news, with the one number they came
   for – the actual total – given real size. */
.org-buy-panel {
    background: linear-gradient(140deg, rgba(54, 96, 255, 0.06), rgba(54, 96, 255, 0.015));
    border-color: rgba(54, 96, 255, 0.22);
    padding: 1.6rem 1.7rem;
}
.org-buy-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; }
.org-buy-badge {
    flex: none;
    background: var(--accent); color: #fff;
    border-radius: 999px; padding: 0.25rem 0.7rem;
    font-size: 0.7rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.04em;
    white-space: nowrap;
}
.org-buy-panel .edu-copy { max-width: 62ch; margin-bottom: 1rem; }

.org-buy-points { margin: 0 0 1.35rem; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: 0.4rem 1.5rem; }
.org-buy-points li {
    position: relative; padding-left: 1.3rem;
    font-size: 0.85rem; color: var(--text-muted);
}
/* Drawn rather than a character: a text tick sits on the baseline and at this
   size looks like a typo next to the label. */
.org-buy-points li::before {
    content: ''; position: absolute; left: 0; top: 0.42em;
    width: 9px; height: 5px;
    border: 2px solid var(--accent); border-top: 0; border-right: 0;
    transform: rotate(-45deg);
}

/* Scoped to the panel rather than given its own single-class name: the base
   .org-buy-form rule (a row, align-items:flex-end) lives further down this
   file, so a same-specificity override up here would lose on source order –
   quietly, and only for the properties the base rule happens to declare. Two
   classes beats one wherever it sits. The setup form on the organisation page
   is outside this panel and keeps the row layout. */
.org-buy-panel .org-buy-form { flex-direction: column; align-items: stretch; gap: 1.1rem; }

/* Grid, not a wrapping flex row. As flex, the three fields came to 765px inside
   a 766px container and Chrome opened a second, empty flex line – 69px of dead
   space above the labels, from a one-pixel margin. Explicit tracks can't do
   that, and the widths here are what these three fields actually want: the name
   takes the slack, the seat count needs room for four digits, the interval for
   its longest option. */
.org-buy-fields {
    display: grid;
    grid-template-columns: minmax(11rem, 1fr) 5.5rem 11rem;
    align-items: end;
    gap: 0.75rem;
}

/* Total on one side, the commitment on the other – the shape .edu-foot already
   uses for "here's the price, here's the button". */
.org-buy-foot {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
    padding-top: 1.1rem; border-top: 1px solid rgba(54, 96, 255, 0.18);
}
.org-buy-total { display: grid; grid-template-columns: auto auto; align-items: baseline; gap: 0 0.35rem; }
.org-buy-total-amt { font-size: 1.6rem; font-weight: 800; letter-spacing: -0.02em; color: var(--text); }
.org-buy-total-per { font-size: 0.85rem; font-weight: 700; color: var(--text-muted); }
.org-buy-total-note { grid-column: 1 / -1; font-size: 0.78rem; color: var(--text-muted); }
/* Past the card ceiling the total is no longer a price, so it stops looking
   like one – the seat count states the ask, and the note carries the address
   to write to. */
.org-buy-total-enquire .org-buy-total-amt { font-size: 1.1rem; font-weight: 700; color: var(--text-muted); }
.org-buy-total-enquire .org-buy-total-note { color: var(--text); max-width: 34ch; }
.org-buy-total-enquire .org-buy-total-note a { color: var(--accent); font-weight: 700; }
/* The purchase-order line is an aside, not part of the price – .org-note's 8px
   left it sitting on top of the total. */
.org-buy-panel .org-note { margin-top: 1rem; }

/* ── Want to get your school involved? ────────────────────
   Between the seat form and the whole-school signpost, and shaped like the
   latter rather than the former: the ask is a click, not a card. Amber where
   those two are blue and purple, because it is the one panel on the page that
   isn't asking anybody to pay for anything. */
.pilot-panel {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 1rem 1.5rem;
    margin-top: 1rem;
    padding: 1.1rem 1.3rem;
    border-radius: 14px;
    border: 1px solid rgba(244, 180, 71, 0.45);
    background: linear-gradient(140deg, rgba(244, 180, 71, 0.14), rgba(244, 180, 71, 0.03));
}
.pilot-panel-text { min-width: 16rem; flex: 1 1 22rem; }
.pilot-panel-eyebrow {
    margin: 0 0 0.15rem;
    font-size: 0.68rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.09em;
    color: #8a5d05;
}
.pilot-panel-head { margin: 0; font-size: 1.02rem; font-weight: 800; color: var(--text); }
.pilot-panel-copy { margin: 0.3rem 0 0; font-size: 0.85rem; line-height: 1.55; color: var(--text-muted); }
.pilot-panel-ctas { flex: none; display: flex; flex-wrap: wrap; gap: 0.5rem; }
/* .btn expects a <button>; as an <a> it takes the underline back. */
a.btn.pilot-panel-cta { text-decoration: none; }

@media (max-width: 560px) {
    .pilot-panel-ctas { width: 100%; }
    .pilot-panel-cta { flex: 1 1 10rem; justify-content: center; }
}

/* ── Whole school or trust ────────────────────────────────
   A signpost under the seat form, for the buyer it can't serve. Deliberately
   small: it was a second panel the size of the form above it, and two pitches
   stacked on a settings page read as neither. Purple where that panel is blue,
   because it is a different rail – a shared pool, on an invoice. */
.inst-panel {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 1rem 1.5rem;
    margin-top: 1rem;
    padding: 1.1rem 1.3rem;
    border-radius: 14px;
    border: 1px solid rgba(134, 87, 209, 0.3);
    background: linear-gradient(140deg, rgba(134, 87, 209, 0.09), rgba(134, 87, 209, 0.02));
}
.inst-text { min-width: 16rem; flex: 1 1 22rem; }
.inst-eyebrow {
    margin: 0 0 0.15rem;
    font-size: 0.68rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.09em;
    color: var(--purple);
}
.inst-head { margin: 0; font-size: 1.02rem; font-weight: 800; color: var(--text); }
.inst-copy { margin: 0.2rem 0 0; font-size: 0.85rem; color: var(--text-muted); }

/* The benefits as tags, not sentences: on this page they are there to be
   recognised at a glance, and the actual pitch happens in the reply to the
   email. */
.inst-tags {
    list-style: none; display: flex; flex-wrap: wrap; gap: 0.35rem;
    margin: 0.7rem 0 0; padding: 0;
}
.inst-tags li {
    font-size: 0.72rem; font-weight: 700;
    color: var(--purple);
    background: rgba(134, 87, 209, 0.12);
    border-radius: 999px;
    padding: 0.2rem 0.6rem;
}
/* Two ways in, side by side: a call for the ones who want to be sold to,
   an email for the ones who want it in writing. */
.inst-ctas { flex: none; display: flex; flex-wrap: wrap; gap: 0.5rem; }
/* .btn expects a <button>; as an <a> the email one takes the underline back. */
a.btn.inst-cta { text-decoration: none; }

@media (max-width: 560px) {
    .inst-ctas { width: 100%; }
    .inst-cta { flex: 1 1 10rem; justify-content: center; }
}

/* An organisation that exists but has never been paid for. Presented as the
   half-finished setup it is, not as an error, so it borrows the school-account
   panel's marks wholesale: filled for what you have, dashed for what you don't
   yet. Only the differences from .edu-key are below. */
.org-setup { max-width: 44rem; }

/* .edu-copy caps its measure at 46ch, which suits the school panel's short
   single-idea sentence. Here the copy is three clauses sitting directly above a
   step list 200px wider, and at that width it reads as squeezed into a corner
   rather than as a comfortable measure. 68ch is still inside the readable range
   and lines up much better with everything under it. */
.org-setup .edu-copy { max-width: 68ch; }
.org-steps { margin: 1.25rem 0 0; padding: 0; list-style: none; counter-reset: none; }

/* The step they are actually on. A tinted edge rather than a filled row: it
   should read as "you are here", not as a warning about something wrong. */
.org-steps .edu-key.is-now {
    border-color: rgba(54, 96, 255, 0.35);
    background: linear-gradient(120deg, rgba(54, 96, 255, 0.07), rgba(54, 96, 255, 0.02));
}
.org-step-pill-now { background: rgba(54, 96, 255, 0.12); color: var(--accent); }

/* Steps that haven't happened sit back a little, but stay legible – they are
   what the customer is buying, so they should read as a promise rather than as
   something disabled. */
.org-steps .edu-key:last-child { opacity: 0.82; }

/* A finished step gets a tick, not the school panel's dot. The dot there means
   "a key you hold", which is a state; here the question is whether the step is
   done, and a tick answers it without the label having to. */
.org-steps .edu-key-mark.is-held::after {
    width: 12px;
    height: 7px;
    border: 2px solid var(--accent);
    border-top: 0;
    border-right: 0;
    border-radius: 1px;
    background: none;
    transform: translateY(-1px) rotate(-45deg);
}

.org-setup-form { margin-top: 1.5rem; padding-top: 1.35rem; border-top: 1px solid var(--border); }

/* Inline in a sentence, so it reads as a link rather than competing with the
   real call to action next to it. */
.org-recheck {
    font: inherit; background: none; border: none; padding: 0;
    color: var(--accent); font-weight: 600; cursor: pointer;
}
.org-recheck:hover { text-decoration: underline; }
.org-recheck:disabled { color: var(--text-muted); cursor: default; text-decoration: none; }

/* Buying seats. A row on wide screens, a stack on narrow ones – the fields
   are short and unrelated, so they read better side by side than listed. */
.org-buy-form { display: flex; align-items: flex-end; gap: 0.75rem; flex-wrap: wrap; margin-top: 1rem; }
.org-buy-form label { display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); }
.org-buy-form label:first-child { flex: 1 1 14rem; }
.org-buy-form input, .org-buy-form select {
    padding: 0.55rem 0.7rem; font: inherit; font-weight: 400; text-transform: none; letter-spacing: normal;
    border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--text);
}
.org-buy-form input[type="number"] { width: 5.5rem; }
.org-buy-form input:focus-visible, .org-buy-form select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* The admin's controls for one organisation: extend it, resize it, switch it
   off. Same inline-row treatment as the seat control on the org's own page. */
.org-admin-controls { display: flex; align-items: flex-end; gap: 0.75rem; flex-wrap: wrap; margin: 1rem 0; }
.org-admin-controls label { display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); }
.org-admin-controls label:last-of-type { flex: 1 1 12rem; }
.org-admin-controls input {
    padding: 0.5rem 0.6rem; font: inherit; font-weight: 400; text-transform: none; letter-spacing: normal;
    border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--text);
}
.org-admin-controls input[type="number"] { width: 6rem; }
.org-admin-controls input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

#admin-org-detail { margin-top: 1.75rem; padding-top: 1.25rem; border-top: 1px solid var(--border); overflow-x: auto; }

/* The join card stands alone: whoever is reading it has no account, no nav
   and no idea what this site is, so it is the only thing on the page. */
.join-card { max-width: 30rem; margin: 3rem auto; }
.join-card:has(.join-state), .join-card:has(.join-invite) { max-width: 27rem; margin: 5rem auto 6rem; padding: 40px 36px 36px; }

.join-state { display: flex; flex-direction: column; align-items: center; text-align: center; }
.join-state-icon {
    display: grid; place-items: center;
    width: 56px; height: 56px; margin-bottom: 20px;
    border-radius: 50%;
}
.join-state-icon svg { width: 26px; height: 26px; }
.join-state-icon.tone-amber { background: rgba(244, 180, 71, 0.18); color: #b7791f; }
.join-state-icon.tone-blue { background: rgba(54, 96, 255, 0.12); color: var(--accent); }
.join-state-title { font-size: 1.4rem; font-weight: 900; letter-spacing: -0.02em; line-height: 1.25; color: var(--text); margin: 0; text-wrap: balance; }
.join-state-text { color: var(--text-muted); font-size: 0.95rem; line-height: 1.6; margin: 10px 0 0; text-wrap: pretty; }
.join-state-hint {
    width: 100%; margin: 22px 0 0; padding: 12px 14px;
    background: var(--surface-2); border: 1px solid var(--border); border-radius: 10px;
    color: var(--text-muted); font-size: 0.85rem; line-height: 1.5; text-wrap: pretty;
}
.join-state-btn { margin-top: 24px; min-width: 14rem; justify-content: center; }
/* An invitation that can be accepted (joinInviteHtml): the organisation's
   initials, its name, who it is for, then the one action. */
.join-invite { display: flex; flex-direction: column; align-items: center; text-align: center; }
.join-invite-mark {
    display: grid; place-items: center;
    width: 56px; height: 56px; margin-bottom: 18px;
    border-radius: 14px; background: rgba(54, 96, 255, 0.12); color: var(--accent);
    font-size: 1.25rem; font-weight: 800; letter-spacing: 0.02em;
}
.join-invite-eyebrow { margin: 0 0 4px; color: var(--text-muted); font-size: 0.95rem; }
.join-invite .join-state-title { font-size: 1.7rem; }
.join-invite-for {
    margin: 18px 0 0; padding: 6px 14px; max-width: 100%;
    border: 1px solid var(--border); border-radius: 999px; background: var(--surface-2);
    color: var(--text-muted); font-size: 0.85rem; overflow-wrap: anywhere;
}
.join-invite-for strong, .join-state-hint strong { color: var(--text); font-weight: 700; overflow-wrap: anywhere; }
.join-invite .error { width: 100%; margin-top: 16px; text-align: left; }
.join-invite .join-state-btn { width: 100%; }
.join-invite .join-form { width: 100%; text-align: left; margin-top: 1.5rem; }
.join-form-names { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.85rem; }
.join-form .join-state-btn { margin-top: 8px; }
.join-form { display: flex; flex-direction: column; gap: 0.85rem; margin-top: 1.25rem; }
.join-form label { display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.8rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); }
.join-form input {
    padding: 0.6rem 0.7rem; font: inherit; font-weight: 400; text-transform: none; letter-spacing: normal;
    border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--text);
}
.join-form input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

@media (max-width: 560px) {
    .billing-plan-row { flex-direction: column; align-items: flex-start; }
    .billing-actions { width: 100%; }
    .billing-actions .btn { flex: 1; }
    .admin-summary { flex-direction: column; }
    .referral-form-grid { grid-template-columns: 1fr; }
    .org-invite-form input { flex-basis: 100%; }
    .join-card { margin: 1.5rem auto; }
    .join-card:has(.join-state) { margin: 2rem auto 3rem; padding: 32px 22px 26px; }
    .join-state-btn { width: 100%; min-width: 0; }
    .org-buy-form, .org-admin-controls { flex-direction: column; align-items: stretch; }
    .org-buy-fields { grid-template-columns: 1fr; }
    .org-buy-top { flex-direction: column-reverse; align-items: flex-start; gap: 0.6rem; }
    .org-buy-badge { align-self: flex-start; }
    .org-buy-points { gap: 0.4rem; }
    .org-buy-points li { flex-basis: 100%; }
    .org-buy-foot { align-items: stretch; }
    /* The name field grows to fill the row on wide screens. Once the form is a
       column that same flex-grow fills the *height* instead, which opens a
       chasm between the first field and the second. Matched on the same
       pseudo-classes as the rules being undone – a plain `label` selector loses
       to `label:first-child` on specificity and silently does nothing. */
    .org-buy-form label:first-child,
    .org-admin-controls label:last-of-type { flex: none; }
    .org-buy-form input[type="number"], .org-admin-controls input[type="number"] { width: 100%; }
    .org-buy-form .btn, .org-admin-controls .btn { width: 100%; }
    /* Wraps rather than stacking: a column would put the icon on a line of its
       own above the text it belongs to. This keeps icon and message together
       and drops only the button onto a second row. */
    .org-alert-bar-inner { flex-wrap: wrap; padding: 0.6rem 16px; }
    .org-alert-bar .btn { width: 100%; }
}

/* ── Confirm dialog (custom window.confirm) ─────────────── */
#confirm-modal {
    position: fixed;
    inset: 0;
    z-index: 10001;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}
#confirm-modal[hidden] { display: none; }

.confirm-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(30, 27, 46, 0.45);
    backdrop-filter: blur(10px) saturate(1.1);
    -webkit-backdrop-filter: blur(10px) saturate(1.1);
    animation: fadeIn 0.25s ease both;
}

.confirm-card {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 380px;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: 18px;
    padding: 1.6rem 1.5rem 1.35rem;
    text-align: center;
    box-shadow: 0 30px 80px rgba(30, 27, 46, 0.28), 0 4px 16px rgba(30, 27, 46, 0.12);
}

.confirm-icon {
    width: 46px;
    height: 46px;
    margin: 0 auto 0.85rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: rgba(54, 96, 255, 0.1);
    color: var(--accent);
}
#confirm-modal.confirm-danger .confirm-icon {
    background: rgba(220, 53, 69, 0.1);
    color: #dc3545;
}

.confirm-card h2 {
    margin: 0 0 0.4rem;
    font-size: 1.15rem;
    letter-spacing: -0.01em;
}
.confirm-card p {
    margin: 0 0 1.3rem;
    color: var(--text-muted);
    font-size: 0.92rem;
    line-height: 1.55;
}

.confirm-actions {
    display: flex;
    gap: 0.6rem;
    justify-content: center;
}
.confirm-actions .btn {
    min-width: 112px;
    justify-content: center;
}
.confirm-actions .btn-ghost {
    border: 1px solid var(--border);
}
#confirm-modal.confirm-danger #confirm-ok-btn {
    background: linear-gradient(90deg, #dc3545, #b02a37);
    border-color: transparent;
    color: #fff;
}
#confirm-modal.confirm-danger #confirm-ok-btn:hover:not(:disabled) {
    background: linear-gradient(90deg, #e04555, #c13543);
}

/* PRO badge on gated slide-count options */
.pro-tag {
    display: inline-block;
    font-size: 8px;
    font-weight: 800;
    letter-spacing: 0.06em;
    padding: 1px 4px;
    border-radius: 4px;
    background: linear-gradient(90deg, #b8860b, #daa520);
    color: #fff;
    vertical-align: middle;
    margin-left: 2px;
}

/* ── Lesson packs ─────────────────────────────────────── */

/* Opt-in companion picker on the generator form */
.pack-options {
    margin-top: 18px;
    padding: 14px 16px;
    border: 1.5px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-2);
}
.pack-options-title {
    font-size: 0.78rem;
    font-weight: 800;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-bottom: 10px;
}
.pack-options-hint {
    font-weight: 700;
    letter-spacing: 0;
    text-transform: none;
    opacity: 0.75;
}
.pack-opt {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 8px 0;
    cursor: pointer;
}
.pack-opt input { margin-top: 3px; accent-color: var(--accent); width: 16px; height: 16px; }
.pack-opt-body { display: flex; flex-direction: column; gap: 1px; }
.pack-opt-label { font-size: 0.92rem; font-weight: 700; }
.pack-opt-desc { font-size: 0.8rem; color: var(--text-muted); }
.pack-options-note {
    margin-top: 8px;
    font-size: 0.78rem;
    color: var(--text-muted);
}

/* Progress dock: sits beside the editor while companions generate */
#pack-dock {
    position: fixed;
    right: 20px;
    bottom: 20px;
    z-index: 900;
    width: 320px;
    max-width: calc(100vw - 32px);
    background: var(--card-bg);
    border: 1.5px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-card);
    overflow: hidden;
}
#pack-dock[hidden] { display: none; }
.pack-dock-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 11px 14px;
    border-bottom: 1px solid var(--border);
    background: var(--surface-2);
}
.pack-dock-title { font-size: 0.85rem; font-weight: 800; }
.pack-dock-close {
    margin-left: auto;
    border: none;
    background: none;
    font-size: 1.2rem;
    line-height: 1;
    color: var(--text-muted);
    cursor: pointer;
}
.pack-dock-list { list-style: none; }
.pack-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 14px;
}
.pack-row + .pack-row { border-top: 1px solid var(--border); }
.pack-row-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.pack-row-label { font-size: 0.88rem; font-weight: 700; }
.pack-row-status { font-size: 0.76rem; color: var(--text-muted); }
.pack-row-action { margin-left: auto; }

/* One dot, four meanings – driven entirely by the row's data-status. */
.pack-row-spin {
    flex: none;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    border: 2px solid var(--border);
}
.pack-row[data-status="generating"] .pack-row-spin {
    border-color: var(--accent);
    border-top-color: transparent;
    animation: pack-spin 0.7s linear infinite;
}
.pack-row[data-status="done"] .pack-row-spin {
    border-color: var(--green);
    background: var(--green);
}
.pack-row[data-status="blocked"] .pack-row-spin { border-color: var(--amber); background: var(--amber); }
.pack-row[data-status="failed"] .pack-row-spin { border-color: var(--red-soft); background: var(--red-soft); }
@keyframes pack-spin { to { transform: rotate(360deg); } }

.pack-row-btn {
    padding: 5px 11px;
    font-size: 0.78rem;
    font-weight: 700;
    font-family: inherit;
    border: 1.5px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    color: var(--text);
    cursor: pointer;
}
.pack-row-btn:hover { border-color: var(--accent); color: var(--accent); }
.pack-row-btn-upgrade {
    border-color: transparent;
    background: linear-gradient(90deg, #b8860b, #daa520);
    color: #fff;
}
.pack-row-btn-upgrade:hover { color: #fff; filter: brightness(1.06); }

@media (max-width: 640px) {
    #pack-dock { left: 16px; right: 16px; width: auto; }
}

/* Marks a deck in the library that has companions */
.pack-badge {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 7px;
    font-size: 0.68rem;
    font-weight: 800;
    letter-spacing: 0.03em;
    border-radius: 999px;
    background: rgba(54, 96, 255, 0.12);
    color: var(--accent);
    vertical-align: middle;
}

/* ── Lesson pack side marker ──────────────────────────────
   The rest of the pack, parked against the edge of the window: a small pill
   holding one dot per resource, coloured to match the library's type coding
   (blue deck, green worksheet, amber quiz). Names appear on hover. */

#pack-side {
    position: fixed;
    right: 14px;
    top: 50%;
    transform: translateY(-50%);
    z-index: 850;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 13px;
    padding: 13px 11px;
    border: 1.5px solid var(--border);
    border-radius: 999px;
    background: var(--card-bg);
    box-shadow: var(--shadow);
}
#pack-side[hidden] { display: none; }

.pks-dot {
    position: relative;
    width: 11px;
    height: 11px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: var(--pks, var(--border));
    cursor: pointer;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.pks-dot[data-kind="presentation"] { --pks: #3660ff; }
.pks-dot[data-kind="worksheet"]    { --pks: #00c78d; }
.pks-dot[data-kind="quiz"]         { --pks: #f4b447; }

.pks-dot:hover,
.pks-dot:focus-visible {
    transform: scale(1.3);
    outline: none;
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--pks) 20%, transparent);
}

/* Where you are: a ring holds the dot, so it reads at a glance. */
.pks-dot[data-current] {
    cursor: default;
    box-shadow: 0 0 0 3.5px color-mix(in srgb, var(--pks) 28%, transparent);
}

/* Name slides out only on demand. */
.pks-tip {
    position: absolute;
    right: calc(100% + 16px);
    top: 50%;
    transform: translateY(-50%) translateX(6px);
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 7px 11px;
    max-width: 230px;
    border-radius: 9px;
    background: var(--card-bg);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-card);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.14s ease, transform 0.14s ease;
}
.pks-dot:hover .pks-tip,
.pks-dot:focus-visible .pks-tip {
    opacity: 1;
    transform: translateY(-50%) translateX(0);
}
.pks-name {
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.pks-meta { font-size: 0.68rem; color: var(--text-muted); white-space: nowrap; }

@media (prefers-reduced-motion: reduce) {
    .pks-dot, .pks-tip { transition: none; }
    .pks-dot:hover, .pks-dot:focus-visible { transform: none; }
}

/* Out of the way on narrow screens: along the bottom edge instead. */
@media (max-width: 820px) {
    #pack-side {
        right: auto;
        left: 50%;
        top: auto;
        bottom: 14px;
        transform: translateX(-50%);
        flex-direction: row;
        padding: 11px 13px;
    }
    .pks-tip { right: auto; left: 50%; top: auto; bottom: calc(100% + 12px); transform: translateX(-50%) translateY(6px); }
    .pks-dot:hover .pks-tip, .pks-dot:focus-visible .pks-tip { transform: translateX(-50%) translateY(0); transform-origin: center bottom; }
}

/* ── Long-answer marking ──────────────────────────────────
   Two colours do the work: green where the answer earned marks, amber where
   something was attempted but under-developed. They reuse the app's existing
   green/amber so a highlight reads the same way a resource chip does. */

/* The global input rule only covers text/email/password, so number inputs
   would otherwise render unstyled next to their neighbours. */
#mk-marks {
    width: 100%;
    padding: 10px 14px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    font-family: inherit;
    font-size: 1rem;
    color: var(--text);
}

.mk-hint { font-size: 0.78rem; color: var(--text-muted); line-height: 1.5; margin: 6px 0; }

/* Sits under every "paste content / notes" box. The privacy policy treats
   generation input as non-personal data, and that only holds if teachers are
   actually told not to put people in it – so the instruction lives here, at the
   point of pasting, not only in the policy. */
.gen-note {
    margin: 8px 0 0;
    font-size: 0.76rem;
    line-height: 1.5;
    color: var(--text-muted);
}
.gen-note a { color: var(--accent); font-weight: 700; }

/* ── Composer: the "what is it about" box ─────────────
   One box that takes a topic, files, or both. The topic is typed straight into
   it, files are dropped anywhere on it or added from the bar along its bottom
   edge, and what has been added shows inside it. It is the first thing every
   composer asks, so it has to read as a single obvious place to start. */
.src-box {
    border: 1.5px solid var(--border);
    border-radius: 16px;
    background: var(--card-bg);
    box-shadow: var(--shadow);
    text-align: left;
    transition: border-color 0.15s, box-shadow 0.15s, background-color 0.15s;
}
.src-box:focus-within { border-color: var(--accent); box-shadow: 0 0 0 4px rgba(54, 96, 255, 0.12); }
.src-box.is-over { border-color: var(--accent); border-style: dashed; background: rgba(54, 96, 255, 0.06); }
.src-box.is-busy .src-box-bar { opacity: 0.6; pointer-events: none; }
.src-box .src-box-topic,
.src-box .src-box-topic:focus {
    border: 0;
    background: transparent;
    box-shadow: none;
    outline: none;
}
.src-box-topic {
    display: block;
    width: 100%;
    min-height: 0;
    padding: 18px 20px 12px;
    border-radius: 16px 16px 0 0;
    color: var(--text);
    font: inherit;
    font-size: 1.15rem;
    font-weight: 600;
    line-height: 1.4;
    resize: none;
    outline: none;
}
.src-box-topic::placeholder { color: var(--text-muted); font-weight: 500; opacity: 0.75; }

.src-drop-files { display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 4px 20px 8px; list-style: none; }
.src-drop-files:empty { display: none; }
.src-drop-file {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    max-width: 100%;
    padding: 4px 6px 4px 12px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface);
    font-size: 0.82rem;
    font-weight: 600;
}
.src-drop-file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.src-drop-file-remove {
    width: 22px;
    height: 22px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--text-muted);
    font-size: 1.05rem;
    line-height: 1;
    cursor: pointer;
}
.src-drop-file-remove:hover { color: var(--text); background: var(--border); }

.src-upload-status { margin: 0; padding: 0 20px 8px; font-size: 0.82rem; line-height: 1.4; color: var(--text-muted); }
.src-upload-status:empty { display: none; }
.src-upload-status[data-tone="ok"] { color: #0b7a57; font-weight: 600; }
.src-upload-status[data-tone="error"] { color: var(--red-soft); font-weight: 600; }

.src-box-panel { padding: 4px 20px 12px; }
.src-box-panel[hidden] { display: none; }
.src-box-panel-label { display: block; margin-bottom: 6px; font-size: 0.78rem; font-weight: 700; color: var(--text-muted); }
.src-box-notes,
.src-box-select {
    display: block;
    width: 100%;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 0.92rem;
    line-height: 1.5;
}
.src-box-notes { resize: vertical; }
.src-box-notes:focus, .src-box-select:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

.src-box-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    border-top: 1px solid var(--border);
    border-radius: 0 0 16px 16px;
    background: var(--surface);
}
.src-box-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 7px 12px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card-bg);
    color: var(--text);
    font: inherit;
    font-size: 0.84rem;
    font-weight: 700;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background-color 0.15s;
}
.src-box-btn svg { width: 16px; height: 16px; flex: none; }
.src-box-btn:hover { border-color: var(--accent); color: var(--accent); }
.src-box-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.src-box-btn[aria-expanded="true"] { border-color: var(--accent); background: rgba(54, 96, 255, 0.1); color: var(--accent-dark); }
/* Said while a file is over the box, which is the only moment it is needed:
   as standing text in the bar it wrapped and grew the box whenever a third
   button appeared. */
.src-box { position: relative; }
.src-box.is-over::after {
    content: "Drop to add";
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    border-radius: 15px;
    background: rgba(246, 248, 255, 0.92);
    color: var(--accent-dark);
    font-size: 1rem;
    font-weight: 700;
    pointer-events: none;
}
.src-box-foot { margin: 12px 4px 0; font-size: 0.78rem; line-height: 1.5; color: var(--text-muted); text-align: center; }
.src-box-foot a { color: var(--accent); font-weight: 700; }

/* ── Composer: who it is for ──────────────────────────
   Subject, exam board and level are the box's last row, in the same pills as
   its buttons: they qualify what was typed above them, so they belong to the
   same object rather than to a form underneath it. Board and level show the
   choice and open their options inside the box. */
.src-box-bar:not(:last-child) { border-radius: 0; }
.cur {
    padding: 10px 12px 12px;
    border-top: 1px solid var(--border);
    border-radius: 0 0 16px 16px;
    background: var(--surface);
    text-align: left;
}
.cur-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.cur-for { padding: 0 2px 0 4px; font-size: 0.78rem; font-weight: 700; color: var(--text-muted); }
.cur-cell { min-width: 0; margin: 0; }
.cur-row .ws-subject-group { flex: 1 1 170px; }
.cur-row .ws-step-help { display: none; }
/* Named for screen readers by the label; sighted users read the pill. */
.cur-row .ws-field-label {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}
.cur .ws-subject-group .t-clear { border-radius: 999px; }
.cur .ws-subject-group .sc-input,
.cur .ws-subject-group .t-clear-mirror,
.cur .ws-subject-group .t-clear-placeholder {
    padding: 8px 30px 8px 14px;
    font-size: 0.84rem;
    font-weight: 700;
    line-height: 1.2;
}
.cur .ws-subject-group .sc-input {
    min-height: 0;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card-bg);
    box-shadow: none;
}
.cur .ws-subject-group .t-clear-placeholder { font-weight: 600; }
/* A pill this small has no room for the search glyph beside its text. */
.cur .ws-field-icon-glyph { display: none; }
.cur-pick {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    max-width: 220px;
    padding: 7px 10px 7px 13px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card-bg);
    color: var(--text-muted);
    font: inherit;
    font-size: 0.84rem;
    font-weight: 600;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background-color 0.15s;
}
.cur-pick:hover { border-color: var(--accent); color: var(--accent); }
.cur-pick:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cur-pick.has-value { color: var(--text); font-weight: 700; }
.cur-pick[aria-expanded="true"] { border-color: var(--accent); background: rgba(54, 96, 255, 0.1); color: var(--accent-dark); }
.cur-pick-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cur-pick-chev { flex: none; width: 14px; height: 14px; transition: transform 0.15s; }
.cur-pick[aria-expanded="true"] .cur-pick-chev { transform: rotate(180deg); }
.cur-panel { margin-top: 10px; }
.cur-panel[hidden] { display: none; }
.cur-panel .ws-form-group { margin: 0; }
.cur-panel .ws-field-label { display: none; }
.cur-panel .ws-tiles { margin-top: 0; }
.cur .spec-coverage { margin-top: 10px; }

/* The specification checklist, opened from the box. Capped and scrolled so a
   board with ninety topics does not turn the step back into a long page. */
.src-box-topics .ws-form-group { margin: 0; }
.src-box-topics .bk-topics-list { max-height: 300px; overflow-y: auto; }
.src-box-btn-spec { border-color: rgba(54, 96, 255, 0.45); color: var(--accent-dark); }
.src-box-btn.has-value { border-color: var(--accent); background: rgba(54, 96, 255, 0.1); color: var(--accent-dark); }
.src-box-count:empty { display: none; }
.src-box-count { padding: 1px 7px; border-radius: 999px; background: var(--accent); color: #fff; font-size: 0.72rem; }
.src-box-btn[hidden] { display: none; }

/* ── Composer: compact choices ────────────────────────
   Board and level are a row of pills, not a wall of tiles: ten boards at tile
   size made the curriculum step two screens tall for a question that is
   optional. The radio and its label are unchanged; only the art is dropped. */
.ws-tiles-board,
.ws-tiles-level {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}
.ws-tiles-board .ws-tile,
.ws-tiles-level .ws-tile {
    flex-direction: row;
    min-height: 0;
    padding: 7px 13px;
    border-radius: 999px;
    font-size: 0.84rem;
}
.ws-tiles-board .ws-tile-icon, .ws-tiles-board .ws-tile-glyph, .ws-tiles-board .ws-tile-mark,
.ws-tiles-level .ws-tile-icon, .ws-tiles-level .ws-tile-glyph, .ws-tiles-level .ws-tile-mark { display: none; }
.ws-tiles-board .ws-tiles-group, .ws-tiles-level .ws-tiles-group,
.ws-tiles-board .ws-tiles-toggle, .ws-tiles-level .ws-tiles-toggle { flex-basis: 100%; }

/* The theme is one row of swatches to scan, so the look and the length fit on
   a single step. */
.ws-theme-tiles { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); }
.ws-theme-tiles .theme-btn { gap: 6px; padding: 7px; font-size: 0.78rem; }
.ws-theme-tiles .theme-dot { height: 22px; }

/* Standing reminder above every way of adding a response. Not dismissible –
   it is the instruction the DPA schedule relies on, so it has to stay put. */
.mk-notice {
    display: flex;
    gap: 9px;
    align-items: flex-start;
    margin: 18px 0 0;
    padding: 10px 13px;
    border-radius: 10px;
    background: rgba(244, 180, 71, 0.13);
    border: 1px solid rgba(244, 180, 71, 0.42);
    font-size: 0.79rem;
    line-height: 1.55;
    color: var(--text);
}
.mk-notice-mark {
    flex-shrink: 0;
    width: 17px;
    height: 17px;
    margin-top: 1px;
    border-radius: 50%;
    background: var(--amber);
    color: #4a3410;
    font-size: 0.68rem;
    font-weight: 900;
    display: flex;
    align-items: center;
    justify-content: center;
}
.mk-notice a { color: var(--accent); font-weight: 700; white-space: nowrap; }
.mk-eyebrow {
    font-size: 0.62rem;
    font-weight: 900;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--text-muted);
}
.mk-empty { font-size: 0.86rem; color: var(--text-muted); padding: 12px 0; }
.mk-count { font-weight: 700; letter-spacing: 0; text-transform: none; color: var(--text-muted); }

.mk-card-head {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
    flex-wrap: wrap;
}
/* No `margin-left: auto` here: once the row wraps, auto keeps right-aligning the
   orphaned line, so the last button strands itself against the trailing edge of
   a card whose every other element is on the leading one. */
.mk-card-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.mk-card-head > .mk-card-actions { margin-inline-start: auto; }
@media (max-width: 33rem) {
    .mk-card-head > .mk-card-actions { margin-inline-start: 0; flex-basis: 100%; }
}

/* The marking run's readout sits beside the button that starts it. */
.mk-progress-status {
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}
.mk-progress-status:empty { display: none; }
/* On a narrow head the status takes its own line so Export all and Mark all
   stay side by side rather than being split across two rows. */
@media (max-width: 33rem) {
    .mk-progress-status { flex-basis: 100%; }
}

.mk-question-card { margin-bottom: 14px; }
.mk-question-text { font-size: 1.02rem; font-weight: 700; line-height: 1.45; margin: 6px 0 4px; }
.mk-question-meta { font-size: 0.78rem; color: var(--text-muted); }

/* Mark scheme editor */
.mk-rubric-card, .mk-responses-card { margin-bottom: 14px; }
.mk-rubric-list { display: flex; flex-direction: column; gap: 10px; }
.mk-criterion {
    padding: 11px 13px;
    border: 1.5px solid var(--border);
    border-radius: 10px;
    background: var(--surface-2);
}
.mk-crit-top { display: flex; gap: 8px; align-items: center; margin-bottom: 7px; }
/* Scoped to .mk-criterion to outrank the global input[type="text"] { width:100% },
   which is an attribute selector and beats a bare class. */
.mk-criterion .mk-crit-label { flex: 1 1 auto; width: auto; min-width: 0; }
.mk-criterion .mk-crit-ao { flex: 0 0 68px; width: 68px; text-align: center; }
.mk-criterion .mk-crit-marks { flex: 0 0 72px; width: 72px; text-align: center; }
.mk-criterion input, .mk-criterion textarea {
    padding: 7px 10px;
    border: 1.5px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    font-family: inherit;
    font-size: 0.85rem;
    color: var(--text);
}
.mk-criterion textarea { width: 100%; resize: vertical; }
.mk-crit-remove {
    flex: none;
    width: 28px; height: 28px;
    border: 0; border-radius: 8px;
    background: none;
    color: var(--text-muted);
    font-size: 1.1rem;
    cursor: pointer;
}
.mk-crit-remove:hover { background: rgba(177, 70, 55, 0.1); color: var(--red-soft); }

/* Adding responses.
   The method picker and the pane it opens are one object: a shared border, no
   gap between them, and the selected segment's bottom edge removed so the pane
   reads as hanging off the choice above it. */
.mk-add { margin-bottom: 20px; }
/* Deliberately not an .mk-eyebrow: it labels a control inside the section, and
   at the same size and case it competed with the section's own heading. */
.mk-add-label {
    font-size: 0.8rem;
    font-weight: 700;
    color: var(--text-muted);
    margin-bottom: 7px;
}
.mk-add-switch {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    padding: 4px;
    border: 1.5px solid var(--border);
    border-radius: 11px 11px 0 0;
    border-bottom: 0;
    background: var(--surface-2);
    width: fit-content;
    max-width: 100%;
}
.mk-add-tab {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 14px;
    border: 0;
    border-radius: 8px;
    background: none;
    font-family: inherit;
    font-size: 0.84rem;
    font-weight: 700;
    color: var(--text-muted);
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.15s ease, color 0.15s ease;
}
.mk-add-tab:hover { color: var(--text); }
.mk-add-tab.active {
    background: var(--card-bg);
    color: var(--accent);
    box-shadow: 0 1px 3px rgba(30, 27, 46, 0.08);
}
.mk-add-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Once the three segments no longer fit on one line, stretch the switch to the
   pane's width: a fit-content switch that has wrapped leaves the selected
   segment floating over a seam it no longer lines up with. */
@media (max-width: 30rem) {
    .mk-add-switch { width: 100%; }
    .mk-add-tab { flex: 1 1 auto; justify-content: center; padding-inline: 10px; }
}

.mk-add-pane {
    display: flex;
    flex-direction: column;
    gap: 8px;
    align-items: flex-start;
    padding: 14px;
    border: 1.5px solid var(--border);
    border-radius: 0 11px 11px 11px;
    background: var(--surface-2);
}
.mk-add-pane[hidden] { display: none; }
.mk-add-pane textarea, .mk-name-input {
    width: 100%;
    padding: 9px 12px;
    border: 1.5px solid var(--border);
    border-radius: 9px;
    background: var(--card-bg);
    font-family: inherit;
    font-size: 0.88rem;
    color: var(--text);
    resize: vertical;
}
.mk-bulk-controls { display: flex; align-items: center; gap: 8px; font-size: 0.82rem; }
.mk-bulk-controls select {
    padding: 6px 10px;
    border: 1.5px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    font-family: inherit;
    color: var(--text);
}
.mk-photo-status { font-size: 0.8rem; color: var(--text-muted); }

/* Bulk preview – confirmed before anything is saved */
.mk-preview-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin: 8px 0;
    max-height: 260px;
    overflow-y: auto;
}
.mk-preview-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 10px;
    border: 1.5px solid var(--border);
    border-radius: 8px;
    background: var(--surface-2);
}
.mk-preview-row .mk-preview-name {
    flex: 0 0 150px;
    width: 150px;
    padding: 5px 9px;
    border: 1.5px solid var(--border);
    border-radius: 7px;
    background: var(--card-bg);
    font-family: inherit;
    font-size: 0.82rem;
    font-weight: 700;
    color: var(--text);
}
.mk-preview-text {
    font-size: 0.8rem;
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Marked responses */
.mk-responses { display: flex; flex-direction: column; gap: 10px; }
.mk-response {
    border: 1.5px solid var(--border);
    border-radius: 10px;
    background: var(--card-bg);
    overflow: hidden;
}
.mk-response-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 11px 14px;
    cursor: pointer;
}
.mk-response-head:hover { background: var(--surface-2); }
/* The card hides the marking – and on an unmarked row, the student's answer
   itself. A hover tint is not a cue that anything is behind it, so the chevron
   is the affordance and the keyboard's way in. */
.mk-response-toggle {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    margin-inline-start: -4px;
    border: 0;
    border-radius: 7px;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
}
.mk-response-toggle svg { width: 16px; height: 16px; transition: transform 0.18s ease; }
.mk-response-toggle:hover { color: var(--text); }
.mk-response-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.mk-response.is-open .mk-response-toggle svg { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) {
    .mk-response-toggle svg { transition: none; }
}
.mk-response-name { font-size: 0.9rem; font-weight: 700; }
.mk-response-score {
    margin-left: auto;
    font-size: 0.82rem;
    font-weight: 800;
    color: var(--text-muted);
}
.mk-response[data-status="done"] .mk-response-score { color: var(--accent); }
.mk-response[data-status="error"] .mk-response-score { color: var(--red-soft); }
/* This student's marked work on its own – sits by the score, since that is
   where a teacher is already looking when they decide to hand one back. */
.mk-response-pdf {
    flex: none;
    padding: 2px 8px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: none;
    color: var(--text-muted);
    font-size: 0.68rem;
    font-weight: 800;
    letter-spacing: 0.04em;
    cursor: pointer;
}
.mk-response-pdf:hover { border-color: var(--accent); color: var(--accent); }
.mk-response-del {
    flex: none;
    border: 0; background: none;
    color: var(--text-muted);
    font-size: 1.1rem;
    cursor: pointer;
    padding: 0 4px;
}
.mk-response-del:hover { color: var(--red-soft); }
.mk-response-body { padding: 0 14px 14px; }

.mk-answer {
    padding: 13px 15px;
    border-radius: 9px;
    background: var(--surface-2);
    font-size: 0.88rem;
    line-height: 1.7;
    white-space: pre-wrap;
    word-break: break-word;
}
.mk-answer-plain { color: var(--text-muted); }

/* The highlights themselves */
.mk-hl {
    padding: 1px 2px;
    border-radius: 3px;
    background: none;
    color: inherit;
    box-decoration-break: clone;
    -webkit-box-decoration-break: clone;
}
.mk-hl-credit      { background: rgba(0, 199, 141, 0.26); border-bottom: 2px solid var(--green); }
/* Development sits under a credit: same colour, wavy, like the examiner mark. */
.mk-hl-development { background: rgba(0, 199, 141, 0.12); border-bottom: 2px wavy var(--green); }
.mk-hl-gap         { background: rgba(244, 180, 71, 0.3); border-bottom: 2px solid var(--amber); }
.mk-hl-off-target  { background: rgba(177, 70, 55, 0.14); border-bottom: 2px dotted var(--red-soft); }
i.mk-hl { display: inline-block; width: 14px; height: 10px; vertical-align: middle; }
.mk-answer .mk-hl { cursor: help; transition: filter 0.12s ease; }
.mk-answer .mk-hl:hover, .mk-answer .mk-hl:focus-visible { filter: brightness(0.94) saturate(1.25); }
.mk-answer .mk-hl:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* The examiner's comment on a highlight. Positioned in JS against the span, so
   a wrapped highlight is still explained beside its first line rather than in
   the browser's own unstyled tooltip. */
.mk-tip {
    position: fixed;
    z-index: 120;
    max-width: min(320px, calc(100vw - 16px));
    padding: 9px 12px 10px;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: var(--shadow-card);
    opacity: 0;
    visibility: hidden;
    /* transitions.dev tooltip timing. The tooltip is positioned in JS
       against the highlight's own line, so it keeps `position: fixed` and
       its data-side offset instead of the recipe's centred transform –
       the motion contract is the recipe's: a delayed, gentle appear and an
       instant, delay-free disappear. */
    transform: translateY(-3px) scale(var(--tt-scale));
    transition:
        opacity    var(--tt-out-dur) var(--tt-out-ease),
        transform  var(--tt-out-dur) var(--tt-out-ease),
        visibility 0s linear var(--tt-out-dur);
    pointer-events: none;
}
.mk-tip[data-side="above"] { transform: translateY(3px) scale(var(--tt-scale)); }
.mk-tip.is-open {
    opacity: 1;
    visibility: visible;
    transform: translateY(0) scale(1);
    /* The intent delay lives only here, so a cursor passing over a
       highlight never flashes the comment, but leaving is immediate. */
    transition:
        opacity    var(--tt-in-dur) var(--tt-in-ease) var(--tt-delay),
        transform  var(--tt-in-dur) var(--tt-in-ease) var(--tt-delay),
        visibility 0s linear var(--tt-delay);
}
/* Pointer back to the highlight; --tip-arrow tracks it after edge-clamping. */
.mk-tip::after {
    content: '';
    position: absolute;
    left: var(--tip-arrow, 50%);
    width: 9px;
    height: 9px;
    margin-left: -5px;
    background: var(--card-bg);
    border: 1px solid var(--border);
    transform: rotate(45deg);
}
.mk-tip[data-side="below"]::after { top: -5px; border-right: 0; border-bottom: 0; }
.mk-tip[data-side="above"]::after { bottom: -5px; border-left: 0; border-top: 0; }

/* The kind line carries the highlight's colour – enough to tie the comment to
   the mark it explains, without a slab of colour on the card itself. */
.mk-tip-kind {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.63rem;
    font-weight: 800;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-bottom: 5px;
}
.mk-tip-kind::before {
    content: '';
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
}
.mk-tip[data-kind="credit"] .mk-tip-kind,
.mk-tip[data-kind="development"] .mk-tip-kind { color: #0d8a66; }
.mk-tip[data-kind="gap"] .mk-tip-kind         { color: #b7791f; }
.mk-tip[data-kind="off-target"] .mk-tip-kind  { color: var(--red-soft); }
.mk-tip-comment { margin: 0; font-size: 0.82rem; line-height: 1.5; color: var(--text); }

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

.mk-legend {
    display: flex;
    gap: 14px;
    flex-wrap: wrap;
    align-items: center;
    margin: 9px 0 12px;
    font-size: 0.73rem;
    color: var(--text-muted);
}
.mk-legend span { display: flex; align-items: center; gap: 5px; }
/* Sits after the four keys rather than being pushed to the trailing edge: at
   anything under ~55rem the row wraps and `auto` left it stranded on a line of
   its own, reading as unrelated to the legend it belongs to. */
.mk-legend-note { font-style: italic; }

.mk-band { font-size: 0.86rem; margin-bottom: 10px; }

.mk-crit-table { width: 100%; border-collapse: collapse; font-size: 0.82rem; }
.mk-crit-table td { padding: 7px 9px; border-top: 1px solid var(--border); vertical-align: top; }
.mk-crit-table tr:first-child td { border-top: 0; }
.mk-crit-table td:first-child { font-weight: 700; width: 30%; }
.mk-crit-score { width: 70px; text-align: center; font-weight: 800; color: var(--accent); }
.mk-ao {
    display: inline-block;
    padding: 0 5px;
    border-radius: 4px;
    background: rgba(54, 96, 255, 0.12);
    color: var(--accent);
    font-size: 0.65rem;
    font-weight: 800;
}

.mk-next, .mk-summary {
    margin-top: 12px;
    padding: 11px 13px;
    border-radius: 9px;
    background: var(--surface-2);
    font-size: 0.84rem;
    line-height: 1.55;
}
.mk-next .mk-eyebrow, .mk-summary .mk-eyebrow { margin-bottom: 4px; }

/* Break where each row actually stops fitting, not at a device preset. The
   criterion and band heads run out of room around 30rem, the preview rows and
   the criteria table around 40rem. */
@media (max-width: 40rem) {
    .mk-preview-row { flex-direction: column; align-items: stretch; }
    .mk-preview-row .mk-preview-name { flex: 0 0 auto; width: 100%; }
    .mk-crit-table td:first-child { width: auto; }
}
@media (max-width: 30rem) {
    .mk-crit-top { flex-wrap: wrap; }
    .mk-criterion .mk-crit-label { flex: 1 1 100%; }
    /* Same treatment as .mk-crit-top: without it the label keeps shrinking past
       the width of its own text and the level name becomes an empty box. */
    .mk-band-head { flex-wrap: wrap; }
    .mk-band-row .mk-band-label { flex: 1 1 100%; }
}

/* Band decision: the level, where in it, and the level ruled out above */
.mk-banddec {
    margin: 10px 0 12px;
    padding: 11px 13px;
    border: 2px solid var(--accent);
    border-radius: 9px;
    background: var(--surface-2);
}
.mk-banddec-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.mk-banddec-label { font-size: 0.95rem; font-weight: 900; }
/* Two-ladder schemes: the part's own marks, since the header total is the sum. */
.mk-banddec-marks { margin-left: auto; font-size: 0.8rem; font-weight: 800; color: var(--text-muted); }
.mk-banddec .mk-eyebrow { margin-bottom: 4px; }
.mk-placement {
    font-size: 0.68rem;
    font-weight: 800;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding: 2px 7px;
    border-radius: 999px;
    background: rgba(54, 96, 255, 0.12);
    color: var(--accent);
}
.mk-placement[data-p="borderline"] { background: rgba(244, 180, 71, 0.22); color: #b7791f; }
.mk-placement[data-p="consistent"] { background: rgba(0, 199, 141, 0.16); color: #0d8a66; }
.mk-banddec-reason { font-size: 0.84rem; line-height: 1.5; margin-top: 5px; }
/* The band ruled out above is the most useful line for a teacher checking a mark. */
.mk-banddec-rejected {
    font-size: 0.78rem;
    line-height: 1.5;
    margin-top: 6px;
    padding-top: 6px;
    border-top: 1px dashed var(--border);
    color: var(--text-muted);
}

.mk-gating {
    margin-bottom: 12px;
    padding: 10px 13px;
    border-radius: 9px;
    background: rgba(244, 180, 71, 0.12);
    font-size: 0.8rem;
}
.mk-gating ul, .mk-gating-row ul { margin: 4px 0 0 16px; }
.mk-gating li, .mk-gating-row li { margin-bottom: 3px; }

/* Mark scheme editor: bands, AO weighting, caps */
.mk-ao-row {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding-bottom: 10px;
    margin-bottom: 4px;
    border-bottom: 1px dashed var(--border);
}
.mk-ao-chip {
    padding: 3px 9px;
    border-radius: 999px;
    background: rgba(54, 96, 255, 0.1);
    color: var(--accent);
    font-size: 0.74rem;
    font-weight: 800;
}
.mk-gating-row {
    padding: 9px 12px;
    border-radius: 9px;
    background: rgba(244, 180, 71, 0.12);
    font-size: 0.79rem;
}

.mk-band-row {
    padding: 11px 13px;
    border: 1.5px solid var(--border);
    border-radius: 10px;
    background: var(--surface-2);
}
.mk-band-head { display: flex; align-items: center; gap: 8px; margin-bottom: 7px; }
.mk-band-range { display: flex; align-items: center; gap: 5px; font-size: 0.8rem; color: var(--text-muted); }
.mk-band-unit { font-size: 0.72rem; }
.mk-band-row input, .mk-band-row textarea {
    padding: 7px 10px;
    border: 1.5px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    font-family: inherit;
    font-size: 0.85rem;
    color: var(--text);
}
.mk-band-row .mk-band-label { flex: 1 1 auto; width: auto; min-width: 0; font-weight: 700; }
.mk-band-row .mk-band-min,
.mk-band-row .mk-band-max { flex: 0 0 62px; width: 62px; text-align: center; }
.mk-band-row textarea { width: 100%; resize: vertical; }
.mk-band-row .mk-band-nutshell { width: 100%; }
.mk-nutshell-label {
    display: block;
    margin: 8px 0 3px;
    font-size: 0.7rem;
    font-weight: 700;
    color: var(--text-muted);
}

/* Published mark scheme picker */
.mk-picker {
    margin-bottom: 14px;
    padding: 13px 15px;
    border: 1.5px solid var(--accent);
    border-radius: var(--radius);
    background: rgba(54, 96, 255, 0.05);
}
.mk-picker[hidden] { display: none; }
.mk-picker-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.mk-picker-title { font-size: 0.92rem; font-weight: 800; }
.mk-picker-filters {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin: 10px 0 2px;
}
.mk-filter {
    display: flex;
    flex-direction: column;
    gap: 3px;
    /* Subject holds the longest names, so let it take the slack. */
    flex: 1 1 150px;
    min-width: 0;
}
.mk-filter:nth-child(2), .mk-filter:nth-child(3) { flex: 0 1 130px; }
.mk-filter-label {
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
}
.mk-filter select {
    width: 100%;
    padding: 6px 8px;
    font: inherit;
    font-size: 0.8rem;
    color: var(--text);
    background: var(--surface, #fff);
    border: 1px solid var(--border);
    border-radius: 7px;
    cursor: pointer;
}
.mk-filter select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.mk-filter select option:disabled { color: var(--text-muted); }
.mk-filter-note {
    margin: 6px 0 0;
    font-size: 0.76rem;
    color: var(--text-muted);
}
.mk-filter-note[hidden] { display: none; }
.mk-picker-close {
    margin-left: auto;
    border: 0; background: none; cursor: pointer;
    font-size: 1.2rem; line-height: 1; color: var(--text-muted);
}

.mk-template-list { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
.mk-template {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 11px 13px;
    border: 1.5px solid var(--border);
    border-radius: 10px;
    background: var(--card-bg);
    font-family: inherit;
    text-align: left;
    cursor: pointer;
    transition: border-color 0.15s, box-shadow 0.15s, transform 0.1s;
}
.mk-template:hover {
    border-color: var(--accent);
    box-shadow: 0 4px 14px rgba(54, 96, 255, 0.12);
    transform: translateY(-1px);
}
.mk-template-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.mk-template-label { font-size: 0.9rem; font-weight: 700; }
.mk-template-shape { font-size: 0.76rem; color: var(--text-muted); }
.mk-template-meta {
    margin-left: auto;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;
    flex: none;
}
.mk-template-board { font-size: 0.76rem; font-weight: 700; color: var(--accent); }
/* Attribution matters: this is the board's own ladder, not a draft. */
.mk-template-source { font-size: 0.68rem; color: var(--text-muted); }

.mk-source {
    margin-bottom: 10px;
    padding: 7px 11px;
    border-radius: 8px;
    background: rgba(0, 199, 141, 0.12);
    color: #0d8a66;
    font-size: 0.76rem;
    font-weight: 700;
}
.mk-source[hidden] { display: none; }

/* Two-ladder schemes: each part read-only, since they come from a real paper */
.mk-strand {
    margin-bottom: 12px;
    border: 1.5px solid var(--border);
    border-radius: 10px;
    overflow: hidden;
}
.mk-strand-head {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 9px 13px;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
}
.mk-strand-label { font-size: 0.88rem; font-weight: 800; }
.mk-strand-marks { margin-left: auto; font-size: 0.78rem; color: var(--text-muted); font-weight: 700; }
.mk-strand-bands { display: flex; flex-direction: column; }
.mk-band-static {
    display: flex;
    align-items: baseline;
    gap: 10px;
    padding: 8px 13px;
    font-size: 0.8rem;
}
.mk-band-static + .mk-band-static { border-top: 1px solid var(--border); }
.mk-band-static-label { font-weight: 700; flex: 0 0 auto; min-width: 90px; }
.mk-band-static-range {
    flex: 0 0 46px;
    font-weight: 800;
    color: var(--accent);
    font-size: 0.76rem;
}
.mk-band-static-desc { color: var(--text-muted); line-height: 1.45; }

@media (max-width: 640px) {
    .mk-template { flex-direction: column; align-items: flex-start; }
    .mk-template-meta { margin-left: 0; align-items: flex-start; }
    .mk-band-static { flex-direction: column; gap: 2px; }
}

/* ── Signed-in footer ─────────────────────────────────
   Quiet by design: it exists so the terms a teacher agreed to stay reachable
   from inside the app, not to compete with the work on the page. */
.app-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 10px 24px;
    max-width: 1100px;
    margin: 56px auto 0;
    padding: 20px 24px 28px;
    border-top: 1px solid var(--border);
    font-size: 0.8rem;
    color: var(--text-muted);
}
.app-footer-links { display: flex; flex-wrap: wrap; gap: 20px; }
.app-footer-links a {
    color: var(--text-muted);
    text-decoration: none;
    border-bottom: 1px solid transparent;
}
.app-footer-links a:hover { color: var(--text); border-bottom-color: var(--accent); }

@media (max-width: 640px) {
    .app-footer { justify-content: center; text-align: center; margin-top: 36px; }
}

/* ── Admin: spend against the cap ─────────────────────
   The whole point of the column is spotting a blocked account at a glance, so
   the states are colour-coded rather than left as plain numbers. */
.admin-cap-near { color: var(--amber); font-weight: 700; }
.admin-cap-hit { color: var(--red-soft); font-weight: 800; }
.admin-pct { font-size: 0.78em; opacity: 0.65; }
.admin-row-capped { background: rgba(177, 70, 55, 0.06); }
.admin-tag-capped { background: var(--red-soft); color: #fff; }
.admin-capped-note {
    margin: 0 0 14px;
    padding: 10px 14px;
    border-radius: 10px;
    background: rgba(177, 70, 55, 0.1);
    border: 1px solid rgba(177, 70, 55, 0.3);
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--red-soft);
}

/* Organisations needing attention, above the list. Same geometry as the
   spend-cap note above – it is the same kind of statement in the same place,
   and a second shape for it would only make the page busier. Two severities,
   because an organisation already dark is a live problem while one expiring
   next week is a deadline, and they want different reactions. */
.admin-org-note {
    margin: 0 0 14px;
    padding: 10px 14px;
    border-radius: 10px;
    font-size: 0.85rem;
    font-weight: 700;
    line-height: 1.5;
}
.admin-org-note-critical {
    background: rgba(177, 70, 55, 0.1);
    border: 1px solid rgba(177, 70, 55, 0.3);
    color: var(--red-soft);
}
.admin-org-note-warning {
    background: rgba(244, 180, 71, 0.14);
    border: 1px solid rgba(244, 180, 71, 0.4);
    color: #8a6210;
}

/* ── Cookie consent banner ───────────────────────────── */
.cc-banner {
    position: fixed;
    left: 16px;
    right: 16px;
    bottom: 16px;
    z-index: 10001;
    max-width: 720px;
    margin: 0 auto;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px 16px;
    padding: 12px 16px;
    border-radius: var(--radius);
    background: var(--card-bg);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-card);
}
.cc-banner-text {
    flex: 1 1 380px;
    margin: 0;
    font-size: 0.85rem;
    line-height: 1.4;
    color: var(--text-muted);
}
.cc-banner-text a { color: var(--accent); }
.cc-banner-actions {
    display: flex;
    gap: 10px;
    flex: 0 0 auto;
}
@media (max-width: 480px) {
    .cc-banner { left: 10px; right: 10px; bottom: 10px; padding: 14px; }
}

/* ── Marketplace cards ────────────────────────────
   A listing is a presentation card plus the two things a browser needs before
   committing: what it covers, and whether anyone else has used it. The footer
   is pinned so those read across a row rather than stepping with title length.
   Declared last so it wins the same-specificity ties against .pres-card-item
   and .pres-item-btn above. */
/* ── Marketplace header ───────────────────────────── */
.mkt-hero {
    margin-bottom: 14px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--border);
}
.mkt-hero-title { font-size: 1.15rem; font-weight: 700; }
.mkt-hero-sub {
    margin-top: 3px;
    max-width: 60ch;
    font-size: 0.84rem;
    line-height: 1.5;
    color: var(--text-muted);
}
/* The way out to everyone else's lessons. This pane is only your own, so
   without it the tab is a dead end. */
.mkt-hero-link {
    display: inline-block;
    margin-top: 8px;
    font-size: 0.84rem;
    font-weight: 700;
    color: var(--accent, #3660ff);
    text-decoration: none;
}
.mkt-hero-link:hover { text-decoration: underline; }

/* Placement only – it is a .thanks-note, same as the publish dialog's. */
.mkt-totals { margin: 0 0 18px; }

/* Also the only safe way to hide a file input that script clicks open. Safari
   – iPad and iPhone included, which is where a photographed exercise book
   comes from – refuses to open the picker for an input that is display:none
   or visibility:hidden, so the hidden attribute leaves the button dead with no
   error at all. This keeps the input rendered and merely out of sight. */
.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;
}

/* ── Loading placeholders ─────────────────────────── */
/* Card-shaped and inside the grid, so results replacing them changes nothing
   about the layout – the old list-row skeleton reflowed the whole pane. */
.mkt-skel {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border);
    border-radius: 14px;
    overflow: hidden;
    background: var(--card-bg);
    animation: skelPulse 1.4s var(--ease-linear) infinite;
}
.mkt-skel-thumb {
    display: block;
    aspect-ratio: 16 / 9;
    background: var(--surface-2);
}
.mkt-skel-lines {
    display: flex;
    flex-direction: column;
    gap: 7px;
    padding: 12px;
}
.mkt-skel-lines i {
    display: block;
    height: 9px;
    border-radius: 5px;
    background: var(--surface-2);
}
@keyframes skelPulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.55; }
}

.mkt-card { cursor: default; }
.mkt-card:hover {
    transform: translateY(-3px);
    box-shadow: var(--shadow-card);
    border-color: var(--accent);
}

/* Results arrive together rather than all at once, which reads as the grid
   filling in instead of flashing. Capped in JS at 10 items. */
.mkt-card-enter {
    animation: mktCardIn var(--duration-fast) var(--ease-smooth-out) both;
    animation-delay: var(--mkt-stagger, 0ms);
}
@keyframes mktCardIn {
    from { opacity: 0; transform: translateY(var(--distance-base)); }
    to   { opacity: 1; transform: none; }
}

/* The card has been a button since it was built, but nothing said so. The
   overlay only appears on hover or keyboard focus, so it never competes with
   the slide it sits on. */
.mkt-card .pres-preview { position: relative; }
.mkt-preview-hint {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: color-mix(in oklab, var(--accent) 78%, transparent);
    color: #fff;
    font-size: 0.82rem;
    font-weight: 700;
    letter-spacing: 0.01em;
    opacity: 0;
    transition: opacity var(--duration-fast) var(--ease-smooth-out);
}
.mkt-card:hover .mkt-preview-hint,
.mkt-card:focus-visible .mkt-preview-hint { opacity: 1; }

.mkt-slide-count {
    position: absolute;
    right: 7px;
    bottom: 7px;
    padding: 2px 7px;
    border-radius: 999px;
    background: rgba(30, 27, 46, 0.72);
    color: #fff;
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.01em;
}
/* Hidden under the overlay rather than layered on top of it. */
.mkt-card:hover .mkt-slide-count,
.mkt-card:focus-visible .mkt-slide-count { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
    .mkt-card-enter { animation: none; }
    .mkt-skel { animation: none; }
    .mkt-card:hover { transform: none; }
}
.mkt-card .pres-card-body {
    flex: 1;
    gap: 3px;
}
.mkt-card .pres-item-meta {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.mkt-desc {
    margin-top: 3px;
    font-size: 0.82rem;
    line-height: 1.4;
    color: var(--text-muted);
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}
.mkt-note {
    margin-top: 3px;
    font-size: 0.78rem;
    line-height: 1.35;
    color: var(--text-muted);
}

.mkt-foot {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: auto;
    padding-top: 10px;
}
/* How many teachers have taken this listing. In the card body rather than the
   footer: the footer holds a status pill and two buttons, and on a grid-width
   card this was being squeezed to "Use…". It belongs with the other facts about
   the listing anyway, next to the link to its public page. */
.mkt-card-uses {
    margin-top: 6px;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-muted);
}

/* The primary action is the whole point of browsing, so unlike a library card's
   actions it never hides behind hover. */
.mkt-card .pres-card-actions {
    opacity: 1;
    margin-top: 0;
    margin-left: auto;
}
.mkt-card .pres-item-btn { border: 1px solid transparent; }
.mkt-card .btn-mkt-clone {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}
.mkt-card .btn-mkt-clone:hover:not(:disabled) { background: #2b4fd6; border-color: #2b4fd6; }
.mkt-card .btn-mkt-clone:disabled { opacity: 0.6; cursor: default; }
.mkt-card .btn-mkt-refresh {
    background: var(--surface-2);
    border-color: var(--border);
    color: var(--text);
}
.mkt-card .btn-mkt-refresh:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.mkt-card .btn-mkt-refresh:disabled { opacity: 0.6; cursor: default; }
.mkt-card .btn-mkt-unpublish {
    background: none;
    color: var(--text-muted);
}
.mkt-card .btn-mkt-unpublish:hover {
    background: rgba(177, 70, 55, 0.1);
    border-color: rgba(177, 70, 55, 0.3);
    color: var(--red-soft);
}

.mktrep-optional {
    font-size: 0.72rem;
    font-weight: 500;
    color: var(--text-muted);
}

/* Whether your own listing is on the open web. Only shown on listings you own,
   because it is a fact about your choice rather than about the deck. */
.mkt-visibility {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    align-self: flex-start;
    margin-top: 4px;
    color: var(--text-muted);
    font-size: 0.72rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
}
.mkt-visibility:hover { color: var(--text); }
.mkt-visibility:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.mkt-visibility-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--text-muted);
    opacity: 0.45;
}
.mkt-visibility-dot.on { background: var(--accent); opacity: 1; }

/* Provenance on a library card: quieter than the marketplace credit, since here
   it is a fact about where your copy came from rather than a headline. */
.pres-source-credit {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 0.74rem;
    font-weight: 600;
    color: var(--text-muted);
    min-width: 0;
}
.pres-source-credit svg { width: 11px; height: 11px; flex-shrink: 0; opacity: 0.8; }
.pres-source-credit span {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ── Publish dialog ───────────────────────────────── */
/* Both dialogs that are built out of .mktpub-* parts, because the centring
   lives on the container and the container is the one piece those classes do
   not cover. Left on the id alone, a second dialog reusing the card renders as
   a static block in normal flow – which is to say, off the bottom of the page.
   Anything else built from these parts belongs in this selector too. */
#mkt-publish-modal,
#mkt-series-modal {
    position: fixed;
    inset: 0;
    z-index: 130;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
}
/* display:flex above would otherwise beat the hidden attribute. */
#mkt-publish-modal[hidden],
#mkt-series-modal[hidden] { display: none; }
.mktpub-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(30, 27, 46, 0.45);
    backdrop-filter: blur(10px) saturate(1.1);
    -webkit-backdrop-filter: blur(10px) saturate(1.1);
    animation: fadeIn var(--duration-fast) var(--ease-smooth-out) both;
}
.mktpub-card {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    width: 100%;
    max-width: 560px;
    max-height: 90vh;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: 16px;
    box-shadow: var(--shadow-card);
    overflow: hidden;
}
.mktpub-head {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 16px 18px 12px;
    border-bottom: 1px solid var(--border);
}
.mktpub-head > div { flex: 1; min-width: 0; }
.mktpub-title { font-size: 1.05rem; font-weight: 700; line-height: 1.3; }
.mktpub-sub {
    margin-top: 3px;
    font-size: 0.8rem;
    color: var(--text-muted);
}
.mktpub-body {
    flex: 1;
    overflow-y: auto;
    padding: 16px 18px;
}
.mktpub-body .form-group { margin-bottom: 14px; }

/* The title comes from the deck and is not editable here – shown so the author
   can see which deck they are publishing, not as another thing to fill in. */
.mktpub-deck {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin-bottom: 14px;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface-2);
}
/* The thank-you note, wherever it appears – the publish dialog explaining the
   offer, and the Published tab reporting where you stand against it. One
   component, because it is one idea: a teacher who reads it in the dialog
   should recognise it on the tab.

   Deliberately quiet. It is a reason to publish, not the reason – a loud offer
   would make sharing look like a transaction, which is neither what this is nor
   what actually persuades teachers to share. */
.thanks-note {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 9px 12px;
    border-radius: 10px;
    background: rgba(0, 199, 141, 0.09);
    font-size: 0.8rem;
    line-height: 1.5;
    color: var(--text-muted);
}
.thanks-note[hidden] { display: none; }
.thanks-note b { font-weight: 800; color: var(--text); font-variant-numeric: tabular-nums; }
.thanks-note-mark { flex-shrink: 0; color: #0d8a66; }
/* Placement only – the look is shared above. */
.mktpub-thanks { margin-bottom: 16px; }
.mktpub-deck-label {
    flex-shrink: 0;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
}
.mktpub-deck-title {
    min-width: 0;
    font-size: 0.86rem;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.mktpub-hint-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    margin-top: 5px;
}
.mktpub-hint { font-size: 0.75rem; color: var(--text-muted); }
.mktpub-count {
    flex-shrink: 0;
    font-size: 0.72rem;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
}

.mktpub-meta-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
}

/* The subject picker inside the publish dialog. The dialog body scrolls
   (overflow-y on .mktpub-body), so an absolutely positioned dropdown is clipped
   at its edge rather than escaping the card – capping the list keeps it inside
   the visible area, and the JS scrolls the field up when it opens so the
   options land in the space below it. */
#mkt-publish-modal .sc-dropdown {
    max-height: 210px;
    overflow-y: auto;
}
#mkt-publish-modal .sc-input { width: 100%; }
/* Room under the last row for the open dropdown, so it is not permanently
   pressed against the confirmation checkbox. */
#mkt-publish-modal .mktpub-meta-grid { scroll-margin-bottom: 220px; }

/* Only ever shown to an account with no name yet – Marketplace sign-up does not
   ask for one, and this is the first moment it costs the author anything. Given
   its own tinted panel rather than dropped into the meta grid so it reads as a
   one-off question about them, not a fourth field about the lesson. */
.mktpub-name-row {
    margin-bottom: 12px;
    padding: 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface-2, rgba(0, 0, 0, 0.02));
}
.mktpub-name-why {
    margin: 0 0 10px;
    font-size: 0.85rem;
    font-weight: 600;
}
.mktpub-name-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
}

.mktpub-check {
    display: flex;
    gap: 10px;
    margin-bottom: 12px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    cursor: pointer;
    transition: border-color var(--duration-fast) var(--ease-smooth-out);
}
.mktpub-check:hover { border-color: var(--accent); }
.mktpub-check input { flex-shrink: 0; margin-top: 2px; }
.mktpub-check > span { display: block; min-width: 0; }
.mktpub-check strong { display: block; font-size: 0.84rem; font-weight: 600; }
.mktpub-check-sub {
    display: block;
    margin-top: 2px;
    font-size: 0.75rem;
    line-height: 1.45;
    color: var(--text-muted);
}

.mktpub-foot {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    padding: 12px 18px;
    border-top: 1px solid var(--border);
}
.mktpub-foot .btn-primary:disabled { opacity: 0.5; cursor: default; }

@media (max-width: 640px) {
    #mkt-publish-modal,
    #mkt-series-modal { padding: 12px; }
    .mktpub-card { max-height: 94vh; }
    .mktpub-meta-grid { grid-template-columns: 1fr; }
    .mktpub-name-grid { grid-template-columns: 1fr; }
}

/* ── Listing preview modal ────────────────────────── */
#mkt-preview-modal {
    position: fixed;
    inset: 0;
    z-index: 120;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
}
#mkt-preview-modal[hidden] { display: none; }
.mktp-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(30, 27, 46, 0.45);
    backdrop-filter: blur(10px) saturate(1.1);
    -webkit-backdrop-filter: blur(10px) saturate(1.1);
    animation: fadeIn 0.25s ease both;
}
.mktp-card {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    width: 100%;
    max-width: 900px;
    max-height: 90vh;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: 16px;
    box-shadow: var(--shadow-card);
    overflow: hidden;
}
.mktp-head {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 16px 18px 12px;
    border-bottom: 1px solid var(--border);
}
.mktp-head-text { min-width: 0; flex: 1; }
.mktp-title {
    font-size: 1.05rem;
    font-weight: 700;
    line-height: 1.3;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}
.mktp-meta {
    margin-top: 3px;
    font-size: 0.8rem;
    color: var(--text-muted);
}
.mktp-close {
    flex-shrink: 0;
    width: 30px;
    height: 30px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid transparent;
    border-radius: 8px;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
}
.mktp-close:hover { background: var(--surface-2); color: var(--text); }

.mktp-body {
    flex: 1;
    overflow-y: auto;
    padding: 18px;
    background: var(--surface-2);
}
/* The stage is sized in JS to the scaled slide's height – a 960x540 slide is
   transform-scaled, which leaves no layout height of its own. */
.mktp-stage {
    position: relative;
    width: 100%;
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--card-bg);
}
.mktp-slide {
    position: absolute;
    top: 0; left: 0;
    width: 960px;
    height: 540px;
    transform-origin: top left;
    pointer-events: none;
    user-select: none;
}

.mktp-foot {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 18px;
    border-top: 1px solid var(--border);
}
.mktp-nav {
    display: flex;
    align-items: center;
    gap: 10px;
}
.mktp-counter {
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--text-muted);
    min-width: 110px;
}
.mktp-foot-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
}
.mktp-nav .btn:disabled { opacity: 0.4; cursor: default; }

/* The author's own account of the deck, above the slides it describes. */
.mktp-desc {
    margin-bottom: 12px;
    font-size: 0.86rem;
    line-height: 1.55;
    color: var(--text);
    white-space: pre-wrap;
}

.mktp-filmstrip {
    display: flex;
    gap: 6px;
    margin-top: 10px;
    padding-bottom: 4px;
    overflow-x: auto;
    scrollbar-width: thin;
}
.mktp-film {
    flex-shrink: 0;
    min-width: 30px;
    height: 30px;
    padding: 0 7px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    color: var(--text-muted);
    font-size: 0.76rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    cursor: pointer;
    transition: border-color var(--duration-fast) var(--ease-smooth-out),
                color var(--duration-fast) var(--ease-smooth-out);
}
.mktp-film:hover { border-color: var(--accent); color: var(--accent); }
.mktp-film:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.mktp-film.active {
    border-color: var(--accent);
    background: var(--accent);
    color: #fff;
}

@media (max-width: 640px) {
    #mkt-preview-modal { padding: 12px; }
    .mktp-card { max-height: 94vh; }
    .mktp-body { padding: 12px; }
    .mktp-counter { min-width: 0; font-size: 0.76rem; }
}

/* Cards that open a preview say so on hover, unlike the plain listing shell. */
.mkt-card-clickable { cursor: pointer; }
.mkt-card-clickable:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* ── Settings: published name ─────────────────────── */
.set-row-stack {
    display: block;
}
.set-name-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 10px;
}
.set-name-input {
    flex: 1;
    min-width: 0;
    padding: 7px 11px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    color: var(--text);
    font-size: 0.88rem;
    outline: none;
    transition: border-color 0.15s, box-shadow 0.15s;
}
.set-name-input:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(54, 96, 255, 0.12);
}
.set-name-msg {
    margin-top: 8px;
    font-size: 0.8rem;
    color: var(--text-muted);
}
.set-name-msg.is-error { color: var(--red-soft); }

/* Accessibility pane. Full-width selects rather than the inline control the
   other rows use: these sit under a paragraph of explanation, and a picker
   tucked to the right of it reads as an afterthought. */
.set-select {
    width: 100%;
    margin-top: 10px;
    padding: 8px 11px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    color: var(--text);
    font-size: 0.88rem;
    outline: none;
    transition: border-color 0.15s, box-shadow 0.15s;
}
.set-select:focus-visible {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(54, 96, 255, 0.12);
}

/* Who the selected layout is for. Empty for Standard, and collapsed when
   empty so the row does not keep a blank line reserved. */
.set-a11y-note { margin-top: 8px; }
.set-a11y-note:empty { display: none; }

/* The public author profile. Stacked rather than in a row like the name
   fields: the bio is a paragraph, not a field, and giving it a line of its own
   is what stops it reading as a third name box. */
.set-profile-fields {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-top: 10px;
}
.set-bio-input {
    width: 100%;
    padding: 8px 11px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 0.88rem;
    line-height: 1.5;
    resize: vertical;
    outline: none;
    transition: border-color 0.15s, box-shadow 0.15s;
}
.set-bio-input:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(54, 96, 255, 0.12);
}
.set-profile-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}
.set-bio-count { font-size: 0.75rem; color: var(--text-muted); }

/* ── Sliding tab pill (transitions.dev tabs-sliding) ───────
   The recipe owns the transform + width tween; these rules keep each
   bar looking like itself – an underline on the dashboard / auth tabs,
   a white capsule inside the PowerPoint mode segment. */
.auth-tabs.t-tabs,
.dashboard-tabs.t-tabs {
    background: none;
    padding: 0;
    border-radius: 0;
}
.auth-tabs .t-tabs-pill,
.dashboard-tabs .t-tabs-pill {
    top: auto;
    bottom: -2px;
    height: 2px;
    border-radius: 2px;
    background: var(--accent);
}
.ppt-mode-tabs .t-tabs-pill {
    top: 3px;
    height: calc(100% - 6px);
    background: #fff;
    border-radius: 6px;
    box-shadow: 0 2px 8px rgba(30, 27, 46, 0.08);
}
.ppt-mode-btn.active { color: var(--accent); }
/* The recipe's own tab metrics must not override each bar's sizing. */
.auth-tab.t-tab, .dash-tab.t-tab, .ppt-mode-btn.t-tab {
    height: auto;
    font-family: inherit;
}

/* ── Support widget (js/support-widget.js) ─────────────────
   Bottom-right, over everything but the toast: it has to be reachable while a
   dialog is open, because the dialog is often what is being reported. Signed
   in only – #app is hidden for everyone else – and never while presenting to
   a class. --sup-lift is the progress dock's height, set by the script. */
.sup-widget {
    position: fixed;
    right: 20px;
    bottom: calc(20px + var(--sup-lift, 0px));
    z-index: 11000;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 12px;
    pointer-events: none;
    transition: bottom var(--duration-fast) var(--ease-smooth-out);
}
body:has(#app[hidden]) .sup-widget,
body:has(#present-overlay:not([hidden])) .sup-widget { display: none; }
@media print { .sup-widget { display: none; } }

.sup-launcher {
    pointer-events: auto;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 46px;
    padding: 0 18px 0 14px;
    border: none;
    border-radius: 999px;
    background: linear-gradient(90deg, var(--accent), #60a5fa);
    color: #fff;
    font-family: inherit;
    font-size: 0.95rem;
    font-weight: 700;
    white-space: nowrap;
    box-shadow: 0 12px 28px rgba(54, 96, 255, 0.32);
    cursor: pointer;
    transition: transform 0.15s, box-shadow 0.15s, background 0.15s;
}
.sup-launcher svg { flex: none; }
.sup-launcher:hover { transform: translateY(-1px); background: linear-gradient(90deg, var(--accent-dark), #4f94e8); box-shadow: 0 16px 32px rgba(54, 96, 255, 0.38); }
.sup-launcher:active { transform: scale(0.97); }
.sup-launcher:focus-visible, .sup-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.sup-panel {
    width: 340px;
    max-width: calc(100vw - 40px);
    max-height: calc(100vh - 110px - var(--sup-lift, 0px));
    overflow-y: auto;
    padding: 16px 18px 18px;
    border: 1px solid var(--border);
    border-radius: 16px;
    background: var(--card-bg);
    box-shadow: 0 24px 60px rgba(30, 27, 46, 0.2);
}
.sup-panel[hidden], .sup-form[hidden], .sup-done[hidden], .sup-shot[hidden],
.sup-shot-img[hidden], .sup-shot-note[hidden], .sup-error[hidden] { display: none; }

.sup-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.sup-head h2 { font-size: 1.05rem; letter-spacing: -0.01em; }
.sup-close {
    display: inline-flex;
    padding: 4px;
    margin-right: -4px;
    border: none;
    border-radius: 8px;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
}
.sup-close:hover { color: var(--text); background: var(--surface-2); }

.sup-form { display: flex; flex-direction: column; gap: 10px; }
.sup-kinds { display: flex; flex-wrap: wrap; gap: 6px; }
.sup-kind { position: relative; cursor: pointer; }
.sup-kind input { position: absolute; opacity: 0; pointer-events: none; }
.sup-kind span {
    display: block;
    padding: 5px 12px;
    border: 1px solid var(--border);
    border-radius: 999px;
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--text-muted);
    transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.sup-kind input:checked + span { background: rgba(54, 96, 255, 0.1); border-color: rgba(54, 96, 255, 0.35); color: var(--accent); }
.sup-kind input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }

.sup-form textarea { font-size: 0.92rem; min-height: 96px; box-shadow: none; }

.sup-shot {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 96px;
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface-2);
}
.sup-shot-img { width: 100%; height: 100%; object-fit: cover; object-position: top center; }
.sup-shot-note { padding: 0 12px; font-size: 0.8rem; color: var(--text-muted); text-align: center; }
.sup-shot-row { display: flex; align-items: center; gap: 8px; font-size: 0.85rem; cursor: pointer; }
.sup-shot-row input { flex: none; width: 16px; height: 16px; accent-color: var(--accent); }

.sup-error { font-size: 0.85rem; color: var(--red-soft); }
.sup-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 2px; }
.sup-reply { min-width: 0; font-size: 0.78rem; color: var(--text-muted); overflow-wrap: anywhere; }
.sup-foot .btn { flex: none; }

.sup-done { text-align: center; padding: 14px 0 4px; }
.sup-done h3 { font-size: 1rem; margin-bottom: 4px; }
.sup-done-text { margin-bottom: 14px; font-size: 0.88rem; color: var(--text-muted); overflow-wrap: anywhere; }

/* ── Toast (transitions.dev toast) ─────────────────────────
   Position + surface only; the rise, cross-blur and open/close
   asymmetry live in the t-toast rule. */
#toast {
    position: fixed;
    left: 50%;
    bottom: 28px;
    margin-left: -0.5px;
    translate: -50% 0;
    z-index: 12000;
    max-width: min(420px, calc(100vw - 32px));
    padding: 11px 18px;
    border-radius: 999px;
    background: #1e1b2e;
    color: #fff;
    font-size: 0.88rem;
    font-weight: 600;
    box-shadow: 0 18px 44px rgba(30, 27, 46, 0.28);
    pointer-events: none;
}

/* ── Series, in the Published tab ─────────────────
   An ordered run of your own lessons. It sits above the list of listings
   because it is built out of them, and it borrows .mkt-card wholesale rather
   than growing a second card: a series card that looked different from a
   listing card would be claiming to be a different kind of thing, and it is
   not – it is a few of them, in order. */
.mkt-series-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin: 0 0 10px;
}
.mkt-series-title { font-size: 0.95rem; font-weight: 700; }
.mkt-series-nudge { margin: 0 0 14px; }
#mkt-series-list { margin-bottom: 26px; }
#mkt-series-list:empty { margin-bottom: 0; }

/* ── The series builder ───────────────────────────
   Two columns: what is in the series, in order, and what is left to put in it.
   The order is the whole point – it is what makes this a scheme of work rather
   than a folder – so the picked column is numbered and comes first, and reading
   down it is reading the sequence a class would be taught. */
/* The picked column gets the wider share. It carries a number, a title and
   three controls where the pool carries a title and one, and it is the column
   being read as a sequence. */
.mktseries-picker {
    display: grid;
    grid-template-columns: 1.25fr 1fr;
    gap: 16px;
    margin-top: 4px;
}
/* A grid column will not shrink below the width of its widest unbreakable
   child unless it is told it may, and "Genetics – GCSE Edexcel Biology" is one
   long unbreakable child. Without this the right column grew past the card and
   took its Add buttons off the edge of the dialog with it. */
.mktseries-col { min-width: 0; }
.mktseries-col-title {
    margin-bottom: 6px;
    font-size: 0.76rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
}
.mktseries-picked,
.mktseries-pool {
    max-height: 260px;
    overflow-y: auto;
    padding: 6px;
    border: 1px solid var(--border);
    border-radius: var(--radius, 12px);
    background: var(--surface-2);
    list-style: none;
}
.mktseries-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    border-radius: 8px;
    font-size: 0.82rem;
}
.mktseries-item + .mktseries-item { margin-top: 3px; }
.mktseries-item:hover { background: var(--card-bg); }
.mktseries-num {
    flex-shrink: 0;
    min-width: 1.5em;
    font-size: 0.74rem;
    font-weight: 700;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}
.mktseries-item-title {
    flex: 1;
    /* Same reasoning as .mktseries-col, one level down: a flex item's automatic
       minimum size is its content, so the ellipsis never engages without it and
       the row pushes its own buttons out of the box instead of truncating. */
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.mktseries-item-acts {
    display: flex;
    flex-shrink: 0;
    gap: 4px;
}
.mktseries-item-acts .pres-item-btn { padding: 2px 7px; font-size: 0.72rem; }
.mktseries-item-acts .pres-item-btn:disabled { opacity: 0.35; cursor: default; }
/* Square, so the cross reads as an icon rather than as a very short word. */
.mktseries-x {
    width: 22px;
    padding: 2px 0;
    font-size: 0.95rem;
    line-height: 1;
}
.mktseries-empty,
.mktseries-note {
    padding: 10px 8px;
    font-size: 0.78rem;
    line-height: 1.45;
    color: var(--text-muted);
}
/* Why a lesson you can see in your library is not offered here. Without it the
   pool just silently lacks it, which reads as a bug. */
.mktseries-note { padding-top: 4px; }

@media (max-width: 600px) {
    .mktseries-picker { grid-template-columns: 1fr; }
}

/* ── User-created slide templates ──────────────────────────────────────────
   Slot affordances live here and never in slides.css: that stylesheet is
   inlined into PDF and PPTX exports, so a dashed outline defined there would
   land in every exported deck. */

.ppt-template-mode [data-slot] {
    outline: 1.5px dashed var(--accent);
    outline-offset: 2px;
}
.ppt-template-mode [data-slot-repeat] {
    outline: 1.5px solid var(--accent);
    outline-offset: 4px;
}

/* Badges are drawn in their own overlay rather than as ::after on the slot,
   because a pseudo-element on a flex/grid child or an <img> changes the very
   layout the teacher is trying to capture. */
#slot-badge-layer {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 60;
}
/* The slide being marked up sits inside the designer modal, so badges have to
   clear the modal layer. Scoped rather than global: at this height they would
   otherwise float over unrelated dialogs (quota, settings) too. */
#slot-badge-layer.is-designing { z-index: 10050; }
.slot-badge {
    position: absolute;
    transform: translateY(-100%);
    display: flex;
    align-items: baseline;
    gap: 5px;
    background: var(--accent);
    color: #fff;
    font: 600 10px/1.5 "Nunito", system-ui, sans-serif;
    padding: 1px 6px;
    border-radius: 5px 5px 5px 0;
    white-space: nowrap;
}
/* The name is the label; the type is the footnote. Dot-separating them at one
   weight read as debug output rather than as a caption. */
.slot-badge-kind { font-weight: 500; opacity: 0.72; }
.slot-badge[data-kind="repeat"] { background: var(--purple); }

/* Slot inspector */
#slot-form {
    position: fixed;
    width: 260px;
    padding: 10px;
    display: flex;
    flex-direction: column;
    gap: 7px;
    /* Above both the modal layer and the context menu that opens it. */
    z-index: 10200;
}
.slot-form-title {
    font-weight: 600;
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.slot-form-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    font-size: 13px;
}

/* ── Students ─────────────────────────────────────────────────────────────
   The roster, the unmatched-work queue and one student's record. */

.nav-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border: 0;
    border-radius: 999px;
    background: none;
    font: inherit;
    font-size: 0.88rem;
    font-weight: 700;
    color: var(--text-muted);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}
.nav-link:hover { background: rgba(255, 255, 255, 0.75); color: var(--text); }
.nav-link[aria-current="page"] {
    background: #fff;
    color: var(--accent);
    box-shadow: 0 1px 2px rgba(40, 30, 60, 0.06), 0 2px 8px rgba(54, 96, 255, 0.12);
}
.nav-link-icon { flex: none; opacity: 0.8; transition: transform 0.18s ease, opacity 0.15s ease; }
.nav-link:hover .nav-link-icon { opacity: 1; transform: translateY(-1px); }
.nav-link[aria-current="page"] .nav-link-icon { opacity: 1; }

/* The places – Pupils and Tutoring – sit together on one soft
   track so they read as a set of destinations, apart from the actions beside
   them. Collapses to nothing when every link in it is hidden. */
.nav-links {
    display: flex;
    align-items: stretch;
    gap: 2px;
    height: var(--nav-h, 36px);
    padding: 3px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface-2);
}
.nav-links .nav-link { padding: 0 12px; }
.nav-links:not(:has(> .nav-link:not([hidden]))) { display: none; }

/* The count of results that are not reaching anybody's record. Red rather
   than a neutral dot: this is data going missing, not a notification. */
.nav-link-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 999px;
    background: var(--red-soft);
    color: #fff;
    font-size: 0.68rem;
    font-weight: 800;
}

.stu-head,
.stu-profile-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 18px;
}
.stu-head-copy { min-width: 0; }
.stu-title { font-size: 1.6rem; font-weight: 800; letter-spacing: -0.01em; }
.stu-sub { color: var(--text-muted); font-size: 0.9rem; margin-top: 4px; }
.stu-eyebrow {
    font-size: 0.72rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
}
.stu-back { margin-bottom: 10px; }
.stu-hint { color: var(--text-muted); font-size: 0.85rem; line-height: 1.5; margin-top: 4px; }

.stu-banner {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    margin-bottom: 18px;
    border: 1px solid rgba(244, 180, 71, 0.5);
    border-radius: var(--radius);
    background: rgba(244, 180, 71, 0.12);
}
.stu-banner-icon { display: flex; flex: none; color: #b7791f; }
.stu-banner-icon svg { width: 20px; height: 20px; }
.stu-banner-copy { flex: 1; min-width: 0; font-size: 0.88rem; }
.stu-banner .btn { flex: none; }

/* "Did you mean…?" on an address that looks mistyped. A question with two
   answers, so it is drawn as the neutral note and not as the red error. */
.email-ask {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 14px;
}
.email-ask[hidden] { display: none; }
.email-ask p { flex: 1 1 auto; margin: 0; }
.email-ask strong { overflow-wrap: anywhere; }
.email-ask-actions { display: flex; align-items: center; gap: 14px; }
/* Not .btn: the sign-up card dresses those as the landing page's big pills,
   and two of them stacked in a note is a second form inside the first. */
.email-ask-yes,
.email-ask-no {
    border: 0;
    font: inherit;
    font-size: 0.85rem;
    font-weight: 700;
    line-height: 1.2;
    cursor: pointer;
}
.email-ask-yes {
    padding: 7px 12px;
    border-radius: 8px;
    background: #3660ff;
    color: #fff;
}
.email-ask-yes:hover { background: #2448d8; }
.email-ask-no {
    padding: 7px 0;
    background: none;
    color: var(--text-muted);
    text-decoration: underline;
    text-underline-offset: 2px;
}
.email-ask-no:hover { color: var(--text); }
.email-ask-yes:focus-visible,
.email-ask-no:focus-visible { outline: 2px solid #3660ff; outline-offset: 2px; }

/* The "check your address" bar at the top of the library. Four things in a row
   is one more than the banner was drawn for, so it wraps. */
.email-confirm { margin-bottom: 18px; }
.email-confirm-bar { flex-wrap: wrap; margin-bottom: 0; }
.email-confirm-bar .stu-banner-copy { flex: 1 1 280px; }
.email-confirm-bar strong { overflow-wrap: anywhere; }
.email-confirm-x {
    flex: none;
    width: 28px;
    height: 28px;
    border: 0;
    border-radius: 50%;
    background: none;
    color: var(--text-muted);
    font-size: 1.2rem;
    line-height: 1;
    cursor: pointer;
}
.email-confirm-x:hover { background: rgba(0, 0, 0, 0.06); color: var(--text); }
.email-fix {
    margin-top: 10px;
    padding: 16px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card-bg);
}
.email-fix-actions { display: flex; gap: 8px; }

.stu-card { padding: 20px; margin-bottom: 18px; }
.stu-card-title { font-size: 1.05rem; font-weight: 800; }
.stu-card-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 14px;
}
.stu-card .ws-field-label { margin-top: 14px; display: block; }
.stu-textarea {
    width: 100%;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--card-bg);
    color: var(--text);
    font: inherit;
    font-size: 0.9rem;
    line-height: 1.6;
    resize: vertical;
}
.stu-textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.stu-actions { display: flex; gap: 8px; margin-top: 16px; flex-wrap: wrap; }
.stu-bulk { display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }

/* Marking by hand, on a quiz response. */
.quiz-marking-banner { margin: 0 0 14px; }

/* The response you asked for, when you arrive from a student's record into a
   list of thirty. Marked rather than merely scrolled to, because a list that
   silently jumped somewhere leaves you working out where you landed. */
.is-focused-response {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
    border-radius: var(--radius);
    animation: focus-response-fade 2.4s ease-out forwards;
}
@keyframes focus-response-fade {
    0%, 55% { outline-color: var(--accent); }
    100% { outline-color: transparent; }
}
html[data-reduce-motion] .is-focused-response { animation: none; }

.stu-needs-mark {
    flex: none;
    padding: 3px 9px;
    border: 0;
    border-radius: 999px;
    background: rgba(244, 180, 71, 0.2);
    color: #b7791f;
    font: inherit;
    font-size: 0.72rem;
    font-weight: 800;
    white-space: nowrap;
}
button.stu-needs-mark { cursor: pointer; }
button.stu-needs-mark:hover { background: rgba(244, 180, 71, 0.38); }
button.stu-needs-mark:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── Marking one student ─────────────────────────────────────────────── */

.stu-mark-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 18px;
}
.stu-mark-head-copy { min-width: 0; }
.stu-mark-scorebox { flex: none; text-align: right; }
.stu-mark-scorenum {
    display: block;
    font-size: 1.7rem;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}
.stu-mark-scorelabel { font-size: 0.8rem; font-weight: 700; color: var(--text-muted); }

.stu-mark-qhead {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 10px;
}
.stu-mark-state {
    margin-left: auto;
    padding: 3px 9px;
    border-radius: 999px;
    font-size: 0.7rem;
    font-weight: 800;
}
.stu-mark-state.is-teacher { background: rgba(54, 96, 255, 0.12); color: var(--accent); }
.stu-mark-state.is-auto { background: var(--surface-2); color: var(--text-muted); }
.stu-mark-state.is-unmarked { background: rgba(244, 180, 71, 0.22); color: #b7791f; }

/* No edge accents on these cards. The state chip in each card's header
   already names what needs reviewing, and a coloured stripe down the side
   only repeats it more loudly. */

.stu-mark-hint { font-size: 0.78rem; color: var(--text-muted); }

.stu-mark-foot {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 20px;
    flex-wrap: wrap;
}

.stu-mark-card {
    padding: 18px 20px;
    margin-bottom: 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card-bg);
    box-shadow: var(--shadow);
    transition: opacity 0.22s ease, transform 0.22s ease;
}
/* Marked work leaves the page rather than sitting there with a tick on it,
   so what remains is always exactly what is still owed. */
.stu-mark-card.is-marked { opacity: 0; transform: translateY(-6px); }

.stu-mark-source {
    display: flex;
    align-items: center;
    gap: 8px;
    padding-bottom: 12px;
    margin-bottom: 14px;
    border-bottom: 1px solid var(--border);
}
.stu-mark-source-title {
    flex: 1;
    min-width: 0;
    font-size: 0.8rem;
    font-weight: 700;
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.stu-mark-source-date { flex: none; font-size: 0.76rem; color: var(--text-muted); }

.stu-mark-q { font-size: 1rem; font-weight: 700; line-height: 1.45; }
.stu-mark-qnum { color: var(--accent); }
.stu-mark-label { margin: 14px 0 6px; }

.stu-mark-img {
    display: block;
    max-width: 100%;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: #fff;
}
.stu-mark-answer {
    padding: 12px 14px;
    border-radius: 10px;
    background: var(--surface);
    font-size: 0.95rem;
    line-height: 1.5;
}
.stu-mark-answer.is-blank { color: var(--text-muted); font-style: italic; }
.stu-mark-longtext {
    padding: 14px 16px;
    border-radius: 10px;
    background: var(--surface);
    font-size: 0.92rem;
    line-height: 1.65;
    white-space: pre-wrap;
    max-height: 340px;
    overflow-y: auto;
}
.stu-mark-indicative {
    margin-top: 10px;
    padding: 10px 12px;
    border-radius: 8px;
    background: rgba(54, 96, 255, 0.07);
    font-size: 0.85rem;
    line-height: 1.5;
}
.stu-mark-note { margin-top: 10px; font-size: 0.8rem; color: var(--text-muted); }

.stu-mark-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 16px;
    flex-wrap: wrap;
}
.stu-mark-marklabel { font-size: 0.82rem; font-weight: 700; color: var(--text-muted); }
.stu-mark-outof { font-size: 0.85rem; color: var(--text-muted); }
.stu-mark-full { margin-left: auto; font-size: 0.8rem; color: var(--accent); }

.stu-mark-done { padding: 44px 20px; text-align: center; }
.stu-mark-done-tick {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 54px;
    height: 54px;
    margin-bottom: 14px;
    border-radius: 50%;
    background: rgba(0, 199, 141, 0.15);
    color: #0d8a66;
}
.stu-mark-done-tick svg { width: 28px; height: 28px; }
.stu-mark-done-title { font-size: 1.2rem; font-weight: 800; }
.stu-mark-done-sub { margin: 6px 0 18px; color: var(--text-muted); font-size: 0.9rem; }

.result-q-award {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 8px;
}
/* Only the questions actually waiting on the teacher announce themselves;
   the override buttons on already-marked questions stay quiet. */
.result-q-award:not(.is-needed) { opacity: 0.45; }
.result-q-award:not(.is-needed):hover,
.result-q-award:not(.is-needed):focus-within { opacity: 1; }

.result-q-award-label {
    margin-right: 2px;
    font-size: 0.72rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: #b7791f;
}

.award-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 26px;
    border: 1px solid var(--border);
    border-radius: 7px;
    background: var(--card-bg);
    font-size: 0.85rem;
    line-height: 1;
    cursor: pointer;
    transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.award-btn:hover { border-color: var(--text-muted); }
.award-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.award-yes { color: #0d8a66; }
.award-no { color: var(--red-soft); }
.award-yes.is-on { background: rgba(0, 199, 141, 0.15); border-color: #0d8a66; }
.award-no.is-on { background: rgba(177, 70, 55, 0.12); border-color: var(--red-soft); }

.result-q-bymarker {
    display: block;
    margin-top: 4px;
    font-size: 0.72rem;
    font-weight: 700;
    color: var(--accent);
}

.result-score {
    flex: none;
    font-weight: 800;
    font-size: 0.86rem;
    font-variant-numeric: tabular-nums;
}
.result-score.is-partial { color: #b7791f; }
.result-score em { font-style: normal; font-weight: 700; font-size: 0.76rem; }

/* The teacher's own mark on an AI-marked long answer. */
.mk-final {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--border);
    flex-wrap: wrap;
}
.mk-final-label { font-size: 0.78rem; font-weight: 700; color: var(--text-muted); }
.mk-final-input {
    width: 62px;
    padding: 5px 8px;
    border: 1px solid var(--border);
    border-radius: 7px;
    background: var(--card-bg);
    color: var(--text);
    font: inherit;
    font-size: 0.85rem;
    text-align: center;
}
.mk-final-note { font-size: 0.74rem; color: var(--text-muted); }
.mk-final-note.is-set { color: var(--accent); font-weight: 700; }

/* ── Share dialog ────────────────────────────────────────────────────── */

#share-modal {
    position: fixed;
    inset: 0;
    z-index: 10000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
}
.share-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(30, 27, 46, 0.4);
    backdrop-filter: blur(5px);
}
.share-card {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 460px;
    max-height: 100%;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: 18px;
    background: var(--card-bg);
    box-shadow: 0 30px 80px rgba(30, 27, 46, 0.28), 0 4px 16px rgba(30, 27, 46, 0.12);
}
.share-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 20px 20px 0 24px;
}
.share-title { font-size: 1.02rem; font-weight: 800; letter-spacing: -0.015em; }
.share-close {
    width: 30px;
    height: 30px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 8px;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
}
.share-close:hover { background: rgba(30, 27, 46, 0.06); color: var(--text); }

.share-body { padding: 16px 24px 4px; }
.share-label {
    display: block;
    margin-bottom: 6px;
    font-size: 0.74rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
}

.share-linkrow { display: flex; gap: 8px; margin-bottom: 20px; }
.share-url {
    flex: 1;
    min-width: 0;
    padding: 9px 12px;
    border: 1px solid var(--border);
    border-radius: 9px;
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 0.85rem;
}
.share-url:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.share-who { border: 0; }

/* Whole-row radio targets: the label and its explanation are part of the
   thing you click, not text sitting beside a small circle. */
.share-opt {
    display: flex;
    gap: 11px;
    padding: 12px 13px;
    margin-bottom: 8px;
    border: 1px solid var(--border);
    border-radius: 12px;
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease;
}
.share-opt:hover { border-color: rgba(54, 96, 255, 0.4); }
/* .is-selected is set in JS rather than relying on :has(). The class picker
   below is shown by the same rule, so a browser without :has() would hide a
   control the dialog needs rather than merely style it differently. */
.share-opt.is-selected { border-color: var(--accent); background: rgba(54, 96, 255, 0.05); }
.share-opt:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
.share-opt input { margin-top: 3px; flex: none; accent-color: var(--accent); }
.share-opt-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.share-opt-title { font-size: 0.9rem; font-weight: 700; }
.share-opt-sub { font-size: 0.8rem; line-height: 1.45; color: var(--text-muted); }

/* The class picker and the warning only concern the option they sit in, so
   they stay out of the way until it is the one chosen. */
.share-class,
.share-warn { display: none; }
.share-opt.is-selected .share-class,
.share-opt.is-selected .share-warn { display: block; }

.share-class {
    width: 100%;
    margin-top: 8px;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: 9px;
    background: var(--card-bg);
    color: var(--text);
    font: inherit;
    font-size: 0.85rem;
}
.share-warn {
    margin-top: 8px;
    padding: 8px 10px;
    border-radius: 8px;
    background: rgba(244, 180, 71, 0.15);
    color: #8a5d12;
    font-size: 0.78rem;
    line-height: 1.45;
}

.share-empty {
    padding: 10px 13px;
    border-radius: 10px;
    background: var(--surface);
    color: var(--text-muted);
    font-size: 0.8rem;
    line-height: 1.45;
}

.share-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 24px 20px;
}
.share-status { font-size: 0.78rem; color: var(--text-muted); }

.stu-tabs {
    display: flex;
    gap: 4px;
    margin-bottom: 16px;
    border-bottom: 1px solid var(--border);
}
.stu-tab {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 9px 14px;
    border: 0;
    border-bottom: 2px solid transparent;
    background: none;
    font: inherit;
    font-size: 0.9rem;
    font-weight: 700;
    color: var(--text-muted);
    cursor: pointer;
    margin-bottom: -1px;
}
.stu-tab:hover { color: var(--text); }
.stu-tab.active { color: var(--accent); border-bottom-color: var(--accent); }
.stu-tab-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 999px;
    background: var(--red-soft);
    color: #fff;
    font-size: 0.68rem;
    font-weight: 800;
}

/* One submission. The typed name is shown even when it is assigned, because
   "Amelia Clarke (came in as 'a clarke')" is the thing that tells a teacher
   the link is right. */
.stu-sub {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    margin-bottom: 8px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card-bg);
    flex-wrap: wrap;
}
.stu-sub-main { flex: 1; min-width: 170px; }
.stu-sub-title {
    font-weight: 700;
    font-size: 0.9rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.stu-sub-meta { color: var(--text-muted); font-size: 0.78rem; margin-top: 2px; }
.stu-sub-typed { font-style: italic; }
.stu-sub-score { flex: none; font-weight: 800; font-variant-numeric: tabular-nums; }
.stu-sub-score.is-unscored { color: var(--text-muted); font-weight: 600; font-size: 0.8rem; }
.stu-sub-open {
    flex: none;
    padding: 4px 9px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: none;
    font: inherit;
    font-size: 0.75rem;
    font-weight: 700;
    color: var(--text-muted);
    cursor: pointer;
}
.stu-sub-open:hover { border-color: var(--accent); color: var(--accent); }

.stu-toolbar { display: flex; gap: 10px; margin-bottom: 14px; flex-wrap: wrap; }
.stu-search-wrap { position: relative; flex: 1; min-width: 180px; }
.stu-search-icon {
    position: absolute;
    left: 11px;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    color: var(--text-muted);
    pointer-events: none;
}
.stu-search-icon svg { width: 16px; height: 16px; }
.stu-search,
.stu-select {
    width: 100%;
    padding: 9px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--card-bg);
    color: var(--text);
    font: inherit;
    font-size: 0.88rem;
}
.stu-search { padding-left: 34px; }
.stu-select { width: auto; min-width: 150px; }
.stu-search:focus-visible,
.stu-select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.stu-group-head {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 18px 0 8px;
}
.stu-group-head:first-child { margin-top: 0; }
.stu-group-count { color: var(--text-muted); font-size: 0.78rem; font-weight: 700; }

/* A register, not a stack of cards.
 *
 * Each pupil used to be a separate bordered card with its own lift on hover,
 * which at 73px a row makes a class of thirty into two metres of scrolling and
 * turns a list into a pile. One surface with hairlines between rows reads as
 * the register it is, and fits a whole class on a screen. */
.stu-row {
    display: flex;
    align-items: center;
    gap: 14px;
    width: 100%;
    padding: 9px 12px;
    border: 0;
    border-bottom: 1px solid var(--border);
    border-radius: 0;
    background: none;
    text-align: left;
    font: inherit;
    color: inherit;
    cursor: pointer;
    transition: background-color 0.12s ease;
}

.stu-row:last-child { border-bottom: 0; }
.stu-row:hover {
    background: rgba(54, 96, 255, 0.05);
    transform: none;
}
.stu-row:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.stu-row-main { flex: 1; min-width: 0; }
.stu-row-name { font-weight: 700; font-size: 0.95rem; }
.stu-row-meta { color: var(--text-muted); font-size: 0.78rem; margin-top: 2px; }
.stu-row-score {
    flex: none;
    font-weight: 800;
    font-size: 0.95rem;
    font-variant-numeric: tabular-nums;
    /* Fixed width, right-aligned: a column of percentages is meant to be read
       down and compared, which it cannot be if each one starts wherever the
       name before it happened to end. */
    min-width: 5.5rem;
    text-align: right;
}
.stu-row-score.is-empty { color: var(--text-muted); font-weight: 600; font-size: 0.8rem; }
.stu-row-del {
    flex: none;
    display: inline-flex;
    padding: 6px;
    border: 0;
    border-radius: 8px;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
}
.stu-row-del:hover { background: rgba(177, 70, 55, 0.1); color: var(--red-soft); }

.stu-class-chip {
    display: inline-flex;
    align-items: center;
    padding: 2px 9px;
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--text-muted);
    font-size: 0.74rem;
    font-weight: 700;
}

/* Sparkline. Deliberately unlabelled and unaxised – it answers "which way is
   this going" at a glance, and the numbers behind it are one click away. */
.stu-spark { display: block; flex: none; }
.stu-spark-line { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.stu-spark-dot { fill: var(--accent); }
.stu-spark-area { fill: rgba(54, 96, 255, 0.1); }

.stu-stats { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 20px; }
.stu-stat {
    flex: 1;
    min-width: 120px;
    padding: 14px 16px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card-bg);
}
.stu-stat-value { font-size: 1.5rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.stu-stat-label { color: var(--text-muted); font-size: 0.76rem; font-weight: 700; margin-top: 2px; }
.stu-stat-value.dir-improving { color: #0d8a66; }
.stu-stat-value.dir-slipping { color: var(--red-soft); }

.stu-trend { margin-bottom: 22px; }
.stu-trend #student-profile-spark { margin-top: 8px; }
.stu-timeline-head { margin-bottom: 8px; }

/* ── Student notes ────────────────────────────────────── */
/* A plain box on the page rather than a card: it is the tutor's own writing,
   and boxing it like the stat tiles above would make it look like another
   thing the app worked out for them. */
.stu-notes { margin-bottom: 22px; }
.stu-notes-box {
    width: 100%;
    padding: 11px 13px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: #fff;
    font-family: inherit;
    font-size: 0.88rem;
    line-height: 1.55;
    color: var(--text);
    resize: vertical;
    outline: none;
}
.stu-notes-box:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(54, 96, 255, 0.12);
}
.stu-notes-box::placeholder { color: var(--text-muted); }

.stu-notes-foot {
    display: flex;
    align-items: baseline;
    gap: 12px;
    margin: 6px 0 0;
    font-size: 0.75rem;
    color: var(--text-muted);
}
.stu-notes-status { flex: 0 0 auto; min-width: 54px; }
/* Not a warning banner. It is a standing condition of the box, and a coloured
   panel over every pupil's notes would be shouting a rule at someone who has
   not broken it. */
.stu-notes-warn { margin-left: auto; text-align: right; line-height: 1.45; }

@media (max-width: 620px) {
    .stu-notes-foot { flex-direction: column; gap: 4px; }
    .stu-notes-warn { margin-left: 0; text-align: left; }
}

.stu-item {
    display: flex;
    align-items: center;
    gap: 14px;
    width: 100%;
    padding: 12px 14px;
    margin-bottom: 8px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card-bg);
    text-align: left;
    font: inherit;
    color: inherit;
    cursor: pointer;
    transition: border-color 0.15s ease;
}
.stu-item:hover { border-color: rgba(54, 96, 255, 0.4); }
.stu-item:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.stu-item-main { flex: 1; min-width: 0; }
.stu-item-title {
    font-weight: 700;
    font-size: 0.92rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.stu-item-meta { color: var(--text-muted); font-size: 0.78rem; margin-top: 2px; }
.stu-item-score { flex: none; font-weight: 800; font-variant-numeric: tabular-nums; }
.stu-item-score.is-unscored { color: var(--text-muted); font-weight: 600; font-size: 0.8rem; }

/* One unmatched name, with whatever the matcher thinks it might be. */
.stu-review-row {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px 0;
    border-bottom: 1px solid var(--border);
    flex-wrap: wrap;
}
.stu-review-row:last-child { border-bottom: 0; }
.stu-review-main { flex: 1; min-width: 160px; }
.stu-review-name { font-weight: 700; font-size: 0.92rem; }
.stu-review-meta { color: var(--text-muted); font-size: 0.78rem; margin-top: 2px; }
.stu-review-suggestion {
    font-size: 0.78rem;
    margin-top: 4px;
    color: #0d8a66;
}
.stu-review-actions { display: flex; gap: 6px; flex-wrap: wrap; }

/* Searchable student picker, used from a quiz or marking response to attach
   it to somebody. A popover rather than a <select>: a roster is hundreds of
   names long and a native dropdown cannot be typed into. */
.stu-picker {
    position: fixed;
    z-index: 1200;
    width: 260px;
    max-height: 300px;
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card-bg);
    box-shadow: var(--shadow-card);
    overflow: hidden;
}
.stu-picker-search {
    flex: none;
    margin: 8px;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 0.85rem;
}
.stu-picker-search:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.stu-picker-list { overflow-y: auto; padding: 0 6px 6px; }
.stu-picker-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    padding: 8px 10px;
    border: 0;
    border-radius: 8px;
    background: none;
    font: inherit;
    font-size: 0.85rem;
    color: var(--text);
    text-align: left;
    cursor: pointer;
}
.stu-picker-item:hover { background: var(--surface-2); }
.stu-picker-item[aria-selected="true"] { color: var(--accent); font-weight: 700; }
.stu-picker-item.is-clear { color: var(--text-muted); }
.stu-picker-empty { padding: 10px; color: var(--text-muted); font-size: 0.82rem; text-align: center; }

/* The link control on a quiz/marking response row. */
.stu-link-btn {
    flex: none;
    max-width: 150px;
    padding: 4px 9px;
    border: 1px dashed var(--border);
    border-radius: 999px;
    background: none;
    font: inherit;
    font-size: 0.75rem;
    font-weight: 700;
    color: var(--text-muted);
    cursor: pointer;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.stu-link-btn:hover { border-color: var(--accent); color: var(--accent); }
.stu-link-btn.is-linked {
    border-style: solid;
    border-color: rgba(54, 96, 255, 0.35);
    background: rgba(54, 96, 255, 0.08);
    color: var(--accent);
}
.slot-form-row > span:first-child { color: var(--text-muted); flex: 0 0 62px; }
.slot-form-row input[type="text"],
.slot-form-row select {
    flex: 1 1 auto;
    min-width: 0;
    padding: 4px 6px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--card-bg);
    color: var(--text);
    font-size: 13px;
}
.slot-form-count { display: flex; align-items: center; gap: 5px; font-size: 12px; color: var(--text-muted); }
.slot-form-count input { width: 46px; padding: 4px 6px; border: 1px solid var(--border); border-radius: 6px; }
.slot-form-check { display: flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--text); }
.slot-form-error { color: var(--red-soft); font-size: 11.5px; }
.slot-form-actions { display: flex; justify-content: flex-end; gap: 6px; margin-top: 2px; }

/* Save + manage modals */
.tmpl-field { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px; }
.tmpl-field > span { font-size: 12.5px; color: var(--text-muted); }
.tmpl-field-label { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.tmpl-suggest-btn { flex: 0 0 auto; padding: 2px 10px; font-size: 12px; }
.tmpl-field input, .tmpl-field textarea {
    padding: 7px 9px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    color: var(--text);
    font: inherit;
    font-size: 13.5px;
    resize: vertical;
}
.tmpl-slots { margin-bottom: 10px; }
.tmpl-slots-title { font-size: 12.5px; color: var(--text-muted); margin-bottom: 4px; }
.tmpl-slot-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 5px; }
.tmpl-slot-list li {
    font-size: 11.5px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 2px 9px;
}
.tmpl-slot-list li .tmpl-slot-type { color: var(--text-muted); }
.tmpl-slot-list li[data-kind="repeat"] { border-color: var(--purple); }
.tmpl-slot-none { color: var(--red-soft); font-size: 12.5px; }

/* ── Template library ──────────────────────────────────────────────────────
   The card itself does not scroll: the header and toolbar stay put and only
   the grid moves, so "New template" and the search box are always reachable
   however long the library gets. */
.tmpl-manager-card {
    max-width: 880px;
    max-height: min(88vh, 820px);
    overflow: hidden;
    gap: 14px;
    padding: 20px 22px 22px;
}
.tmpl-count {
    margin-left: 8px;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 1px 8px;
    vertical-align: 1px;
}

/* Library-wide toolbar: search on the left, the two things you can add on the
   right. Everything else that acts on one template lives on that card. */
.tmpl-manager-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
}
.tmpl-manager-bar-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.tmpl-search { position: relative; flex: 1 1 220px; max-width: 300px; min-width: 0; }
.tmpl-search-icon {
    position: absolute;
    left: 10px;
    top: 50%;
    transform: translateY(-50%);
    width: 15px;
    height: 15px;
    color: var(--text-muted);
    pointer-events: none;
}
.tmpl-search input {
    width: 100%;
    padding: 7px 10px 7px 31px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 13px;
}
.tmpl-search input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.tmpl-search input::-webkit-search-cancel-button { cursor: pointer; }

/* Quiet bordered button for the template UI. .btn-ghost grows a border only on
   hover, so rows of them jiggle and read as loose text until you touch them –
   these keep a constant box, which is what stops the toolbars looking mushed. */
.tmpl-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 6px 12px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1.3;
    cursor: pointer;
    white-space: nowrap;
    transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.tmpl-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.tmpl-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.tmpl-btn[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }

/* Commands that are two halves of one choice, joined so they read as one. */
.tmpl-btn-group { display: inline-flex; }
.tmpl-btn-group .tmpl-btn { border-radius: 0; }
.tmpl-btn-group .tmpl-btn:first-child { border-radius: 8px 0 0 8px; }
.tmpl-btn-group .tmpl-btn:last-child { border-radius: 0 8px 8px 0; margin-left: -1px; }
.tmpl-btn-group .tmpl-btn:hover { position: relative; z-index: 1; }
.tmpl-bar-sep { width: 1px; align-self: stretch; background: var(--border); margin: 2px 2px; }

.tmpl-import-panel {
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface-2);
    padding: 12px 14px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.tmpl-import-label { font-size: 12.5px; color: var(--text-muted); line-height: 1.45; }

.tmpl-note { font-size: 12.5px; color: var(--text-muted); line-height: 1.45; }
.tmpl-note-ok { color: var(--text); }

.tmpl-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(238px, 1fr));
    gap: 14px;
    align-content: start;
    overflow-y: auto;
    min-height: 0;      /* a flex child must be allowed to shrink to scroll */
    padding: 2px 4px 4px 2px;
    margin: -2px -4px -4px -2px;   /* claw back the room the focus rings need */
}
.tmpl-grid:empty { display: none; }

.tmpl-card {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: visible;   /* the card menu is fixed-positioned, but focus rings are not */
    background: var(--card-bg);
    display: flex;
    flex-direction: column;
    transition: border-color 0.14s, box-shadow 0.14s;
}
.tmpl-card:hover { border-color: rgba(54, 96, 255, 0.35); box-shadow: 0 6px 18px rgba(30, 27, 46, 0.07); }
.tmpl-card.is-menu-open { border-color: var(--accent); }

/* The slide is a fixed 960x540 box scaled down to fit, so the tile carries the
   same 16:9 ratio and there is never letterboxing to explain. The scale factor
   is written by fitTemplatePreviews() – it depends on the rendered column
   width, which CSS cannot divide by 960 for us. */
.tmpl-card-preview {
    aspect-ratio: 16 / 9;
    overflow: hidden;
    position: relative;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
    border-radius: calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0 0;
    /* Decorative: the name below says what this is, and 960px of slide text is
       illegible at this size anyway. */
    pointer-events: none;
}
.tmpl-card-preview .slide {
    transform-origin: top left;
    transform: scale(var(--tmpl-preview-scale, 0.229));
}

.tmpl-card-body {
    padding: 10px 12px 12px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    flex: 1 1 auto;
}
.tmpl-card-head { display: flex; align-items: flex-start; gap: 6px; }
.tmpl-card-name {
    font-weight: 700;
    font-size: 13.5px;
    line-height: 1.35;
    color: var(--text);
    flex: 1 1 auto;
    min-width: 0;
    /* Two lines then ellipsis: a long name must not push the card taller than
       its neighbours and break the grid's rhythm. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    overflow-wrap: anywhere;
}
.tmpl-card-desc {
    font-size: 12px;
    color: var(--text-muted);
    line-height: 1.45;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    overflow-wrap: anywhere;
}
.tmpl-card-desc.is-empty { font-style: italic; opacity: 0.8; }

/* Inline editors replace the name / description in place, so renaming never
   throws a browser prompt over the card you are looking at. */
.tmpl-inline-edit {
    width: 100%;
    padding: 5px 8px;
    border: 1px solid var(--accent);
    border-radius: 7px;
    background: var(--card-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 12.5px;
    line-height: 1.45;
    resize: vertical;
}
.tmpl-inline-hint { font-size: 11px; color: var(--text-muted); }

.tmpl-card-tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: auto; padding-top: 2px; }
.tmpl-tag {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--text-muted);
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 2px 8px;
    white-space: nowrap;
}
.tmpl-tag-theme { color: var(--purple); border-color: rgba(134, 87, 209, 0.3); background: rgba(134, 87, 209, 0.08); }
.tmpl-tag-pending { color: #9a7108; border-color: rgba(244, 180, 71, 0.5); background: rgba(244, 180, 71, 0.16); }
.tmpl-tag-shared { color: var(--accent); border-color: rgba(54, 96, 255, 0.3); background: rgba(54, 96, 255, 0.08); }
/* On the Marketplace is the strongest thing a template can say about itself,
   so it gets the one colour nothing else in this row uses. */
.tmpl-tag-listed { color: #0d8a66; border-color: rgba(0, 199, 141, 0.4); background: rgba(0, 199, 141, 0.12); }

/* One switch and one share control – the rest moved into the ⋯ menu, which is
   what unclumps the card. */
.tmpl-card-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 8px 12px;
    border-top: 1px solid var(--border);
    background: var(--surface-2);
    border-radius: 0 0 calc(var(--radius) - 1px) calc(var(--radius) - 1px);
}
.tmpl-ai-toggle {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--text-muted);
    cursor: pointer;
    min-width: 0;
}
/* Travel is the track width less the thumb and its two 3px insets – the
   t-toggle recipe animates the thumb by this variable. */
.tmpl-ai-toggle .toggle-switch { width: 32px; height: 19px; --toggle-travel: 13px; }
.tmpl-ai-toggle .toggle-thumb { width: 13px; height: 13px; top: 3px; left: 3px; }
.tmpl-ai-toggle .toggle-switch[data-on="true"] ~ .tmpl-ai-label { color: var(--text); }
.tmpl-ai-toggle .tmpl-ai-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The share code is the thing you came for, so it is the button: one click
   copies it. "Stop sharing" lives in the menu with the other rare commands. */
/* No top padding: the body above already ends in its own 12px, and the row
   needs the clearance underneath so the code does not sit on the footer rule. */
.tmpl-card-share {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 0 12px 10px;
}
.tmpl-card-taken { font-size: 11px; color: var(--text-muted); white-space: nowrap; }
.tmpl-share-code-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    max-width: 100%;
    padding: 4px 9px;
    border: 1px solid var(--border);
    border-radius: 7px;
    background: var(--card-bg);
    color: var(--text);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 11px;
    letter-spacing: 0.03em;
    cursor: pointer;
    transition: border-color 0.12s, color 0.12s;
}
.tmpl-share-code-btn:hover { border-color: var(--accent); color: var(--accent); }
.tmpl-share-code-btn svg { width: 12px; height: 12px; flex: 0 0 auto; }
/* Never truncated: half a share code is no use to the person you send it to. */
.tmpl-share-code-btn .tmpl-share-code-text { white-space: nowrap; }

.tmpl-card-menu-btn {
    flex: 0 0 auto;
    width: 26px;
    height: 26px;
    margin: -3px -4px 0 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid transparent;
    border-radius: 7px;
    background: none;
    color: var(--text-muted);
    font-family: inherit;
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.tmpl-card-menu-btn:hover,
.tmpl-card-menu-btn[aria-expanded="true"] {
    background: var(--surface-2);
    border-color: var(--border);
    color: var(--text);
}

/* Fixed, and a direct child of <body>: the grid scrolls with overflow, which
   would clip any menu positioned inside a card. */
.tmpl-menu {
    position: fixed;
    z-index: 10400;
    min-width: 184px;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 14px 34px rgba(30, 27, 46, 0.18);
    padding: 5px;
    display: flex;
    flex-direction: column;
}
.tmpl-menu-item {
    display: block;
    width: 100%;
    text-align: left;
    padding: 7px 10px;
    border: none;
    border-radius: 6px;
    background: none;
    color: var(--text);
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
}
.tmpl-menu-item:hover { background: var(--surface-2); }
.tmpl-menu-item.is-danger { color: var(--red-soft); }
.tmpl-menu-item.is-danger:hover { background: rgba(177, 70, 55, 0.1); }
.tmpl-menu-sep { height: 1px; background: var(--border); margin: 4px 6px; }

.tmpl-empty {
    color: var(--text-muted);
    font-size: 13px;
    padding: 26px 16px 30px;
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
}
.tmpl-empty-slim { padding: 22px 8px; }
.tmpl-empty-art { position: relative; width: 78px; height: 50px; margin-bottom: 2px; }
.tmpl-empty-slide {
    position: absolute;
    inset: 0;
    border: 1.5px dashed var(--border);
    border-radius: 8px;
    background: var(--surface-2);
}
.tmpl-empty-slide:first-child { transform: rotate(-7deg) translate(-5px, 3px); opacity: 0.65; }
.tmpl-empty-slide:last-child { transform: rotate(4deg); }
.tmpl-empty-title { font-size: 14.5px; font-weight: 700; color: var(--text); }
.tmpl-empty-body { max-width: 360px; line-height: 1.5; margin: 0; }
.tmpl-empty-acts { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; align-items: center; }
/* An <a> styled as .tmpl-btn needs the line-height a <button> gets for free. */
a.tmpl-btn { text-decoration: none; }

/* Options presented as rows rather than bare checkboxes, so the explanation
   under each one has somewhere to live. */
.tmpl-options { display: flex; flex-direction: column; gap: 8px; }
.tmpl-option {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    padding: 9px 11px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface-2);
    cursor: pointer;
    transition: border-color 0.12s;
}
.tmpl-option:hover { border-color: rgba(54, 96, 255, 0.35); }
.tmpl-option input { margin-top: 2px; flex: 0 0 auto; cursor: pointer; }
.tmpl-option-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.tmpl-option-title { font-size: 12.5px; font-weight: 600; color: var(--text); }
.tmpl-option-sub { font-size: 11.5px; color: var(--text-muted); line-height: 1.4; }

@media (max-width: 560px) {
    .tmpl-manager-card { padding: 16px 14px 18px; max-height: 92vh; }
    .tmpl-search { max-width: none; flex-basis: 100%; }
    .tmpl-manager-bar-actions { margin-left: 0; width: 100%; }
    .tmpl-manager-bar-actions .btn,
    .tmpl-manager-bar-actions .tmpl-btn { flex: 1 1 0; justify-content: center; }
    .tmpl-grid { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
    .tmpl-card, .tmpl-btn, .tmpl-option, .tmpl-card-menu-btn, .tmpl-share-code-btn { transition: none; }
}
.tmpl-chip-row { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 4px; }
.tmpl-chip {
    border: 1px solid var(--border);
    background: var(--surface-2);
    border-radius: 999px;
    padding: 4px 11px;
    font-size: 12.5px;
    cursor: pointer;
    color: var(--text);
}
.tmpl-chip:hover { border-color: var(--accent); color: var(--accent); }
.gen-template-hint { font-size: 11.5px; color: var(--text-muted); margin-top: 6px; }

/* ── Slide-layout picker ───────────────────────────────────────────────────
   Tiles render real slides through SlideRenderer at 1/4 scale, so what the
   teacher picks from is what the deck will actually look like. */
/* ── Scaffolded practice switch ────────────────────────────────────────────
   The four stages are drawn rather than described: the row is the explanation,
   so the switch beside it needs no sentence saying what it adds. */
.gen-support {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding: 10px 12px;
    border: 1.5px solid var(--border);
    border-radius: var(--radius);
    background: var(--card-bg);
}
.gen-support-steps {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: 0.84rem;
    font-weight: 600;
    color: var(--text);
    transition: opacity 0.15s;
}
.gen-support-steps li {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    border-radius: 999px;
    background: var(--surface-2);
    white-space: nowrap;
}
/* The arrow sits outside the pill it follows, pointing at the next one. */
.gen-support-steps li + li { margin-left: 16px; position: relative; }
.gen-support-steps li + li::before {
    content: '\2192';
    position: absolute;
    left: -17px;
    font-weight: 400;
    color: var(--text-muted);
}
.gen-support:has(input:not(:checked)) .gen-support-steps { opacity: 0.4; }

.gen-template-summary { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.gen-template-card { max-width: 860px; max-height: 86vh; overflow-y: auto; }
.gen-template-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 4px;
}
.gen-template-toolbar-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-left: auto;
}
.gen-template-hint { flex: 1 1 200px; min-width: 0; }
.gen-template-section {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin: 4px 0;
}
.gen-template-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 10px;
}
.gen-template-tile {
    border: 2px solid var(--border);
    border-radius: 10px;
    overflow: hidden;
    background: var(--card-bg);
    cursor: pointer;
    padding: 0;
    text-align: left;
    display: flex;
    flex-direction: column;
    transition: border-color 0.15s, box-shadow 0.15s;
}
.gen-template-tile:hover { border-color: var(--text-muted); }
.gen-template-tile.active { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(54, 96, 255, 0.14); }

.gen-template-preview {
    aspect-ratio: 16 / 9;
    overflow: hidden;
    position: relative;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
    /* The preview is decorative – the label underneath names the layout, and a
       960px slide's text is illegible at this size anyway. */
    pointer-events: none;
}
.gen-template-preview .slide {
    transform-origin: top left;
    transform: scale(var(--gen-preview-scale, 0.19));
}
.gen-template-tile-body { padding: 7px 9px 9px; display: flex; flex-direction: column; gap: 3px; }
.gen-template-tile-name { font-size: 12.5px; font-weight: 600; display: flex; align-items: center; gap: 5px; }
.gen-template-tile-blurb { font-size: 11px; color: var(--text-muted); line-height: 1.35; }
.gen-template-check {
    margin-left: auto;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: 1.5px solid var(--border);
    flex: 0 0 auto;
    position: relative;
}
.gen-template-tile.active .gen-template-check { border-color: var(--accent); background: var(--accent); }
/* Centred by transform rather than hardcoded offsets: the tick is rotated, so
   px offsets only line up for one exact circle size and drift the moment
   either changes. translate(-50%, -50%) puts its box on the circle's centre
   whatever the size; 46% is the optical correction, since a checkmark's visual
   weight sits below its geometric centre. */
.gen-template-tile.active .gen-template-check::after {
    content: '';
    position: absolute;
    left: 50%;
    top: 46%;
    width: 4px;
    height: 8px;
    border: solid #fff;
    border-width: 0 2px 2px 0;
    transform: translate(-50%, -50%) rotate(45deg);
}

.gen-template-cell { position: relative; }
.gen-template-del {
    position: absolute;
    top: 6px;
    right: 6px;
    width: 22px;
    height: 22px;
    line-height: 1;
    font-size: 16px;
    border-radius: 50%;
    border: 1px solid var(--border);
    background: var(--card-bg);
    color: var(--text-muted);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    /* Hidden until the tile is hovered or something in the cell has focus, so
       a grid of layouts doesn't read as a grid of delete buttons – but always
       reachable by keyboard. */
    opacity: 0;
    transition: opacity 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.gen-template-cell:hover .gen-template-del,
.gen-template-del:focus-visible { opacity: 1; }
.gen-template-del:hover { color: var(--red-soft); border-color: var(--red-soft); }
.gen-template-del:disabled { opacity: 0.4; cursor: default; }

@media (prefers-reduced-motion: reduce) {
    .gen-template-del { transition: none; }
}

/* ── Template designer ─────────────────────────────────────────────────────
   #stage-canvas and #ppt-direct-bar are moved in here while designing, so
   these rules style borrowed nodes rather than copies of them. */
.designer-card { max-width: 900px; max-height: 92vh; overflow-y: auto; gap: 12px; }
.designer-intro { font-size: 12.5px; color: var(--text-muted); margin: 0; line-height: 1.45; }
#designer-stage-host { width: 100%; }
#designer-toolbar-host:empty { display: none; }
.designer-slots {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

/* The borrowed toolbar carries buttons that only mean something inside a deck.
   Hidden rather than removed – they are the deck's own nodes and go back. */
#ppt-direct-bar.is-designing {
    flex-wrap: wrap;
    margin: 0;
}
/* The designer borrows this toolbar: slot marking is template work and means
   nothing on a deck slide. */
#ppt-direct-bar:not(.is-designing) [data-designer-only] {
    display: none;
}

/* The designer has no presentation, and uploads are stored against one
   (presentation_images is keyed by presentation_id). Unsplash search needs no
   deck, so that half of the row works here and this button does not. */
#template-designer-modal #ppt-upload-img-btn { display: none; }
#template-designer-modal #ppt-img-row {
    display: flex;
    gap: 6px;
    align-items: center;
    margin-top: 8px;
}
#template-designer-modal #ppt-img-row[hidden] { display: none; }
#template-designer-modal #ppt-img-row input {
    flex: 1 1 auto;
    min-width: 0;
    padding: 6px 9px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    color: var(--text);
    font-size: 13px;
}

/* The two steps. Step 1 is a page in its own right rather than a panel bolted
   above the canvas – describing is the way you make a template, not an
   alternative to the way you make one. */
.designer-step { display: flex; flex-direction: column; gap: 12px; }
.designer-step[hidden] { display: none; }

/* Step 1 is short, so the card should not open at full designer height and
   then collapse once the slide arrives. */
.designer-card:has(#designer-step-describe:not([hidden])) { max-width: 620px; }

#designer-describe-input {
    width: 100%;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    color: var(--text);
    font: inherit;
    font-size: 13px;
    line-height: 1.5;
    resize: vertical;
}
#designer-describe-input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Worked phrasings, not topics: the shape of the sentence is what a teacher
   staring at an empty box actually needs shown to them. */
.designer-describe-examples { display: flex; flex-wrap: wrap; gap: 6px; }
.designer-describe-example {
    border: 1px dashed var(--border);
    background: transparent;
    border-radius: 999px;
    padding: 3px 10px;
    font-family: inherit;
    font-size: 11.5px;
    color: var(--text-muted);
    cursor: pointer;
    text-align: left;
    transition: border-color 0.12s, color 0.12s;
}
.designer-describe-example:hover { border-color: var(--accent); color: var(--accent); }

.designer-describe-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 10px;
    flex-wrap: wrap;
}
.designer-describe-msg {
    font-size: 12px;
    color: var(--text-muted);
    margin-right: auto;
    line-height: 1.4;
}
.designer-describe-msg.is-error { color: var(--red-soft); }

/* Step 2's header line: what you asked for, and the way back to change it. */
.designer-recap {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    border: 1px solid var(--border);
    border-radius: 9px;
    background: var(--surface-2);
}
.designer-recap-text {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 12.5px;
    color: var(--text-muted);
    font-style: italic;
    /* One line: the description can run to 600 characters and this is a
       reminder of what you asked for, not the thing you are reading. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.designer-recap-actions { display: flex; gap: 6px; flex: 0 0 auto; }

/* The stage is tall enough to push Save off the bottom of the card, which is
   the one control the whole step exists to reach. The card is the scroller, so
   the action row sticks to its floor. */
#designer-step-adjust .add-slide-actions {
    position: sticky;
    bottom: -28px;          /* the card's own bottom padding */
    z-index: 2;
    background: var(--card-bg);
    border-top: 1px solid var(--border);
    margin: 0 -28px -28px;
    padding: 12px 28px 16px;
}

@media (prefers-reduced-motion: reduce) {
    .designer-describe-example { transition: none; }
}

/* ── Grouping ──────────────────────────────────────────────────────────────
   .slide__group and .slide__repeat are structural containers the teacher
   creates while designing. They carry no visual style of their own, so a
   grouped design looks identical to the ungrouped one it came from. The
   selection tint is editor chrome and lives here, never in slides.css, which
   is inlined into PDF and PPTX exports. */
.is-multi-selected {
    outline: 2px solid var(--accent) !important;
    outline-offset: 1px;
    background: rgba(54, 96, 255, 0.06);
}
.ppt-template-mode .slide__group { outline: 1px dotted var(--text-muted); outline-offset: 1px; }

/* ── Repeat area preview ───────────────────────────────────────────────────
   Ghost rows show where the AI's items will land and how many fit. They are
   editor chrome: getSlideHtml() strips them from every saved slide, every
   undo snapshot and every stored template, so these styles never ship. */
.slide__repeat-ghost {
    opacity: 0.3;
    filter: grayscale(0.5);
    pointer-events: none;
    outline: 1px dashed var(--accent);
    outline-offset: 2px;
}
/* The repeat container is the area, so make it read as one: a tinted region
   with a solid edge, distinct from the dashed outline a plain slot gets. */
.ppt-template-mode [data-slot-repeat] {
    outline: 2px solid var(--accent);
    outline-offset: 4px;
    background: rgba(54, 96, 255, 0.05);
    border-radius: 4px;
}
.slot-form-hint {
    font-size: 11.5px;
    color: var(--text-muted);
    line-height: 1.45;
    background: var(--surface-2);
    border-radius: 6px;
    padding: 6px 8px;
}

/* ── Template sharing ──────────────────────────────────────────────────── */
.tmpl-import-row { display: flex; gap: 8px; }
.tmpl-import-row input {
    flex: 1 1 auto;
    min-width: 0;
    padding: 7px 9px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    color: var(--text);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 13px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
.tmpl-import-row input::placeholder { text-transform: none; letter-spacing: normal; opacity: 0.7; }
/* The tile is no longer the grid item – the cell is – so it must fill it. */
.gen-template-tile { width: 100%; }
/* No hover on touch, so the delete control would never appear there. */
@media (hover: none) {
    .gen-template-del { opacity: 1; }
}
.slot-form-hint-warn {
    color: var(--red-soft);
    background: rgba(177, 70, 55, 0.08);
}
.designer-slot-actions { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
#designer-preview-topic {
    width: 210px;
    padding: 5px 8px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    color: var(--text);
    font-size: 12px;
}
#designer-preview.active { border-color: var(--accent); color: var(--accent); }
/* An open group – double-clicked into, so single clicks now reach its
   contents. Shown so "why is my click selecting a different thing" always has
   a visible answer. */
.slide__group.is-entered {
    outline: 1.5px dashed var(--text-muted);
    outline-offset: 3px;
}

/* ── Shared library (dashboard "Shared" tab) ───────────────
   The organisation's shelf. It reads as a grid of cards rather than as the
   plain rows either side of it, because what is on it was made by a person
   with a name and the card has to carry that name, the subject and who else
   has taken it. Deliberately monochrome: the app's colours mean something
   (accent = the thing you are on, green = done, amber = attention), and a
   palette handed out per item type would say nothing the icon does not. */

/* The count on the tab itself. Sized off the tab's own text so it tracks the
   accessibility text scale with everything else. */
.dash-tab-count {
    display: inline-block;
    margin-left: 7px;
    padding: 1px 7px;
    border-radius: 999px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    font-size: 0.72em;
    font-weight: 700;
    line-height: 1.5;
    color: var(--text-muted);
    vertical-align: 1px;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.dash-tab.active .dash-tab-count {
    background: rgba(54, 96, 255, 0.12);
    border-color: rgba(54, 96, 255, 0.22);
    color: var(--accent);
}

.olib-hero {
    margin-bottom: 14px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--border);
}
.olib-hero-title { font-size: 1.15rem; font-weight: 700; }
.olib-hero-sub {
    margin-top: 3px;
    max-width: 60ch;
    font-size: 0.84rem;
    line-height: 1.5;
    color: var(--text-muted);
}
.olib-count {
    margin-top: 6px;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-muted);
}

.olib-toolbar {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}
.olib-search {
    position: relative;
    flex: 1 1 240px;
    min-width: 0;
}
.olib-search-icon {
    position: absolute;
    left: 11px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-muted);
    pointer-events: none;
}
.olib-search-icon svg { width: 16px; height: 16px; display: block; }
#olib-search {
    width: 100%;
    padding: 9px 12px 9px 34px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card-bg);
    color: var(--text);
    font-size: 0.88rem;
}
#olib-search:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(54, 96, 255, 0.14);
}
.olib-kinds { flex: 0 0 auto; flex-wrap: wrap; }
.olib-kinds .mkt-mode:disabled {
    opacity: 0.45;
    cursor: default;
}
.olib-kind-count {
    margin-left: 6px;
    font-size: 0.76em;
    font-weight: 700;
    opacity: 0.65;
}

/* The staff row. Scrolls sideways rather than wrapping into a block: it is a
   secondary filter and should never push the shelf itself off the screen. */
.olib-people {
    display: flex;
    gap: 8px;
    margin-bottom: 16px;
    padding-bottom: 4px;
    overflow-x: auto;
    scrollbar-width: thin;
}
.olib-person {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    flex: 0 0 auto;
    padding: 5px 12px 5px 6px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card-bg);
    color: var(--text-muted);
    font-size: 0.82rem;
    font-weight: 600;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.olib-person[data-person="all"] { padding-left: 12px; }
.olib-person:hover { color: var(--text); border-color: var(--text-muted); }
.olib-person.active {
    border-color: var(--accent);
    color: var(--accent);
    background: rgba(54, 96, 255, 0.07);
}
.olib-person-count {
    padding: 0 6px;
    border-radius: 999px;
    background: var(--surface-2);
    font-size: 0.74em;
    font-weight: 700;
}

.olib-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(268px, 1fr));
    gap: 14px;
}

.olib-card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 14px 15px 13px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--card-bg);
    overflow: hidden;
    transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s;
    animation: itemIn 0.32s cubic-bezier(0.22, 0.8, 0.36, 1) both;
}
.olib-card:nth-child(1) { animation-delay: 0.02s; }
.olib-card:nth-child(2) { animation-delay: 0.05s; }
.olib-card:nth-child(3) { animation-delay: 0.08s; }
.olib-card:nth-child(4) { animation-delay: 0.11s; }
.olib-card:nth-child(5) { animation-delay: 0.14s; }
.olib-card:nth-child(n+6) { animation-delay: 0.17s; }
.olib-card:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-card);
    border-color: var(--accent);
}

.olib-card-top {
    display: flex;
    align-items: center;
    gap: 8px;
}
.olib-card-icon {
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    border-radius: 8px;
    background: var(--surface-2);
    color: var(--text-muted);
}
.olib-card:hover .olib-card-icon { color: var(--text); }
.olib-card-icon svg { width: 16px; height: 16px; }
/* Gives way first: on a narrow card the flag and the remove control are worth
   more than the last letters of "PRESENTATION". */
.olib-card-kind {
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--text-muted);
}
.olib-flag {
    flex: 0 0 auto;
    white-space: nowrap;
    padding: 1px 8px;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: var(--surface-2);
    color: var(--text-muted);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.02em;
}
/* The one flag that keeps a colour, and it keeps the app's existing one for
   "this is done" – it is answering "have I already got this?". */
.olib-flag-have {
    border-color: rgba(0, 199, 141, 0.3);
    background: rgba(0, 199, 141, 0.12);
    color: #0c7a5b;
}
/* Inherits .pres-delete-btn, which is invisible until its own row is hovered –
   this card is not that row, so it needs its own reveal, and a keyboard reveal
   too since the control is otherwise unreachable without a mouse. */
.olib-card .olib-remove { margin-left: auto; padding: 3px 7px; }
.olib-card:hover .olib-remove,
.olib-card:focus-within .olib-remove,
.olib-card .olib-remove:focus-visible { opacity: 1; }

.olib-card-title {
    font-size: 0.98rem;
    font-weight: 700;
    line-height: 1.35;
    /* Two lines, then an ellipsis: a title long enough to need a third is a
       paragraph, and the grid's rows would step to match the worst of them. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.olib-card-facets {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
}
.olib-facet {
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--surface-2);
    font-size: 0.73rem;
    font-weight: 600;
    color: var(--text-muted);
}

.olib-card-foot {
    display: flex;
    align-items: center;
    gap: 9px;
    margin-top: auto;
    padding-top: 11px;
    border-top: 1px solid var(--border);
}
.olib-card-by {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1 1 auto;
}
.olib-card-who {
    font-size: 0.8rem;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Wraps rather than clipping: "taken by four colleagues" is the line that
   makes a shelf worth reading, and a card narrow enough to cut it in half
   would cut off exactly the number. */
.olib-card-uses {
    font-size: 0.72rem;
    line-height: 1.35;
    color: var(--text-muted);
}
.olib-card-foot .btn { flex: 0 0 auto; }

/* Initials, so a shelf of thirty items can be scanned by who put them there.
   One ink for everybody: a colour per colleague would be decoration standing
   in for the name that is already written beside it. */
.olib-avatar {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    font-size: 0.72rem;
    font-weight: 800;
    letter-spacing: 0.02em;
    background: var(--surface-2);
    border: 1px solid var(--border);
    color: var(--text-muted);
}
/* Your own name, wherever it appears, is the one that is filled in – the
   distinction that actually earns a colour is mine versus everyone else's. */
.olib-avatar-me {
    background: rgba(54, 96, 255, 0.1);
    border-color: rgba(54, 96, 255, 0.22);
    color: var(--accent-dark);
}
.olib-avatar-sm { width: 22px; height: 22px; font-size: 0.62rem; }

.olib-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 32px 24px;
    border: 1px dashed var(--border);
    border-radius: 14px;
    background: var(--surface-2);
    text-align: center;
}
.olib-empty-title { font-size: 0.98rem; font-weight: 700; }
.olib-empty-sub {
    max-width: 46ch;
    font-size: 0.86rem;
    line-height: 1.55;
    color: var(--text-muted);
}
.olib-empty-cta { margin-top: 6px; }

@media (max-width: 640px) {
    .olib-hero { padding: 16px; }
    .olib-grid { grid-template-columns: 1fr; }
}
/* Hover never arrives on a touch screen, so the reveal above would hide the
   remove control on a tablet entirely. */
@media (hover: none) {
    .olib-card .olib-remove { opacity: 1; }
}

/* ── Booklets ────────────────────────────────────────────────
   The composer's length/extras step, the plan-approval list and the reader.
   The printed document itself is styled by booklet-print.css, which the reader
   does NOT load – what the reader shows is the same renderer's HTML restyled
   for a screen, so the two never fight over the same class names. */

/* Length ---------------------------------------------------- */

/* These tiles are buttons, not the label+radio the other .ws-tiles are, so
   none of the :has(input:checked) styling reaches them and the selected state
   is written out here against .is-selected. Everything that says "this one is
   chosen" is stated more than once – the tick, the border, the fill and the
   heavier name – so it survives being looked at quickly, on a projector, or
   by someone who cannot separate the tint from the plain background. */
.bk-length-tiles {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 10px;
    margin-top: 4px;
}
@media (max-width: 720px) { .bk-length-tiles { grid-template-columns: 1fr; } }

.bk-length-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 3px;
    /* Room at the right for the tick, which is absolutely positioned so that
       adding it does not reflow the text under it. */
    padding: 14px 40px 14px 14px;
    border: 1.5px solid var(--border);
    border-radius: 12px;
    background: var(--card-bg);
    text-align: left;
    cursor: pointer;
    /* A <button> does not inherit the page font – it takes the browser's own
       UI font unless told otherwise. Every other .ws-tile is a <label>, which
       does inherit, which is why the rest of the composer never needed this. */
    font-family: inherit;
    transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}
.bk-length-tile:hover { border-color: var(--accent); }
.bk-length-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.bk-length-tile.is-selected {
    border-color: var(--accent);
    background: rgba(54, 96, 255, 0.08);
    box-shadow: 0 4px 14px rgba(54, 96, 255, 0.13), inset 0 0 0 1px rgba(54, 96, 255, 0.25);
}

.bk-length-name {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.98rem;
    font-weight: 650;
    line-height: 1.2;
    color: var(--text);
}
.bk-length-tile.is-selected .bk-length-name { font-weight: 750; color: var(--accent-dark); }

.bk-length-count {
    font-size: 0.78rem;
    font-weight: 650;
    letter-spacing: 0.01em;
    color: var(--accent);
}
.bk-length-blurb {
    font-size: 0.82rem;
    font-weight: 400;
    line-height: 1.35;
    color: var(--text-muted, #59607a);
}

/* The tick is the unambiguous half of the selected state, so it scales in
   rather than appearing – and holds still where motion is unwelcome. */
.bk-length-mark {
    position: absolute;
    top: 12px;
    right: 12px;
    display: grid;
    place-items: center;
    width: 20px;
    height: 20px;
    border-radius: 999px;
    background: var(--accent);
    color: #fff;
    opacity: 0;
    transform: scale(0.6);
    transition: opacity 0.15s, transform 0.15s cubic-bezier(0.2, 1.4, 0.5, 1);
}
.bk-length-mark svg { width: 12px; height: 12px; }
.bk-length-tile.is-selected .bk-length-mark { opacity: 1; transform: scale(1); }
@media (prefers-reduced-motion: reduce) {
    .bk-length-tile, .bk-length-mark { transition: none; }
}

/* Locked options stay legible – they are what the teacher is being sold, so
   dimming them to unreadability would defeat the point. */
.bk-length-tile.is-locked { border-style: dashed; }
.bk-length-tile.is-locked .bk-length-count,
.bk-length-tile.is-locked .bk-length-blurb { opacity: 0.75; }

.bk-pro {
    font-size: 0.62rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: #fff;
    background: var(--accent);
    border-radius: 999px;
    padding: 2px 6px;
    line-height: 1.2;
}

/* Include (workbook / diagrams) ------------------------------ */
/* Cards of the same build as the length tiles above them, so the one step
   reads as one control rather than as two borrowed ones. A square tick rather
   than the length tiles' round one, because these are independent choices and
   the shape is the only thing that says so before you click. */

.bk-extras {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
    margin-top: 4px;
}
@media (max-width: 720px) { .bk-extras { grid-template-columns: 1fr; } }

.bk-extra {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    padding: 14px 40px 14px 14px;
    border: 1.5px solid var(--border);
    border-radius: 12px;
    background: var(--card-bg);
    cursor: pointer;
    user-select: none;
    transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}
.bk-extra:hover { border-color: var(--accent); }

/* The native input keeps state and keyboard focus; the drawn tick is what is
   actually seen, so the input only has to be reachable, not visible. */
.bk-extra input[type="checkbox"] {
    position: absolute;
    width: 18px;
    height: 18px;
    top: 12px;
    right: 12px;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}
.bk-extra:has(input:checked) {
    border-color: var(--accent);
    background: rgba(54, 96, 255, 0.08);
    box-shadow: 0 4px 14px rgba(54, 96, 255, 0.13), inset 0 0 0 1px rgba(54, 96, 255, 0.25);
}
.bk-extra:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

.bk-extra-check {
    position: absolute;
    top: 12px;
    right: 12px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    border-radius: 5px;
    background: transparent;
    box-shadow: inset 0 0 0 1.5px var(--border);
    transition: background 0.15s, box-shadow 0.15s;
}
.bk-extra-check svg {
    width: 10px;
    height: 10px;
    fill: none;
    stroke: #fff;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
    opacity: 0;
    transform: scale(0.6);
    transition: opacity 0.15s, transform 0.15s cubic-bezier(0.2, 1.4, 0.5, 1);
}
.bk-extra:has(input:checked) .bk-extra-check {
    background: var(--accent);
    box-shadow: inset 0 0 0 1.5px var(--accent);
}
.bk-extra:has(input:checked) .bk-extra-check svg { opacity: 1; transform: scale(1); }
@media (prefers-reduced-motion: reduce) {
    .bk-extra, .bk-extra-check, .bk-extra-check svg { transition: none; }
}

.bk-extra-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    flex: none;
    border-radius: 9px;
    background: var(--tone-bg);
    color: var(--tone);
}
.bk-extra-icon svg { width: 17px; height: 17px; }

/* Spaced with margins rather than a flex gap: the name belongs to the icon
   above it more closely than the description belongs to the name. */
.bk-extra-icon { margin-bottom: 9px; }
.bk-extra-name { margin-bottom: 3px; }

.bk-extra-name { font-size: 0.98rem; font-weight: 650; line-height: 1.2; color: var(--text); }
.bk-extra:has(input:checked) .bk-extra-name { font-weight: 750; color: var(--accent-dark); }

.bk-extra-desc { font-size: 0.82rem; line-height: 1.35; color: var(--text-muted, #59607a); }

/* Not yet available: greyed out, and grey all the way through. The card is
   the only place on this step with no colour in it, which is what separates
   "not available" from the tinted card beside it that means "chosen" – a
   coloured icon badge was enough on its own to make this one read as selected.
   The tick box goes entirely: an empty checkbox reads as "off, but you could
   turn it on", and this is not a choice being offered. */
.bk-extra.is-soon {
    cursor: default;
    border-color: rgba(221, 213, 200, 0.6);
    background: var(--surface-2);
}
.bk-extra.is-soon:hover { border-color: rgba(221, 213, 200, 0.6); }
.bk-extra.is-soon .bk-extra-check { display: none; }

/* Overriding the tone variables rather than desaturating with a filter, so the
   badge is a real grey in the palette instead of a washed-out teal. */
.bk-extra.is-soon .bk-extra-icon {
    --tone: var(--text-muted);
    --tone-bg: rgba(30, 27, 46, 0.06);
    opacity: 0.75;
}
.bk-extra.is-soon .bk-extra-name { color: var(--text-muted); font-weight: 600; }
.bk-extra.is-soon .bk-extra-desc { opacity: 0.8; }

.bk-soon {
    font-size: 0.62rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-muted);
    background: rgba(30, 27, 46, 0.07);
    border-radius: 999px;
    padding: 2px 6px;
    line-height: 1.2;
}

/* A ghost button until hovered, so the destructive action in the reader's
   toolbar does not compete with Print / Download beside it. */
.bk-delete-btn:hover:not(:disabled) {
    background: #ffebee;
    border-color: #f3c3c0;
    color: #c62828;
}

/* Generating banner ----------------------------------------- */
/* The card itself is .gen-progress, shared with the presentation generator.
   Only what a booklet has and a presentation does not is written here: a
   second line of text, a count, and a determinate bar. */

.bk-generating {
    position: relative;
    overflow: hidden;
    margin: 0 0 18px;
}

.bk-gen-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.bk-gen-sub { font-size: 0.9rem; font-weight: 500; color: var(--text); }
.bk-gen-note { font-size: 0.78rem; color: var(--text-muted); }

/* Only the working line pulses. Pulsing the reassurance underneath it too
   would make the whole card breathe, which is the thing that reads as noise
   rather than as progress. */
.bk-gen-sub { animation: statusPulse 2s ease-in-out infinite; }

/* Tabular figures so the count does not jitter sideways as it counts up. */
.bk-gen-count {
    flex-shrink: 0;
    font-size: 0.9rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
}

/* Sits where #progress-bar's indeterminate sweep does, so the two cards read
   as the same object doing the same job. */
.bk-gen-bar {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 3px;
    background: rgba(30, 27, 46, 0.07);
}
.bk-gen-bar-fill {
    display: block;
    width: 0;
    height: 100%;
    border-radius: 0 2px 2px 0;
    background: var(--accent);
    transition: width 0.4s ease;
}

/* Finished: the pulse stops and the bar turns green, so the card reads as an
   outcome rather than as work still in progress. */
.bk-generating.is-done .bk-gen-sub { animation: none; font-weight: 600; }
.bk-generating.is-done .bk-gen-bar-fill { background: var(--green); }

@media (prefers-reduced-motion: reduce) {
    .bk-gen-bar-fill { transition: none; }
    .bk-gen-sub { animation: none; }
}

/* Plan approval --------------------------------------------- */

/* Set as the contents page it will become, on a sheet of its own: chapters as
   headings, sections numbered beneath them. Every heading is editable, but it
   reads as text until it is touched, so the page looks like a plan to approve
   rather than a form to fill in. */
.bk-plan-list {
    list-style: none;
    width: 100%;
    max-width: 680px;
    margin: 18px auto 22px;
    padding: 10px 26px 18px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--card-bg);
    box-shadow: var(--shadow);
    counter-reset: bk-chap bk-sec;
}
.bk-plan-list:empty { display: none; }

.bk-plan-chap {
    margin-top: 18px;
    padding: 0 0 6px;
    border-bottom: 1px solid var(--border);
    font-size: 0.8rem;
    font-weight: 800;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--accent-dark);
    counter-increment: bk-chap;
    /* counter-set, not counter-reset: a reset here opens a new scope that the
       sibling rows after it do not read, so the numbers ran on (2.5, 3.8). */
    counter-set: bk-sec 0;
}
.bk-plan-chap::before { content: counter(bk-chap) "\00a0\00a0"; color: var(--text-muted); }
/* Sections the planner filed under no chapter: no heading, plain numbers. */
.bk-plan-chap[data-untitled] { margin: 0; padding: 0; border: 0; height: 0; overflow: hidden; }

.bk-plan-row {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 1px 0;
    counter-increment: bk-sec;
}
.bk-plan-row::before {
    content: counter(bk-chap) "." counter(bk-sec);
    flex: none;
    width: 34px;
    font-size: 0.84rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
}
/* Read by approval, shown by the chapter heading above. */
.bk-plan-chapter { display: none; }

.bk-plan-list .bk-plan-heading {
    flex: 1 1 auto;
    min-width: 0;
    padding: 8px 10px;
    border: 1px solid transparent;
    border-radius: 8px;
    background: transparent;
    box-shadow: none;
    color: var(--text);
    font: inherit;
    font-size: 0.98rem;
    line-height: 1.4;
    min-height: 0;
    resize: none;
    overflow: hidden;
    /* Grows to fit its text, so a wrapped heading shows whole. */
    field-sizing: content;
    transition: border-color 0.15s, background-color 0.15s;
}
.bk-plan-list .bk-plan-heading:hover { background: var(--surface); border-color: var(--border); }
.bk-plan-list .bk-plan-heading:focus {
    outline: none;
    background: var(--card-bg);
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(54, 96, 255, 0.12);
}

.bk-plan-drop {
    flex: none;
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--text-muted);
    opacity: 0.45;
    cursor: pointer;
    transition: opacity 0.15s, color 0.15s, background-color 0.15s;
}
.bk-plan-drop svg { width: 15px; height: 15px; }
.bk-plan-row:hover .bk-plan-drop, .bk-plan-drop:focus-visible { opacity: 1; }
.bk-plan-drop:hover { color: var(--red-soft); background: rgba(177, 70, 55, 0.1); }
.bk-plan-drop:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.bk-plan-loading { padding: 14px 0 10px; font-size: 0.92rem; color: var(--text-muted); text-align: center; }
.bk-plan-skel {
    height: 14px;
    margin: 14px 0;
    border-radius: 7px;
    background: linear-gradient(90deg, var(--surface-2) 25%, var(--border) 50%, var(--surface-2) 75%);
    background-size: 200% 100%;
    animation: bk-plan-shimmer 1.4s linear infinite;
}
.bk-plan-skel:nth-child(odd) { width: 72%; }
.bk-plan-skel:nth-child(even) { width: 88%; }
@keyframes bk-plan-shimmer { to { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) { .bk-plan-skel { animation: none; } }
@media (max-width: 560px) {
    .bk-plan-list { padding: 6px 14px 14px; }
    .bk-plan-drop { opacity: 1; }
}

/* Reader ------------------------------------------------------
   The same renderer's markup as the printed guide, restyled for a screen. The
   printed idiom carries over deliberately — banner headings, numbered points,
   underlined key terms — because a teacher checking a booklet on screen is
   checking what will come out of the printer. What changes is only what has
   to: the page becomes a stack of cards, and the margin callouts come back
   into the flow because a screen column has no margin to float into. */

.bk-reader-body { max-width: 780px; margin: 0 auto; padding: 8px 0 60px; }

.bk-r-section {
    background: #fffdf9;
    border: 1px solid #e0d8c8;
    border-radius: var(--radius);
    padding: 4px 26px 22px;
    margin-bottom: 14px;
    box-shadow: var(--shadow-card);
    font-family: Georgia, 'Times New Roman', serif;
    color: #1a1a1a;
    line-height: 1.55;
}
.bk-r-section.is-failed { border-color: #e8b0ab; background: #fdeceb; }

/* The banner, as on paper — full card width, which is why it pulls out past
   the card's own padding. */
.bk-r-heading {
    font-family: Georgia, 'Palatino Linotype', serif;
    font-style: italic;
    font-weight: 700;
    font-size: 1.05rem;
    background: #fbe6cf;
    border-top: 1px solid #d9a86a;
    border-bottom: 1px solid #d9a86a;
    padding: 6px 14px;
    margin: 16px -26px 10px;
}

/* Marks a section still being written. Only ever on a skeleton: when the real
   section lands, upsertBookletSection replaces the whole heading, so there is
   no state to clear.
   These rules went missing once already — they were written immediately before
   the "Plan approval" comment, and a later rewrite of the generating banner
   replaced everything up to that comment and took them with it. Unstyled, the
   tag is bare inline text hard against the heading's last word: "…plant and
   animal cellsWriting…". Keep them here, inside the reader's own section. */
.bk-r-writing {
    display: inline-block;
    margin-left: 10px;
    vertical-align: middle;
    padding: 2px 8px;
    border-radius: 999px;
    background: rgba(54, 96, 255, 0.12);
    color: var(--accent-dark);
    /* The heading is Georgia italic; the tag is UI furniture, not part of the
       printed document, so it takes the interface font. */
    font-family: "Nunito", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    font-style: normal;
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    animation: statusPulse 1.6s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .bk-r-writing { animation: none; } }

.bk-r-tools { display: flex; justify-content: flex-end; gap: 4px; margin: 8px 0 -8px; }
/* Editing and rewriting are both for when something reads wrong, which is not
   most of the time — they stay out of the way until the section is hovered or
   the button itself is focused. */
.bk-r-tools .bk-r-regen,
.bk-r-tools .bk-r-edit { opacity: 0; transition: opacity var(--duration-fast) var(--ease-smooth-out); }
.bk-r-section:hover .bk-r-regen,
.bk-r-section:hover .bk-r-edit,
.bk-r-tools .bk-r-regen:focus-visible,
.bk-r-tools .bk-r-edit:focus-visible { opacity: 1; }
/* While a form is open the section has its own Save and Cancel. Edit would do
   nothing, and Rewrite would throw away what is being typed. */
.bk-r-section.is-editing .bk-r-tools { display: none; }

/* ── The workbook, on screen ──────────────────────────── */
/* The printed workbook's classes translated out of millimetres. Same shapes as
   booklet-print.css so the two documents read alike, with one deliberate
   difference: the ruled answer lines are drawn much fainter here. On paper
   they are where a pupil writes; on screen nobody writes on them, and at print
   weight a stack of eight rules is the loudest thing in the section. */
.bk-r-rendered .bk-tier { margin-bottom: 18px; }
.bk-r-rendered .bk-tier h4 {
    font-family: 'Trebuchet MS', Arial, sans-serif;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: #6b5b45;
    padding-bottom: 4px;
    margin-bottom: 10px;
    border-bottom: 1px solid var(--border);
}
.bk-r-rendered .bk-tier-exam_style h4 { color: #1f5fa8; border-bottom-color: #b9cde6; }

.bk-r-rendered .bk-ex { margin-bottom: 14px; }
.bk-r-rendered .bk-ex-q { margin-bottom: 5px; }
/* The number sits in its own column so a question that wraps lines up under
   itself rather than under the number. A gutter with the number placed in it,
   not a flex row: in a flex row every run of text and every KaTeX formula
   becomes its own flex item, so a question with maths in it was laid out as
   side-by-side columns split at each formula. */
.bk-r-rendered .bk-ex-q,
.bk-r-rendered .bk-part-q { position: relative; padding-left: 28px; }
.bk-r-rendered .bk-ex-n,
.bk-r-rendered .bk-part-letter {
    position: absolute;
    left: 0;
    font-weight: 700;
    color: #1f5fa8;
}

.bk-r-rendered .bk-part { margin: 8px 0 0 22px; }
.bk-r-rendered .bk-part-q { margin-bottom: 4px; }
.bk-r-rendered .bk-part-letter { color: var(--text-muted); }

.bk-r-rendered .bk-marks {
    font-family: 'Trebuchet MS', Arial, sans-serif;
    font-size: 0.72rem;
    color: var(--text-muted);
    white-space: nowrap;
}

.bk-r-rendered .bk-lines { margin: 6px 0 0 22px; }
.bk-r-rendered .bk-line {
    height: 20px;
    border-bottom: 1px solid rgba(30, 27, 46, 0.09);
}

/* ── Reader: guide / workbook toggle ──────────────── */
/* The toggle itself is .bkp-kind-toggle, shared with the preview modal – same
   choice between the same two documents, so not a second control that looks
   nearly but not quite like the first. Only the grouping is new: Back and the
   toggle travel together on the leading edge, so the toolbar reads as two
   zones rather than leaving a third child adrift in a space-between row. */
.bk-reader-nav { display: flex; align-items: center; gap: 14px; min-width: 0; }
/* The one thing this instance needs that the modal's does not. .bkp-kind-toggle
   is a cream well, which reads as a well against the modal’s white card and
   vanishes against the cream page – leaving Guide as a pill floating next to
   loose text. A hairline gives the group an edge to be a group inside. */
.bk-view-toggle { border: 1px solid var(--border); }
/* Five controls will not sit on one line on a phone, and a nowrap row answers
   that by wrapping the labels instead – “Set as / lesson” over two lines. The
   two zones break apart instead, which is the seam that was already there. */
@media (max-width: 720px) {
    #bk-reader .ws-preview-toolbar { flex-wrap: wrap; gap: 10px 12px; }
    #bk-reader .bk-reader-nav { flex: 1 1 100%; }
}

.bk-r-empty {
    margin: 0;
    padding: 18px 0;
    font-size: 0.9rem;
    color: var(--text-muted);
}

/* ── Reader: editing a section by hand ────────────────── */
/* The form replaces the rendered section in place rather than opening over it:
   a teacher edits a section because of something they just read in it, and a
   modal would cover the thing that sent them here.

   Everything here is tuned for density. A section is up to 24 blocks, so the
   difference between a comfortable row and a roomy one is the difference
   between reading a section and scrolling through one. */
.bk-r-section.is-editing { background: var(--surface); }

.bk-ed { display: flex; flex-direction: column; gap: 10px; padding-top: 2px; }
.bk-ed-items { display: flex; flex-direction: column; gap: 8px; counter-reset: bk-ed-item; }

.bk-ed-item {
    counter-increment: bk-ed-item;
    display: flex;
    flex-direction: column;
    gap: 7px;
    padding: 9px 11px 11px;
    border: 1px solid var(--border);
    border-radius: 9px;
    background: var(--card-bg);
}
.bk-ed-item:focus-within { border-color: #c8d3f2; }

.bk-ed-head { display: flex; align-items: center; gap: 8px; min-height: 24px; }
/* Numbered by counter so adding, removing and reordering keep it right without
   a re-render — and so "Block 3 is missing something" points at something the
   teacher can actually see. */
.bk-ed-num::before {
    content: counter(bk-ed-item);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 19px;
    height: 19px;
    padding: 0 5px;
    border-radius: 6px;
    background: rgba(30, 27, 46, 0.06);
    font-size: 0.7rem;
    font-weight: 800;
    color: var(--text-muted);
}
.bk-ed-kind {
    font-size: 0.7rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--text-muted);
}

/* The controls are always present but quiet, and come up to full strength when
   the block is touched. Three dark buttons on every one of 24 blocks is a
   toolbar competing with the writing. */
.bk-ed-acts { display: inline-flex; gap: 2px; margin-left: auto; opacity: 0.5; transition: opacity 0.15s; }
.bk-ed-item:hover .bk-ed-acts,
.bk-ed-item:focus-within .bk-ed-acts { opacity: 1; }

.bk-ed-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 0;
    border: 0;
    border-radius: 6px;
    background: transparent;
    font: inherit;
    font-size: 0.82rem;
    line-height: 1;
    color: var(--text-muted);
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}
.bk-ed-btn:hover { background: rgba(30, 27, 46, 0.07); color: var(--text); }
.bk-ed-del:hover { background: rgba(177, 70, 55, 0.1); color: var(--red-soft); }
.bk-ed-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.bk-ed-f { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
/* A drawn diagram's controls: its menus in a row, the picture under them. */
.bk-ed-curve-controls { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: flex-end; }
.bk-ed-curve-preview { flex: 1 0 100%; max-width: 380px; }
.bk-ed-curve-preview svg { display: block; width: 100%; height: auto; background: #fff; border-radius: 6px; }
.bk-ed-f > span {
    display: flex;
    align-items: baseline;
    gap: 7px;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.01em;
    color: var(--text-muted);
}
/* The hint is part of the label, not a line under the box: how a list is
   separated has to be read before it is typed, not after. */
.bk-ed-f > span small { font-weight: 400; color: var(--text-muted); opacity: 0.85; }

.bk-ed-f input,
.bk-ed-f textarea,
.bk-ed-f select,
.bk-ed-tier select,
/* ── The picture panel in the section editor ─────────────

   Sits between the blocks and the "add a block" row, because that is where it
   sits in the printed page: after the writing, before whatever comes next. It
   is a panel rather than another .bk-ed-item on purpose — a picture is not a
   block a teacher can reorder or delete, it is an attribute of the section, and
   dressing it as a block would invite both. */
.bk-ed-image {
    margin-top: 12px;
    padding: 12px;
    border: 1px solid var(--border);
    border-radius: 9px;
    background: rgba(255, 255, 255, 0.55);
}
.bk-ed-image-head { display: flex; align-items: center; gap: 10px; margin-bottom: 9px; }
.bk-ed-image-head .btn { margin-left: auto; }

.bk-ed-image-chosen { display: flex; align-items: center; gap: 10px; }
.bk-ed-image-chosen img {
    width: 108px;
    height: 76px;
    object-fit: contain;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 7px;
}
.bk-ed-image-chosen figcaption { font-size: 0.76rem; color: var(--text-muted); }
.bk-ed-image-none { font-size: 0.82rem; color: var(--text-muted); margin: 0; }

.bk-ed-image-ways {
    display: flex;
    align-items: flex-end;
    gap: 10px;
    margin-top: 12px;
    flex-wrap: wrap;
}
.bk-ed-image-ways .bk-ed-f { flex: 1 1 220px; min-width: 0; }
.bk-ed-image-ways input[type="url"] {
    width: 100%;
    padding: 6px 9px;
    border: 1px solid var(--border);
    border-radius: 7px;
    background: #fff;
    font: inherit;
    font-size: 0.84rem;
    color: var(--text);
}
.bk-ed-image-ways input[type="url"]:focus { border-color: var(--accent); outline: none; }

/* The label IS the button: a bare file input cannot be styled to match the
   rest of the editor, so the real one is hidden and the label wears the
   button. Keyboard users still reach the input itself, which is why it is
   hidden with the `hidden` attribute rather than display:none on a wrapper. */
.bk-ed-upload { display: inline-flex; cursor: pointer; }
.bk-ed-upload .btn { pointer-events: none; }
.bk-ed-upload:focus-within .btn { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); }
/* Below the row rather than beside the button: as a third column it dragged
   the button up off the input's baseline, and the difference between a linked
   picture and an uploaded one is worth a full line to explain. */
.bk-ed-upload-note { margin: 8px 0 0; font-size: 0.7rem; line-height: 1.5; color: var(--text-muted); }

.bk-ed-image-msg { margin: 9px 0 0; font-size: 0.78rem; color: var(--text-muted); }
.bk-ed-image-msg.is-bad { color: var(--danger, #b3372b); }

.bk-ed-add-kind {
    width: 100%;
    padding: 6px 9px;
    border: 1px solid var(--border);
    border-radius: 7px;
    background: #fff;
    font-family: inherit;
    font-size: 0.84rem;
    line-height: 1.5;
    color: var(--text);
    outline: none;
    transition: border-color 0.15s, box-shadow 0.15s;
}
/* Height is set by bkAutosize on every keystroke, so the box is always exactly
   as tall as its content. overflow:hidden stops a scrollbar flashing up in the
   frame between typing and resizing. */
/* The app-wide textarea floor is 80px, which is right for a composer box and
   wrong for every box in here – it pinned a one-word answer to five lines and
   made bkAutosize a no-op. Released locally rather than globally: the other
   forms want it. */
.bk-ed-f textarea { min-height: 0; resize: none; overflow: hidden; display: block; }
.bk-ed-f input:focus,
.bk-ed-f textarea:focus,
.bk-ed-tier select:focus,
.bk-ed-add-kind:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(54, 96, 255, 0.1);
}

/* Question and marks share a line. Marks is two characters; a row of its own
   put more space between a question and its answer than between one question
   and the next. */
.bk-ed-row { display: flex; align-items: flex-start; gap: 9px; }
.bk-ed-grow { flex: 1 1 auto; }
.bk-ed-marks { flex: 0 0 62px; }
.bk-ed-marks input { text-align: center; padding-left: 4px; padding-right: 4px; }

.bk-ed-tier { flex: 0 0 auto; width: 132px; }
.bk-ed-tier select { padding: 4px 8px; font-size: 0.78rem; font-weight: 700; }

/* A part is a question inside a question: lettered, indented, and ruled rather
   than boxed again. Nested cards at two levels stop reading as a hierarchy and
   start reading as clutter. The letters are a counter for the same reason the
   numbers are. */
.bk-ed-parts { display: flex; flex-direction: column; gap: 8px; counter-reset: bk-ed-part; }
.bk-ed-parts:not(:empty) { margin-top: 1px; }
.bk-ed-part {
    counter-increment: bk-ed-part;
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 8px 8px 9px 10px;
    border-left: 2px solid var(--border);
    border-radius: 0 7px 7px 0;
    background: rgba(30, 27, 46, 0.022);
}
.bk-ed-part:focus-within { border-left-color: var(--accent); }
.bk-ed-part-letter::before {
    content: counter(bk-ed-part, lower-alpha) ")";
    display: block;
    padding-top: 15px;             /* sits on the first input, not its label */
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--text-muted);
}
.bk-ed-part-body { flex: 1 1 auto; display: flex; flex-direction: column; gap: 7px; min-width: 0; }
.bk-ed-part .bk-ed-del { margin-top: 12px; }
/* A question with parts carries no answer of its own — validateExercise drops
   it — so the box goes rather than sitting there collecting text that will be
   thrown away on save. */
.bk-ed-ex[data-parted="1"] .bk-ed-answer { display: none; }

/* Both add controls read as "one more of these", quietly, under what they add
   to. Neither is a primary action: the primary action is Save. */
.bk-ed-addpart {
    align-self: flex-start;
    padding: 3px 8px;
    border: 1px dashed var(--border);
    border-radius: 7px;
    background: transparent;
    font: inherit;
    font-size: 0.75rem;
    font-weight: 700;
    color: var(--text-muted);
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s;
}
.bk-ed-addpart:hover { border-color: var(--accent); color: var(--accent); }

.bk-ed-add { display: flex; align-items: center; gap: 7px; }
.bk-ed-add .bk-ed-add-kind { width: auto; min-width: 158px; padding: 5px 9px; font-size: 0.8rem; }

.bk-ed-foot {
    display: flex;
    align-items: center;
    gap: 8px;
    padding-top: 9px;
    border-top: 1px dashed var(--border);
}

@media (max-width: 620px) {
    .bk-ed-row { flex-wrap: wrap; }
    .bk-ed-marks { flex: 0 0 62px; }
    .bk-ed-tier { width: 118px; }
    .bk-ed-add { flex-wrap: wrap; }
}

.bk-r-skel { height: 13px; border-radius: 6px; margin-bottom: 9px; }
.bk-r-skel.short { width: 55%; }
.bk-r-failed { color: #b3372b; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

.bk-r-rendered .bk-p { margin-bottom: 9px; }
.bk-r-rendered .bk-codes,
.bk-r-section .bk-codes {
    font-family: 'Trebuchet MS', Arial, sans-serif;
    font-size: 0.68rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #8a8375;
    margin-bottom: 8px;
}
.bk-r-heading .bk-num { color: #b3372b; font-style: normal; margin-right: 8px; }

/* Key terms — the thing that makes a revision guide skimmable. Two colours so
   a point dense with terms does not become a solid block of red. */
.bk-r-section .bk-key {
    color: #b3372b;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.bk-r-rendered .bk-points li:nth-child(even) .bk-key,
.bk-r-rendered .bk-facts li:nth-child(even) .bk-key { color: #1f5fa8; }

/* Numbered points — the backbone, drawn with a counter so the number can be
   "1)" in a hanging indent rather than a plain list marker. */
.bk-r-rendered .bk-points { margin-bottom: 12px; }
.bk-r-rendered .bk-points-title {
    font-family: 'Trebuchet MS', Arial, sans-serif;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #6b5b45;
    margin-bottom: 6px;
}
.bk-r-rendered .bk-points ol { list-style: none; counter-reset: bk-point; margin: 0; }
.bk-r-rendered .bk-points li {
    counter-increment: bk-point;
    position: relative;
    padding-left: 30px;
    margin-bottom: 8px;
}
.bk-r-rendered .bk-points li::before {
    content: counter(bk-point) ")";
    position: absolute;
    left: 0;
    width: 22px;
    text-align: right;
    font-weight: 700;
}

/* Boxes. */
.bk-r-rendered .bk-example,
.bk-r-rendered .bk-facts,
.bk-r-rendered .bk-worked,
.bk-r-rendered .bk-check,
.bk-r-rendered .bk-equation,
.bk-r-rendered .bk-aside {
    border-radius: 8px;
    padding: 11px 14px;
    margin: 12px 0;
    font-size: 0.95rem;
}
.bk-r-rendered .bk-example {
    background: #e8f1fb;
    border: 1px solid #a9c8e8;
    display: flex;
    gap: 10px;
    align-items: flex-start;
}
.bk-r-rendered .bk-example-tag {
    flex: none;
    font-family: 'Trebuchet MS', Arial, sans-serif;
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: #fff;
    background: #3a7bbf;
    border-radius: 4px;
    padding: 2px 6px;
    margin-top: 2px;
}
.bk-r-rendered .bk-facts { background: #eef7ec; border: 1px dashed #86b877; }
.bk-r-rendered .bk-facts h4 { color: #3d7a2e; }
.bk-r-rendered .bk-facts ul { margin-left: 16px; }
.bk-r-rendered .bk-worked { background: #f6f4ef; border: 1px solid #d8d2c4; }
.bk-r-rendered .bk-worked h4 { color: #6b5b45; }
.bk-r-rendered .bk-check { background: #fdf6e0; border: 1px solid #e3cf8d; }
.bk-r-rendered .bk-check h4 { color: #8a6d18; }
.bk-r-rendered .bk-check ol { margin-left: 18px; }
.bk-r-rendered .bk-qc-a { display: block; color: #8a6d18; font-style: italic; font-size: 0.88rem; }
.bk-r-rendered .bk-qc-a::before { content: '→ '; }
.bk-r-rendered .bk-equation { background: #f6f4ef; border: 1px solid #d8d2c4; }
.bk-r-rendered .bk-equation-tex { text-align: center; margin-bottom: 6px; }
.bk-r-rendered .bk-equation-meaning { font-size: 0.87rem; color: #4a463d; }

.bk-r-rendered h4 {
    font-family: 'Trebuchet MS', Arial, sans-serif;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    margin-bottom: 6px;
}

/* On screen there is no margin to float into, so a callout is a full-width
   box. It keeps its colour, which is what identifies it. */
.bk-r-rendered .bk-aside { float: none; width: auto; }
.bk-r-rendered .bk-tip { background: #fdf3e0; border: 1px solid #e6c98d; }
.bk-r-rendered .bk-tip .bk-aside-label { color: #9a6c14; }
.bk-r-rendered .bk-mistake { background: #fdeceb; border: 1px solid #e8b0ab; }
.bk-r-rendered .bk-mistake .bk-aside-label { color: #b3372b; }
.bk-r-rendered .bk-aside-label {
    display: block;
    font-family: 'Trebuchet MS', Arial, sans-serif;
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    margin-bottom: 4px;
}

.bk-r-rendered .bk-def { border-left: 3px solid #d9a86a; padding-left: 12px; margin-bottom: 9px; }
.bk-r-rendered .bk-term { font-weight: 700; font-style: italic; margin-right: 6px; }

.bk-r-rendered .bk-steps { margin: 0 0 6px 18px; }
.bk-r-rendered .bk-worked-a { border-top: 1px solid #d8d2c4; padding-top: 7px; font-weight: 700; }
.bk-r-rendered .bk-worked-a span {
    font-family: 'Trebuchet MS', Arial, sans-serif;
    font-size: 0.62rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: #6b5b45;
    margin-right: 6px;
}

/* The sign-off. */
.bk-r-rendered .bk-roundup { border-top: 2px solid #1a1a1a; margin-top: 18px; padding-top: 9px; }
.bk-r-rendered .bk-roundup-quip {
    font-family: Georgia, 'Palatino Linotype', serif;
    font-style: italic;
    font-weight: 700;
    font-size: 1rem;
    text-transform: none;
    letter-spacing: normal;
    margin-bottom: 4px;
}
.bk-r-rendered .bk-roundup-summary { font-size: 0.9rem; color: #3a352c; }

.bk-r-rendered .bk-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; margin: 12px 0; }
.bk-r-rendered .bk-table th,
.bk-r-rendered .bk-table td { border: 1px solid #b9b2a4; padding: 6px 9px; text-align: left; vertical-align: top; }
.bk-r-rendered .bk-table th { background: #fbe6cf; font-family: 'Trebuchet MS', Arial, sans-serif; font-size: 0.82rem; }
/* A wide table scrolls inside its own box rather than stretching the card. */
.bk-r-rendered .bk-table-wrap { overflow-x: auto; }
.bk-r-rendered .bk-table-title {
    font-family: 'Trebuchet MS', Arial, sans-serif;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #6b5b45;
    margin-bottom: 5px;
}

/* Figures, in the same frame the print stylesheet gives them: a centred plate
   narrower than the measure, a white ground so transparent PNGs read the same
   as opaque ones, and no upscaling. The preview is what a teacher decides on
   before spending a print run on it, so a picture that looks reasonable here
   and blows out on paper is worse than no preview at all - hence the height
   cap, which the old rule had nowhere: a portrait diagram 2000px tall filled
   the card and pushed the rest of the section off the screen. */
.bk-r-rendered .bk-figure {
    max-width: min(420px, 100%);
    margin: 16px auto;
    text-align: center;
}
.bk-r-rendered .bk-figure img {
    display: inline-block;
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: 260px;
    padding: 5px;
    background: #fff;
    border: 1px solid #d8d2c4;
    border-radius: 8px;
}
.bk-r-rendered .bk-figure figcaption {
    font-family: 'Trebuchet MS', Arial, sans-serif;
    font-size: 0.78rem;
    line-height: 1.4;
    color: #6b5b45;
    margin-top: 7px;
}
.bk-r-rendered .bk-credit { display: block; font-size: 0.68rem; opacity: 0.7; margin-top: 2px; }

/* The hole a picture is about to fill.

   Only ever rendered during a live generation run — pendingDiagram() in
   booklet-render.js is gated on `imagePending`, which nothing but the streaming
   path sets, so this never reaches the PDF or the student view.

   Deliberately the same box a real figure gets, at a fixed 3:2, because the
   point is that nothing moves when the picture arrives: a placeholder that is
   not the figure's size just relocates the reflow rather than preventing it.
   Reuses the shimmer keyframe the slide thumbs and list rows use, in the
   booklet's own paper tones rather than the interface's blue-greys. */
.bk-r-rendered .bk-figure-pending { max-width: min(420px, 100%); margin: 16px auto; }
.bk-r-rendered .bk-figure-skeleton {
    aspect-ratio: 3 / 2;
    /* The tallest a real figure can be: the img's own 260px cap plus its 5px
       of padding either side. Without it the 3:2 box is 280px against a
       landscape figure's 270px, and the section still jumps 10-30px when the
       picture arrives — which is the one thing the placeholder exists to
       stop. A 3:2 image now lands pixel-identical. */
    max-height: 270px;
    border: 1px solid #d8d2c4;
    border-radius: 8px;
    background: linear-gradient(90deg, #ece6da 25%, #f7f3ea 50%, #ece6da 75%);
    background-size: 200% 100%;
    animation: shimmer 1.4s infinite;
}
/* A shimmer is decoration; the caption and aria-busy already say it is
   working. Someone who has asked for less motion gets a still box. */
@media (prefers-reduced-motion: reduce) {
    .bk-r-rendered .bk-figure-skeleton { animation: none; background: #ece6da; }
}

/* A booklet card says how long it is and whether it finished, because
   generation can be interrupted – see the status labels in renderBooklets. */
.bk-card-status {
    display: block;
    margin-top: 4px;
    font-size: 0.8rem;
    color: var(--amber);
    font-weight: 600;
}
.bk-card-status.is-done { color: var(--text-muted); font-weight: 500; }

/* ── Workbook lessons ─────────────────────────────────────
   The teacher's side. The pupil's player is a separate standalone page with
   its own stylesheet (public/css/learn.css). */

/* A lesson row: what it is on the left, where it has got to on the right. */
/* One surface with hairlines between rows, matching the roster on Results and
   the work list inside a class. Three pages that each invented their own row
   is how a product stops feeling like one product. */
.lsn-row {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 11px 12px;
    border: 0;
    border-bottom: 1px solid var(--border, #e3e1ea);
    border-radius: 0;
    background: none;
    cursor: pointer;
    text-align: left;
    font: inherit;
    transition: background-color .12s ease;
}

.lsn-row:last-child { border-bottom: 0; }
.lsn-row:hover { background: rgba(54, 96, 255, 0.05); }
.lsn-row:focus-visible { outline: 2px solid var(--accent, #5b4bd6); outline-offset: 2px; }

.lsn-row-main { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
.lsn-row-title { font-weight: 600; }
.lsn-row-meta { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.lsn-chip, .lsn-row-due { font-size: .8rem; color: var(--muted, #6b6880); }
.lsn-row-due { white-space: nowrap; }

.lsn-row-progress {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 5px;
    min-width: 9em;
}

/* Marked progress. One bar answers "how far through are they" faster than
   three numbers do. */
.lsn-bar {
    width: 100%;
    max-width: 11em;
    height: 6px;
    border-radius: 999px;
    background: #edeaf6;
    overflow: hidden;
}
.lsn-bar-fill {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: var(--accent, #5b4bd6);
    transition: width .3s ease;
}

.lsn-row-counts { font-size: .8rem; color: var(--muted, #6b6880); white-space: nowrap; }

/* State pills. Lesson states (draft/open/closed) say whether pupils can get
   in; attempt states say where one pupil's work stands. Both live here so the
   colours stay one vocabulary. */
.lsn-pill {
    display: inline-block;
    font-size: .72rem;
    font-weight: 600;
    padding: 3px 9px;
    border-radius: 999px;
    background: #eceff4;
    color: var(--muted, #6b6880);
    white-space: nowrap;
}
.lsn-pill-draft { background: #f1eefc; color: #5b4bd6; }
.lsn-pill-open, .lsn-pill-marked { background: #e0f2e7; color: #1c7a4a; }
.lsn-pill-closed { background: #eceff4; color: #6b6880; }
.lsn-pill-in_progress { background: #fff3d6; color: #8a6100; }
.lsn-pill-submitted, .lsn-pill-marking { background: #e4edff; color: #27508f; }
.lsn-pill-error { background: #fdecec; color: #b23b3b; }

/* Empty state: one motion – make the work, hand out its code. */
.lsn-empty {
    margin-top: 8px;
    padding: 26px 24px;
    border: 1px dashed var(--border, #e3e1ea);
    border-radius: 14px;
    text-align: center;
}
.lsn-empty h2 { margin: 0 0 8px; font-size: 1.15rem; }
.lsn-empty p {
    max-width: 430px;
    margin: 0 auto 14px;
    color: var(--muted, #6b6880);
    font-size: .92rem;
}

/* Detail header: the lesson is the title of its own page, with its state and
   deadline read as one line of caption underneath. */
.lsn-detail-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 20px;
}
.lsn-detail-id { min-width: 0; flex: 1 1 20em; }
.lsn-detail-title {
    font-size: 1.5rem;
    font-weight: 800;
    letter-spacing: -0.015em;
    line-height: 1.25;
    text-wrap: balance;
}
.lsn-detail-meta {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 8px;
}
.lsn-detail-due { font-size: .82rem; color: var(--text-muted); }
.lsn-detail-actions {
    display: flex;
    gap: 4px;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    /* Nudged down so the button row optically centres on the title rather
       than sitting on its ascender. */
    margin-top: 2px;
}
.lsn-delete-btn { color: var(--text-muted); }
.lsn-delete-btn:hover { color: var(--red-soft); background: rgba(177, 70, 55, 0.08); }

/* ── Getting the class in ────────────────────────
   Two routes in, side by side and equal in weight: the code you say out loud,
   and the link you send. The code is set as separate character cells because
   it gets read across a room – spaced characters are what stop a B becoming
   an 8 on the way to the back row. */
.lsn-join {
    display: grid;
    grid-template-columns: auto auto minmax(16em, 1fr);
    align-items: center;
    gap: 4px 22px;
    padding: 18px 22px;
    margin-bottom: 18px;
}

.lsn-join-label {
    font-size: 0.7rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--text-muted);
}

.lsn-code {
    display: flex;
    gap: 5px;
    margin: 9px 0 8px;
}
.lsn-code-cell {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 1.55em;
    padding: 6px 2px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: linear-gradient(180deg, #fffdf9, #f4eee5);
    box-shadow: 0 1px 0 rgba(30, 27, 46, 0.05);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 1.5rem;
    font-weight: 700;
    line-height: 1.05;
    color: var(--text);
}
.lsn-join-where { font-size: 0.82rem; color: var(--text-muted); }
.lsn-join-where strong { color: var(--text); font-weight: 700; }

/* A plain hairline. The two labels already say which route is which, and a
   word floating in the gap needs an opaque background that can never match a
   card painted with a gradient. */
.lsn-join-rule {
    align-self: stretch;
    width: 1px;
    margin: 4px 0;
    background: var(--border);
}

.lsn-join-link { min-width: 0; }
.lsn-url-row { display: flex; gap: 8px; align-items: center; margin: 8px 0; }
.lsn-url {
    flex: 1 1 auto;
    min-width: 0;
    padding: 9px 11px;
    border: 1px solid var(--border);
    border-radius: 9px;
    background: var(--surface-2);
    color: var(--text-muted);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.85rem;
    text-overflow: ellipsis;
}
.lsn-url:focus { outline: 2px solid var(--accent); outline-offset: 1px; color: var(--text); }
.lsn-join-note { font-size: 0.8rem; color: var(--text-muted); line-height: 1.5; }
.lsn-url-status { color: #1c7a4a; font-weight: 700; }
#lsn-nologin-warn { color: #8a6100; font-weight: 500; }

@media (max-width: 780px) {
    .lsn-join { grid-template-columns: minmax(0, 1fr); gap: 0; }
    .lsn-join-rule {
        width: auto;
        height: 1px;
        margin: 16px 0;
    }
}

/* ── Progress ─────────────────────────────────
   The chips give the shape of the class in one glance; the rows below name
   the individuals. */
.lsn-progress-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

/* A live feed should look live. The dot carries the claim: it stops when the
   stream does, which is the one thing the words alone cannot show. */
.lsn-live {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-muted);
}
.lsn-live-dot {
    width: 7px;
    height: 7px;
    flex: none;
    border-radius: 50%;
    background: var(--green);
    animation: lsn-pulse 2s ease-in-out infinite;
}
.lsn-live.is-paused .lsn-live-dot { background: var(--text-muted); animation: none; }
@keyframes lsn-pulse {
    50% { opacity: 0.25; transform: scale(0.8); }
}
@media (prefers-reduced-motion: reduce) {
    .lsn-live-dot { animation: none; }
}

.lsn-summary {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 14px;
    padding-bottom: 14px;
    border-bottom: 1px solid var(--border);
}
.lsn-summary-chip {
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
    padding: 5px 11px 5px 9px;
    border-radius: 999px;
    background: var(--surface-2);
    font-size: 0.8rem;
    color: var(--text-muted);
}
.lsn-summary-count {
    font-size: 0.95rem;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    color: var(--text);
}

/* Row: name on the left, state and score anchored right in one group, so the
   eye scans a single column of statuses rather than a ragged edge across a
   wide screen.

   The chevron gets a column of its own rather than sitting inside that group,
   because only some rows have one: as a flex child it would take its width
   from whether the row happens to be clickable, and push those rows' pills
   out of line with the rest. A fixed column is the same width either way. */
.lsn-student {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto 16px;
    gap: 12px;
    align-items: center;
    padding: 11px 12px;
    border-radius: 8px;
}
/* Drawn as a pseudo-element inset past the corners rather than as a border or
   an inset shadow: either of those gets clipped by the radius and leaves the
   hairline with curled, foreshortened ends. */
.lsn-student + .lsn-student::before {
    content: '';
    position: absolute;
    top: 0;
    left: 12px;
    right: 12px;
    height: 1px;
    background: var(--border);
}
.lsn-student-main { display: flex; align-items: center; gap: 8px; min-width: 0; }
.lsn-student-name {
    font-weight: 600;
    font-size: 0.95rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.lsn-student-state { display: flex; align-items: center; gap: 12px; justify-content: flex-end; }
/* One tidy column of states rather than pills of six different widths. */
.lsn-student-state .lsn-pill {
    min-width: 8.2em;
    justify-content: center;
    text-align: center;
}

.lsn-student-open { cursor: pointer; transition: background .15s ease; }
.lsn-student-open:hover { background: var(--surface-2); }
.lsn-student-open:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* Only rows with a script behind them get the affordance, but every row keeps
   the space for it. */
.lsn-student-go {
    color: var(--text-muted);
    opacity: 0;
    transition: opacity .15s ease, transform .15s ease;
}
.lsn-student-open:hover .lsn-student-go,
.lsn-student-open:focus-visible .lsn-student-go { opacity: 1; transform: translateX(2px); }
.lsn-student-go svg { display: block; width: 15px; height: 15px; }

/* A pupil whose typed name matches nobody on the roster. Stated, not warned
   about: on the first lesson of a new class this is every row, and a page of
   amber flags would be shouting about the normal case. */
.lsn-unmatched {
    flex: none;
    padding: 1px 7px;
    border: 1px dashed var(--border);
    border-radius: 999px;
    font-size: 0.7rem;
    font-weight: 700;
    color: var(--text-muted);
    white-space: nowrap;
}
.lsn-student-score {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    font-size: 0.9rem;
    min-width: 3.6em;
    text-align: right;
}

.lsn-roll-empty {
    padding: 26px 12px;
    text-align: center;
    font-size: 0.88rem;
    color: var(--text-muted);
}

/* One pupil's script. The back button is a way out of this view rather than
   an action on it, so it sits above the head as a link would. */
.lsn-script-back { margin-bottom: 14px; }
.lsn-script-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    padding-bottom: 14px;
    margin-bottom: 20px;
    border-bottom: 1px solid var(--border);
}
.lsn-script-title { font-size: 1.25rem; font-weight: 800; letter-spacing: -0.01em; }
.lsn-script-score {
    font-size: 1rem;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
}
.lsn-script-score.is-unmarked { font-weight: 600; color: var(--text-muted); }

.lsn-script-section { margin-bottom: 26px; }
.lsn-script-section h4 {
    margin: 0 0 12px;
    font-size: 0.72rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--text-muted);
}

/* An answer is a card, not a bar. The verdict already has somewhere to live –
   its own chip, in the same colour vocabulary as the pills on the class roll –
   so a coloured stripe down the edge only says it a second time. */
.lsn-answer {
    padding: 14px 16px;
    margin-bottom: 12px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--card-bg);
}
.lsn-answer-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 12px;
}

.lsn-verdict {
    flex: none;
    padding: 3px 9px;
    border-radius: 999px;
    font-size: .72rem;
    font-weight: 700;
    white-space: nowrap;
    background: #eceff4;
    color: var(--text-muted);
}
.lsn-verdict-correct { background: #e0f2e7; color: #1c7a4a; }
.lsn-verdict-partial { background: #fff3d6; color: #8a6100; }
.lsn-verdict-incorrect { background: #fdecec; color: #b23b3b; }

.lsn-answer-q { font-weight: 700; min-width: 0; }
.lsn-answer-block { margin-bottom: 8px; }
.lsn-answer-block p { margin: 2px 0; white-space: pre-wrap; }

.lsn-answer-label {
    font-size: .7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--muted, #6b6880);
}

.lsn-answer-mark {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
}
.lsn-answer-mark label { display: flex; align-items: center; gap: 6px; font-size: .9rem; }
.lsn-mark-input { width: 4em; padding: 4px 6px; border: 1px solid var(--border, #e3e1ea); border-radius: 6px; }

/* The set-as-lesson dialog. Its positioning comes from the shared
   #ws-preview-modal rule above — see the note there. */
.lsn-set-shell { max-width: 520px; width: 100%; height: auto; max-height: calc(100vh - 48px); }
.lsn-set-head-copy { min-width: 0; }
.lsn-set-context { margin: 2px 0 0; font-size: .88rem; color: var(--muted, #6b6880); }
/* The body owns the spacing between questions, and each .lsn-field owns the
   spacing inside one. Block flow with margins on individual controls is what
   left NAME sitting flush against the checkboxes once the pacing radios that
   had been separating them were removed. */
.lsn-set-body {
    padding: 18px 20px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 20px;
}
.lsn-field { display: flex; flex-direction: column; gap: 7px; }
/* A date is eight characters. Full width it reads as an empty text box the
   teacher has failed to fill in. */
.lsn-date { max-width: 190px; }
.lsn-set-foot {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    padding: 14px 20px;
    border-top: 1px solid var(--border, #e3e1ea);
}

.lsn-radios { display: flex; flex-direction: column; gap: 8px; margin: 6px 0 14px; }
.lsn-radio, .lsn-check { display: flex; gap: 10px; align-items: flex-start; cursor: pointer; }
.lsn-radio { padding: 11px 13px; border: 1px solid var(--border, #e3e1ea); border-radius: 10px; transition: border-color .15s ease, background .15s ease; }
.lsn-radio:hover { border-color: var(--accent, #5b4bd6); }
.lsn-radio:has(input:checked) { border-color: var(--accent, #5b4bd6); background: #f7f6ff; box-shadow: inset 0 0 0 1px var(--accent, #5b4bd6); }
.lsn-radio strong { display: block; font-size: .95rem; }
.lsn-radio small, .lsn-check small { display: block; color: var(--muted, #6b6880); margin-top: 2px; }
.lsn-check { margin-bottom: 0; }   /* .lsn-field’s gap owns this now */

/* What automatic marking is about to spend. Three states, because the answer
   is not one thing: an ordinary reminder of the charge, a warning that the
   allowance will not cover a class, and a plain statement that there is
   nothing left to spend. Only the last two raise their voice – a note that
   shouts every time is one a teacher stops seeing by the third lesson. */
.lsn-mark-note {
    margin: 2px 0 0 30px;
    font-size: .85rem;
    line-height: 1.45;
    color: var(--muted, #6b6880);
}
.lsn-mark-note.is-warning {
    margin-left: 0;
    padding: 9px 11px;
    border-radius: 9px;
    border: 1px solid var(--border, #e3e1ea);
    background: var(--surface-2, #f7f6fa);
}
.lsn-mark-note.is-low {
    border-color: rgba(244, 180, 71, 0.55);
    background: rgba(244, 180, 71, 0.12);
    color: #7a5608;
}
.lsn-mark-note.is-out {
    border-color: rgba(214, 69, 69, 0.45);
    background: rgba(214, 69, 69, 0.09);
    color: #96241f;
}

/* Renaming is the rare case, so it folds away under the class/mode questions
   instead of leading the form. */
.lsn-set-name { margin-top: 14px; border-top: 1px dashed var(--border, #e3e1ea); padding-top: 12px; }
.lsn-set-name summary {
    cursor: pointer;
    color: var(--accent, #5b4bd6);
    font-size: .9rem;
    font-weight: 600;
    width: fit-content;
}
.lsn-set-name input { margin-top: 8px; }

@media (max-width: 640px) {
    .lsn-row { flex-direction: column; align-items: stretch; }
    .lsn-row-progress { align-items: stretch; }
    .lsn-row-progress .lsn-bar { max-width: none; }
    .lsn-detail-title { font-size: 1.3rem; }
    .lsn-detail-actions { justify-content: flex-start; width: 100%; }
    .lsn-join { padding: 16px; }
    /* The chevron has no hover to reveal it on touch, so its column goes too. */
    .lsn-student { grid-template-columns: minmax(0, 1fr) auto; }
    .lsn-student-go { display: none; }
    .lsn-student-state { gap: 8px; }
    .lsn-student-state .lsn-pill { min-width: 0; }
    .lsn-student-score { min-width: 0; }
}

/* ── Booklet topic picker ────────────────────────────────────────────────
   The tick list that replaces the free-text brief where we hold an authored
   topic list. Denser than .bk-extra deliberately: those are four cards a
   teacher reads once, this is up to thirty rows they scan and tick, so the
   row is one line of text with the tick on the left where the eye returns
   for the next one. */
.bk-topics-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}
.bk-topics-actions {
    display: flex;
    align-items: baseline;
    gap: 12px;
}
.bk-topics-count {
    font-size: 0.82rem;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}
.bk-topics-clear {
    border: 0;
    background: none;
    padding: 0;
    font: inherit;
    font-size: 0.82rem;
    color: var(--accent);
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.bk-topics-clear[disabled] {
    color: var(--text-muted);
    cursor: default;
    text-decoration: none;
}

.bk-topics-list {
    margin-top: 6px;
    border: 1.5px solid var(--border);
    border-radius: 12px;
    background: var(--card-bg);
    /* Capped rather than growing without limit: thirty topics would push the
       wizard's Next button off the screen on a laptop, and the teacher would
       have to scroll back up to find it. */
    max-height: min(52vh, 460px);
    overflow-y: auto;
    overscroll-behavior: contain;
}

.bk-topic-domain + .bk-topic-domain { border-top: 1px solid var(--border); }

.bk-topic-domain-name {
    position: sticky;
    top: 0;
    z-index: 1;
    padding: 10px 14px 6px;
    background: var(--card-bg);
    font-size: 0.76rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.bk-topic {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 9px 14px 9px 14px;
    cursor: pointer;
    user-select: none;
    transition: background 0.15s;
}
.bk-topic:hover { background: rgba(54, 96, 255, 0.06); }
.bk-topic:has(input:checked) { background: rgba(54, 96, 255, 0.09); }
.bk-topic:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Same split as .bk-extra: the native input carries state and focus, the
   drawn box is what is seen. */
.bk-topic input[type="checkbox"] {
    position: absolute;
    width: 18px;
    height: 18px;
    left: 14px;
    top: 10px;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}
.bk-topic-check {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    margin-top: 1px;
    border-radius: 5px;
    box-shadow: inset 0 0 0 1.5px var(--border);
    transition: background 0.15s, box-shadow 0.15s;
}
.bk-topic-check svg {
    width: 10px;
    height: 10px;
    fill: none;
    stroke: #fff;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    opacity: 0;
    transform: scale(0.6);
    transition: opacity 0.15s, transform 0.15s;
}
.bk-topic:has(input:checked) .bk-topic-check {
    background: var(--accent);
    box-shadow: inset 0 0 0 1.5px var(--accent);
}
.bk-topic:has(input:checked) .bk-topic-check svg { opacity: 1; transform: scale(1); }

/* The row is the label plus its disclosure. The label takes the space so the
   whole line stays clickable, and the button sits at the end where it does not
   move as labels wrap to different heights. */
.bk-topic-row {
    display: flex;
    align-items: flex-start;
}
.bk-topic-row .bk-topic { flex: 1; min-width: 0; }

.bk-topic-toggle {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    align-self: stretch;
    padding: 0 12px;
    border: 0;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
    transition: color 0.15s;
}
.bk-topic-toggle:hover { color: var(--accent); }
.bk-topic-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: 6px; }
.bk-topic-toggle-count { font-size: 0.78rem; font-variant-numeric: tabular-nums; }
.bk-topic-toggle svg {
    width: 13px;
    height: 13px;
    transition: transform 0.15s;
}
.bk-topic-toggle[aria-expanded="true"] svg { transform: rotate(90deg); }

/* Indented and quieter than the topic above it: these are the objectives
   inside one topic, and they should read as belonging to it rather than
   competing with the next topic down. */
.bk-topic-points {
    padding-bottom: 4px;
    background: rgba(0, 0, 0, 0.015);
    box-shadow: inset 2px 0 0 var(--border);
    margin-left: 28px;
}
.bk-topic-point { padding-left: 12px; padding-top: 6px; padding-bottom: 6px; }
.bk-topic-point input[type="checkbox"] { left: 12px; top: 7px; }
.bk-topic-point .bk-topic-label { font-size: 0.86rem; }

/* A topic with only some of its points ticked. Without this the parent box
   sits empty and the topic reads as excluded, when it is partly included. */
.bk-topic-group.is-partial > .bk-topic-row .bk-topic-check {
    background: var(--accent);
    box-shadow: inset 0 0 0 1.5px var(--accent);
}
/* display:none, not opacity:0 — the check is a centring flex box, so a
   transparent tick still takes a slot and shoves the dash off centre. */
.bk-topic-group.is-partial > .bk-topic-row .bk-topic-check svg { display: none; }
.bk-topic-group.is-partial > .bk-topic-row .bk-topic-check::after {
    content: "";
    width: 8px;
    height: 2px;
    border-radius: 1px;
    background: #fff;
}

.bk-topic-text { min-width: 0; }
.bk-topic-label { font-size: 0.92rem; line-height: 1.35; }
.bk-topic-note {
    display: block;
    margin-top: 2px;
    font-size: 0.8rem;
    line-height: 1.35;
    color: var(--text-muted);
}

@media (prefers-reduced-motion: reduce) {
    .bk-topic, .bk-topic-check, .bk-topic-check svg,
    .bk-topic-toggle, .bk-topic-toggle svg { transition: none; }
}

/* ── Classes: the organisation flow ────────────────────────────────────
 *
 * Built from the app's own furniture — .card for a section, .stu-row's shape
 * for a row, .stu-title / .stu-hint for text — rather than a private set of
 * boxes. The first version of this page invented its own and looked like a
 * different product bolted on.
 *
 * NOTE: .stu-sub is the SUBMISSION CARD class, not a subtitle class. Using it
 * for a line of muted text under a heading is what put "Year 10 · 6 pupils"
 * inside a stray white box. Subtitles here are .stu-hint.
 */

/* ── The class list ──────────────────────────────────────────────────── */

.cls-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: 12px;
}

.cls-card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    text-align: left;
    padding: 18px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card-bg);
    font: inherit;
    color: inherit;
    cursor: pointer;
    transition: border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}

.cls-card:hover {
    border-color: rgba(54, 96, 255, 0.35);
    transform: translateY(-1px);
    box-shadow: var(--shadow);
}

/* The card is a status object, not a label. Its job is to answer "has 10B done
   the homework" from across the room, so the reading order is: which class,
   whether anything is late, how far through, and only then the roll count —
   which the teacher already knew. */
.cls-card-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    width: 100%;
}

.cls-card-name { font-weight: 800; font-size: 1rem; }

/* The one place on this page allowed to be loud. Overdue work is the only
   thing here with a consequence attached, so it gets the colour and nothing
   else competes for it. */
.cls-card-late {
    flex: none;
    font-size: 0.72rem;
    font-weight: 800;
    color: #b23b3b;
    background: #fdecec;
    padding: 2px 8px;
    border-radius: 999px;
}

.cls-card-bar {
    display: block;
    width: 100%;
    height: 5px;
    margin: 10px 0 8px;
    border-radius: 999px;
    background: rgba(30, 27, 46, 0.08);
    overflow: hidden;
}

.cls-card-bar-fill {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: var(--accent);
}

.cls-card-status { font-size: 0.85rem; }
.cls-card-status strong { font-weight: 800; }
.cls-card-done { color: #1c7a4a; font-weight: 700; }
.cls-card-quiet { color: var(--text-muted); }
.cls-card-meta { color: var(--text-muted); font-size: 0.78rem; margin-top: 2px; }

/* ── Rows: pupils, teachers, work set ────────────────────────────────── */
/*
 * Inside a .card, so they need no border of their own — a border inside a
 * border is the busiest a list of thirty names can look. A hairline between
 * rows is enough, and the last one does without.
 */

.cls-member {
    position: relative;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 2px;
    border-bottom: 1px solid var(--border);
}

.cls-member:last-child { border-bottom: 0; }
.cls-member-name { flex: 1; min-width: 0; font-weight: 600; font-size: 0.92rem; }

/* How they are keeping up, and how they are doing. Fixed widths so a class
   reads DOWN as two columns rather than as thirty differently-indented lines. */
.cls-member-done {
    flex: none;
    min-width: 5.5rem;
    text-align: right;
    font-size: 0.82rem;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
}

/* Behind the rest of the class — relative, not absolute, because "nobody has
   handed it in" is a class that was set work this morning. */
.cls-member-done.is-behind { color: #8a6100; font-weight: 700; }

.cls-member-avg {
    flex: none;
    min-width: 3.5rem;
    text-align: right;
    font-weight: 800;
    font-size: 0.9rem;
    font-variant-numeric: tabular-nums;
}

.cls-member-avg.is-empty { color: var(--text-muted); font-weight: 500; }

/* The username: monospaced because a teacher reads it out or copies it onto a
   slip. Needed about twice a term, so it waits under the hover with the
   actions rather than taking a column from the numbers that are read daily. */
/* Lives inside .cls-member-actions, which floats over the right of the row on
   hover — so it costs the numeric columns no width at all. */
.cls-member-user {
    flex: none;
    align-self: center;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.78rem;
    color: var(--text-muted);
    background: rgba(30, 27, 46, 0.04);
    padding: 2px 7px;
    border-radius: 6px;
    margin-right: 4px;
}

.cls-member-role { color: var(--text-muted); font-size: 0.8rem; }

/* Only pupils who have NOT signed in are flagged: badging the ones who have
   would mark every row by half term and mean nothing. */
/* Sentence case, not capitals. It is a note beside a name, not an alarm, and
   tracked-out capitals make every one of them shout the same volume as the
   things that matter. */
.cls-flag {
    display: inline-block;
    margin-left: 8px;
    font-size: 0.72rem;
    font-weight: 600;
    padding: 2px 8px;
    border-radius: 999px;
    background: #fff3d6;
    color: #8a6100;
    vertical-align: middle;
}

/* Lifted out of the flow and floated over the right of the row.
 *
 * In the flow they reserved their width whether visible or not, which left a
 * finger-wide hole between every pupil's name and their username. Over the top,
 * the row reads as a clean two-column list and the actions arrive on top of the
 * username when they are wanted — which is the moment the username stops being
 * the thing you are looking at. */
.cls-member-actions {
    position: absolute;
    right: 0;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    gap: 4px;
    padding-left: 24px;
    background: linear-gradient(90deg, rgba(250, 246, 241, 0), var(--card-bg) 24px);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.12s ease;
}

/* Row actions appear on hover, and on keyboard focus so they are reachable
   without a mouse. Thirty rows each showing "Reset password / Remove" is a wall
   of blue before it is a register. */
.cls-member:hover .cls-member-actions,
.cls-member:focus-within .cls-member-actions { opacity: 1; pointer-events: auto; }

@media (hover: none) {
    /* Nothing hovers on a touch screen, so they come back into the flow and
       are simply always there. */
    .cls-member-actions {
        position: static;
        transform: none;
        opacity: 1;
        pointer-events: auto;
        padding-left: 0;
        background: none;
    }
}

.cls-link-btn {
    background: none;
    border: 0;
    padding: 4px 8px;
    border-radius: 7px;
    font: inherit;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--text-muted);
    cursor: pointer;
}

.cls-link-btn:hover { background: rgba(54, 96, 255, 0.08); color: var(--accent); }
/* --red-soft is a strong red (#b14637), not a tint — using it as a background
   painted red text on red and the label vanished on hover. The wash matches
   .msg-error's, which is the app's own "this is the destructive one" colour. */
.cls-link-danger:hover { background: rgba(177, 70, 55, 0.1); color: var(--red-soft); }

/* ── Work set to this class ──────────────────────────────────────────── */

.cls-asg { border-bottom: 1px solid var(--border); }
.cls-asg:last-child { border-bottom: 0; }

.cls-asg-row {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 12px 2px;
    background: none;
    border: 0;
    text-align: left;
    font: inherit;
    color: inherit;
    cursor: pointer;
}

.cls-asg-row:hover .cls-asg-name { color: var(--accent); }
.cls-asg-main { flex: 1; min-width: 0; }
.cls-asg-name { display: block; font-weight: 600; font-size: 0.92rem; }
.cls-asg-meta { display: block; color: var(--text-muted); font-size: 0.78rem; margin-top: 1px; }

/* Past its date and not everyone in: the one status on this row with a
   consequence, so the one allowed any colour. */
.cls-asg-meta.is-late { color: #b23b3b; font-weight: 700; }

/* The count is the answer; the bar is the same answer at a glance. Fixed width
   so a column of them lines up and can be compared down the page rather than
   each one starting somewhere different. */
.cls-asg-progress {
    flex: none;
    display: flex;
    align-items: center;
    gap: 10px;
}

.cls-asg-count {
    font-size: 0.8rem;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
    min-width: 4.5rem;
    text-align: right;
}

.cls-asg-bar {
    display: block;
    width: 72px;
    height: 5px;
    border-radius: 999px;
    background: rgba(30, 27, 46, 0.08);
    overflow: hidden;
}

.cls-asg-bar-fill { display: block; height: 100%; border-radius: 999px; background: var(--accent); }
.cls-asg-chev { color: var(--text-muted); font-size: 0.75rem; }

@media (max-width: 600px) {
    /* The bar is the first thing to go: the count says the same thing and the
       name needs the room more. */
    .cls-asg-bar { display: none; }
}

/* The dashboard, expanded in place. Inset and tinted so it reads as belonging
   to the row above it rather than as the next item in the list. */
.cls-asg-body {
    padding: 4px 2px 18px;
    border-left: 2px solid rgba(54, 96, 255, 0.25);
    padding-left: 14px;
    margin-bottom: 8px;
}

.cls-hint { color: var(--text-muted); font-size: 0.85rem; line-height: 1.5; margin: 4px 0 0; }
.cls-optional { color: var(--text-muted); font-weight: 400; }
.cls-count-live { min-height: 1.2em; font-weight: 700; color: var(--accent); }
.cls-names { font-family: inherit; resize: vertical; min-height: 8rem; width: 100%; }

.cls-teachers-fold { margin-top: 20px; }

.cls-teachers-fold summary {
    cursor: pointer;
    color: var(--text-muted);
    font-size: 0.85rem;
    padding: 6px 0;
}

/* ── The class's overflow menu ───────────────────────────────────────── */

.cls-more-wrap { position: relative; display: inline-block; }

.cls-more-menu {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    z-index: 30;
    min-width: 12rem;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 12px 32px rgba(30, 27, 46, 0.14);
    padding: 5px;
}

.cls-more-menu button {
    display: block;
    width: 100%;
    text-align: left;
    background: none;
    border: 0;
    border-radius: 7px;
    padding: 8px 10px;
    font: inherit;
    font-size: 0.88rem;
    cursor: pointer;
}

.cls-more-menu button:hover { background: rgba(54, 96, 255, 0.08); }

/* Deleting a class takes thirty logins with it, so it does not look like
   renaming one. Separated as well as coloured: the item above it is a
   once-a-year tidy-up and this one is not undoable. */
.cls-more-danger {
    margin-top: 4px;
    padding-top: 9px;
    border-top: 1px solid var(--border);
    border-radius: 0 0 7px 7px;
    color: #b14637;
}

.cls-more-menu .cls-more-danger:hover { background: rgba(177, 70, 55, 0.1); }

/* ── Setting work ────────────────────────────────────────────────────── */

.setwork-fixed { margin-bottom: 14px; }
.setwork-fixed-name { font-weight: 700; margin: 2px 0 0; }
.setwork-select { width: 100%; }

.setwork-classes {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-bottom: 14px;
    max-height: 14rem;
    overflow-y: auto;
}

.setwork-class {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: 8px;
    cursor: pointer;
}

.setwork-class:hover { background: rgba(54, 96, 255, 0.06); }
.setwork-class-name { flex: 1; font-weight: 600; }

/* Due dates as chips: "next week" is how a teacher says it, and working out
   the date is arithmetic the page can do. */
.setwork-due { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0 14px; }

.setwork-chip {
    background: none;
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 6px 13px;
    font: inherit;
    font-size: 0.85rem;
    cursor: pointer;
}

.setwork-chip:hover { border-color: var(--accent); }

.setwork-chip.is-on {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
    font-weight: 700;
}

.setwork-date { margin-bottom: 14px; width: 100%; }

/* ── The dialogs ─────────────────────────────────────────────────────── */
/*
 * These reuse the share modal's card and backdrop, so they must reuse its
 * positioning too: .share-card is `position: relative` and centres by being a
 * flex child. Setting only inset on the wrapper left the card in the corner.
 */

#newclass-modal,
#addpupils-modal,
#setwork-modal,
#logins-modal,
.cls-picker-modal {
    position: fixed;
    inset: 0;
    z-index: 10000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
}

/* Wider than the share modal's 460px: these hold a register of thirty names, or
   a class list with a username beside each one. */
#newclass-modal .share-card,
#addpupils-modal .share-card,
#setwork-modal .share-card,
#logins-modal .share-card { max-width: 560px; }

/* ── One pupil's paper, over the class ─────────────────────────────────
 *
 * Wider and taller than any other dialog here because it holds a whole
 * paper — question, what they wrote, the model answer — and a page of
 * writing squeezed into 560px is why this was a separate page before.
 * The head and foot stay put so the name and the way out are never
 * scrolled off the top of somebody's long answer.
 */
.work-modal .share-card {
    display: flex;
    flex-direction: column;
    max-width: 720px;
    max-height: min(86vh, 900px);
}

.work-modal .work-foot { flex: none; }

/* Stacked, not the share head's two columns: a lesson title beside the name
   crowds it, and the pupil is what this dialog is about. */
.work-modal .share-head { flex: none; display: block; }

.work-modal .share-body {
    flex: 1 1 auto;
    overflow-y: auto;
    background: rgba(30, 27, 46, 0.02);
}

.work-sub {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    margin: 4px 0 0;
    font-size: 0.9rem;
    color: var(--text-muted);
}

.work-score { flex: none; font-weight: 800; color: var(--ink, #1e1b2e); }
.work-score.is-unmarked { font-weight: 600; color: var(--text-muted); }

/* The mark for one question, editable here as it is on the lesson's own script
   pane — the same control, so a teacher changing a number does not have to
   learn a second way of doing it. */
.work-mark {
    display: flex;
    justify-content: flex-end;
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
}

.work-mark label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.9rem;
    font-weight: 600;
}

.work-mark-input {
    width: 4em;
    padding: 4px 6px;
    border: 1px solid var(--border, #e3e1ea);
    border-radius: 6px;
    font: inherit;
}

.work-foot {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    padding: 14px 20px;
    border-top: 1px solid var(--border);
}

/* Saved / could not save, beside the button that caused it rather than as a
   toast that has gone by the time you look up from the paper. */
.work-foot-msg {
    margin-right: auto;
    font-size: 0.86rem;
    font-weight: 600;
    color: var(--text-muted);
}

.work-foot-msg.is-error { color: #b14637; }

.work-modal .lsn-script-section h4 {
    margin: 0 0 10px;
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--text-muted);
}

@media (max-width: 700px) {
    .work-modal .share-card { max-height: 92vh; }
}

/* The list inside a picker.
 *
 * Labels are inline by default, so without this the options flow across the
 * dialog as one paragraph — "Amelia Clarke 1 piece ○ Priya Shah 1 piece" — and
 * choosing the wrong one merges two children. They stack. */
.cls-picker {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-height: 45vh;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 4px;
}

.cls-picker-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: 8px;
    cursor: pointer;
}

.cls-picker-row:hover { background: rgba(54, 96, 255, 0.07); }
.cls-picker-row:has(input:checked) { background: rgba(54, 96, 255, 0.1); }
.cls-picker-name { flex: 1; min-width: 0; font-weight: 600; }

.cls-picker-actions { display: flex; align-items: center; gap: 8px; margin-top: 16px; }
.cls-picker-spacer { flex: 1; }

/* ── The assignment dashboard, inline ────────────────────────────────── */

.asg-bar {
    height: 8px;
    border-radius: 999px;
    background: rgba(30, 27, 46, 0.08);
    overflow: hidden;
}

.asg-bar-fill {
    height: 100%;
    background: var(--accent);
    border-radius: 999px;
    transition: width 0.3s ease;
}

.asg-bar-label { margin: 8px 0 14px; color: var(--text-muted); font-size: 0.85rem; }

.asg-pupil {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 7px 0;
    font-size: 0.88rem;
}

.asg-pupil-name { flex: 1; min-width: 0; font-weight: 600; }
.asg-pupil-state { color: var(--text-muted); font-size: 0.82rem; }
.asg-pupil-marks { font-variant-numeric: tabular-nums; font-weight: 800; }

/* Who has not started is the question this is opened to answer, so those names
   are sorted to the top and dimmed rather than shouted at — the eye finds the
   block of grey without anything having to be red. */
.asg-pupil-not_started .asg-pupil-name { color: var(--text-muted); font-weight: 500; }

/* ── The credential sheet ────────────────────────────────────────────── */

.logins-list {
    max-height: 50vh;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    margin: 12px 0;
}

.logins-row {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 9px 12px;
    border-bottom: 1px solid var(--border);
    font-size: 0.88rem;
}

.logins-row:last-child { border-bottom: 0; }
.logins-name { flex: 1; font-weight: 600; }

.logins-user,
.logins-pass {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.82rem;
}

.logins-pass { font-weight: 700; }

/*
 * Printing the sheet.
 *
 * Everything but the dialog comes off the page rather than a second window
 * being opened: pop-up blockers eat those, and a new window loses the app's
 * fonts. The backdrop and the buttons go too — a printed slip should be names
 * and passwords and nothing else.
 */
@media print {
    body.printing-logins > *:not(#logins-modal) { display: none !important; }
    body.printing-logins #logins-modal { position: static; display: block; padding: 0; }
    body.printing-logins .share-backdrop,
    body.printing-logins .share-close,
    body.printing-logins .cls-picker-actions { display: none !important; }
    body.printing-logins .share-card {
        position: static;
        box-shadow: none;
        border: 0;
        max-width: none;
        width: auto;
    }
    body.printing-logins .logins-list { max-height: none; overflow: visible; }
    /* A pupil's row must not be split across two sheets of paper. */
    body.printing-logins .logins-row { break-inside: avoid; }
}

/* Breathing room between a field and the label of the next one.
 *
 * .share-label was written for the share dialog, which has one field per
 * section; stacked into a form it sits directly on the input above it. Scoped
 * to these dialogs so the share dialog's own spacing is untouched. */
#newclass-modal .share-label,
#addpupils-modal .share-label,
#setwork-modal .share-label {
    display: block;
    margin-top: 16px;
}

#newclass-modal .share-body > .share-label:first-child,
#setwork-modal .share-body > .share-label:first-child { margin-top: 0; }

/* "optional" reads as an aside, not as part of the label. */
.cls-optional {
    text-transform: none;
    letter-spacing: 0;
    font-size: 0.78rem;
    opacity: 0.75;
}

/* A hint belongs to the field under it, so it sits tight to that rather than
   floating between the two. */
#newclass-modal .cls-hint,
#addpupils-modal .cls-hint { margin: 4px 0 8px; }

/* The credential sheet's warning.
 *
 * Its own class, not .share-label: that is an uppercase field label, and a
 * three-line warning set in capitals is a paragraph nobody reads — which is
 * the opposite of what a warning is for. Amber rather than red: this is the
 * one irreversible moment in the flow, not an error. */
/* Where pupils sign in, on the class page.
   Quiet: it is a fact a teacher needs twice a year and reads once, so it sits
   under the class name at hint size rather than competing with the markbook. */
.cls-portal {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 8px;
    margin: 6px 0 0;
    font-size: 0.85rem;
    color: var(--text-muted);
}

.cls-portal-url {
    font-weight: 700;
    color: var(--ink, #1e1b2e);
    /* A URL is read character by character when it is being copied onto a
       whiteboard, and a proportional face makes l, 1 and I the same shape. */
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.86rem;
    letter-spacing: -0.01em;
}

/* The address on the printed login sheet. Larger than the warning above it,
   because on paper this is the first thing a child needs and the passwords are
   useless without it. */
.logins-where {
    margin: 0 0 14px;
    font-size: 0.95rem;
}

.logins-where strong {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 1rem;
}

.logins-warning {
    margin: 0 0 4px;
    padding: 10px 12px;
    border-radius: var(--radius);
    background: #fff3d6;
    color: #6b4a00;
    font-size: 0.85rem;
    line-height: 1.5;
}

/* The register's surface. Rows inside it are hairline-separated rather than
   individually bordered, so the container is what makes it a list. */
#stu-roster,
#stu-submissions {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card-bg);
    overflow: hidden;
}

#stu-roster:empty,
#stu-submissions:empty { border: 0; background: none; }

/* Submissions are their own bordered cards (.stu-sub), which suits them — each
   one is a separate piece of work rather than a row in a register — so that
   list keeps its own surface off. */
#stu-submissions { border: 0; background: none; overflow: visible; }

/* The class grouping inside the register. */
.stu-group-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    padding: 11px 12px 7px;
    border-bottom: 1px solid var(--border);
    background: rgba(30, 27, 46, 0.02);
}

/* Sentence case, not tracked-out capitals: this is a class name, and 10B
   Biology is how it is written everywhere else in the product. */
.stu-group-head .stu-eyebrow {
    text-transform: none;
    letter-spacing: 0;
    font-size: 0.82rem;
    font-weight: 700;
}

.stu-group-count { color: var(--text-muted); font-size: 0.78rem; }

/* The lessons list's surface, matching #stu-roster. */
#lsn-list,
#lsn-closed-list {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card-bg);
    overflow: hidden;
}

#lsn-list:empty,
#lsn-closed-list:empty { border: 0; background: none; }

/* ── Pupils: lessons, classes and handed-in work on one page ──────────── */

/* A section is a heading and one list. The space between sections does the
   separating; a rule or a card around each would be boxes inside boxes, since
   every list already has its own surface. */
.pup-section { margin-bottom: 36px; }
.pup-section:last-child { margin-bottom: 0; }

.pup-section-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}
.pup-section-title { font-size: 1.15rem; font-weight: 800; letter-spacing: -0.01em; }

/* With no lessons and no classes, handed-in work is the whole page, and a
   section heading directly under the page's own is a heading for a heading.
   Its one line of explanation moves up to sit under "Pupils" instead. */
#students-view:not(:has(#lsn-list-pane:not([hidden]), #cls-list-pane:not([hidden]))) #stu-names-heading { display: none; }
#students-view:not(:has(#lsn-list-pane:not([hidden]), #cls-list-pane:not([hidden]))) > .stu-head { margin-bottom: 0; }

/* Closed lessons, folded under the current ones. */
.pup-fold { margin-top: 10px; }
.pup-fold > summary {
    display: inline-block;
    padding: 4px 2px;
    color: var(--text-muted);
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
}
.pup-fold > summary:hover { color: var(--text); }
.pup-fold > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.pup-fold[open] > summary { margin-bottom: 8px; }

/* ── Results: names, and the work under them ──────────────────────────── */

/* Other spellings arriving under this name. Quiet and inline, because it is
   reassurance that the grouping is right rather than something to act on. */
.stu-also {
    margin-left: 8px;
    font-weight: 400;
    font-size: 0.78rem;
    color: var(--text-muted);
}

/* A tutoring client's pupil. A pill in the accent colour rather than the quiet
   grey of .stu-also: it says what kind of row this is, which decides what a
   tutor can do with it, where "also a clarke" is only reassurance. */
.stu-client-tag {
    flex: none;
    padding: 4px 9px;
    border-radius: 999px;
    background: rgba(54, 96, 255, 0.12);
    color: var(--accent);
    font-size: 0.7rem;
    font-weight: 700;
    /* The pill's own height is its text plus its padding, so it centres on
       the name beside it instead of hanging off that line's baseline. */
    line-height: 1;
    white-space: nowrap;
}

/* Name and pill as two boxes centred on each other. Inline, the pill sat on
   the baseline of text a third bigger than its own and read as lopsided. */
.stu-row-name.has-tag {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

/* "Same person?" — proposals, never applied on their own. */
.stu-suggest-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 9px 0;
    border-bottom: 1px solid var(--border);
    flex-wrap: wrap;
}

.stu-suggest-row:last-child { border-bottom: 0; }
.stu-suggest-pair { flex: 1; min-width: 12rem; font-size: 0.92rem; }
.stu-suggest-arrow { color: var(--text-muted); margin: 0 6px; }

/* The reason the match was proposed. Shown because the teacher is the one
   deciding, and "initials match" is checkable at a glance where a bare
   proposal is a coin toss they are being asked to sign. */
.stu-suggest-why { color: var(--text-muted); font-size: 0.8rem; }

/* ── One pupil's work, opened in place ────────────────────────────────── */

.stu-detail {
    padding: 4px 12px 14px;
    border-bottom: 1px solid var(--border);
    background: rgba(54, 96, 255, 0.03);
}

/* A button, because it opens the marked paper. Styled as a row rather than as
   a button so a list of six does not read as six calls to action. */
.stu-detail-row {
    display: flex;
    align-items: baseline;
    gap: 12px;
    width: 100%;
    padding: 7px 8px;
    margin: 0 -8px;
    border: 0;
    border-radius: 8px;
    background: none;
    font: inherit;
    font-size: 0.86rem;
    color: inherit;
    text-align: left;
    cursor: pointer;
    transition: background-color 0.12s ease;
}

.stu-detail-row:hover { background: rgba(54, 96, 255, 0.08); }
.stu-detail-row:hover .stu-detail-title { color: var(--accent); }
.stu-detail-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.stu-detail-title { flex: 1; min-width: 0; }

/* The spelling this piece actually arrived under, where it differs from the
   name on display. This is the line that proves the grouping is right. */
.stu-detail-typed {
    margin-left: 8px;
    color: var(--text-muted);
    font-style: italic;
    font-size: 0.8rem;
}

.stu-detail-when { color: var(--text-muted); font-size: 0.8rem; white-space: nowrap; }

.stu-detail-mark {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    min-width: 4.5rem;
    text-align: right;
}

.stu-detail-mark.is-empty { color: var(--text-muted); font-weight: 500; font-size: 0.8rem; }

.stu-detail-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--border);
}

/* ── Classes: the visual pass ──────────────────────────────────────────
 *
 * What was wrong, so it is not undone by accident:
 *
 * 1. Rows spanned the full 1050px column with the title hard left and the
 *    number hard right — 870px of nothing between them. Two columns halves
 *    every row and puts the number beside the name it describes.
 * 2. The progress bars were 5px hairlines that, at 0%, rendered as a faint
 *    grey line indistinguishable from a divider.
 * 3. .card's gradient is within a few percent of the page's own, so the cards
 *    did not lift off the background at all.
 * 4. Card headings were 1.05rem — barely above body text, so nothing on the
 *    page was clearly the top of anything.
 */

.cls-columns {
    display: grid;
    gap: 16px;
    align-items: start;
}

@media (min-width: 900px) {
    /* Work set is the wider column: a piece of work expands in place to show
       the whole class, and the register never needs more than a name and two
       numbers. */
    .cls-columns { grid-template-columns: 1.25fr 1fr; }
}

/* These two lift off the page rather than blending into it. */
.cls-columns .card {
    background: var(--card-bg);
    padding: 20px 22px;
    margin-bottom: 0;
}

.cls-columns .stu-card-title {
    font-size: 1.15rem;
    font-weight: 800;
    letter-spacing: -0.01em;
}

.cls-columns .stu-card-head { margin-bottom: 12px; }

/* ── The class list ──────────────────────────────────────────────────── */

.cls-grid { gap: 14px; }

@media (min-width: 700px) {
    .cls-grid { grid-template-columns: repeat(auto-fill, minmax(17.5rem, 1fr)); }
}

/* A fixed row template, so three cards in different states still line up:
   name, meta, then the status block pinned to the bottom. Without it a class
   with nothing set sat two lines higher than its neighbours and the row read
   as broken rather than as different. */
.cls-card {
    display: grid;
    grid-template-rows: auto auto 1fr;
    gap: 0;
    padding: 20px;
    min-height: 9.5rem;
}

.cls-card-top { align-items: start; margin-bottom: 2px; }

/* The class name is the hero of its card. Nothing else on it competes. */
.cls-card-name {
    font-size: 1.2rem;
    font-weight: 800;
    letter-spacing: -0.01em;
    line-height: 1.25;
}

.cls-card-meta {
    color: var(--text-muted);
    font-size: 0.82rem;
    /* Directly under the name, where it belongs: it describes the class, not
       the work, and it sat at the bottom competing with the status. */
    margin: 0 0 14px;
}

/* The status block sits at the bottom of every card, whatever is above it, so
   three classes in three different states still line up along one baseline. */
.cls-card-foot {
    align-self: end;
    display: block;
    width: 100%;
}

.cls-card-status {
    display: block;
    font-size: 0.88rem;
    line-height: 1.4;
}

/* Darker than the hairline it was: at 0% a track this size has to still read
   as an empty bar rather than as a rule someone left behind. */
.cls-card-bar {
    height: 8px;
    margin: 0 0 8px;
    background: rgba(30, 27, 46, 0.14);
}

.cls-card-bar-fill { transition: width 0.3s ease; }

/* Overdue is the only red on the page and belongs at the card's edge, not
   jammed against the name it was crowding. */
.cls-card-late { align-self: start; }

/* ── Work set rows ───────────────────────────────────────────────────── */

.cls-asg-row { padding: 11px 2px; gap: 10px; }
.cls-asg-name { font-size: 0.95rem; }

.cls-asg-progress { gap: 8px; }

.cls-asg-count {
    min-width: 3.6rem;
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--ink, #1e1b2e);
}

/* Visible at 0%: a hairline track reads as a divider, not as an empty bar. */
.cls-asg-bar {
    width: 56px;
    height: 8px;
    background: rgba(30, 27, 46, 0.14);
}

.cls-asg-bar-fill { transition: width 0.3s ease; }

/* ── The register ────────────────────────────────────────────────────── */

.cls-member { padding: 9px 2px; gap: 10px; }
.cls-member-name { font-size: 0.9rem; }
.cls-member-done { min-width: 4.6rem; font-size: 0.8rem; }
.cls-member-avg { min-width: 3rem; font-size: 0.88rem; }

/* An average that does not exist yet. A lone em dash at the far right of every
   row read as a mistake; at the same weight as the muted text around it, it
   reads as "nothing here yet", which is what it means. */
.cls-member-avg.is-empty { color: rgba(107, 101, 128, 0.55); }

@media (max-width: 900px) {
    /* Stacked, so the bar has the room it needs back. */
    .cls-asg-bar { width: 72px; }
}

/* ── The markbook ──────────────────────────────────────────────────────
 *
 * Names down the side, work across the top, a mark in the cell. It is how a
 * teacher has tracked a class for a century, so it needs no explaining to the
 * person using it — and unlike the panels it replaced, it answers four
 * questions at once: who has not done it (a gap in a column), who is
 * struggling (a low row), which task went badly (a low column), and who is
 * drifting (a sparse row).
 *
 * It also wants the width. The layouts before it stranded a number at the far
 * right of a 1050px row with nothing in between; here every column earns its
 * space.
 */

.cls-book {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card-bg);
    overflow: hidden;
}

/* A class of thirty with ten pieces of work scrolls sideways rather than
   crushing the columns to unreadable slivers. The name column is what you
   scroll against, so it stays put. */
.mb-scroll { overflow-x: auto; }
.mb-grid { min-width: max-content; }

.mb-row,
.mb-head,
.mb-foot {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 0 14px;
    border-bottom: 1px solid var(--border);
}

.mb-row:hover { background: rgba(54, 96, 255, 0.04); }
.mb-foot { border-bottom: 0; }

/* The name column. Sticky so a wide markbook can be scrolled sideways without
   losing track of whose row you are reading. */
.mb-name {
    position: sticky;
    left: 0;
    z-index: 2;
    flex: none;
    width: 11.5rem;
    padding: 9px 0;
    background: inherit;
    font-size: 0.9rem;
    font-weight: 600;
}

.mb-head .mb-name,
.mb-foot .mb-name {
    font-weight: 700;
    font-size: 0.78rem;
    color: var(--text-muted);
}

.mb-head { background: rgba(30, 27, 46, 0.025); align-items: stretch; }
.mb-foot { background: rgba(30, 27, 46, 0.025); }
.mb-row { background: var(--card-bg); }

.mb-cells { display: flex; gap: 6px; flex: none; }

/* ── Column headings ─────────────────────────────────────────────────── */

.mb-col {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    gap: 2px;
    width: 5.5rem;
    padding: 10px 6px 8px;
    border: 0;
    background: none;
    font: inherit;
    text-align: left;
    cursor: pointer;
    border-radius: 8px 8px 0 0;
}

.mb-col:hover { background: rgba(54, 96, 255, 0.07); }

/* Two lines, clamped. A quiz title is a sentence and a column is 5.5rem, so
   the choice is between truncating and a column wide enough to make the grid
   unreadable. The full title is in the tooltip and on the panel. */
.mb-col-name {
    font-size: 0.76rem;
    font-weight: 700;
    line-height: 1.25;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    /* A single long word ("Photosynthesis") has no break opportunity, so it
       overflowed the column and was clipped mid-letter rather than wrapping
       into the second line the clamp had already reserved for it. */
    overflow-wrap: anywhere;
}

.mb-col-due { font-size: 0.7rem; color: var(--text-muted); }
.mb-col-due.is-late { color: #b23b3b; font-weight: 700; }

/* ── Cells ───────────────────────────────────────────────────────────── */
/*
 * Banded, so a row reads as a shape rather than as six numbers to compare in
 * your head. The bands are wide on purpose — this is "roughly how did they
 * do", and a teacher who wants the real mark opens the paper.
 */

.mb-cell {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 5.5rem;
    height: 34px;
    border: 0;
    border-radius: 8px;
    font: inherit;
    font-size: 0.82rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    cursor: pointer;
}

.mb-cell.is-hi { background: #e3f4ea; color: #1c7a4a; }
.mb-cell.is-mid { background: #fdf3d7; color: #8a6100; }
.mb-cell.is-lo { background: #fdecec; color: #b23b3b; }

/* Read. A worksheet or a booklet has no mark and never will have one, so the
   cell says the only thing there is to say — they opened it. A tick rather than
   a number, because a number here would invite comparison with the marks in the
   columns either side. */
.mb-cell.is-read {
    background: #e3f4ea;
    color: #1c7a4a;
    font-size: 0.9rem;
    cursor: default;
}

/* Handed in, not marked. A state of its own: showing it as a gap would read as
   "did not do it", which is the one thing a markbook must not say by mistake
   about a pupil who handed their work in on time. */
.mb-cell.is-in {
    background: rgba(30, 27, 46, 0.05);
    color: var(--text-muted);
    font-weight: 600;
    font-size: 0.74rem;
}

/* Nothing handed in. A faint dot rather than an empty box: a column of holes
   is hard to count, and a column of dots reads as a row of absences. */
.mb-cell.is-none {
    color: rgba(107, 101, 128, 0.4);
    background: none;
    cursor: default;
    font-weight: 400;
}

.mb-cell:not(.is-none):hover { outline: 2px solid var(--accent); outline-offset: -2px; }
.mb-cell:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ── Average and row actions ─────────────────────────────────────────── */

.mb-avg {
    flex: none;
    width: 3.4rem;
    text-align: right;
    font-weight: 800;
    font-size: 0.9rem;
    font-variant-numeric: tabular-nums;
}

.mb-head-avg,
.mb-head-name { display: flex; align-items: flex-end; padding-bottom: 8px; }

.mb-head-avg {
    font-size: 0.72rem;
    font-weight: 700;
    color: var(--text-muted);
    justify-content: flex-end;
}

.mb-avg.is-empty { color: rgba(107, 101, 128, 0.45); font-weight: 500; }

/* How many handed each piece in. The question a teacher asks the moment a
   deadline passes, in the place a total belongs. */
.mb-total {
    width: 5.5rem;
    text-align: center;
    padding: 8px 0;
    font-size: 0.76rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
}

.mb-total.is-short { color: #8a6100; }

.mb-actions {
    flex: none;
    display: flex;
    gap: 2px;
    width: 12rem;
    justify-content: flex-end;
    opacity: 0;
    transition: opacity 0.12s ease;
}

.mb-row:hover .mb-actions,
.mb-row:focus-within .mb-actions { opacity: 1; }

@media (hover: none) { .mb-actions { opacity: 1; } }

@media (max-width: 800px) {
    /* The row actions are the first thing to go: they are a once-a-term job and
       the grid is the point of the page. More → Print class list still reaches
       every pupil. */
    .mb-actions { display: none; }
    .mb-name { width: 8.5rem; }
}

/* ── The assignment panel ────────────────────────────────────────────── */

.asg-panel-due { margin: 0 0 12px; font-weight: 600; }
.asg-panel-due.is-late { color: #b23b3b; }

.asg-panel-missing {
    margin: 4px 0 0;
    font-size: 0.88rem;
    line-height: 1.5;
    color: var(--text-muted);
}

/* ── The classes list ──────────────────────────────────────────────────
 *
 * Rows, not cards. A teacher has four or five classes, and at that count
 * aligned columns are read DOWN in one pass — which class needs me — where
 * cards make each one its own little layout to parse. It also matches the
 * markbook each row opens into, so the two pages read as one product.
 */
.cls-list {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card-bg);
    overflow: hidden;
}

.cls-row {
    display: flex;
    align-items: center;
    gap: 16px;
    width: 100%;
    padding: 14px 16px;
    border: 0;
    border-bottom: 1px solid var(--border);
    background: none;
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
    transition: background-color 0.12s ease;
}

.cls-row:last-child { border-bottom: 0; }
.cls-row:hover { background: rgba(54, 96, 255, 0.05); }
.cls-row:hover .cls-row-name { color: var(--accent); }
.cls-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.cls-row-name { flex: none; width: 11rem; font-weight: 700; font-size: 0.98rem; }
.cls-row-year { flex: none; width: 4.5rem; color: var(--text-muted); font-size: 0.85rem; }
.cls-row-size { flex: none; width: 5rem; color: var(--text-muted); font-size: 0.85rem; }

.cls-row-bar {
    flex: none;
    width: 90px;
    height: 8px;
    border-radius: 999px;
    background: rgba(30, 27, 46, 0.14);
    overflow: hidden;
}

.cls-row-bar-fill {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: var(--accent);
    transition: width 0.3s ease;
}

.cls-row-status { flex: 1; min-width: 0; font-size: 0.88rem; }
.cls-row-status strong { font-weight: 800; }
.cls-row-done { color: #1c7a4a; font-weight: 600; }
.cls-row-quiet { color: var(--text-muted); }
.cls-row-late { flex: none; }

@media (max-width: 760px) {
    /* Stacked: the bar and the year go first, since the name and what needs
       doing are the two things that survive at any width. */
    .cls-row { flex-wrap: wrap; gap: 8px 12px; }
    .cls-row-bar, .cls-row-year { display: none; }
    .cls-row-name { width: auto; flex: 1; }
    .cls-row-status { flex-basis: 100%; }
}

/* Nothing set: the track holds its column so the rows stay aligned, but is not
   drawn — an empty bar reads as "none of it done", which is a different and
   much worse thing to say about a class than "nothing has been set". */
.cls-row-bar.is-none { visibility: hidden; }

/* Its own class, not .share-label — that is an uppercase FIELD label, and a
   heading over a list of children's names should not shout. Third time this
   trap has been walked into in this file. */
.asg-panel-heading {
    margin: 16px 0 2px;
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--text-muted);
}

/* ── Tutoring (public/js/tutoring.js) ──────────────────────────────────────
   Built from the app's own tokens so it follows the theme. Status is carried
   by a pill and a background tint, never by a coloured edge. */

.tut-view { padding-bottom: 48px; }

.tut-tabs {
    display: flex;
    gap: 4px;
    flex-wrap: wrap;
    margin-bottom: 18px;
    border-bottom: 1px solid var(--border);
}
.tut-tab {
    padding: 10px 14px;
    color: var(--text-muted);
    font-weight: 700;
    font-size: 0.92rem;
    text-decoration: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
}
.tut-tab:hover { color: var(--text); }
.tut-tab[aria-current="page"] { color: var(--text); border-bottom-color: var(--accent); }

.tut-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 18px;
}
.tut-title { font-size: 1.6rem; font-weight: 800; letter-spacing: -0.01em; }
.tut-hint { color: var(--text-muted); font-size: 0.85rem; line-height: 1.5; margin-top: 4px; }
.tut-more { margin: 12px 0; }
.tut-more > summary { cursor: pointer; font-weight: 600; font-size: 0.9rem; padding: 4px 0; }
.tut-head-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.tut-head-actions .tut-input { width: auto; min-width: 160px; }
.tut-back { margin-bottom: 10px; display: inline-flex; }
.tut-loading { color: var(--text-muted); padding: 24px 0; }

/* The week: seven columns on a wide screen, a list of days on a phone. */
.tut-week {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 8px;
}
.tut-day {
    background: var(--surface-2);
    border-radius: var(--radius);
    padding: 8px;
    min-height: 140px;
}
.tut-day.is-today { background: rgba(54, 96, 255, 0.07); }
.tut-day-head {
    font-size: 0.78rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
    margin: 2px 4px 8px;
}
.tut-day-head span { color: var(--text); margin-left: 2px; }
.tut-day.is-today .tut-day-head span { color: var(--accent); }
.tut-day-empty { color: var(--text-muted); font-size: 0.85rem; margin: 4px; }
.tut-empty-note { color: var(--text-muted); margin-top: 16px; }

@media (max-width: 900px) {
    .tut-week { grid-template-columns: 1fr; }
    .tut-day { min-height: 0; }
}

.tut-session {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 8px 10px;
    margin-bottom: 6px;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    color: var(--text);
    text-decoration: none;
    font-size: 0.85rem;
    min-width: 0;
}
.tut-session:hover { border-color: var(--accent); }
.tut-session:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tut-session.is-cancelled,
.tut-session.is-cancelled_chargeable { opacity: 0.6; }
.tut-session.is-cancelled .tut-session-title { text-decoration: line-through; }
.tut-session.is-complete { background: rgba(0, 199, 141, 0.06); }
.tut-session-time { font-weight: 700; font-variant-numeric: tabular-nums; }
.tut-session-title { font-weight: 700; overflow-wrap: anywhere; }
.tut-session-meta { color: var(--text-muted); overflow-wrap: anywhere; }
.tut-session-foot { display: flex; gap: 4px; flex-wrap: wrap; }
.tut-session-foot:empty { display: none; }

.tut-pill,
.tut-flag {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 800;
    white-space: nowrap;
}
.tut-pill { color: var(--tone); background: var(--tone-bg); }
.tut-flag { color: var(--text-muted); background: var(--surface-2); }
.tut-flag-todo { color: #8f620d; background: rgba(244, 180, 71, 0.24); }
.tut-flag-done { color: #067f5c; background: rgba(0, 199, 141, 0.16); }

.tut-cols {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
    gap: 16px;
    align-items: start;
}
@media (max-width: 900px) { .tut-cols { grid-template-columns: 1fr; } }

.tut-card {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 18px 20px;
    margin-bottom: 16px;
}
.tut-card-title {
    font-size: 1rem;
    font-weight: 800;
    margin: 18px 0 10px;
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}
.tut-card-title:first-child { margin-top: 0; }
.tut-prose { white-space: pre-wrap; line-height: 1.6; }

.tut-list,
.tut-register { list-style: none; margin: 0 0 10px; padding: 0; }
.tut-list li,
.tut-register li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    padding: 8px 0;
    border-bottom: 1px solid var(--border);
}
.tut-list li:last-child,
.tut-register li:last-child { border-bottom: 0; }
.tut-register .tut-input { width: auto; }

.tut-rates { display: flex; gap: 20px; margin: 12px 0 0; flex-wrap: wrap; }
.tut-rates dt { font-size: 0.75rem; color: var(--text-muted); font-weight: 700; }
.tut-rates dd { margin: 0; font-weight: 700; }

.tut-chips { display: flex; gap: 8px; flex-wrap: wrap; margin: 8px 0; }
.tut-code {
    display: inline-block;
    padding: 1px 6px;
    border-radius: 6px;
    background: var(--surface-2);
    font-variant-numeric: tabular-nums;
    font-size: 0.8rem;
    font-weight: 700;
}

.tut-field { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; min-width: 0; }
.tut-field-end { justify-content: flex-end; }
.tut-label { font-size: 0.82rem; font-weight: 700; }
.tut-label em { font-style: normal; font-weight: 500; color: var(--text-muted); }
.tut-input {
    width: 100%;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    color: var(--text);
    font: inherit;
    font-size: 0.92rem;
}
.tut-input:focus { outline: 2px solid var(--accent); outline-offset: 0; border-color: var(--accent); }
textarea.tut-input { resize: vertical; line-height: 1.5; }
.tut-check { display: flex; gap: 8px; align-items: center; font-size: 0.9rem; margin: 6px 0 12px; }
.tut-grid2,
.tut-grid3 { display: grid; gap: 0 12px; }
.tut-grid2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.tut-grid3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 640px) { .tut-grid2, .tut-grid3 { grid-template-columns: 1fr; } }

.tut-table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius); background: var(--card-bg); }
.tut-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.tut-table th,
.tut-table td { padding: 10px 12px; text-align: left; border-bottom: 1px solid var(--border); white-space: nowrap; }
.tut-table th { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); }
.tut-table tr:last-child td { border-bottom: 0; }
.tut-table a { color: var(--accent); font-weight: 700; text-decoration: none; }
.tut-table a:hover { text-decoration: underline; }

.tut-empty { text-align: center; padding: 40px 16px; color: var(--text-muted); }
.tut-empty h2 { color: var(--text); font-size: 1.15rem; margin-bottom: 6px; }
.tut-empty p { max-width: 480px; margin: 0 auto; line-height: 1.55; }

.tut-menu-wrap { position: relative; }
.tut-menu {
    position: absolute;
    right: 0;
    top: calc(100% + 4px);
    z-index: 20;
    min-width: 230px;
    padding: 6px;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: var(--shadow-card);
}
.tut-menu button {
    display: block;
    width: 100%;
    text-align: left;
    padding: 8px 10px;
    border: 0;
    border-radius: 6px;
    background: none;
    color: var(--text);
    font: inherit;
    font-size: 0.9rem;
    cursor: pointer;
}
.tut-menu button:hover,
.tut-menu button:focus-visible { background: var(--surface-2); outline: none; }
.tut-menu .tut-menu-danger { color: var(--red-soft); }

/* The global reset's `margin: 0` removes the browser's own centring of a
   modal <dialog> (it is `margin: auto` against `inset: 0`), so put it back. */
.tut-dialog {
    position: fixed;
    inset: 0;
    margin: auto;
    height: fit-content;
    overflow-y: auto;
    width: min(520px, calc(100vw - 32px));
    max-height: calc(100vh - 48px);
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--card-bg);
    color: var(--text);
    box-shadow: var(--shadow-card);
}
.tut-dialog-wide { width: min(720px, calc(100vw - 32px)); }
.tut-dialog::backdrop { background: rgba(30, 27, 46, 0.4); }
.tut-dialog-form { padding: 20px 22px; }
.tut-dialog-title { font-size: 1.15rem; font-weight: 800; margin-bottom: 14px; }
.tut-dialog-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 8px; }

.tut-warn {
    padding: 10px 14px;
    border-radius: 8px;
    background: rgba(244, 180, 71, 0.14);
    border: 1px solid rgba(244, 180, 71, 0.45);
    font-size: 0.88rem;
    margin: 8px 0;
}
.tut-warn ul { margin: 6px 0 0 18px; }
.tut-cred {
    padding: 12px 14px;
    border-radius: 8px;
    background: var(--surface-2);
    font-size: 0.9rem;
    line-height: 1.6;
}
.tut-cred code { font-size: 1rem; font-weight: 700; }

.tut-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-bottom: 8px; }
@media (max-width: 640px) { .tut-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.tut-stats > div { background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 16px; }
.tut-stat { display: block; font-size: 1.4rem; font-weight: 800; font-variant-numeric: tabular-nums; }

.btn-danger { background: var(--red-soft); color: #fff; border: 0; }
.btn-danger:hover:not(:disabled) { filter: brightness(0.95); }
.msg-info { background: rgba(54, 96, 255, 0.07); color: var(--text); border: 1px solid rgba(54, 96, 255, 0.2); }
.tut-res-head {
    margin: 16px 0 8px;
    font-size: 0.75rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
}
.tut-res { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.tut-res-item {
    position: relative;
    display: flex;
    align-items: center;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--card-bg);
    transition: border-color 0.15s, background-color 0.15s;
}
.tut-res-item:hover { border-color: var(--tone, var(--accent)); background: var(--surface-2); }
.tut-res-link {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    color: var(--text);
    text-decoration: none;
    border-radius: 10px;
}
.tut-res-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tut-res-icon {
    flex: none;
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    border-radius: 8px;
    color: var(--tone);
    background: var(--tone-bg);
}
.tut-res-text { display: flex; flex-direction: column; min-width: 0; }
.tut-res-title {
    font-weight: 700;
    font-size: 0.9rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.tut-res-meta { font-size: 0.78rem; color: var(--text-muted); }
.tut-res-remove {
    flex: none;
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    margin-right: 6px;
    border: 0;
    border-radius: 8px;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.15s, background-color 0.15s, color 0.15s;
}
/* Share sits beside remove and is the same quiet square, but always visible:
   it is the thing still to do on the row, where remove is the exception. */
.tut-res-share {
    flex: none;
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    margin-right: 2px;
    border: 0;
    border-radius: 8px;
    background: none;
    color: var(--accent);
    cursor: pointer;
    transition: background-color 0.15s;
}
.tut-res-share:hover,
.tut-res-share:focus-visible { background: color-mix(in srgb, var(--accent) 12%, transparent); outline: none; }
.tut-res-share:disabled { opacity: 0.5; cursor: default; }
/* Shared: the same button, pressed. Clicking it stops sharing. */
.tut-res-share.is-on { color: var(--green); background: color-mix(in srgb, var(--green) 14%, transparent); }
.tut-res-share.is-on:hover,
.tut-res-share.is-on:focus-visible { background: color-mix(in srgb, var(--green) 24%, transparent); }
.tut-res-shared b { color: var(--green); font-weight: 600; }
/* Shown on hover or keyboard focus; always on touch, which has no hover. */
.tut-res-item:hover .tut-res-remove,
.tut-res-item:focus-within .tut-res-remove { opacity: 1; }
@media (hover: none) { .tut-res-remove { opacity: 1; } }
.tut-res-remove:hover,
.tut-res-remove:focus-visible { background: rgba(177, 70, 55, 0.1); color: var(--red-soft); outline: none; }

/* Tutoring office: enquiries, applications, profiles, checks, website. */
.tut-badge {
    display: inline-grid;
    place-items: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    margin-left: 4px;
    border-radius: 999px;
    background: var(--accent);
    color: #fff;
    font-size: 0.7rem;
    font-weight: 800;
    vertical-align: 1px;
}
.tut-narrow { max-width: 760px; }
.tut-card-title .btn { margin-left: auto; }

.tut-details { margin: 0 0 16px; display: grid; gap: 10px; }
.tut-details > div { display: grid; grid-template-columns: 120px minmax(0, 1fr); gap: 12px; }
.tut-details dt { color: var(--text-muted); font-size: 0.82rem; font-weight: 700; padding-top: 2px; }
.tut-details dd { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.tut-details a { color: var(--accent); }
@media (max-width: 520px) { .tut-details > div { grid-template-columns: 1fr; gap: 2px; } }

.tut-matches { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.tut-match {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--card-bg);
}
.tut-match.is-chosen { background: rgba(0, 199, 141, 0.07); border-color: rgba(0, 199, 141, 0.4); }
.tut-match-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.tut-match-name { font-weight: 800; display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.tut-match-why { font-size: 0.84rem; color: #067f5c; }
.tut-match-warn { font-size: 0.84rem; color: #8f620d; }

/* The reply to an enquiry: each tutor's trial times, tickable while it is a draft. */
.tut-reply-slots { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.tut-reply-slot {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 9px;
    border: 1px solid var(--border);
    border-radius: 999px;
    font-size: 0.84rem;
    font-variant-numeric: tabular-nums;
}
label.tut-reply-slot { cursor: pointer; }
.tut-row-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-top: 12px; }

/* A lesson its tutor can't take: amber, because someone has to act on it. */
.tut-banner-warn { background: rgba(244, 180, 71, 0.14); border-color: rgba(244, 180, 71, 0.6); }
.tut-cover-asks { border-color: rgba(54, 96, 255, 0.45); }
.tut-match > input[type="checkbox"] { flex: none; width: 18px; height: 18px; }

.tut-avail { overflow-x: auto; }
.tut-avail table { border-collapse: collapse; }
.tut-avail th, .tut-avail td { padding: 5px 8px; text-align: center; font-size: 0.82rem; }
.tut-avail thead th { color: var(--text-muted); font-weight: 700; }
.tut-avail tbody th { text-align: left; font-weight: 700; white-space: nowrap; }
.tut-avail input { width: 17px; height: 17px; accent-color: var(--accent); }

.tut-checks li { align-items: flex-start; }
.tut-check-main { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-width: 0; }
.tut-check-meta { flex-basis: 100%; margin-top: 0; }
.tut-danger-link { color: var(--red-soft); margin-top: 4px; }

.tut-photo-row { display: flex; align-items: center; gap: 14px; margin: 12px 0 16px; flex-wrap: wrap; }
.tut-photo {
    width: 72px;
    height: 72px;
    border-radius: 50%;
    overflow: hidden;
    display: grid;
    place-items: center;
    background: var(--surface-2);
    color: var(--text-muted);
    font-size: 0.72rem;
    font-weight: 700;
    flex: none;
}
.tut-photo img { width: 100%; height: 100%; object-fit: cover; }
.tut-file { cursor: pointer; }

.tut-copy-row { display: flex; gap: 8px; align-items: flex-start; }
.tut-copy-row .tut-input { flex: 1; min-width: 0; }
.tut-code-box { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.78rem; }
.tut-switch { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; font-size: 0.85rem; }
.tut-switch input { width: 16px; height: 16px; accent-color: var(--accent); }
.tut-switch input:disabled + span { color: var(--text-muted); }
.tut-indent { margin: -2px 0 10px 26px; }

/* Tutoring subject and level pickers. */
.tut-pick { display: flex; flex-direction: column; gap: 6px; }
.tut-pick-many { display: flex; flex-direction: column; gap: 8px; }
.tut-chosen { display: flex; flex-wrap: wrap; gap: 6px; }
.tut-chosen:empty { display: none; }
.tut-chip {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 3px 4px 3px 10px;
    border-radius: 999px;
    background: var(--surface-2);
    font-size: 0.84rem;
    font-weight: 700;
}
.tut-chip button {
    display: grid;
    place-items: center;
    width: 20px;
    height: 20px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: none;
    color: var(--text-muted);
    font: inherit;
    line-height: 1;
    cursor: pointer;
}
.tut-chip button:hover, .tut-chip button:focus-visible { background: rgba(177, 70, 55, 0.12); color: var(--red-soft); }
.tut-pick-row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.tut-pick-row .tut-input { flex: 1 1 200px; width: auto; }
.tut-level-checks { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 0 12px; }
.tut-level-checks .tut-check { margin: 4px 0; }

/* Tutoring: dashboard, reviews, settings, branch filter. */
.tut-tabs { align-items: flex-end; }
.tut-branch-pick { margin-left: auto; padding-bottom: 6px; }
.tut-branch-pick .tut-input { width: auto; min-width: 170px; padding: 6px 10px; font-size: 0.85rem; }
.tut-subtabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 16px; }
.tut-subtabs button {
    padding: 7px 14px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card-bg);
    color: var(--text-muted);
    font: inherit;
    font-size: 0.88rem;
    font-weight: 700;
    cursor: pointer;
}
.tut-subtabs button[aria-current="page"] { color: var(--text); border-color: var(--accent); background: rgba(54, 96, 255, 0.07); }
.tut-labels { display: flex; gap: 6px; flex-wrap: wrap; margin: 0 0 12px; }
.tut-colour-row { display: flex; flex-wrap: wrap; gap: 8px 14px; }
.tut-colour-row label { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
.tut-url { font-size: 0.8rem; overflow-wrap: anywhere; }
.tut-cols-even { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 900px) { .tut-cols-even { grid-template-columns: 1fr; } }

.tut-stats > div .tut-hint { display: block; margin-top: 2px; }

/* Charts: one series, the app accent; recessive grid; rounded data ends. */
.tut-chart-card { position: relative; }
.tut-chart { position: relative; }
.tut-vbars {
    position: relative;
    display: grid;
    grid-template-columns: repeat(var(--n), minmax(0, 1fr));
    gap: 6px;
    align-items: end;
    height: 200px;
    margin: 10px 0 30px;
    padding-left: 56px;
    border-bottom: 1px solid var(--border);
}
.tut-grid-line { position: absolute; left: 56px; right: 0; border-top: 1px dashed var(--border); }
.tut-grid-line em { position: absolute; left: -56px; top: -8px; width: 50px; text-align: right; font-style: normal; font-size: 0.72rem; color: var(--text-muted); }
.tut-vbar { position: relative; height: 100%; display: flex; align-items: flex-end; outline: none; cursor: default; }
.tut-vbar-fill { display: block; width: 100%; max-width: 44px; margin: 0 auto; background: var(--accent); border-radius: 4px 4px 0 0; transition: opacity 0.12s; }
.tut-vbar:hover .tut-vbar-fill, .tut-vbar:focus-visible .tut-vbar-fill { opacity: 0.8; }
.tut-vbar:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tut-vbar-label { position: absolute; bottom: -22px; left: 0; right: 0; text-align: center; font-size: 0.72rem; color: var(--text-muted); white-space: nowrap; }
.tut-hbars { display: flex; flex-direction: column; gap: 8px; margin: 8px 0 12px; }
.tut-hbar { display: grid; grid-template-columns: minmax(80px, 30%) 1fr auto; gap: 10px; align-items: center; outline: none; }
.tut-hbar:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.tut-hbar-label { font-size: 0.85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tut-hbar-track { height: 14px; }
.tut-hbar-fill { display: block; height: 100%; background: var(--accent); border-radius: 0 4px 4px 0; }
.tut-hbar-value { font-size: 0.82rem; font-variant-numeric: tabular-nums; color: var(--text-muted); }
.tut-chart-table { margin: 8px 0; }
.tut-chart-toggle { margin-top: 2px; }
.tut-tooltip {
    position: absolute;
    z-index: 30;
    transform: translate(-50%, -100%);
    padding: 6px 10px;
    border-radius: 8px;
    background: var(--text);
    color: var(--card-bg);
    font-size: 0.8rem;
    font-weight: 700;
    white-space: nowrap;
    pointer-events: none;
}

.tut-reviews { list-style: none; margin: 0 0 10px; padding: 0; }
.tut-reviews li { padding: 12px 0; border-bottom: 1px solid var(--border); }
.tut-reviews li:last-child { border-bottom: 0; }
.tut-review-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; }
.tut-review-text { margin: 4px 0 8px; line-height: 1.55; white-space: pre-wrap; }
.tut-stars { color: #d69e2e; letter-spacing: 1px; }
.tut-stars span { color: var(--border); }

/* Tutoring: a sidebar of grouped sections beside the page. On a phone it
   becomes one row that scrolls sideways, headings dropped. */
.tut-layout { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 28px; align-items: start; }
.tut-side { position: sticky; top: 16px; display: flex; flex-direction: column; gap: 12px; }
.tut-side .tut-branch-pick { margin: 0; padding: 0; }
.tut-side .tut-branch-pick .tut-input { width: 100%; min-width: 0; }
.tut-nav { display: flex; flex-direction: column; gap: 1px; }
.tut-nav-group {
    margin: 14px 10px 4px;
    font-size: 0.7rem;
    font-weight: 800;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
}
.tut-nav-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 8px 10px;
    border-radius: 8px;
    color: var(--text-muted);
    font-weight: 700;
    font-size: 0.92rem;
    text-decoration: none;
}
.tut-nav-item:hover { color: var(--text); background: var(--surface-2); }
.tut-nav-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tut-nav-item[aria-current="page"] { color: var(--accent); background: rgba(54, 96, 255, 0.09); }
.tut-nav-item .tut-badge { margin-left: 0; }
@media (max-width: 900px) {
    .tut-layout { grid-template-columns: minmax(0, 1fr); gap: 12px; }
    .tut-side { position: static; }
    .tut-nav { flex-direction: row; overflow-x: auto; gap: 4px; padding-bottom: 4px; border-bottom: 1px solid var(--border); }
    .tut-nav-group { display: none; }
    .tut-nav-item { flex: none; white-space: nowrap; }
}

/* Tutoring: search and New, at the top of the sidebar */
.tut-quick { display: flex; flex-direction: column; gap: 8px; }
.tut-search { position: relative; }
.tut-search-input { padding-right: 32px; }
.tut-kbd { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); pointer-events: none;
    padding: 1px 6px; border: 1px solid var(--border); border-radius: 5px; background: var(--surface-2);
    font: inherit; font-size: 0.72rem; font-weight: 700; color: var(--text-muted); }
.tut-search-input:focus + .tut-kbd,
.tut-search-input:not(:placeholder-shown) + .tut-kbd { display: none; }
.tut-search-results { position: absolute; left: 0; top: calc(100% + 4px); z-index: 30; width: max(100%, 340px); max-width: calc(100vw - 32px);
    max-height: 60vh; overflow-y: auto; padding: 6px; background: var(--card-bg); border: 1px solid var(--border);
    border-radius: 10px; box-shadow: var(--shadow-card); }
.tut-search-hit { display: flex; flex-direction: column; gap: 1px; padding: 7px 10px; border-radius: 6px; color: var(--text); text-decoration: none; }
.tut-search-hit:hover,
.tut-search-hit.is-active { background: var(--surface-2); }
.tut-search-title { font-weight: 700; font-size: 0.9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tut-search-meta { font-size: 0.78rem; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tut-search-kind { font-weight: 700; }
.tut-search-none { padding: 8px 10px; margin: 0; color: var(--text-muted); font-size: 0.85rem; }
.tut-new-btn { width: 100%; }
.tut-qb-course { margin: -6px 0 12px; }
.tut-menu-left { left: 0; right: auto; min-width: 180px; }
.tut-badge.is-alert { background: var(--red-soft); }
@media (max-width: 900px) {
    .tut-quick { flex-direction: row; }
    .tut-search { flex: 1; min-width: 0; }
    .tut-new-btn { width: auto; white-space: nowrap; }
    .tut-kbd { display: none; }
}

/* Tutoring: book by clicking an empty time on the week */
.tut-cal-col { cursor: pointer; }
.tut-cal-ghost { position: absolute; left: 2px; right: 2px; z-index: 0; pointer-events: none; padding: 4px 7px;
    border: 1px dashed var(--accent); border-radius: 8px; background: rgba(54, 96, 255, 0.06);
    color: var(--accent); font-size: 0.75rem; font-weight: 800; font-variant-numeric: tabular-nums; }
@media (hover: none) { .tut-cal-ghost { display: none; } }

/* Tutoring: Home */
.tut-home { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); }
@media (max-width: 900px) { .tut-home { grid-template-columns: 1fr; } }
.tut-allclear { color: var(--text-muted); line-height: 1.55; margin: 0; }
.tut-mark { list-style: none; margin: 0 0 8px; padding: 0; }
.tut-mark-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
    padding: 8px 0; border-bottom: 1px solid var(--border); }
.tut-mark-row:last-child { border-bottom: 0; }
.tut-mark-what { display: flex; flex-direction: column; min-width: 0; color: var(--text); text-decoration: none; }
.tut-mark-what .tut-hint { margin-top: 0; }
.tut-mark-what:hover .tut-mark-title { color: var(--accent); }
.tut-mark-title { font-weight: 700; overflow-wrap: anywhere; }
.tut-mark-acts { display: flex; gap: 6px; }

/* Tutoring: a lesson's own online room */
.tut-room { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 8px 0 0; font-size: 0.88rem; }
.tut-room-label { font-weight: 700; color: var(--text-muted); }
.tut-room-url { color: var(--text-muted); overflow-wrap: anywhere; }
.tut-title a { color: inherit; text-decoration: none; }
.tut-title a:hover { text-decoration: underline; }
.tut-neighbours { margin: -4px 0 14px; }
.tut-nextup { margin-bottom: 16px; }
.tut-last summary { cursor: pointer; font-size: 0.88rem; color: var(--text-muted); }
.tut-last .tut-hint { margin: 10px 0 2px; font-weight: 700; }
.tut-section-tabs .tut-badge { margin-left: 6px; }
/* Today: the one thing the clock says comes next */
.tut-hero { margin-bottom: 16px; border-color: var(--accent); }
.tut-hero-kicker { margin: 0 0 2px; font-size: 0.8rem; font-weight: 700; color: var(--accent); }
.tut-hero-title { margin: 0 0 12px; font-size: 1.25rem; }
.tut-hero .tut-last { margin-top: 14px; }
.tut-report.is-review { outline: 2px solid var(--accent); outline-offset: 2px; }
.tut-report-tabs { margin-left: auto; font-weight: 400; }
.tut-report-tabs button { padding: 5px 11px; font-size: 0.8rem; }
.tut-report-tabs button span { font-size: 0.72rem; margin-left: 4px; }
.tut-report-sent { margin: 0 0 10px; }
.tut-mark-acts { flex-wrap: wrap; }
@media (max-width: 900px) {
    .tut-section-tabs { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 4px; }
    .tut-section-tabs button { flex: none; white-space: nowrap; }
    .tut-hero .btn { flex: 1 1 auto; text-align: center; }
}

/* Tutoring: lesson requests from families */
.tut-reqs { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.tut-req { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px 16px; flex-wrap: wrap;
    padding: 14px 18px; background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius); }
.tut-req-main { flex: 1; min-width: 260px; }
.tut-req-main p { margin: 0 0 4px; }
.tut-req-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.tut-req-head .tut-hint { margin: 0; }
.tut-req-what { font-variant-numeric: tabular-nums; }
.tut-req-what s { color: var(--text-muted); }
.tut-req-kind { display: inline-block; margin-right: 4px; padding: 1px 8px; border-radius: 999px; background: var(--surface-2);
    font-size: 0.72rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); }
.tut-req-note { font-style: italic; overflow-wrap: anywhere; }
.tut-req-acts { display: flex; gap: 6px; flex-wrap: wrap; }

/* Tutoring: messages */
.tut-inbox { background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.tut-inbox-row { display: flex; align-items: flex-start; gap: 10px; padding: 12px 16px; color: var(--text); text-decoration: none; border-bottom: 1px solid var(--border); }
.tut-inbox-row:last-of-type { border-bottom: 0; }
.tut-inbox-row:hover { background: var(--surface-2); }
.tut-inbox-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tut-inbox-dot { flex: none; width: 8px; height: 8px; margin-top: 7px; border-radius: 50%; }
.tut-inbox-row.is-unread .tut-inbox-dot { background: var(--accent); }
.tut-inbox-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.tut-inbox-top { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.tut-inbox-title { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tut-inbox-row.is-unread .tut-inbox-title { font-weight: 800; }
.tut-inbox-context { font-size: 0.78rem; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tut-inbox-preview { font-size: 0.88rem; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tut-inbox-row.is-unread .tut-inbox-preview { color: var(--text); }
.tut-inbox-time { flex: none; font-size: 0.78rem; color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }

.tut-chat { display: flex; flex-direction: column; background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.tut-chat-log { display: flex; flex-direction: column; gap: 6px; padding: 16px; min-height: 240px; max-height: calc(100vh - 320px); overflow-y: auto; }
.tut-chat-empty { margin: auto; color: var(--text-muted); }
.tut-chat-day { display: flex; justify-content: center; margin: 10px 0 4px; }
.tut-chat-day span { font-size: 0.72rem; font-weight: 800; color: var(--text-muted); background: var(--surface-2); padding: 2px 10px; border-radius: 999px; }
.tut-msg { align-self: flex-start; max-width: min(560px, 85%); display: flex; flex-direction: column; gap: 2px; }
.tut-msg.is-mine { align-self: flex-end; align-items: flex-end; }
.tut-msg-who { font-size: 0.75rem; font-weight: 700; color: var(--text-muted); padding: 0 4px; }
.tut-msg-who em { font-style: normal; font-weight: 600; opacity: 0.8; }
.tut-msg-body { padding: 8px 12px; border-radius: 14px; background: var(--surface-2); color: var(--text); white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.45; }
.tut-msg.is-mine .tut-msg-body { background: var(--accent); color: #fff; }
.tut-msg-time { font-size: 0.7rem; color: var(--text-muted); padding: 0 4px; font-variant-numeric: tabular-nums; }
.tut-chat-compose { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 8px; align-items: end; padding: 12px 16px; border-top: 1px solid var(--border); }
.tut-chat-compose textarea { resize: vertical; min-height: 44px; max-height: 240px; }
.tut-chat-compose .msg, .tut-chat-tip { grid-column: 1 / -1; margin: 0; }
.tut-chat-closed { margin: 0; padding: 12px 16px; border-top: 1px solid var(--border); color: var(--text-muted); font-size: 0.9rem; }
@media (max-width: 640px) { .tut-chat-log { max-height: 60vh; } .tut-chat-tip { display: none; } }

/* Tutoring: filtering a list */
.tut-filter { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
.tut-filter-q { width: min(320px, 100%); }
.tut-seg { display: inline-flex; flex-wrap: wrap; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; background: var(--card-bg); }
.tut-seg button { border: 0; background: none; padding: 7px 12px; font: inherit; font-size: 0.85rem; font-weight: 700; color: var(--text-muted); cursor: pointer; }
.tut-seg button + button { border-left: 1px solid var(--border); }
.tut-seg button span { font-weight: 600; opacity: 0.75; margin-left: 2px; font-variant-numeric: tabular-nums; }
.tut-seg button:hover { color: var(--text); }
.tut-seg button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tut-seg button[aria-pressed="true"] { color: var(--accent); background: rgba(54, 96, 255, 0.09); }
.tut-filter-empty { padding: 20px 16px; margin: 0; color: var(--text-muted); text-align: center; }

/* The report's "What was covered" list: scrolls rather than stretching the form. */
.tut-covered .bk-topics-list { max-height: 340px; overflow-y: auto; }
/* Report by voice. */
.tut-hw-set { margin: -4px 0 12px; align-items: flex-start; }
.tut-hw-score { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--text); }
.tut-voice { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: -4px 0 12px; }
.tut-voice-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--text-muted); margin-right: 6px; }
#tut-voice.is-recording { border-color: var(--red-soft); color: var(--red-soft); }
#tut-voice.is-recording .tut-voice-dot { background: var(--red-soft); animation: tut-pulse 1.2s ease-in-out infinite; }
@keyframes tut-pulse { 50% { opacity: 0.3; } }
@media (prefers-reduced-motion: reduce) { #tut-voice.is-recording .tut-voice-dot { animation: none; } }

/* Tutoring: import preview */
.tut-imp-table { width: 100%; border-collapse: collapse; margin: 0 0 12px; font-size: 0.92rem; }
.tut-imp-table th, .tut-imp-table td { text-align: left; padding: 8px 0; border-bottom: 1px solid var(--border); vertical-align: top; }
.tut-imp-table th { width: 110px; font-weight: 600; padding-right: 12px; }
/* Reading from TutorCruncher: a hairline bar, the step under it. */
.tut-imp-status { margin-top: 16px; }
.tut-card > #tut-imp-before { margin-top: 10px; }
.tut-imp-bar { height: 4px; border-radius: 2px; background: var(--border); overflow: hidden; }
.tut-imp-bar > span { display: block; width: 0; height: 100%; border-radius: 2px; background: var(--accent); transition: width 0.3s ease-out; }
.tut-imp-step { margin: 10px 0 2px; font-size: 0.9rem; font-variant-numeric: tabular-nums; }
.tut-imp-status.is-waiting .tut-imp-bar > span { animation: tut-imp-wait 1.6s ease-in-out infinite; }
.tut-imp-status.is-waiting .tut-imp-step { color: var(--text-muted); }
@keyframes tut-imp-wait { 50% { opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) {
    .tut-imp-bar > span { transition: none; }
    .tut-imp-status.is-waiting .tut-imp-bar > span { animation: none; opacity: 0.5; }
}
.tut-imp-failed { margin: 0 0 12px; padding-left: 18px; font-size: 0.9rem; color: var(--text-muted); }

/* Tutoring: invoices */
.tut-stats-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); margin-bottom: 16px; }
@media (max-width: 640px) { .tut-stats-3 { grid-template-columns: 1fr; } }
.tut-num, .tut-table .tut-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.tut-view a.btn { text-decoration: none; }
.tut-inv-lines tfoot th, .tut-inv-lines tfoot td { border-top: 2px solid var(--border); padding-top: 10px; }
.tut-inv-lines tfoot th { text-align: left; }
.tut-inv-add { display: grid; grid-template-columns: minmax(0, 1fr) 220px auto; gap: 0 12px; align-items: end; margin-top: 16px; }
@media (max-width: 640px) { .tut-inv-add { grid-template-columns: 1fr; } }
.tut-inv-footer { white-space: pre-line; }
.tut-inv-lines td:first-child { white-space: normal; }
.tut-inv-lines tr:hover .tut-res-remove, .tut-inv-lines tr:focus-within .tut-res-remove { opacity: 1; }
.tut-inv-lines td.tut-num:last-child .tut-res-remove { margin-left: auto; }

/* Tutoring: tutor profile, read as a record */
.tut-prof-head { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; margin-bottom: 20px; }
.tut-prof-photo-wrap { position: relative; flex: none; }
.tut-prof-photo { width: 96px; height: 96px; border-radius: 50%; overflow: hidden; display: grid; place-items: center;
    background: var(--surface); border: 1px solid var(--border); font-size: 1.8rem; font-weight: 800; color: var(--text-muted); }
.tut-prof-photo img { width: 100%; height: 100%; object-fit: cover; }
.tut-prof-photo-btn { position: absolute; right: 0; bottom: 0; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center;
    background: var(--card-bg); border: 1px solid var(--border); color: var(--text); cursor: pointer; box-shadow: var(--shadow); }
.tut-prof-photo-btn:hover, .tut-prof-photo-btn:focus-within { color: var(--accent); border-color: var(--accent); }
.tut-prof-id { flex: 1; min-width: 200px; }
.tut-prof-id .tut-title { margin: 0; }
.tut-prof-headline { margin: 4px 0 0; color: var(--text-muted); }
.tut-prof-pills { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 8px 0 0; }
.tut-prof-head .tut-head-actions .btn, .tut-prof-edit { display: inline-flex; align-items: center; gap: 6px; }

.tut-banner { display: grid; gap: 4px; padding: 14px 18px; margin-bottom: 20px; border-radius: var(--radius);
    background: rgba(54, 96, 255, 0.07); border: 1px solid rgba(54, 96, 255, 0.35); color: var(--text); }

.tut-prof-cols { display: grid; grid-template-columns: minmax(0, 2fr) minmax(280px, 1fr); gap: 20px; align-items: start; }
@media (max-width: 900px) { .tut-prof-cols { grid-template-columns: 1fr; } }
.tut-prof-main, .tut-prof-side { display: grid; gap: 16px; min-width: 0; }
.tut-prof-card { padding: 0; margin: 0; overflow: hidden; }
.tut-prof-card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 20px; border-bottom: 1px solid var(--border); }
.tut-prof-card-head .tut-card-title { margin: 0; }
.tut-prof-card-body { padding: 16px 20px; }
.tut-prof-card-body > :last-child { margin-bottom: 0; }

/* Rows that run edge to edge, like a record */
.tut-prof-rows { list-style: none; margin: 0 -20px; padding: 0; }
.tut-prof-card-body > .tut-prof-rows:first-child, .tut-prof-card-body > .tut-facts:first-child { margin-top: -16px; }
.tut-prof-card-body > .tut-prof-rows:last-child, .tut-prof-card-body > .tut-facts:last-child { margin-bottom: -16px; }
.tut-prof-rows + .tut-facts { border-top: 1px solid var(--border); margin-top: 0; }
.tut-prof-rows li { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 12px 20px; border-bottom: 1px solid var(--border); }
.tut-prof-rows li:last-child { border-bottom: 0; }
.tut-prof-rows + :not(.tut-facts) { margin-top: 14px; }
.tut-quals li { display: block; }
.tut-dot { color: var(--text-muted); }
.tut-prof-levels { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 0 0 8px; }
.tut-prof-levels .tut-label { margin-right: 4px; }
.tut-level-badge { display: inline-block; padding: 2px 8px; border-radius: 6px; background: var(--text); color: var(--card-bg); font-size: 0.75rem; font-weight: 700; }

/* Label left, value right */
.tut-facts { margin: 0 -20px; }
.tut-facts > div { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 12px 20px; border-bottom: 1px solid var(--border); }
.tut-facts > div:last-child { border-bottom: 0; }
.tut-facts dt { color: var(--text-muted); }
.tut-facts dd { margin: 0; text-align: right; overflow-wrap: anywhere; }
.tut-facts a { color: var(--accent); }
.tut-fact-sub { display: block; font-size: 0.8rem; }
.tut-facts + * { margin-top: 14px; }
.tut-facts dd .tut-level-badge + .tut-level-badge { margin-left: 4px; }
.tut-tick { color: var(--green); font-size: 1.15rem; font-weight: 800; }
.tut-prof-address { font-style: normal; white-space: pre-line; margin: 4px 0 0; }

.tut-avail-read td { min-width: 34px; }
.tut-avail-on { display: inline-block; width: 14px; height: 14px; border-radius: 50%; background: var(--accent); }

/* A tutor's hours, painted on a week: a column a day, a cell each half hour. */
.tut-paint { --row: 18px; --time-w: 46px; margin-top: 12px; user-select: none; -webkit-user-select: none; }
.tut-paint-head,
.tut-paint-body { display: grid; grid-template-columns: var(--time-w) repeat(7, minmax(0, 1fr)); column-gap: 4px; }
.tut-paint-head { position: sticky; top: 0; z-index: 2; padding: 6px 0; background: var(--card-bg); font-size: 0.8rem; font-weight: 800; text-align: center; }
/* Dragging paints rather than scrolls; the page scrolls from anywhere else. */
.tut-paint-body { touch-action: none; padding: 8px 0; }
.tut-paint-times { position: relative; height: calc(var(--rows) * var(--row)); }
.tut-paint-times span { position: absolute; right: 6px; top: calc(var(--at) * var(--row)); transform: translateY(-50%);
    font-size: 0.7rem; color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.tut-paint-col { display: grid; grid-auto-rows: var(--row); border-radius: 8px; background: var(--surface-2); overflow: hidden; cursor: crosshair; }
.tut-paint-cell { all: unset; box-sizing: border-box; display: block; min-width: 0; height: var(--row); padding: 0 5px;
    font-size: 0.66rem; font-weight: 800; line-height: var(--row); color: #fff; font-variant-numeric: tabular-nums; overflow: hidden; white-space: nowrap; }
/* A line on each hour; the half hours are left clear. */
.tut-paint-cell:nth-child(odd) { box-shadow: inset 0 1px 0 var(--border); }
.tut-paint-cell:first-child { box-shadow: none; }
.tut-paint-cell:hover { background: color-mix(in srgb, var(--accent) 14%, transparent); }
.tut-paint-cell.is-on { background: var(--accent); box-shadow: none; }
.tut-paint-cell.is-start { border-radius: 6px 6px 0 0; }
.tut-paint-cell.is-end { border-radius: 0 0 6px 6px; }
.tut-paint-cell.is-start.is-end { border-radius: 6px; }
/* Mid-drag over painted time: what is about to go. */
.tut-paint-cell.is-going { background: repeating-linear-gradient(135deg, color-mix(in srgb, var(--accent) 35%, transparent) 0 4px, transparent 4px 8px); box-shadow: none; }
.tut-paint-cell:focus-visible { outline: 2px solid var(--text); outline-offset: -2px; }
.tut-paint-foot { display: flex; align-items: flex-start; gap: 16px; padding-top: 10px; border-top: 1px solid var(--border); }
.tut-paint-summary { flex: 1; margin: 0; display: flex; flex-wrap: wrap; gap: 2px 14px; font-size: 0.86rem; color: var(--text-muted); font-variant-numeric: tabular-nums; min-height: 2.6em; }
.tut-paint-summary b { color: var(--text); margin-right: 3px; }
.tut-paint-summary.is-live { color: var(--text); font-weight: 700; }
.tut-paint-foot .tut-linkbtn { flex: none; font-size: 0.85rem; }
@media (max-width: 560px) { .tut-paint { --row: 20px; --time-w: 36px; } .tut-paint-head, .tut-paint-body { column-gap: 2px; } }

/* The same week to read: a bar a day, the free stretches filled in. */
.tut-hweek { margin: 0; display: grid; gap: 7px; }
.tut-hweek > div { display: grid; grid-template-columns: 36px minmax(0, 1fr); gap: 10px; align-items: center; }
.tut-hweek dt { font-weight: 800; font-size: 0.82rem; }
.tut-hweek dt abbr { text-decoration: none; }
.tut-hweek dd { margin: 0; display: grid; grid-template-columns: minmax(80px, 1fr) minmax(0, 1.2fr); gap: 12px; align-items: center; }
.tut-hweek-track { position: relative; height: 10px; border-radius: 5px; background: var(--surface-2); overflow: hidden; }
.tut-hweek-track span { position: absolute; top: 0; bottom: 0; background: var(--accent); border-radius: 5px; }
.tut-hweek-text { font-size: 0.86rem; font-variant-numeric: tabular-nums; }
.tut-hweek .is-off dt, .tut-hweek .is-off .tut-hweek-text { color: var(--text-muted); font-weight: 500; }
@media (max-width: 520px) { .tut-hweek dd { grid-template-columns: 1fr; gap: 3px; } }

/* Qualification rows in the edit dialog */
.tut-qual-list { display: grid; gap: 12px; margin-bottom: 12px; }
.tut-qual-row { display: grid; grid-template-columns: 1.3fr 1fr 0.6fr 1.3fr 0.7fr auto; gap: 0 8px; align-items: end;
    padding-bottom: 12px; border-bottom: 1px solid var(--border); }
.tut-qual-row .tut-field { margin-bottom: 0; }
.tut-qual-remove { opacity: 1; margin: 0 0 6px; }
@media (max-width: 640px) { .tut-qual-row { grid-template-columns: 1fr 1fr; } }
@media (max-width: 520px) { .tut-avail-read td { min-width: 0; } .tut-avail-read th, .tut-avail-read td { padding: 5px 4px; } }

/* Tutoring: the schedule as a time grid */
.tut-cal-headbar { align-items: center; }
.tut-cal-summary { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 4px 0 0; color: var(--text-muted); font-size: 0.9rem; }
.tut-cal-nav { display: inline-flex; border: 1px solid var(--border); border-radius: 10px; overflow: hidden; background: var(--card-bg); }
.tut-cal-nav-btn { border: 0; background: none; color: var(--text); font: inherit; font-size: 0.88rem; font-weight: 700; padding: 7px 12px; cursor: pointer; min-width: 36px; }
.tut-cal-nav-btn + .tut-cal-nav-btn { border-left: 1px solid var(--border); }
.tut-cal-nav-btn:hover { background: var(--surface); }
.tut-cal-nav-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tut-cal-nav-btn[aria-pressed="true"] { color: var(--accent); }
.tut-cal-nav-btn:not(.tut-cal-nav-today) { font-size: 1.15rem; line-height: 1; }

.tut-cal { --axis-w: 56px; background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.tut-cal-days { display: grid; grid-template-columns: var(--axis-w) repeat(7, minmax(0, 1fr)); border-bottom: 1px solid var(--border);
    position: sticky; top: 0; z-index: 3; background: var(--card-bg); }
.tut-cal-dayhead { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 10px 4px 8px; }
.tut-cal-wd { font-size: 0.72rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); }
.tut-cal-dn { font-size: 1.15rem; font-weight: 800; width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; font-variant-numeric: tabular-nums; }
.tut-cal-dayhead.is-today .tut-cal-wd { color: var(--accent); }
.tut-cal-dayhead.is-today .tut-cal-dn { background: var(--accent); color: #fff; }

.tut-cal-grid { position: relative; display: grid; grid-template-columns: var(--axis-w) repeat(7, minmax(0, 1fr)); }
.tut-cal-axis { position: relative; }
.tut-cal-axis span { position: absolute; right: 8px; transform: translateY(-50%); font-size: 0.72rem; color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.tut-cal-col { position: relative; border-left: 1px solid var(--border);
    background-image: linear-gradient(to bottom, var(--border) 1px, transparent 1px);
    background-size: 100% var(--hour-h); }
.tut-cal-col.is-today { background-color: rgba(54, 96, 255, 0.04); }

.tut-cal-block { position: absolute; display: flex; flex-direction: column; gap: 1px; padding: 5px 7px; overflow: hidden;
    border-radius: 8px; background: var(--tone-bg); color: var(--text); text-decoration: none; font-size: 0.78rem; line-height: 1.3;
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tone) 22%, transparent); z-index: 1; transition: box-shadow 0.12s, transform 0.12s; }
.tut-cal-block:hover { box-shadow: inset 0 0 0 1px var(--tone), 0 4px 12px rgba(0, 0, 0, 0.1); z-index: 2; }
.tut-cal-block:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; z-index: 2; }
.tut-cal-title { font-weight: 800; color: var(--tone); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tut-cal-time { font-variant-numeric: tabular-nums; color: var(--text); opacity: 0.8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tut-cal-meta { color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tut-cal-block.is-tiny { flex-direction: row; align-items: center; gap: 6px; padding: 2px 7px; }
.tut-cal-block.is-tiny .tut-cal-title { flex: 1; min-width: 0; }
.tut-cal-flag { margin-top: auto; align-self: flex-start; font-size: 0.68rem; font-weight: 800; padding: 1px 6px; border-radius: 999px; white-space: nowrap; }
.tut-cal-flag.is-todo { background: rgba(244, 180, 71, 0.35); color: #7a520a; }
.tut-cal-flag.is-done { background: rgba(0, 199, 141, 0.2); color: #067f5c; }
/* Short blocks: the flag shrinks to a dot (or the tick) in the corner, and the title makes room for it */
.tut-cal-block.is-short, .tut-cal-block.is-tiny { padding-right: 18px; }
.tut-cal-block.is-short .tut-cal-flag, .tut-cal-block.is-tiny .tut-cal-flag { position: absolute; top: 5px; right: 5px; margin: 0; padding: 0;
    width: 10px; height: 10px; display: grid; place-items: center; font-size: 0.6rem; line-height: 1; }
.tut-cal-block.is-short .tut-cal-flag.is-todo, .tut-cal-block.is-tiny .tut-cal-flag.is-todo { background: #d99a1e; }
.tut-cal-block.is-short .tut-cal-flag > span, .tut-cal-block.is-tiny .tut-cal-flag > span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.tut-cal-block.is-tiny .tut-cal-flag { top: 50%; transform: translateY(-50%); }
.tut-cal-block.is-cancelled, .tut-cal-block.is-cancelled_chargeable {
    background: repeating-linear-gradient(135deg, var(--surface) 0 6px, transparent 6px 12px), var(--surface);
    box-shadow: inset 0 0 0 1px var(--border); opacity: 0.8; }
.tut-cal-block.is-cancelled .tut-cal-title, .tut-cal-block.is-cancelled_chargeable .tut-cal-title { color: var(--text-muted); text-decoration: line-through; }

.tut-cal-now { position: absolute; left: -1px; right: 0; height: 2px; background: #e5484d; z-index: 2; pointer-events: none; }
.tut-cal-now::before { content: ''; position: absolute; left: -5px; top: -4px; width: 10px; height: 10px; border-radius: 50%; background: #e5484d; }

/* Phones: the week as a list */
.tut-cal-list { display: none; }
@media (max-width: 900px) {
    .tut-cal { display: none; }
    .tut-cal-list { display: grid; }
}

/* Tutoring: tutor pay */
.tut-run-steps { margin: 0 0 16px; }
.tut-run-tutor .tut-prof-card-head { align-items: flex-start; }
.tut-run-tutor .tut-card-title a { color: inherit; text-decoration: none; }
.tut-run-tutor .tut-card-title a:hover { color: var(--accent); }
.tut-run-sub { margin: 4px 0 0; }
.tut-run-total { font-size: 1.25rem; font-weight: 800; font-variant-numeric: tabular-nums; white-space: nowrap; }
.tut-run-lines { margin: 0; }
.tut-run-lines td { padding-left: 20px; padding-right: 20px; }
.tut-run-lines tr:last-child td { border-bottom: 0; }
.tut-run-tutor .tut-prof-card-body { border-top: 1px solid var(--border); padding-top: 12px; padding-bottom: 12px; }
.tut-run-lines tr:hover .tut-res-remove, .tut-run-lines tr:focus-within .tut-res-remove { opacity: 1; }
.tut-linkbtn { border: 0; background: none; padding: 0; font: inherit; font-weight: 700; color: var(--accent); cursor: pointer; }
.tut-linkbtn:hover { text-decoration: underline; }
.tut-run-tutor { margin-bottom: 16px; }
.tut-run-total { text-align: right; }
.tut-run-vat { display: block; font-size: 0.78rem; font-weight: 600; }
.tut-pay-side { display: grid; gap: 16px; align-content: start; }
.tut-pay-side .tut-card { margin: 0; }
.tut-pay-statements { margin-top: 16px; }

/* Tutoring: My pay */
.tut-pay-block { margin-bottom: 16px; }
.tut-pay-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; align-items: start; margin-bottom: 16px; }
.tut-pay-grid .tut-prof-card { margin: 0; }
@media (max-width: 900px) { .tut-pay-grid { grid-template-columns: 1fr; } }
.tut-pay-form { margin-top: 16px; }
.tut-prof-card-body > .tut-pay-form:first-child { margin-top: 0; }
.tut-pay-remove { margin-left: auto; color: var(--red-soft); }

/* Choices as bordered rows: the whole row is the target */
.tut-options { border: 0; margin: 0 0 14px; padding: 0; display: grid; gap: 8px; }
.tut-option { display: flex; align-items: flex-start; gap: 10px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px; cursor: pointer; background: var(--card-bg); }
.tut-option:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); }
.tut-option:has(input:checked) { border-color: var(--accent); background: rgba(54, 96, 255, 0.05); }
.tut-option input { margin: 3px 0 0; accent-color: var(--accent); flex: none; }
.tut-option > span { display: grid; gap: 1px; }
.tut-option strong { font-size: 0.92rem; }
.tut-option .tut-hint { font-size: 0.82rem; }

/* Settings → Enquiries. Two groups under small headings, with room between
   them; hints sit under their boxes so the three boxes stay level. */
.tut-enq-lede { margin: 0 0 20px; max-width: 62ch; color: var(--text-muted); line-height: 1.55; }
.tut-enq-settings .tut-res-head { padding: 0; margin: 0 0 4px; }
.tut-enq-settings .tut-options { margin-bottom: 24px; }
.tut-enq-trial { border: 0; margin: 0 0 8px; padding: 0; min-width: 0; }
.tut-enq-trial .tut-res-head { margin-bottom: 10px; }
.tut-enq-trial .tut-grid3 { gap: 0 16px; }
.tut-enq-trial .tut-hint { margin-top: 2px; font-size: 0.8rem; }
.tut-money { position: relative; display: block; }
.tut-money > span { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--text-muted); pointer-events: none; }
.tut-view .tut-money .tut-input,
.tut-dialog .tut-money .tut-input,
.tut-money .tut-input { padding-left: 26px; font-variant-numeric: tabular-nums; }
.tut-enq-foot { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; padding-top: 16px; margin: 8px 0 12px; border-top: 1px solid var(--border); }
.tut-enq-summary { margin: 0; font-size: 0.9rem; font-weight: 600; }

/* Settings → Integrations → Lessonspace: its switches as rows, then the way out. */
.tut-ls-lede { margin: 0 0 14px; color: var(--text-muted); line-height: 1.55; }
.tut-ls-options { margin-bottom: 0; }
.tut-ls-options .tut-option .tut-hint { margin-top: 2px; line-height: 1.5; }
/* A choice that can't be made yet: still readable, since its line says why. */
.tut-option.is-disabled { cursor: default; background: transparent; border-style: dashed; }
.tut-option.is-disabled:hover { border-color: var(--border); }
.tut-option.is-disabled strong { color: var(--text-muted); }
.tut-ls-foot { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); }

/* Self-billing */
.tut-sb-lead { margin: 0 0 14px; max-width: 64ch; }
.tut-terms { margin: 6px 0 16px; padding-left: 20px; display: grid; gap: 6px; color: var(--text-muted); font-size: 0.9rem; max-width: 72ch; }
.tut-agreement { margin: 0 0 14px; }
.tut-agreement summary { cursor: pointer; color: var(--accent); font-weight: 700; font-size: 0.9rem; }
.tut-agreement[open] summary { margin-bottom: 4px; }
.tut-sb-actions { margin-top: 4px; }
/* Statements on a phone: each one a two-column block rather than a clipped table row */
@media (max-width: 640px) {
    .tut-stmt-table thead { display: none; }
    .tut-stmt-table tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 12px; padding: 12px 20px; border-bottom: 1px solid var(--border); }
    .tut-stmt-table tr:last-child { border-bottom: 0; }
    .tut-stmt-table td { border: 0; padding: 0; white-space: normal; text-align: left; }
    .tut-stmt-table td.tut-num { text-align: right; }
    .tut-stmt-table td:nth-child(2) { grid-column: 1; grid-row: 2; color: var(--text-muted); font-size: 0.88rem; }
    .tut-stmt-table td:nth-child(3) { grid-column: 1; grid-row: 3; }
    .tut-stmt-table td:nth-child(4) { grid-column: 2; grid-row: 1; font-weight: 700; }
    .tut-stmt-table td:nth-child(5) { grid-column: 2; grid-row: 2; }
}

/* Tutoring: subjects each with their own levels */
.tut-level-badges { display: flex; flex-wrap: wrap; gap: 4px; justify-content: flex-end; }
.tut-teach-list { display: grid; gap: 10px; margin-bottom: 12px; }
.tut-teach-row { border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; background: var(--card-bg); }
.tut-teach-head { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.tut-teach-head .tut-input { flex: 1; min-width: 0; }
.tut-teach-remove { opacity: 1; margin: 0; }
.tut-teach-levels { display: flex; flex-wrap: wrap; gap: 6px; }
.tut-level-toggle { position: relative; cursor: pointer; }
.tut-level-toggle input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.tut-level-toggle span { display: inline-block; padding: 4px 10px; border: 1px solid var(--border); border-radius: 999px; font-size: 0.82rem; font-weight: 600; color: var(--text-muted); background: var(--card-bg); }
.tut-level-toggle input:checked + span { background: var(--text); border-color: var(--text); color: var(--card-bg); }
.tut-level-toggle input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }
.tut-level-toggle:hover span { border-color: var(--text-muted); }
.tut-teach-add { margin-bottom: 18px; }

/* ── Tutoring: one rhythm ──────────────────────────────────────────────────
   The rules above grew page by page. These settle what they left uneven:
   every field the same size whatever its type, links that look like links
   of this app, rows that keep their actions on the right, and cards that
   end where their content does. Last in the file so they decide. */

/* The app styles text, email and password inputs, textareas and selects —
   but not tel, url, number, date or search — so a form mixing them had two
   sizes of box, some with a shadow. Tutoring's are all one. */
.tut-view .tut-input,
.tut-dialog .tut-input {
    padding: 8px 10px;
    min-height: 38px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--card-bg);
    box-shadow: none;
    font-size: 0.92rem;
    line-height: 1.4;
}
.tut-view textarea.tut-input,
.tut-dialog textarea.tut-input { line-height: 1.5; }
.tut-view .tut-input:focus,
.tut-dialog .tut-input:focus { outline: 2px solid var(--accent); outline-offset: 0; border-color: var(--accent); box-shadow: none; }

.tut-body a:not([class]),
.tut-dialog a:not([class]) { color: var(--accent); font-weight: 600; text-decoration: none; }
.tut-body a:not([class]):hover,
.tut-dialog a:not([class]):hover { text-decoration: underline; }
.tut-body .tut-title a { color: inherit; font-weight: inherit; }

/* Page head: the back link lines up with the title under it. */
.tut-back { margin: 0 0 6px; padding-left: 0; }
.tut-back:hover:not(:disabled) { background: none; border-color: transparent; color: var(--accent); }
.tut-head { margin-bottom: 20px; }
.tut-head .tut-hint + .tut-hint { margin-top: 6px; }
.tut-room { margin-top: 10px; }
.tut-neighbours { margin: -6px 0 16px; }

/* Cards: a section's title sits close to what it heads and well clear of
   what came before, and the card ends at its last line. */
.tut-card-title { margin: 24px 0 6px; }
.tut-card-title > .btn,
.tut-card-title > .tut-linkbtn { margin-left: auto; }
.tut-card > :last-child { margin-bottom: 0; }
.tut-card > ul:last-child > li:last-child { padding-bottom: 0; }
.tut-card > .tut-hint { margin-top: 0; }
.tut-card > .btn { margin-top: 10px; }

/* Rows: what it is on the left, taking the room; what you do on the right. */
.tut-list li > :first-child,
.tut-register li > :first-child,
.tut-mark-what { flex: 1 1 180px; min-width: 0; }
.tut-list li .tut-hint,
.tut-register li .tut-hint { margin-top: 0; }
.tut-list li .tut-chips { margin: 0; gap: 0; }
.tut-list li .btn-ghost.btn-sm { padding-inline: 8px; }
.tut-row-main { display: flex; flex-direction: column; gap: 1px; }
.tut-mark-row { padding: 10px 0; }

/* Tabs, wherever they are — a section's pages or Settings' own — are one
   control: labels on a line, the current one underlined. */
.tut-subtabs { gap: 2px; margin-bottom: 20px; border-bottom: 1px solid var(--border); }
.tut-subtabs button {
    padding: 8px 12px;
    margin-bottom: -1px;
    border: 0;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    background: none;
}
.tut-subtabs button:hover { color: var(--text); }
.tut-subtabs button[aria-current="page"] { color: var(--text); background: none; border-bottom-color: var(--accent); }
@media (max-width: 900px) {
    .tut-subtabs { flex-wrap: nowrap; overflow-x: auto; }
    .tut-subtabs button { flex: none; white-space: nowrap; }
}

/* Forms: ticks in an even column, and Copy beside what it copies. */
.tut-check { margin: 10px 0; align-items: flex-start; line-height: 1.4; }
.tut-check input { margin-top: 3px; flex: none; }
.tut-indent { margin: -4px 0 10px 26px; }
.tut-copy-row { align-items: center; }
.tut-copy-row:has(textarea) { align-items: flex-start; }
.tut-field { margin-bottom: 14px; }
.tut-dialog-actions { margin-top: 16px; }
/* A select draws its own few pixels of inset; without this its text starts
   further in than the text of the input beside it. */
.tut-view select.tut-input,
.tut-dialog select.tut-input { padding-left: 6px; }
.tut-register li > :first-child { flex-basis: 90px; }
.tut-list li .tut-chips { margin-left: -8px; }
/* Cards in a grid are spaced by the grid, not by their own margin as well:
   stacked on a phone, the two added up to a double gap. */
.tut-cols { margin-bottom: 16px; }
.tut-cols > .tut-card,
.tut-cols > div > .tut-card:last-child { margin-bottom: 0; }

/* ── Tutoring: nothing floats ──────────────────────────────────────────────
   Buttons sit on the page instead of hovering over it, every action looks
   like one, and a lesson's row is a grid: when, what, and what to do. */

/* The app's primary button carries a wide coloured glow and lifts on hover.
   On a marketing page that is a call to action; repeated on every row of a
   working list it is a row of things hovering. Flat here. */
.tut-view .btn-primary,
.tut-dialog .btn-primary { box-shadow: none; }
.tut-view .btn-primary:hover:not(:disabled),
.tut-dialog .btn-primary:hover:not(:disabled) { box-shadow: none; transform: none; }

/* A ghost button was bare text until hovered, so "Edit  Archive" at the end
   of a row read as two loose words. It is a quiet button now, with an edge. */
.tut-view .btn-ghost,
.tut-dialog .btn-ghost { border: 1px solid var(--border); background: transparent; color: var(--text); }
.tut-view .btn-ghost:hover:not(:disabled),
.tut-dialog .btn-ghost:hover:not(:disabled) { background: var(--card-bg); border-color: rgba(54, 96, 255, 0.35); }
.tut-view .tut-back,
.tut-view .tut-back:hover:not(:disabled) { border: 0; background: none; color: var(--text-muted); }
.tut-view .tut-back:hover:not(:disabled) { color: var(--accent); }
.tut-list li .tut-chips { margin-left: 0; gap: 6px; }
.tut-list li .btn-ghost.btn-sm { padding-inline: 12px; }
.tut-list li { padding: 10px 0; }

/* More than one thing at the end of a card's title. */
.tut-card-actions { display: flex; gap: 6px; margin-left: auto; }

/* A lesson: its time in a fixed column, so a list of them lines up down the
   left; then what it is; then what to do. In a narrow card the actions drop
   to a second line under the title — still in the grid, not loose below. */
.tut-mark { container-type: inline-size; list-style: none; margin: 0; padding: 0; }
.tut-lrow {
    display: grid;
    grid-template-columns: 46px minmax(0, 1fr) auto;
    column-gap: 14px;
    row-gap: 8px;
    align-items: center;
}
.tut-lrow.has-date { grid-template-columns: 88px minmax(0, 1fr) auto; }
.tut-lrow-time {
    align-self: start;
    display: flex;
    flex-direction: column;
    padding-top: 2px;
    font-size: 0.8rem;
    line-height: 1.4;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}
.tut-lrow-time b { font-size: 0.85rem; font-weight: 700; color: var(--text); }
.tut-lrow .tut-mark-acts { justify-content: flex-end; }
@container (max-width: 470px) {
    .tut-lrow .tut-mark-acts { grid-column: 2; justify-content: flex-start; }
}
.tut-side select { min-height: 38px; padding: 8px 6px; font-size: 0.92rem; background: var(--card-bg); box-shadow: none; }

/* ── Tutoring: the redesign ────────────────────────────────────────────────
   One idea runs through it: a tutoring company runs on time and on courses,
   so those two carry the page. Every lesson is a ticket whose tile is the
   course's colour and says when; the day reads top to bottom with "now"
   drawn in it; the lesson about to happen is the one dark thing on a light
   page. Surfaces are lifted by a soft shadow instead of boxed by a line,
   and the only button with weight is the one you came for.
   Last in the file, so it decides over everything above. */

.tut-view {
    --tut-ink: #1e1b2e;
    --tut-lift: 0 1px 2px rgba(30, 27, 46, 0.04), 0 10px 28px rgba(30, 27, 46, 0.06);
    --tut-tile: rgba(30, 27, 46, 0.035);
}

/* The frame: a panel of places on the left, the work on the right. */
.tut-layout { grid-template-columns: 216px minmax(0, 1fr); gap: 32px; }
.tut-side { padding: 12px; border-radius: 18px; background: var(--card-bg); box-shadow: var(--tut-lift); gap: 10px; }
.tut-nav { gap: 2px; }
.tut-nav-item { display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: 10px; font-weight: 700; }
.tut-nav-ico { flex: none; opacity: 0.75; }
.tut-nav-item > span:first-of-type { flex: 1; min-width: 0; }
.tut-nav-item:hover { background: var(--tut-tile); }
.tut-nav-item[aria-current="page"] { background: var(--tut-ink); color: #fff; }
.tut-nav-item[aria-current="page"] .tut-nav-ico { opacity: 1; }
.tut-nav-item[aria-current="page"] .tut-badge { background: #fff; color: var(--tut-ink); }
.tut-view .tut-search-input { border-radius: 10px; background: var(--tut-tile); border-color: transparent; }
.tut-view .tut-search-input:focus { background: var(--card-bg); }
@media (max-width: 900px) {
    .tut-side { padding: 10px; }
    .tut-nav { border-bottom: 0; padding-bottom: 0; }
    .tut-nav-item > span:first-of-type { flex: none; }
}

/* Type: a page says what it is in one confident line, with the small print
   above it, not under. */
.tut-eyebrow { margin: 0 0 4px; font-size: 0.74rem; font-weight: 800; letter-spacing: 0.09em; text-transform: uppercase; color: var(--text-muted); }
.tut-title { font-size: 1.9rem; letter-spacing: -0.02em; line-height: 1.15; }
.tut-head { margin-bottom: 22px; align-items: center; }

/* Surfaces. */
.tut-card { padding: 22px 24px; border: 0; border-radius: 18px; box-shadow: var(--tut-lift); }
.tut-card-title { font-size: 1.02rem; }
.tut-stats > div { border: 0; border-radius: 16px; box-shadow: var(--tut-lift); padding: 16px 18px; }
.tut-stat { font-size: 1.55rem; letter-spacing: -0.01em; }
.tut-table-wrap { border: 0; border-radius: 18px; box-shadow: var(--tut-lift); }
.tut-table th { padding: 14px 16px 10px; }
.tut-table td { padding: 14px 16px; }
.tut-table tbody tr:hover { background: var(--tut-tile); }
.tut-cal { border: 0; box-shadow: var(--tut-lift); }

/* Buttons: one filled, one outlined, one tinted. None of them glow. */
.tut-view .btn,
.tut-dialog .btn { border-radius: 10px; }
.tut-view .btn-primary,
.tut-dialog .btn-primary { background: var(--accent); }
.tut-view .btn-primary:hover:not(:disabled),
.tut-dialog .btn-primary:hover:not(:disabled) { background: var(--accent-dark); }
.tut-view .btn-secondary,
.tut-dialog .btn-secondary { background: var(--card-bg); }
.tut-view .btn-ghost,
.tut-dialog .btn-ghost { border: 0; background: var(--tut-tile); color: var(--text); }
.tut-view .btn-ghost:hover:not(:disabled),
.tut-dialog .btn-ghost:hover:not(:disabled) { border: 0; background: rgba(30, 27, 46, 0.08); }
.tut-view .tut-back,
.tut-view .tut-back:hover:not(:disabled) { background: none; }
.tut-view .tut-input,
.tut-dialog .tut-input { border-radius: 10px; }
.tut-dialog { border: 0; border-radius: 20px; }

/* The ticket. */
.tut-mark:has(> .tut-lrow) { display: flex; flex-direction: column; gap: 8px; }
.tut-lrow {
    display: grid;
    grid-template-columns: 58px minmax(0, 1fr) auto;
    column-gap: 14px;
    row-gap: 10px;
    align-items: center;
    padding: 8px 12px 8px 8px;
    border: 0;
    border-radius: 14px;
    background: var(--tut-tile);
    transition: background-color 0.15s;
}
.tut-lrow:hover { background: rgba(30, 27, 46, 0.06); }
.tut-lrow-time,
.tut-lrow-time.is-date {
    align-self: stretch;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 54px;
    padding: 6px 4px;
    border-radius: 10px;
    background: var(--tone-bg);
    color: var(--tone);
    font-size: 0.68rem;
    font-weight: 700;
    line-height: 1.2;
    text-align: center;
    font-variant-numeric: tabular-nums;
}
.tut-lrow-time b { font-size: 0.95rem; font-weight: 800; color: var(--tone); }
.tut-lrow-time.is-date span { text-transform: uppercase; letter-spacing: 0.06em; }
.tut-lrow-time.is-date b { font-size: 1.15rem; line-height: 1.1; }
.tut-lrow .tut-mark-what { gap: 3px; }
.tut-lrow .tut-mark-title { font-weight: 800; }
.tut-lrow-meta { display: flex; align-items: center; gap: 8px; min-width: 0; color: var(--text-muted); font-size: 0.84rem; line-height: 1.35; }
.tut-faces { display: flex; flex: none; }
.tut-face {
    display: grid;
    place-items: center;
    width: 20px;
    height: 20px;
    margin-left: -5px;
    border: 2px solid var(--card-bg);
    border-radius: 50%;
    background: var(--tone-bg);
    color: var(--tone);
    font-size: 0.62rem;
    font-weight: 800;
}
.tut-face:first-child { margin-left: 0; }
.tut-lrow.is-past .tut-lrow-time { background: rgba(30, 27, 46, 0.06); color: var(--text-muted); }
.tut-lrow.is-past .tut-lrow-time b { color: var(--text-muted); }
.tut-lrow.is-cancelled .tut-mark-title,
.tut-lrow.is-cancelled_chargeable .tut-mark-title { text-decoration: line-through; color: var(--text-muted); }
.tut-lrow .tut-mark-acts { justify-content: flex-end; }
@container (max-width: 470px) {
    .tut-lrow .tut-mark-acts { grid-column: 2; justify-content: flex-start; }
}

/* "Now", drawn in the day between what's done and what's coming. */
.tut-now { display: flex; align-items: center; gap: 10px; padding: 2px 0; color: var(--red-soft); font-size: 0.72rem; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase; list-style: none; }
.tut-now::before { content: ''; flex: none; width: 8px; height: 8px; border-radius: 50%; background: currentColor; }
.tut-now::after { content: ''; flex: 1; height: 2px; border-radius: 2px; background: currentColor; opacity: 0.35; }

/* The lesson about to happen: a plain white card like the rest, only bigger.
   The course colour shows in the kicker pill and nowhere else. */
.tut-hero { padding: 26px 28px; border: 0; border-radius: 22px; }
.tut-hero-kicker {
    display: inline-block;
    margin-bottom: 10px;
    padding: 4px 10px;
    border-radius: 999px;
    background: var(--tone-bg);
    color: var(--tone);
    font-size: 0.72rem;
    font-weight: 800;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}
.tut-hero-title { font-size: 1.6rem; font-weight: 800; letter-spacing: -0.02em; margin-bottom: 18px; }
.tut-hero .tut-chips { margin: 0; gap: 10px; }
.tut-view .tut-hero .btn { padding: 11px 20px; font-size: 0.95rem; }
.tut-hero .tut-last { margin-top: 20px; padding-top: 16px; border-top: 1px solid rgba(30, 27, 46, 0.08); }

/* A lesson's own page opens on a band in its course's colour. */
.tut-band { padding: 22px 24px; border-radius: 18px; background: var(--tone-bg); margin-bottom: 14px; align-items: flex-start; }
.tut-band .tut-hint { color: var(--text); opacity: 0.72; }
.tut-band .tut-room-label { color: var(--text); opacity: 0.72; }

/* Prepare, teach, report. */
.tut-steps { display: flex; gap: 6px; margin: 0 0 16px; padding: 0; list-style: none; }
.tut-steps li {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 9px 12px;
    border-radius: 12px;
    background: var(--tut-tile);
    color: var(--text-muted);
    font-size: 0.85rem;
    font-weight: 700;
}
.tut-step-dot { display: grid; place-items: center; flex: none; width: 20px; height: 20px; border-radius: 50%; background: rgba(30, 27, 46, 0.1); font-size: 0.7rem; font-weight: 800; }
.tut-steps .is-done { color: #067f5c; background: rgba(0, 199, 141, 0.12); }
.tut-steps .is-done .tut-step-dot { background: #067f5c; color: #fff; }
.tut-steps .is-now { color: #fff; background: var(--tut-ink); }
.tut-steps .is-now .tut-step-dot { background: #fff; color: var(--tut-ink); }
.tut-lrow { padding: 10px 12px 10px 10px; }
.tut-band .tut-hint,
.tut-band .tut-room-label { color: rgba(30, 27, 46, 0.72); opacity: 1; }
.tut-band .tut-pill { background: var(--card-bg); }
.tut-lrow-meta > span:last-child { min-width: 0; }
/* On a phone the places run along the top and the work takes the full width.
   Restated here because the frame above is declared after the original rule. */
@media (max-width: 900px) {
    .tut-layout { grid-template-columns: minmax(0, 1fr); gap: 14px; }
    .tut-side { position: static; }
    .tut-nav { flex-direction: row; overflow-x: auto; }
    .tut-nav-item { flex: none; white-space: nowrap; }
    .tut-title { font-size: 1.55rem; }
    .tut-hero { padding: 20px; border-radius: 18px; }
    .tut-hero-title { font-size: 1.3rem; }
    .tut-card { padding: 18px; }
    .tut-steps li { padding: 8px; font-size: 0.8rem; }
}

/* Small alignments the redesign left off.
   - A ticket keeps its own padding even as the last row of a card (an older
     rule strips the last row's, which left its buttons flush with the edge).
   - Its time tile is a fixed square at the top, not stretched to the height
     of a title that has wrapped.
   - Pupils' initials sit level with the first line of the names, not centred
     against however many lines those run to.
   - A page's actions sit level with its title, not centred against the
     title and its small print together. */
.tut-card > ul:last-child > li.tut-lrow:last-child { padding-bottom: 10px; }
.tut-lrow { align-items: start; }
.tut-lrow-time,
.tut-lrow-time.is-date { align-self: start; height: 56px; min-height: 0; }
.tut-lrow .tut-mark-what { align-self: center; }
.tut-lrow .tut-mark-acts { align-self: center; }
.tut-lrow-meta { align-items: flex-start; }
.tut-faces { padding-top: 1px; }
.tut-head { align-items: flex-start; }
.tut-head-actions { padding-top: 4px; }
@container (max-width: 470px) {
    .tut-lrow .tut-mark-what { align-self: start; }
}

/* Tutoring: the guide */
.tut-guide-steps,
.tut-guide-rules { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.tut-guide-steps li { display: flex; align-items: center; gap: 14px; padding: 12px 14px; border-radius: 14px; background: var(--tut-tile); }
.tut-guide-steps .tut-step-dot { width: 26px; height: 26px; font-size: 0.8rem; }
.tut-guide-steps .is-done .tut-step-dot { background: #067f5c; color: #fff; }
.tut-guide-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.tut-guide-text > span,
.tut-guide-rules li > span { color: var(--text-muted); font-size: 0.88rem; line-height: 1.5; }
.tut-guide-steps em,
.tut-guide-rules em { font-style: normal; font-weight: 700; color: var(--text); }
.tut-guide-lead { margin: 0 0 14px; line-height: 1.6; max-width: 70ch; }
.tut-guide-rules { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.tut-guide-rules li { display: flex; flex-direction: column; gap: 4px; padding: 14px 16px; border-radius: 14px; background: var(--tut-tile); }
@media (max-width: 700px) {
    .tut-guide-rules { grid-template-columns: 1fr; }
    .tut-guide-steps li { flex-wrap: wrap; }
    .tut-guide-text { flex-basis: calc(100% - 44px); }
}
.tut-guide-lead { margin-bottom: 10px; font-size: 1.02rem; }
/* The guide videos' player (guide-video.js): a video and its own controls. The scrub bar is one segment per
   chapter, each filling as it plays. The bar stays up while the video is
   paused, hovered or focused, and gets out of the way otherwise. */
.guide-vid { position: relative; max-width: 960px; margin-top: 4px; border-radius: 14px; overflow: hidden; background: #1e1b2e; color: #fff; }
.guide-vid video { display: block; width: 100%; aspect-ratio: 419 / 270; cursor: pointer; }
.guide-vid:fullscreen { max-width: none; border-radius: 0; display: grid; align-items: center; }
.guide-vid:fullscreen video { max-height: 100vh; }
.guide-vid-big { position: absolute; inset: 0; margin: auto; width: 76px; height: 76px; display: grid; place-items: center; border: 0; border-radius: 50%; background: var(--accent); color: #fff; cursor: pointer; box-shadow: 0 10px 30px rgba(30, 27, 46, 0.35); transition: transform 0.15s ease; }
.guide-vid-big svg { width: 32px; height: 32px; }
.guide-vid-big:hover { transform: scale(1.06); }
.guide-vid.is-started .guide-vid-big { display: none; }
.guide-vid-flash { position: absolute; top: 14px; left: 14px; max-width: calc(100% - 28px); margin: 0; padding: 7px 13px; border-radius: 999px; background: rgba(30, 27, 46, 0.82); font-size: 0.86rem; font-weight: 800; opacity: 0; transform: translateY(-6px); transition: opacity 0.25s ease, transform 0.25s ease; pointer-events: none; }
.guide-vid-flash.is-on { opacity: 1; transform: none; }
.guide-vid-bar { position: absolute; left: 0; right: 0; bottom: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; padding: 34px 12px 12px; background: linear-gradient(to top, rgba(20, 18, 32, 0.88), rgba(20, 18, 32, 0)); transition: opacity 0.2s ease; }
.guide-vid:not(.is-started) .guide-vid-bar,
.guide-vid.is-playing:not(:hover):not(:focus-within) .guide-vid-bar { opacity: 0; pointer-events: none; }
.guide-vid-btn { flex: none; width: 34px; height: 34px; display: grid; place-items: center; border: 0; border-radius: 9px; background: transparent; color: inherit; cursor: pointer; }
.guide-vid-btn:hover { background: rgba(255, 255, 255, 0.16); }
.guide-vid-btn[aria-pressed="false"] { opacity: 0.5; }
/* Subtitles sit above the controls, and drop down when the controls hide. */
.guide-vid-subs { position: absolute; left: 50%; bottom: 84px; transform: translateX(-50%); width: max-content; max-width: min(88%, 44em); margin: 0; padding: 5px 12px; border-radius: 8px; background: rgba(20, 18, 32, 0.82); font-size: clamp(0.82rem, 1.7vw, 1.05rem); font-weight: 700; line-height: 1.4; text-align: center; text-wrap: balance; pointer-events: none; transition: bottom 0.2s ease; }
.guide-vid.is-playing:not(:hover):not(:focus-within) .guide-vid-subs { bottom: 16px; }
.guide-vid:fullscreen .guide-vid-subs { font-size: 1.5rem; }
.guide-vid-time { flex: none; font-size: 0.8rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.guide-vid-now { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.8rem; font-weight: 700; color: rgba(255, 255, 255, 0.72); }
.guide-vid-track { order: -1; position: relative; flex: 0 0 100%; display: flex; gap: 3px; padding: 8px 0; cursor: pointer; touch-action: none; border-radius: 6px; }
.guide-vid-seg { flex: 1 1 0; min-width: 0; height: 5px; border-radius: 3px; overflow: hidden; background: rgba(255, 255, 255, 0.32); transition: height 0.12s ease; }
.guide-vid-track:hover .guide-vid-seg { height: 8px; }
.guide-vid-seg > i { display: block; width: 0; height: 100%; background: #fff; }
.guide-vid-tip { position: absolute; bottom: 100%; transform: translateX(-50%); padding: 5px 10px; border-radius: 8px; background: rgba(20, 18, 32, 0.94); font-size: 0.78rem; font-weight: 700; white-space: nowrap; pointer-events: none; }
.guide-vid-big:focus-visible,
.guide-vid-btn:focus-visible,
.guide-vid-track:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
/* Chapters read down each column like a contents page, so the times line up
   and a short last column is the only loose end. */
.guide-vid-chapters { list-style: none; max-width: 960px; margin: 14px 0 0; padding: 0; columns: 3 240px; column-gap: 20px; }
.guide-vid-chapters li { break-inside: avoid; padding-bottom: 2px; }
.guide-vid-chapters button { width: 100%; display: flex; align-items: baseline; gap: 12px; padding: 8px 12px; border: 0; border-radius: 10px; background: transparent; color: var(--text); font: inherit; font-size: 0.88rem; font-weight: 700; line-height: 1.35; text-align: left; cursor: pointer; }
.guide-vid-chapters button span { flex: none; width: 3.2em; color: var(--text-muted); font-size: 0.78rem; font-variant-numeric: tabular-nums; text-align: right; }
.guide-vid-chapters button:hover { background: rgba(30, 27, 46, 0.05); }
.guide-vid-chapters button[aria-current] { background: var(--accent); color: #fff; }
.guide-vid-chapters button[aria-current] span { color: rgba(255, 255, 255, 0.8); }
.guide-vid-chapters button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
    .guide-vid-big, .guide-vid-flash, .guide-vid-bar, .guide-vid-seg, .guide-vid-subs { transition: none; }
}

/* The same player in a dialog, for the dashboard's "Watch the guide". */
.guide-dialog { position: fixed; inset: 0; z-index: 3000; display: grid; place-items: center; padding: 16px; }
.guide-dialog[hidden] { display: none; }
.guide-dialog-backdrop { position: absolute; inset: 0; background: rgba(30, 27, 46, 0.45); backdrop-filter: blur(10px) saturate(1.1); -webkit-backdrop-filter: blur(10px) saturate(1.1); animation: fadeIn 0.25s ease both; }
.guide-dialog-card { position: relative; width: 100%; max-width: 1000px; max-height: calc(100vh - 32px); overflow-y: auto; padding: 16px 20px 20px; border: 1px solid var(--border); border-radius: 18px; background: var(--card-bg); box-shadow: 0 30px 80px rgba(30, 27, 46, 0.28), 0 4px 16px rgba(30, 27, 46, 0.12); }
.guide-dialog-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
.guide-dialog-head h2 { margin: 0; font-size: 1.1rem; font-weight: 900; color: var(--text); }
.guide-dialog-x { flex: none; width: 34px; height: 34px; border: 0; border-radius: 10px; background: transparent; color: var(--text-muted); font-size: 1.5rem; line-height: 1; cursor: pointer; }
.guide-dialog-x:hover { background: rgba(30, 27, 46, 0.07); color: var(--text); }
.guide-dialog-x:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.guide-dialog .guide-vid { margin-top: 0; }

/* Dashboard: the way in to the guide, beside the time-saved ticket. */
.dash-bar-side { display: flex; align-items: center; gap: 12px 14px; flex-wrap: wrap; }
.dash-guide-btn { display: inline-flex; align-items: center; gap: 8px; padding: 6px 13px; border: 1px solid var(--border); border-radius: 12px; background: var(--card-bg); color: var(--text); font: inherit; font-size: 0.85rem; font-weight: 800; cursor: pointer; }
.dash-guide-btn svg { flex: none; color: var(--accent); }
.dash-guide-btn span { font-weight: 700; color: var(--text-muted); }
.dash-guide-btn:hover { border-color: var(--accent); }
.dash-guide-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (max-width: 640px) { .dash-bar-side { width: 100%; } }
.tut-guide-example { display: inline-block; margin: 0 0 16px; padding: 8px 14px; border-radius: 10px; background: var(--tut-tile); font-size: 0.92rem; }
.tut-guide-example span { margin-right: 6px; font-size: 0.7rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted); }
.tut-guide-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.tut-guide-list li { position: relative; padding-left: 28px; line-height: 1.5; color: var(--text-muted); }
.tut-guide-list li strong { color: var(--text); }
.tut-guide-list li::before { content: '✓'; position: absolute; left: 0; top: 1px; display: grid; place-items: center; width: 18px; height: 18px; border-radius: 50%; background: rgba(0, 199, 141, 0.16); color: #067f5c; font-size: 0.68rem; font-weight: 800; }

/* Tables fit their card. Cells used to be kept on one line, so a long course
   name or a list of tutors pushed the table wider than the page and it had
   to be scrolled sideways. Text wraps now; the first column keeps enough
   room to read, and only a phone ever needs the scroll. */
.tut-table td { white-space: normal; overflow-wrap: break-word; vertical-align: top; }
.tut-table th { white-space: nowrap; }
.tut-table td:first-child { min-width: 170px; }
.tut-table th,
.tut-table td { padding-left: 14px; padding-right: 10px; }
.tut-table th:last-child,
.tut-table td:last-child { padding-right: 16px; }

/* A lesson's page heading: a white card with the course's colour held in the
   date tile, the same tile its ticket has everywhere else. A whole band of
   the tint went muddy against the cream page. */
.tut-band { flex-wrap: nowrap; gap: 18px; padding: 20px 22px; background: var(--card-bg); box-shadow: var(--tut-lift); }
.tut-band-main { flex: 1; min-width: 0; }
.tut-band-tile,
.tut-band-tile.is-date { flex: none; width: 64px; height: 68px; }
.tut-band-tile.is-date b { font-size: 1.4rem; }
.tut-band .tut-title { font-size: 1.6rem; }
.tut-band .tut-hint,
.tut-band .tut-room-label { color: var(--text-muted); }
.tut-band .tut-pill { background: var(--tone-bg); }
.tut-band .tut-pill.tone-green { --tone: #067f5c; --tone-bg: rgba(0, 199, 141, 0.16); }
/* Steps done are quiet and white with a green tick; only the one you are on
   is filled. */
.tut-steps li { background: var(--card-bg); box-shadow: var(--tut-lift); }
.tut-steps .is-done { background: var(--card-bg); color: var(--text); }
.tut-steps .is-now { background: var(--tut-ink); color: #fff; }
@media (max-width: 700px) {
    .tut-band { flex-wrap: wrap; }
    .tut-band-main { flex-basis: calc(100% - 82px); }
}

/* A lesson's way in: the buttons in a row, the small print under them. */
.tut-way { margin-top: 14px; }
.tut-way-acts { display: flex; flex-wrap: wrap; gap: 8px; }
.tut-way-note { margin: 8px 0 0; font-size: 0.82rem; line-height: 1.5; color: var(--text-muted); }
.tut-way-note .tut-linkbtn { font-size: inherit; font-weight: 700; }

/* Tabs: the line under them is drawn inside the row, so no button hangs a
   pixel below it — which, once the row can scroll sideways, showed up as a
   vertical scrollbar. */
.tut-subtabs { border-bottom: 0; box-shadow: inset 0 -1px 0 var(--border); overflow-y: hidden; }
.tut-subtabs button { margin-bottom: 0; }

/* ── Forms page ──
   One card per form: what it is and whether it is live across the top, where
   it lives in one bar under that, and (for applications) the questions it
   asks as a list read top to bottom, the way an applicant meets them. */
.tut-fcard-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.tut-fcard-title { font-size: 1.02rem; font-weight: 800; }
.tut-fcard-head .tut-hint { margin-top: 2px; }
.tut-fcard-state { display: flex; align-items: center; gap: 10px; flex: none; padding-top: 1px; font-size: 0.85rem; font-weight: 600; color: var(--text-muted); }
.tut-share {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 16px;
    padding: 6px 6px 6px 14px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface-2);
}
.tut-share-url {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.8rem;
    color: var(--text);
    text-decoration: none;
}
.tut-share-url:hover { text-decoration: underline; }
.tut-share .btn { flex: none; }
.tut-share-off { margin-top: 14px; }
.tut-qhead { margin: 22px 0 2px; padding-top: 18px; border-top: 1px solid var(--border); font-size: 0.82rem; font-weight: 700; }
.tut-qlist { list-style: none; margin: 0; padding: 0; }
.tut-qrow { display: flex; align-items: center; gap: 16px; padding: 13px 0; border-bottom: 1px solid var(--border); }
.tut-qrow-main { flex: 1; min-width: 0; }
.tut-qrow-q { font-size: 0.93rem; font-weight: 600; line-height: 1.35; }
.tut-qrow-meta { display: flex; align-items: center; gap: 6px; min-width: 0; margin-top: 5px; font-size: 0.8rem; line-height: 1.4; color: var(--text-muted); }
.tut-qrow-meta .tut-flag { flex: none; }
.tut-qrow-opts { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tut-qrow-side { display: flex; align-items: center; gap: 2px; flex: none; }
.tut-qadd {
    display: block;
    width: 100%;
    margin-top: 14px;
    padding: 11px;
    border: 1px dashed var(--border);
    border-radius: 12px;
    background: none;
    color: var(--text-muted);
    font: inherit;
    font-size: 0.88rem;
    font-weight: 600;
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.tut-qadd:hover { border-color: var(--accent); color: var(--accent); background: rgba(54, 96, 255, 0.05); }
.tut-qadd:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tut-card > .tut-qnote { margin-top: 12px; }
/* The same rows as a card's whole body (Website): ruled off from its heading,
   and nothing under the last one. A dropdown in a row is as wide as its words. */
.tut-qlist-top { margin-top: 16px; border-top: 1px solid var(--border); }
.tut-qlist-top .tut-qrow:last-child { border-bottom: 0; padding-bottom: 0; }
.tut-qrow-side .tut-input { width: auto; }
.tut-fcard-head-form { margin-bottom: 18px; }
@media (max-width: 600px) {
    .tut-share { flex-wrap: wrap; padding: 10px; }
    .tut-share-url { flex-basis: 100%; margin-bottom: 4px; }
    .tut-qrow { flex-wrap: wrap; gap: 8px; }
    .tut-qrow-main { flex-basis: 100%; }
}

/* ── Tutoring: the classroom's own pages (classroomPage and friends in
      public/js/tutoring.js) ───────────────────────────────────────────

   Made of the same things as the rest of tutoring: a lifted card on the
   paper, ink for what matters, the accent for the one series in the chart.
   The only drawing on these pages is the recording posters and the
   classroom in miniature. */
.cls-hero {
    margin-bottom: 18px;
    padding: 24px 26px;
    border-radius: 18px;
    background: var(--card-bg);
    color: var(--text);
    box-shadow: var(--tut-lift);
}
.cls-hero a { color: inherit; }

.cls-hero-top { display: flex; gap: 28px; align-items: flex-start; justify-content: space-between; }
.cls-hero-main { min-width: 0; }
.cls-eyebrow { display: flex; align-items: center; gap: 8px; margin: 0 0 14px; font-size: 0.74rem; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-muted); }
.cls-figure { margin: 0; font-size: clamp(2.4rem, 5vw, 3.3rem); font-weight: 900; line-height: 1; letter-spacing: -0.03em; }
.cls-figure small { font-size: 0.34em; font-weight: 800; letter-spacing: 0; margin-left: 0.12em; color: var(--text-muted); }
.cls-figure-label { margin: 8px 0 0; font-size: 1.05rem; font-weight: 700; color: var(--text-muted); }
.cls-delta { display: inline-flex; align-items: center; gap: 6px; margin: 12px 0 0; padding: 4px 10px; border-radius: 999px; background: var(--surface-2); font-size: 0.8rem; font-weight: 800; color: var(--text-muted); }
.cls-delta.is-up, .cls-chip.is-up { background: rgba(0, 199, 141, 0.16); color: #067f5c; }
.cls-delta i, .cls-chip i { font-style: normal; }
.cls-headline { margin: 0; font-size: clamp(1.9rem, 3.6vw, 2.3rem); font-weight: 900; line-height: 1.1; letter-spacing: -0.02em; color: var(--text); max-width: 16em; }
.cls-lede { margin: 10px 0 0; max-width: 34em; font-size: 1rem; line-height: 1.5; color: var(--text-muted); }
.cls-cta { margin-top: 20px; min-height: 46px; padding: 0 22px; border: 0; border-radius: 10px; background: var(--accent); color: #fff; font: inherit; font-size: 1rem; font-weight: 800; cursor: pointer; }
.cls-cta:hover:not(:disabled) { background: var(--accent-dark); }
.cls-cta:disabled { opacity: 0.6; cursor: default; }
a.cls-cta, .cls-hero a.cls-cta { display: inline-flex; align-items: center; color: #fff; text-decoration: none; }
a.cls-cta:hover { background: var(--accent-dark); }
.cls-switch-row.is-disabled a { pointer-events: auto; }
.cls-cta:focus-visible, .cls-join:focus-visible, .cls-months button:focus-visible, .cls-bar:focus-visible, .cls-search button:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* A dot that says something is being filmed. */
.cls-dot { flex: none; width: 9px; height: 9px; border-radius: 50%; background: rgba(30, 27, 46, 0.25); }
.cls-dot.is-rec { background: #e5484d; box-shadow: 0 0 0 0 rgba(229, 72, 77, 0.55); animation: cls-pulse 1.6s ease-out infinite; }
@keyframes cls-pulse { to { box-shadow: 0 0 0 9px rgba(255, 255, 255, 0); } }

/* Who is in a room now: a tinted panel on the card's right. */
.cls-live { flex: 0 1 340px; min-width: 240px; padding: 16px 18px; border-radius: 14px; background: var(--tut-tile); }
.cls-live-head { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; font-size: 0.78rem; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; }
.cls-live-quiet, .cls-live-more { margin: 0; font-size: 0.92rem; line-height: 1.45; color: var(--text-muted); }
.cls-live-row { display: flex; align-items: center; gap: 12px; padding: 10px 0; border-top: 1px solid var(--border); }
.cls-live-row:first-of-type { border-top: 0; padding-top: 0; }
.cls-live-row > div { flex: 1; min-width: 0; display: grid; gap: 2px; }
.cls-live-row a:first-child { color: var(--text); font-weight: 800; text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cls-live-row a:first-child:hover { text-decoration: underline; }
.cls-live-row span { font-size: 0.82rem; color: var(--text-muted); }
.cls-join { flex: none; padding: 8px 16px; border-radius: 10px; background: var(--accent); color: #fff !important; font-weight: 800; font-size: 0.9rem; text-decoration: none; }
.cls-join:hover { background: var(--accent-dark); }

/* Month by month, as chips. */
.cls-months { display: flex; gap: 4px; margin: 22px 0 0; overflow-x: auto; scrollbar-width: none; }
.cls-months::-webkit-scrollbar { display: none; }
.cls-months button { flex: none; min-height: 34px; padding: 0 14px; border: 0; border-radius: 999px; background: transparent; color: var(--text-muted); font: inherit; font-size: 0.88rem; font-weight: 800; cursor: pointer; }
.cls-months button:hover { background: var(--tut-tile); color: var(--text); }
.cls-months button[aria-pressed="true"] { background: var(--tut-ink); color: #fff; }

/* The month, in marker: a day a stroke, standing on a ruled line. One
   series, one pen; the day you point at goes to ink and the handwriting
   above says what it holds. */
.cls-sky { margin-top: 14px; }
.cls-sky-read { margin: 0 0 10px; min-height: 1.4em; font-size: 0.9rem; font-weight: 700; color: var(--text-muted); }
.cls-sky-read.is-day { color: var(--text); }
.cls-sky-plot { position: relative; display: grid; grid-template-columns: repeat(var(--n), minmax(0, 1fr)); gap: 3px; height: 150px; padding-bottom: 22px; border-top: 1px solid var(--border); }
.cls-sky-top { position: absolute; right: 0; top: 4px; font-size: 0.72rem; font-weight: 700; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.cls-bar { position: relative; display: flex; align-items: flex-end; justify-content: center; height: 100%; padding: 0; border: 0; background: transparent; cursor: pointer; }
.cls-bar i { display: block; width: 100%; max-width: 22px; height: var(--h); min-height: 3px; border-radius: 5px 5px 0 0; background: var(--accent); transform-origin: bottom; animation: cls-rise 0.7s cubic-bezier(0.2, 0.9, 0.3, 1) backwards; }
.cls-bar:nth-child(3n) i { animation-delay: 0.06s; }
.cls-bar:nth-child(3n + 1) i { animation-delay: 0.12s; }
.cls-bar.is-empty i { height: 3px; border-radius: 2px; background: rgba(30, 27, 46, 0.16); }
.cls-bar.is-ahead i { background: rgba(30, 27, 46, 0.07); }
.cls-bar:hover i, .cls-bar:focus-visible i { background: var(--tut-ink); }
.cls-bar em { position: absolute; left: 50%; bottom: -20px; transform: translateX(-50%); font-style: normal; font-size: 0.72rem; font-weight: 700; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.cls-bar.is-today em { font-weight: 900; color: var(--text); }
@keyframes cls-rise { from { transform: scaleY(0); } }

.cls-search { flex: 0 1 360px; display: flex; gap: 6px; padding: 4px; border-radius: 12px; background: var(--card-bg); box-shadow: var(--tut-lift); }
.cls-search input { flex: 1; min-width: 0; padding: 0 12px; border: 0; background: transparent; color: var(--text); font: inherit; font-weight: 600; }
.cls-search input::placeholder { color: var(--text-muted); }
.cls-search input:focus { outline: none; }
.cls-search:focus-within { border-color: var(--accent); }
.cls-search button { min-height: 38px; padding: 0 16px; border: 0; border-radius: 10px; background: var(--accent); color: #fff; font: inherit; font-weight: 800; cursor: pointer; }

/* A classroom in miniature: a board being written on, and two faces. */
.cls-mock { flex: none; width: min(320px, 38%); display: grid; grid-template-columns: 1fr 26%; gap: 8px; }
.cls-mock-board { aspect-ratio: 16 / 11; border: 2.5px solid #1e1b2e; border-radius: 12px; background: #e6ecff; }
.cls-mock-board svg { display: block; width: 100%; height: 100%; }
.cls-ink { stroke-dasharray: 320; stroke-dashoffset: 320; animation: cls-write 5s ease-in-out infinite; }
.cls-ink.a { stroke: #3660ff; }
.cls-ink.b { stroke: #1e1b2e; animation-delay: 0.5s; }
.cls-ink.c { stroke: #1e1b2e; animation-delay: 0.9s; }
.cls-ink.d { stroke: #8657d1; animation-delay: 1.3s; }
@keyframes cls-write { 0% { stroke-dashoffset: 320; } 45%, 85% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: 320; } }
.cls-mock-faces { display: grid; gap: 8px; align-content: start; }
.cls-mock-faces span { aspect-ratio: 1; display: grid; place-items: center; border: 2.5px solid #1e1b2e; border-radius: 12px; background: #f4b447; font-weight: 900; font-size: 1.1rem; color: #1e1b2e; }
.cls-mock-faces span + span { background: #00c78d; }

/* ── Below the card ── */

.cls-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; margin-bottom: 18px; }
.cls-tile { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 4px 14px; align-items: center; padding: 16px 18px; border-radius: 16px; background: var(--card-bg); box-shadow: var(--tut-lift); }
.cls-ico { display: grid; place-items: center; width: 42px; height: 42px; border-radius: 12px; background: var(--tut-tile); color: var(--text); }
.cls-tile b { display: block; font-size: 1.55rem; font-weight: 900; letter-spacing: -0.01em; line-height: 1.1; }
.cls-tile div span { display: block; font-size: 0.88rem; font-weight: 600; color: var(--text-muted); line-height: 1.4; }
.cls-chip { grid-column: 1 / -1; justify-self: start; margin-top: 8px; padding: 4px 10px; border-radius: 999px; background: var(--surface-2); font-style: normal; font-size: 0.78rem; font-weight: 800; color: var(--text-muted); }
.cls-points .cls-tile b { font-size: 1.05rem; letter-spacing: 0; margin-bottom: 4px; }
.cls-points .cls-tile { align-items: start; }

.cls-split { display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr); gap: 18px; align-items: start; }
.cls-block { padding: 22px 24px; border-radius: 18px; background: var(--card-bg); box-shadow: var(--tut-lift); }
.cls-block-title { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 0 0 16px; font-size: 1.1rem; font-weight: 900; letter-spacing: -0.01em; }
.cls-block-title a { font-size: 0.86rem; font-weight: 800; color: var(--accent); text-decoration: none; }
.cls-block-title a:hover { text-decoration: underline; }
.cls-none { margin: 0; color: var(--text-muted); line-height: 1.55; }

/* Recordings: a sheet of squared paper in one of the site's colours, with a
   line of marker across it and a button to press. */
.cls-recs { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 18px; }
.cls-split .cls-recs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cls-rec { display: flex; flex-direction: column; padding: 10px; border-radius: 18px; background: var(--card-bg); box-shadow: var(--tut-lift); }
.cls-block .cls-rec { padding: 0; border-radius: 0; background: none; box-shadow: none; }
.cls-rec-poster { --p: #3660ff; --p-soft: #e6ecff; position: relative; display: block; width: 100%; aspect-ratio: 16 / 9; padding: 0; border: 2.5px solid #1e1b2e; border-radius: 12px; overflow: hidden; color: #1e1b2e; font: inherit; cursor: pointer; background-color: var(--p-soft); background-image: linear-gradient(rgba(30, 27, 46, 0.07) 1px, transparent 1px), linear-gradient(90deg, rgba(30, 27, 46, 0.07) 1px, transparent 1px); background-size: 18px 18px; background-position: -1px -1px; transition: transform 0.15s ease, box-shadow 0.15s ease; }
.cls-rec-poster::before { content: ''; position: absolute; left: 6%; right: 6%; bottom: 10%; height: 42%; background: var(--p); opacity: 0.45; -webkit-mask: var(--cls-wave) center / 100% 100% no-repeat; mask: var(--cls-wave) center / 100% 100% no-repeat; --cls-wave: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 60' preserveAspectRatio='none'%3E%3Cpath d='M4 42C28 2 48 2 68 30s40 30 60 2 42-28 68 6' fill='none' stroke='black' stroke-width='5' stroke-linecap='round'/%3E%3C/svg%3E"); }
.cls-rec-poster.g2 { --p: #00a37a; --p-soft: #ddf6ec; }
.cls-rec-poster.g3 { --p: #d99314; --p-soft: #fdeecd; }
.cls-rec-poster.g4 { --p: #8657d1; --p-soft: #efe4fb; }
.cls-rec-poster.g5 { --p: #1e1b2e; --p-soft: #f1e9dd; }
.cls-rec-poster.is-live { --p: #e5484d; --p-soft: #fde5e1; cursor: default; }
.cls-rec-poster.is-failed { --p-soft: #efe9df; cursor: default; }
.cls-rec-poster.is-failed::before { display: none; }
button.cls-rec-poster:hover { transform: translate(-2px, -2px); box-shadow: 4px 4px 0 var(--p); }
button.cls-rec-poster:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.cls-play { position: absolute; left: 50%; top: 50%; display: grid; place-items: center; width: 54px; height: 54px; margin: -27px 0 0 -27px; padding-left: 3px; border-radius: 50%; background: #1e1b2e; color: #fff; box-shadow: 3px 3px 0 var(--p); transition: transform 0.18s cubic-bezier(0.3, 1.4, 0.5, 1); }
button.cls-rec-poster:hover .cls-play { transform: scale(1.1); }
.cls-rec-date, .cls-rec-len, .cls-rec-state { position: absolute; padding: 2px 9px; border: 2px solid #1e1b2e; border-radius: 999px; background: #fff; font-size: 0.74rem; font-weight: 800; }
.cls-rec-date { left: 8px; top: 8px; }
.cls-rec-len { right: 8px; bottom: 8px; font-variant-numeric: tabular-nums; }
.cls-rec-state { left: 50%; top: 50%; transform: translate(-50%, -50%); display: inline-flex; align-items: center; gap: 8px; padding: 5px 14px; font-size: 0.85rem; white-space: nowrap; }
.cls-rec-poster.is-failed .cls-rec-state { color: var(--text-muted); border-color: rgba(30, 27, 46, 0.45); }
.cls-rec-body { display: grid; gap: 4px; padding: 12px 4px 4px; }
.cls-rec-title { font-weight: 800; line-height: 1.3; color: var(--text); text-decoration: none; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.cls-rec-title:hover { color: var(--accent); }
.cls-rec-who { margin: 0; font-size: 0.86rem; color: var(--text-muted); }
.cls-rec-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: 6px 0 0; font-size: 0.8rem; font-weight: 700; color: var(--text-muted); }
.cls-rec-del { padding: 4px 10px; border: 0; border-radius: 999px; background: transparent; color: var(--text-muted); font: inherit; font-weight: 800; cursor: pointer; }
.cls-rec-del:hover { background: rgba(177, 70, 55, 0.12); color: var(--red-soft, #b14637); }
.cls-rec-del:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.cls-more { text-align: center; margin: 22px 0 0; }
.cls-video { display: block; width: 100%; max-height: 70vh; border-radius: 14px; background: #1e1b2e; }
.cls-empty { display: grid; justify-items: center; gap: 6px; padding: 44px 16px; text-align: center; color: var(--text-muted); }
.cls-empty .cls-mock { width: 220px; margin-bottom: 14px; }
.cls-empty h2 { margin: 0; font-size: 1.25rem; font-weight: 900; color: var(--text); }
.cls-empty p { max-width: 460px; margin: 0; line-height: 1.55; }

/* Who is teaching here: a name to a line, and a bar as long as the time
   they have taught. */
.cls-board { list-style: none; margin: 0; padding: 0; display: grid; }
.cls-board li { --c: #3660ff; --c-ink: #fff; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 8px 12px; align-items: center; padding: 14px 0; border-top: 1px solid var(--border); }
.cls-board li:first-child { padding-top: 0; border-top: 0; }
.cls-board li:last-child { padding-bottom: 0; }
.cls-board li:nth-child(4n + 2) { --c: #00c78d; --c-ink: #1e1b2e; }
.cls-board li:nth-child(4n + 3) { --c: #f4b447; --c-ink: #1e1b2e; }
.cls-board li:nth-child(4n) { --c: #8657d1; }
.cls-face { display: grid; place-items: center; width: 38px; height: 38px; border-radius: 50%; background: var(--c); color: var(--c-ink); font-weight: 900; }
.cls-board-who { display: grid; min-width: 0; }
.cls-board-who b { font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cls-board-who span { font-size: 0.8rem; color: var(--text-muted); }
.cls-board-time { font-weight: 800; font-variant-numeric: tabular-nums; white-space: nowrap; }
.cls-board-track { grid-column: 1 / -1; height: 6px; border-radius: 999px; background: var(--tut-tile); overflow: hidden; }
.cls-board-track i { display: block; height: 100%; border-radius: 999px; background: var(--c); }

/* Setup: one card, a setting to a row. */
.cls-panel { max-width: 640px; padding: 4px 24px; border-radius: 18px; background: var(--card-bg); box-shadow: var(--tut-lift); }
.cls-panel > * { padding: 20px 0; border-top: 1px solid var(--border); }
.cls-panel > :first-child { border-top: 0; }
.cls-switch-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; cursor: pointer; }
.cls-switch-row > span { display: grid; gap: 4px; }
.cls-switch-row strong, .cls-keep strong { font-size: 1rem; font-weight: 800; }
.cls-switch-row > span > span { font-size: 0.88rem; line-height: 1.45; color: var(--text-muted); }
.cls-switch-row.is-disabled { opacity: 0.6; cursor: default; }
.cls-switch { appearance: none; -webkit-appearance: none; flex: none; position: relative; width: 54px; height: 32px; margin: 2px 0 0; border-radius: 999px; background: #cfc8bd; cursor: pointer; transition: background 0.2s ease; }
.cls-switch::after { content: ''; position: absolute; left: 3px; top: 3px; width: 26px; height: 26px; border-radius: 50%; background: #fff; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25); transition: transform 0.2s cubic-bezier(0.3, 1.4, 0.5, 1); }
.cls-switch:checked { background: var(--accent); }
.cls-switch:checked::after { transform: translateX(22px); }
.cls-switch:disabled { cursor: default; }
.cls-switch:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.cls-keep { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; }
.cls-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.cls-chips button { min-height: 38px; padding: 0 16px; border: 1px solid var(--border); border-radius: 999px; background: transparent; color: var(--text); font: inherit; font-size: 0.9rem; font-weight: 800; cursor: pointer; }
.cls-chips button:hover { border-color: var(--text); }
.cls-chips button[aria-pressed="true"] { border-color: transparent; background: #1e1b2e; color: #fff; }
.cls-chips button:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

@media (max-width: 1000px) {
    .cls-split { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 720px) {
    .cls-hero { padding: 20px 18px; }
    .cls-hero-top { flex-direction: column; align-items: stretch !important; gap: 22px; }
    .cls-live, .cls-search { flex: none; min-width: 0; }
    .cls-months { gap: 0; }
    .cls-months button { padding: 0 9px; }
    .cls-mock { width: 78%; align-self: center; }
    .cls-sky-plot { gap: 2px; height: 126px; }
    .cls-tiles { grid-template-columns: minmax(0, 1fr); gap: 10px; }
    .cls-split .cls-recs, .cls-recs { grid-template-columns: minmax(0, 1fr); }
    .cls-block { padding: 18px; }
    .cls-panel { padding: 2px 18px; }
}
@media (prefers-reduced-motion: reduce) {
    .cls-dot, .cls-bar i, .cls-ink { animation: none; }
    .cls-ink { stroke-dashoffset: 0; }
    .cls-play, .cls-rec-poster { transition: none; }
}

/* ── Tutoring: Home (dashboardPage in public/js/tutoring.js) ────────────────

   The classroom's pieces, used for the office's front page: a card that
   leads, tiles under it, blocks under those. Only what Home needs on top of
   them is here. */
.home-hero .cls-hero-top, .home-numbers .cls-hero-top { align-items: stretch; }
.home-hero .cls-live { flex-basis: 420px; }
.home-hero .cls-headline { max-width: none; }
.home-acts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; }
.home-acts .btn { min-height: 44px; padding: 0 20px; font-weight: 800; }
.cls-hero a.btn-primary { color: #fff; }
.cls-live .tut-lrow { background: var(--card-bg); }
.cls-live .tut-mark + .cls-live-more { margin-top: 10px; }
.cls-live-more a { font-weight: 700; }

/* What is waiting elsewhere: the count where a tile's picture goes, in the
   colour of how much it matters, and the whole tile the way there. */
.home-tiles { grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); }
.home-todo { color: inherit; text-decoration: none; transition: transform 0.15s ease; }
.home-todo:hover { transform: translateY(-2px); }
.home-todo:hover b { color: var(--accent); }
.home-todo:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.home-todo .cls-ico { background: var(--tone-bg, var(--tut-tile)); color: var(--tone, var(--text)); font-size: 1.05rem; font-weight: 900; font-variant-numeric: tabular-nums; }
.home-todo b { font-size: 1.02rem; letter-spacing: 0; margin-bottom: 2px; }

/* What finished lessons still need: one block, a list at a time, a lesson
   to a line with its answer at the end of it. Five show; the rest open in
   place and scroll, so the page under them stays where it was. */
.home-pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 360px), 1fr)); gap: 18px; align-items: start; margin-bottom: 18px; }
.home-ends { margin-bottom: 18px; }
.home-ends-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 16px; margin-bottom: 6px; }
.home-ends-head .cls-block-title { margin: 0; justify-content: flex-start; align-items: center; }
.home-ends-head .cls-months { margin: 0; padding: 4px; border-radius: 999px; background: var(--tut-tile); }
.home-ends-head .cls-months button { display: inline-flex; align-items: center; gap: 8px; }
.home-ends-head .cls-months button:hover { background: rgba(30, 27, 46, 0.06); }
.home-ends-head .cls-months button[aria-pressed="true"] { background: var(--tut-ink); }
.home-ends-head .cls-months [aria-pressed="true"] .home-count { background: rgba(255, 255, 255, 0.2); color: #fff; }
.home-count { padding: 2px 10px; border-radius: 999px; background: var(--tut-tile); font-size: 0.82rem; font-weight: 800; letter-spacing: 0; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.home-ends-panel > .tut-hint { margin: 0 0 6px; }
.home-list .tut-mark-row { flex-wrap: nowrap; gap: 14px; }
.home-list .tut-mark-what { flex: 1 1 0; }
.home-list .tut-mark-title, .home-list .tut-mark-what .tut-hint { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; overflow-wrap: normal; }
.home-list .tut-mark-acts { flex: none; flex-wrap: nowrap; }
.home-list li:nth-child(n + 6) { display: none; }
.home-ends-panel:not(.is-all) .home-list li:nth-child(5) { border-bottom: 0; }
.home-ends-panel.is-all .home-list { max-height: 372px; overflow-y: auto; margin-right: -10px; padding-right: 10px; }
.home-ends-panel.is-all .home-list li { display: flex; }
.home-all { margin-top: 12px; }
@media (max-width: 520px) {
    .home-list .tut-mark-row { flex-wrap: wrap; gap: 8px; }
    .home-list .tut-mark-what { flex-basis: 100%; }
}
.cls-block .tut-vbars { margin-top: 4px; }

/* The numbers: set a little apart from the day above them. */
.home-numbers { margin-top: 34px; }
.home-numbers .cls-hero-main { display: flex; flex-direction: column; align-items: flex-start; }
/* While a new period is on its way the old numbers stay where they are, a little fainter. */
.home-slot { transition: opacity 0.15s ease; }
.home-loading .home-slot { opacity: 0.45; }
.home-numbers .cls-months { margin: auto 0 0 -14px; padding-top: 22px; max-width: 100%; }
.home-dl { display: grid; margin: 0; }
.home-dl > div { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; padding: 7px 0; border-top: 1px solid var(--border); font-size: 0.9rem; }
.home-dl > div:first-child { padding-top: 0; border-top: 0; }
.home-dl > div:last-child { padding-bottom: 0; }
.home-dl dt { color: var(--text-muted); font-weight: 600; }
.home-dl dd { margin: 0; font-weight: 800; font-variant-numeric: tabular-nums; }
@media (max-width: 720px) {
    .home-numbers .cls-months { margin-left: -9px; }
    .home-numbers .cls-months button { padding: 0 8px; }
}
@media (prefers-reduced-motion: reduce) {
    .home-todo, .home-slot { transition: none; }
}

/* ── Tutoring: Invoices (invoicesPage in public/js/tutoring.js) ─────────────

   Home's layout again, for money: what is owed leads, beside the form that
   drafts the month's invoices. It borrows Home's .home-acts, .home-count and
   .home-ends-head as they are. */
.money-hero .cls-hero-top { align-items: stretch; }
.money-hero .cls-live { flex-basis: 380px; }
.money-hero .cls-live-head { font-size: 0.78rem; }
.money-hero .cls-live-quiet { margin-bottom: 14px; }
.money-hero .cls-live .tut-input { background: var(--card-bg); }
.money-hero .cls-live .btn { width: 100%; min-height: 44px; margin-top: 4px; font-weight: 800; }
.cls-delta.is-due { background: rgba(177, 70, 55, 0.12); color: var(--red-soft); }
.money-tile .cls-ico { background: var(--tone-bg, var(--tut-tile)); color: var(--tone, var(--text)); }
.money-tile b { font-variant-numeric: tabular-nums; }
a.money-tile { color: inherit; text-decoration: none; transition: transform 0.15s ease; }
a.money-tile:hover { transform: translateY(-2px); }
a.money-tile:hover b { color: var(--accent); }
a.money-tile:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* The invoices: a table in the block, ruled, not a card inside a card. */
.money-list { margin-bottom: 18px; }
.money-list .home-ends-head { margin-bottom: 10px; }
.money-list .tut-table-wrap { margin: 0 -10px; border-radius: 0; background: none; box-shadow: none; }
.money-list .tut-table th { padding: 10px; }
.money-list .tut-table td { padding: 13px 10px; }
.money-list .tut-table tbody tr:last-child td { border-bottom: 0; }
.money-list .tut-table tbody tr:hover { background: var(--tut-tile); }
.money-list .tut-empty { padding: 28px 16px 12px; }

.money-setup { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); }
.money-setup { margin-bottom: 18px; }
.money-setup > .money-list { margin-bottom: 0; }
.money-cards p { margin: 0 0 8px; line-height: 1.5; }
.money-cards .tut-chips { margin-top: 12px; }
.money-state .tut-flag { margin-right: 6px; }
.money-sub { margin: 20px 0 10px; padding-top: 18px; border-top: 1px solid var(--border); font-size: 0.95rem; font-weight: 800; }

/* The rest of Money (Credit, Tutor pay, Exports, My pay) is made of the same
   pieces. A list in a block: a row to a thing, what to do about it at the end. */
.money-rows li { align-items: center; gap: 10px 14px; flex-wrap: wrap; }
.money-rows li:first-child { padding-top: 0; }
.money-rows li:last-child { padding-bottom: 0; border-bottom: 0; }
.money-rows .tut-row-main { flex: 1 1 200px; min-width: 0; }
.money-amount { font-weight: 800; font-variant-numeric: tabular-nums; }
.money-note { margin: 14px 0 0; }
.cls-block-title .btn, .cls-block-title .tut-pill { letter-spacing: 0; }
.money-list > .cls-block-title { align-items: center; }
.money-hero .cls-lede { margin-top: 12px; }
.money-hero .home-slot > .tut-hint { margin: 12px 0 0; }
.money-hero .cls-hero-main a.btn-secondary { color: var(--text); }
.cls-block .tut-facts { margin: 0; }
.cls-block .tut-facts > div { padding-inline: 0; }
.cls-block .tut-facts > div:first-child { padding-top: 0; }
.cls-block .tut-pay-form:first-of-type { margin-top: 16px; }
.cls-block > form > .btn, .cls-block > .btn { margin-top: 10px; }
@media (max-width: 1000px) {
    .money-setup { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 720px) {
    .money-list .home-ends-head .cls-months { max-width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
    a.money-tile { transition: none; }
}

/* ── Tutoring: going from page to page (nextBody in public/js/tutoring.js) ──

   The page on show stays put until the next one is drawn, then the new one
   fades in where it stood. Nothing slides: the frame around it never moves,
   so the only thing that changes is the thing you asked for. */
/* A short page has no scrollbar and a long one has, and the app is centred,
   so without room kept for the bar every page sits a few pixels left or
   right of the last. Kept only while tutoring is the view on show. */
html:has(#tutoring-view:not([hidden])) { scrollbar-gutter: stable; }
@supports not (scrollbar-gutter: stable) {
    html:has(#tutoring-view:not([hidden])) { overflow-y: scroll; }
}
/* Two pages are in the column for an instant; the browser is not to chase either with the scroll. */
.tut-main { position: relative; overflow-anchor: none; }
/* The page being drawn is laid out at its real width but takes up no room,
   so the one still on show does not move while it waits. */
.tut-body.is-pending { position: absolute; left: 0; right: 0; opacity: 0; pointer-events: none; }
.tut-ghost { pointer-events: none; transition: opacity 0.2s ease; }
.tut-ghost.is-waiting { opacity: 0.4; }
.tut-body.tut-enter { animation: tut-enter 0.18s ease-out; }
@keyframes tut-enter { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
    .tut-ghost { transition: none; }
    .tut-body.tut-enter { animation: none; }
}

/* The agency's plan (planCard in tutoring.js). Two cards side by side: what
   you choose, and the bill it comes to. Each choice is drawn the way the rest
   of the tutoring area draws a lesson, as a ticket: a tile that says the one
   thing to know, then the detail. Amber is resources and purple is classroom
   wherever they appear, slider, bar or bill; green is only ever what is on us. */
.tut-plan-wrap { margin-bottom: 16px; }
.tut-plan-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 12px; }
.tut-plan-head h2 { margin: 0; font-size: 1.15rem; font-weight: 800; letter-spacing: -0.01em; }
.tut-plan { display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr); gap: 16px; align-items: start; }
.tut-plan > .tut-card { margin: 0; }
.tut-plan-choose { display: grid; gap: 10px; }

.tut-plan-dial { display: grid; grid-template-columns: 88px minmax(0, 1fr); gap: 16px; align-items: start; padding: 12px 16px 14px 12px; border-radius: 14px; background: var(--tut-tile); }
.tut-plan-dial.is-buckets { --hue: var(--amber); --hue-deep: #d3900f; --tone: #8f620d; --tone-bg: rgba(244, 180, 71, 0.24); }
.tut-plan-dial.is-hours { --hue: var(--purple); --hue-deep: #6f43b8; --tone: #6f43b8; --tone-bg: rgba(134, 87, 209, 0.15); }
.tut-plan-side { min-width: 0; }
.tut-plan-choose > .tut-eyebrow { margin: 0 2px 2px; }
.tut-plan-choose > .tut-eyebrow span { font-weight: 700; letter-spacing: 0.04em; }
/* The other way to more resources, said where the choice is made: the slider
   is for every month from the next one, this is for now. Always there, so the
   box is the same height whatever the slider says. */
.tut-plan-now { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 8px 12px; margin-top: 6px; padding: 8px 10px 8px 14px; border-radius: 10px; background: rgba(255, 255, 255, 0.75); }
.tut-plan-now p { display: grid; gap: 1px; margin: 0; min-width: 0; font-size: 0.86rem; line-height: 1.35; }
.tut-plan-now strong { font-weight: 800; }
.tut-plan-now small { font-size: 0.78rem; color: var(--text-muted); }
.tut-plan-now .btn { flex: none; }
.tut-topup-held b { font-weight: 800; color: #067f5c; }
.tut-topup-held { margin: 14px 0 0; padding: 10px 12px; border-radius: 10px; background: rgba(0, 199, 141, 0.12); font-size: 0.86rem; line-height: 1.45; }
.tut-plan-note .tut-linkbtn { font-size: inherit; }
/* The top-up dialog: the sizes as tiles to pick from, then what a top-up is. */
.tut-topup-lead { margin: -6px 0 14px; font-size: 0.9rem; line-height: 1.5; color: var(--text-muted); }
.tut-topup-opts { display: grid; grid-template-columns: repeat(auto-fit, minmax(84px, 1fr)); gap: 6px; }
.tut-topup-opt { position: relative; display: grid; gap: 1px; padding: 12px 6px 10px; border-radius: 12px; background: var(--tut-tile, rgba(30, 27, 46, 0.035)); text-align: center; cursor: pointer; transition: background-color 0.15s; }
.tut-topup-opt input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.tut-topup-opt b { font-size: 1.25rem; font-weight: 900; line-height: 1.15; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.tut-topup-opt small { font-size: 0.68rem; font-weight: 700; color: var(--text-muted); }
.tut-topup-opt span { margin-top: 6px; font-size: 0.86rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.tut-topup-opt:hover { background: rgba(30, 27, 46, 0.06); }
.tut-topup-opt:has(input:checked) { background: rgba(244, 180, 71, 0.24); box-shadow: inset 0 0 0 2px #d3900f; }
.tut-topup-opt:has(input:checked) b, .tut-topup-opt:has(input:checked) small { color: #8f620d; }
.tut-topup-opt:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.tut-topup-facts { display: grid; gap: 6px; margin: 16px 0 0; padding: 0; list-style: none; font-size: 0.88rem; }
.tut-topup-facts li { display: flex; align-items: baseline; gap: 8px; }
.tut-topup-facts li::before { content: '✓'; flex: none; font-weight: 900; color: #067f5c; }
.tut-topup .tut-dialog-actions .btn-primary { min-width: 7.5rem; justify-content: center; }
.tut-plan-tile { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; min-height: 80px; padding: 8px 6px; border-radius: 12px; background: var(--tone-bg); color: var(--tone); text-align: center; }
.tut-plan-tile b { font-size: 1.9rem; font-weight: 900; line-height: 1; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }
.tut-plan-tile span { font-size: 0.62rem; font-weight: 800; line-height: 1.2; letter-spacing: 0.06em; text-transform: uppercase; white-space: nowrap; }
/* The one tile that is filled in: what comes with the plan. */
.tut-plan-tile.is-onus { background: var(--green); color: var(--tut-ink, #1e1b2e); }

.tut-plan-dial-main { min-width: 0; padding-top: 4px; }
.tut-plan-dial-top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; white-space: nowrap; }
.tut-plan-dial-top label { font-size: 1.02rem; font-weight: 800; }
.tut-plan-dial-top output { font-size: 0.88rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.tut-plan-dial-top output b { font-size: 1.15rem; font-weight: 800; letter-spacing: -0.01em; color: var(--text); }
/* The price steps. A fixed row, so sliding never changes its height: the
   steps reached are tinted, the one the next unit lands on is outlined, and
   each says how much less it costs than the first. */
.tut-plan-steps { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 4px; margin: 2px 0 0; padding: 0; list-style: none; }
/* With no resources chosen, the steps give way to what going without means.
   Both are laid in the same cell and only one is shown, so the box is as tall
   with either and doesn't jump as the slider reaches nought. */
.tut-plan-stack { display: grid; }
.tut-plan-stack > * { grid-area: 1 / 1; }
/* One cell the size of the row of steps, in their own white, with the
   tutoring area's own pill and button: nothing here is styled specially. */
.tut-plan-zero { align-self: stretch; display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 2px 0 0; padding: 8px 12px 8px 14px; border-radius: 10px; background: rgba(255, 255, 255, 0.75); visibility: hidden; }
.tut-plan-zero p { display: grid; gap: 3px; margin: 0; min-width: 0; font-size: 0.8rem; line-height: 1.35; color: var(--text-muted); white-space: nowrap; }
.tut-plan-zero strong { font-size: 0.88rem; font-weight: 800; color: var(--text); }
.tut-plan-zero .btn { flex: none; gap: 0; }
.tut-plan-zero .btn span { white-space: pre; }
/* One click, one … : the three things a bucket makes, turning over in turn.
   All three share a cell, so the line is as wide as the longest and never
   reflows; the first is the one left showing where motion is switched off. */
.tut-plan-flip { display: inline-grid; vertical-align: middle; }
.tut-plan-flip b { grid-area: 1 / 1; justify-self: start; opacity: 0; }
.tut-plan-flip b:first-child { opacity: 1; }
.tut-plan-stack.is-zero .tut-plan-flip b { animation: tut-plan-flip 6.6s infinite both; }
.tut-plan-stack.is-zero .tut-plan-flip b:nth-child(2) { animation-delay: 2.2s; }
.tut-plan-stack.is-zero .tut-plan-flip b:nth-child(3) { animation-delay: 4.4s; }
@keyframes tut-plan-flip {
    0% { opacity: 0; transform: translateY(60%); }
    5%, 30% { opacity: 1; transform: none; }
    35%, 100% { opacity: 0; transform: translateY(-60%); }
}
.tut-plan-stack.is-zero .tut-plan-zero { animation: tut-plan-zero-in 0.25s ease both; }
@keyframes tut-plan-zero-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
    .tut-plan-stack.is-zero .tut-plan-flip b, .tut-plan-stack.is-zero .tut-plan-zero { animation: none; }
}
html[data-reduce-motion] .tut-plan-stack.is-zero .tut-plan-flip b,
html[data-reduce-motion] .tut-plan-stack.is-zero .tut-plan-zero { animation: none; }
.tut-plan-stack.is-zero .tut-plan-zero { visibility: visible; }
.tut-plan-stack.is-zero .tut-plan-steps { visibility: hidden; }
.tut-plan-steps li { display: grid; gap: 1px; padding: 7px 2px 8px; border-radius: 10px; background: rgba(255, 255, 255, 0.75); text-align: center; color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; transition: background-color 0.15s; }
.tut-plan-steps span { font-size: 0.68rem; font-weight: 700; line-height: 1.3; }
.tut-plan-steps b { font-size: 0.95rem; font-weight: 800; line-height: 1.25; color: var(--text); }
.tut-plan-steps small { font-size: 0.66rem; font-weight: 700; line-height: 1.3; }
.tut-plan-steps .is-saving small { color: #067f5c; font-weight: 800; }
.tut-plan-steps li.is-on { background: var(--tone-bg); }
.tut-plan-steps li.is-now { box-shadow: inset 0 0 0 2px var(--hue-deep); }
.tut-plan-steps li.is-onus { background: rgba(0, 199, 141, 0.16); color: #067f5c; }
.tut-plan-steps li.is-onus b { color: #067f5c; }
.tut-plan-steps li.is-onus.is-now { box-shadow: inset 0 0 0 2px var(--green); }

/* The slider, with this month's use pointed out beneath it on the same scale. */
.tut-plan-slide { position: relative; display: flex; align-items: center; gap: 3px; margin-top: 6px; padding-bottom: 30px; --seg: 0px; --gap: 0px; }
.tut-plan-slide.has-onus { --seg: max(34px, calc(var(--free) * 100%)); --gap: 3px; }
.tut-plan-onus { flex: 0 0 var(--seg); height: 12px; border-radius: 999px 3px 3px 999px; background: var(--green); }
.tut-plan-slide input[type="range"] { -webkit-appearance: none; appearance: none; flex: 1; min-width: 0; height: 26px; margin: 0; background: transparent; cursor: pointer; }
.tut-plan-slide input[type="range"]::-webkit-slider-runnable-track { height: 12px; border-radius: 999px; background: linear-gradient(to right, var(--hue) var(--fill, 0%), rgba(30, 27, 46, 0.1) var(--fill, 0%)); }
.tut-plan-slide.has-onus input[type="range"]::-webkit-slider-runnable-track { border-radius: 3px 999px 999px 3px; }
.tut-plan-slide input[type="range"]::-moz-range-track { height: 12px; border-radius: 999px; background: rgba(30, 27, 46, 0.1); }
.tut-plan-slide input[type="range"]::-moz-range-progress { height: 12px; border-radius: 999px; background: var(--hue); }
.tut-plan-slide.has-onus input[type="range"]::-moz-range-track { border-radius: 3px 999px 999px 3px; }
.tut-plan-slide.has-onus input[type="range"]::-moz-range-progress { border-radius: 3px 999px 999px 3px; }
.tut-plan-slide input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; box-sizing: border-box; width: 24px; height: 24px; margin-top: -6px; border-radius: 50%; border: 3px solid var(--hue-deep); background: #fff; box-shadow: 0 2px 6px rgba(30, 27, 46, 0.22); transition: transform 0.12s ease; }
.tut-plan-slide input[type="range"]::-moz-range-thumb { box-sizing: border-box; width: 24px; height: 24px; border-radius: 50%; border: 3px solid var(--hue-deep); background: #fff; box-shadow: 0 2px 6px rgba(30, 27, 46, 0.22); }
.tut-plan-slide input[type="range"]:hover::-webkit-slider-thumb { transform: scale(1.08); }
.tut-plan-slide input[type="range"]:focus-visible { outline: 2px solid var(--hue-deep); outline-offset: 3px; border-radius: 999px; }
/* --x is where the pointer sits: the thumb's centre runs half its own width
   short of each end of the track, and the hours on us are a piece before it.
   The label sits under the pointer, starting beside it; the spacer in front
   gives way when there isn't room, so the label slides back, not off the row. */
.tut-plan-mark { position: absolute; left: 0; right: 0; top: 21px; display: flex; padding-top: 8px; pointer-events: none; --x: calc(var(--seg) + var(--gap) + 12px + var(--at) * (100% - var(--seg) - var(--gap) - 24px)); }
.tut-plan-mark.in-onus { --x: calc(var(--at) * var(--seg)); }
.tut-plan-mark::before { content: ''; position: absolute; left: calc(var(--x) - 5px); top: 0; border: 5px solid transparent; border-top: 0; border-bottom: 7px solid var(--text); }
.tut-plan-mark i { flex: 0 1 calc(var(--x) - 5px); min-width: 0; }
.tut-plan-mark em { flex: 0 0 auto; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-style: normal; font-size: 0.78rem; line-height: 17px; color: var(--text-muted); }
.tut-plan-mark em b { font-weight: 800; color: var(--text); }
.tut-plan-mark em b.is-spare { color: #067f5c; }
.tut-plan-mark.is-over::before { border-bottom-color: var(--red-soft); }
.tut-plan-mark.is-over em, .tut-plan-mark.is-over em b { color: var(--red-soft); }
.tut-plan-mark.is-over em span { display: none; }
.tut-plan-mark.is-over em::after { content: ', more than this'; }
.tut-plan-small { margin: 4px 2px 0; font-size: 0.82rem; line-height: 1.5; color: var(--text-muted); }

.tut-plan-bill { display: flex; flex-direction: column; }
.tut-plan-total { margin: 0; font-size: 2.3rem; font-weight: 900; line-height: 1.1; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }
.tut-plan-total small { font-size: 0.85rem; font-weight: 600; letter-spacing: 0; color: var(--text-muted); }
.tut-plan-bar { display: flex; gap: 3px; height: 8px; margin: 14px 0 6px; }
.tut-plan-bar span { flex: 0 0 0; min-width: 6px; border-radius: 999px; transition: flex-grow 0.2s ease; }
.tut-plan-bar span[hidden] { display: none; }
.tut-plan-bar .is-base, .tut-plan-key.is-base { background: var(--accent); }
.tut-plan-bar .is-buckets, .tut-plan-key.is-buckets { background: var(--amber); }
.tut-plan-bar .is-hours, .tut-plan-key.is-hours { background: var(--purple); }
.tut-plan-key { display: inline-block; width: 9px; height: 9px; margin-right: 8px; border-radius: 3px; }
.tut-plan-key.is-onus { background: var(--green); }
.tut-plan-lines { margin: 0 0 4px; padding: 0; list-style: none; }
.tut-plan-lines li { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 9px 0; border-top: 1px solid rgba(30, 27, 46, 0.07); font-size: 0.92rem; }
.tut-plan-lines li:first-child { border-top: 0; }
.tut-plan-lines small { margin-left: 4px; font-size: 0.8rem; color: var(--text-muted); }
.tut-plan-lines b { font-weight: 800; font-variant-numeric: tabular-nums; white-space: nowrap; }
.tut-plan-lines li.is-onus { border-top: 0; margin-top: -6px; padding-top: 0; color: #067f5c; font-weight: 700; }
.tut-plan-note { margin: 10px 0 0; font-size: 0.86rem; font-weight: 700; line-height: 1.5; }
.tut-plan-note:empty, .tut-plan-rest:empty { display: none; }
.tut-plan-rest { margin: 14px 0 0; padding: 11px 12px; border-radius: 10px; background: var(--tut-tile); font-size: 0.86rem; font-weight: 700; text-align: center; color: var(--text-muted); }
.tut-plan-rest[hidden], .tut-plan-bill .btn[hidden] { display: none; }
.tut-plan-bill .btn-primary { justify-content: center; width: 100%; margin-top: 14px; }
.tut-plan-bill .tut-linkbtn { align-self: center; margin-top: 10px; font-size: 0.86rem; }
@media (max-width: 860px) { .tut-plan { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 560px) {
    /* Too narrow for the slider to sit beside the tile: it runs the full
       width beneath, and the tile keeps the title company. */
    .tut-plan-dial { grid-template-columns: 72px minmax(0, 1fr); gap: 4px 12px; align-items: center; padding: 10px 14px 12px 10px; }
    .tut-plan-dial-main { display: contents; }
    .tut-plan-dial-top { flex-direction: column; align-items: flex-start; gap: 0; }
    .tut-plan-slide, .tut-plan-steps, .tut-plan-stack, .tut-plan-now { grid-column: 1 / -1; }
    .tut-plan-zero .btn span { display: none; }
    .tut-plan-tile { min-height: 60px; }
    .tut-plan-tile b { font-size: 1.4rem; }
    .tut-plan-tile span { font-size: 0.5rem; letter-spacing: 0.04em; }
}
@media (prefers-reduced-motion: reduce) { .tut-plan-bar span, .tut-plan-slide input[type="range"]::-webkit-slider-thumb { transition: none; } }

/* The people of the organisation, in the tutoring area's Admin tab
   (renderOrgEmbedded in app.js): one card of who is here and who has been
   asked, with the invitation box above them, then what was made this month
   and the logo, side by side. */
.tut-people-wrap .error { margin-bottom: 12px; }
.tut-people-sum { margin-left: 8px; font-size: 0.86rem; font-weight: 600; letter-spacing: 0; color: var(--text-muted); }
.tut-invite { display: flex; gap: 8px; }
.tut-invite .tut-input { flex: 1; min-width: 0; padding: 10px 12px; }
.tut-invite .btn { flex: none; }
.tut-invite-bulk { margin-top: 10px; }
.tut-invite-bulk summary { font-size: 0.86rem; }
.tut-people-list { display: grid; gap: 6px; margin: 16px 0 0; padding: 0; list-style: none; }
.tut-person { display: grid; grid-template-columns: 40px minmax(0, 1fr) auto auto auto; gap: 4px 14px; align-items: center; padding: 8px 10px 8px 8px; border-radius: 14px; background: var(--tut-tile); }
.tut-person-face { display: grid; place-items: center; width: 40px; height: 40px; border-radius: 10px; background: var(--tone-bg, rgba(30, 27, 46, 0.06)); color: var(--tone, var(--text-muted)); font-size: 0.86rem; font-weight: 800; letter-spacing: 0.02em; }
.tut-person-who { display: grid; min-width: 0; }
.tut-person-who b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 800; }
.tut-person-who small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.82rem; color: var(--text-muted); }
.tut-person-tags { display: flex; gap: 6px; }
.tut-person-when { font-size: 0.82rem; color: var(--text-muted); white-space: nowrap; }
.tut-person-acts { display: flex; gap: 6px; justify-content: flex-end; }
.tut-people > .tut-plan-small { margin-top: 14px; }
.tut-people-more { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; align-items: start; }
.tut-people-more > .tut-card { margin: 0 0 16px; }
.tut-people-more .tut-card-title .tut-linkbtn { margin-left: auto; font-size: 0.82rem; text-decoration: none; }
.tut-activity-total { margin: 0 0 10px; font-size: 0.88rem; color: var(--text-muted); }
.tut-activity-total b { font-size: 1.5rem; font-weight: 900; letter-spacing: -0.02em; color: var(--text); margin-right: 4px; }
.tut-activity { display: grid; gap: 10px; margin: 0; padding: 0; list-style: none; }
.tut-activity li { display: grid; grid-template-columns: minmax(0, 1fr) 72px 4ch; gap: 12px; align-items: center; }
.tut-activity li.is-idle { opacity: 0.6; }
.tut-activity-bar { height: 8px; border-radius: 999px; background: linear-gradient(to right, var(--amber) var(--share), rgba(30, 27, 46, 0.1) var(--share)); }
.tut-activity-n { min-width: 2ch; text-align: right; font-weight: 800; font-variant-numeric: tabular-nums; }
.tut-logo { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.tut-logo .org-brand-preview { width: 9rem; height: 4.5rem; border-color: rgba(30, 27, 46, 0.14); border-radius: 12px; }
.tut-logo-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-width: 0; }
.tut-logo-acts .tut-hint { flex-basis: 100%; margin: 2px 0 0; }
.tut-people-more .org-brand-toggle { margin-top: 14px; }
.tut-logo-foot { margin-top: 14px; padding-top: 12px; border-top: 1px solid rgba(30, 27, 46, 0.07); }
@media (max-width: 760px) {
    .tut-people-more { grid-template-columns: minmax(0, 1fr); }
    .tut-person { grid-template-columns: 40px minmax(0, 1fr) auto; }
    .tut-person-when { display: none; }
    .tut-person-acts { grid-column: 2 / -1; justify-content: flex-start; }
    .tut-invite { flex-wrap: wrap; }
    .tut-invite .btn { flex: 1 1 100%; justify-content: center; }
}

/* The upgrade dialog for someone in a tutoring agency whose plan isn't running
   (showQuotaModal in app.js). The agency's plan, not personal Pro: laid out
   plainly on the card, in the tutoring area's manner, with what it costs. */
.quota-card.is-agency .quota-hero { padding: 2rem 1.8rem 0.4rem; background: none; color: var(--text); }
.quota-card.is-agency .quota-hero-orb, .quota-card.is-agency .quota-hero-icon { display: none; }
.quota-card.is-agency .quota-hero h2 { font-size: 1.5rem; font-weight: 900; letter-spacing: -0.02em; text-wrap: balance; }
.quota-card.is-agency #quota-message { max-width: 34ch; color: var(--text-muted); font-size: 0.92rem; }
.quota-card.is-agency .quota-close { background: rgba(30, 27, 46, 0.06); color: var(--text-muted); }
.quota-card.is-agency .quota-close:hover { background: rgba(30, 27, 46, 0.12); color: var(--text); }
.quota-card.is-agency .quota-body { padding-top: 1.1rem; }
.quota-agency { margin-bottom: 1.2rem; padding: 16px 18px 4px; border-radius: 14px; background: rgba(30, 27, 46, 0.035); }
.quota-agency-price { margin: 0 0 14px; padding-bottom: 12px; border-bottom: 1px solid rgba(30, 27, 46, 0.07); font-size: 0.92rem; color: var(--text-muted); }
.quota-agency-price b { margin-right: 4px; font-size: 1.9rem; font-weight: 900; letter-spacing: -0.03em; line-height: 1; color: var(--text); }
.quota-agency-price small { display: block; margin-top: 3px; font-size: 0.8rem; }
.quota-agency .quota-perks li { align-items: flex-start; line-height: 1.4; }
.quota-agency .quota-perk-check { margin-top: 1px; }
.quota-card.is-agency .quota-cta { background: var(--accent); box-shadow: none; }
.quota-card.is-agency .quota-cta:hover { background: var(--accent-dark); box-shadow: none; }
