/* Form 3 English Quest — theme, glassmorphism cards, 3D buttons, animations.

   GENERATED PALETTE — edit palette.cjs and run `node palette.cjs`, not this block.

   Every Tailwind colour family keeps its own hue, so the paper stays colourful;
   tailwind.config (in index.html) points each utility at these variables.

   Shade roles:  200-400 text/accent · 500-600 borders and tints · 700-950 deep
   surfaces. The deep shades and the whole neutral ramp INVERT between themes,
   which is what keeps bg-slate-900 / text-slate-200 correct in both. */

:root, :root[data-theme="dark"] {
    --c-fg-strong: 247 250 251;
    --c-ink:   8  14  17;

    --c-slate-200: 229 234 235;
    --c-slate-300: 194 205 209;
    --c-slate-400: 145 164 172;
    --c-slate-500:  96 120 128;
    --c-slate-600:  66  81  87;
    --c-slate-700:  44  54  58;
    --c-slate-800:  29  35  38;
    --c-slate-900:  18  22  23;
    --c-slate-950:  10  12  13;

    --c-indigo-300: 164 234 226;
    --c-indigo-400: 115 222 210;
    --c-indigo-500:  57 208 191;
    --c-indigo-600:  41 174 158;
    --c-indigo-700:  31 132 120;

    --c-purple-300: 212 160 238;
    --c-purple-400: 196 122 233;
    --c-purple-500: 161  47 218;

    --c-pink-500: 224  41 133;

    --c-cyan-100: 219 243 250;
    --c-cyan-300: 157 223 241;
    --c-cyan-400: 103 205 233;
    --c-cyan-500:  41 185 224;
    --c-cyan-700:  20 116 143;
    --c-cyan-950:   8  44  54;

    --c-blue-400: 103 160 233;
    --c-blue-500:  41 120 224;

    --c-amber-300: 248 219 150;
    --c-amber-400: 245 199  92;
    --c-amber-500: 240 176  25;
    --c-amber-700: 153 110  10;
    --c-amber-900:  81  59   5;

    --c-orange-400: 245 158  92;
    --c-orange-500: 240 118  25;

    --c-emerald-300: 160 238 209;
    --c-emerald-400: 108 229 184;
    --c-emerald-500:  47 218 155;
    --c-emerald-600:  32 182 127;

    --c-green-300: 164 234 190;
    --c-green-400: 115 222 154;
    --c-green-500:  57 208 112;

    --c-teal-400: 112 225 221;
    --c-teal-500:  53 212 207;

    --c-rose-300: 239 159 168;
    --c-rose-400: 233 113 126;
    --c-rose-500: 221  44  65;
    --c-rose-800: 105  17  27;
    --c-rose-950:  53   9  14;

    --c-red-400: 234 116 102;
    --c-red-500: 226  58  40;

    --glass-bg:     rgba(14, 20, 25, .60);
    --glass-border: rgba(226, 234, 238, .11);
    --glass-sheen:  rgba(226, 234, 238, .07);
    --glass-shadow: 0 18px 40px -18px rgba(0, 0, 0, .75);
    --dot-grid:     rgba(142, 163, 174, .13);
    --glow-1: rgba(45, 196, 176, .20);
    --glow-2: rgba(168,  85, 230, .16);
    --glow-3: rgba(248, 170,  60, .13);
    --glow-4: rgba( 56, 160, 220, .16);
}

:root[data-theme="light"] {
    --c-fg-strong:  13  21  20;
    --c-ink: 255 255 255;

    --c-slate-200:  26  33  35;
    --c-slate-300:  66  81  87;
    --c-slate-400:  92 114 122;
    --c-slate-500: 115 141 150;
    --c-slate-600: 185 198 202;
    --c-slate-700: 220 226 229;
    --c-slate-800: 240 243 244;
    --c-slate-900: 255 255 255;
    --c-slate-950: 246 248 248;

    --c-indigo-300:  24 103  94;
    --c-indigo-400:  26 111 103;
    --c-indigo-500:  39 160 145;
    --c-indigo-600:  57 208 191;
    --c-indigo-700: 197 241 236;

    --c-purple-300:  79  19 108;
    --c-purple-400: 123  30 169;
    --c-purple-500: 142  34 195;

    --c-pink-500: 201  29 115;

    --c-cyan-100:  18 102 125;
    --c-cyan-300:  16  91 112;
    --c-cyan-400:  16 108 133;
    --c-cyan-500:  27 154 189;
    --c-cyan-700: 193 234 246;
    --c-cyan-950: 237 249 252;

    --c-blue-400:  25  90 174;
    --c-blue-500:  29 103 201;

    --c-amber-300: 120  86   8;
    --c-amber-400: 130  93  12;
    --c-amber-500: 186 134  14;
    --c-amber-700: 251 232 188;
    --c-amber-900: 253 245 226;

    --c-orange-400: 156  74  12;
    --c-orange-500: 216 101  14;

    --c-emerald-300:  19 108  76;
    --c-emerald-400:  17 113  79;
    --c-emerald-500:  28 163 113;
    --c-emerald-600:  47 218 155;

    --c-green-300:  24 103  53;
    --c-green-400:  27 114  61;
    --c-green-500:  39 160  81;

    --c-teal-400:  22 110 108;
    --c-teal-500:  34 158 153;

    --c-rose-300: 110  18  29;
    --c-rose-400: 171  28  45;
    --c-rose-500: 197  32  51;
    --c-rose-800: 248 211 215;
    --c-rose-950: 252 237 239;

    --c-red-400: 175  39  24;
    --c-red-500: 202  45  28;

    --glass-bg:     rgba(255, 255, 255, .74);
    --glass-border: rgba(26, 36, 35, .10);
    --glass-sheen:  rgba(255, 255, 255, .60);
    --glass-shadow: 0 14px 34px -20px rgba(20, 40, 38, .34);
    --dot-grid:     rgba(104, 118, 117, .18);
    --glow-1: rgba(17, 150, 136, .14);
    --glow-2: rgba(150,  70, 200, .12);
    --glow-3: rgba(230, 160,  40, .13);
    --glow-4: rgba( 60, 150, 210, .12);
}

/* ------------------------------------------------------------------ base */

/* index.html gets a reset from Tailwind's preflight; login.html deliberately
   loads no Tailwind, so the reset has to live here. These rules match what
   preflight already does, so they are a no-op on the paper. */
*, *::before, *::after { box-sizing: border-box; }

h1, h2, h3, h4, p, ul, ol, figure, blockquote { margin: 0; }
ul, ol { padding: 0; list-style: none; }
button, input, select, textarea { font: inherit; color: inherit; }
img, svg, video { display: block; max-width: 100%; }

html {
    color-scheme: dark;
    -webkit-text-size-adjust: 100%;
}
html[data-theme="light"] { color-scheme: light; }

body {
    font-family: 'Plus Jakarta Sans', ui-sans-serif, system-ui, sans-serif;
    background-color: rgb(var(--c-slate-950));
    color: rgb(var(--c-slate-200));
    overflow-x: hidden;
    background-image: radial-gradient(var(--dot-grid) 1px, transparent 1px);
    background-size: 26px 26px;
    transition: background-color .25s ease, color .25s ease;
}

/* Two soft colour fields behind the dot grid — this is what gives the glass
   something to refract instead of blurring flat darkness. */
body::before {
    content: "";
    position: fixed;
    inset: -25vmax;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(40vmax 40vmax at 14%  6%, var(--glow-1), transparent 62%),
        radial-gradient(36vmax 36vmax at 86% 20%, var(--glow-2), transparent 62%),
        radial-gradient(34vmax 34vmax at 10% 78%, var(--glow-3), transparent 62%),
        radial-gradient(38vmax 38vmax at 82% 90%, var(--glow-4), transparent 62%);
}

.font-tech {
    font-family: 'Space Grotesk', ui-monospace, monospace;
    font-feature-settings: "tnum" 1;
}

/* ----------------------------------------------------------- glass cards */
.glass-card {
    position: relative;
    background: var(--glass-bg);
    -webkit-backdrop-filter: blur(18px) saturate(1.5);
    backdrop-filter: blur(18px) saturate(1.5);
    border: 1px solid var(--glass-border);
    box-shadow: var(--glass-shadow);
    transition: border-color .2s ease, box-shadow .2s ease, transform .2s ease;
}

/* hairline of light along the top edge — the detail that sells the material */
.glass-card::before {
    content: "";
    position: absolute;
    inset: 0 0 auto;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--glass-sheen), transparent);
    border-radius: inherit;
    pointer-events: none;
}

.glass-panel {
    background: var(--glass-bg);
    -webkit-backdrop-filter: blur(14px) saturate(1.4);
    backdrop-filter: blur(14px) saturate(1.4);
    border: 1px solid var(--glass-border);
}

/* Browsers without backdrop-filter get a solid surface rather than a muddy one. */
@supports not (backdrop-filter: blur(1px)) {
    .glass-card, .glass-panel { background: rgb(var(--c-slate-900)); }
}

/* -------------------------------------------------------- tactile buttons */
.btn-3d {
    border-bottom-width: 4px;
    transition: all .1s ease-in-out;
}

.btn-3d:active:not(:disabled) {
    border-bottom-width: 2px;
    transform: translateY(2px);
}

/* keyboard users get the same affordance as mouse users */
:focus-visible {
    outline: 2px solid rgb(var(--c-primary-400));
    outline-offset: 2px;
}

/* ------------------------------------------------------------ scrollbars */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
    background: rgb(var(--c-slate-600));
    border-radius: 9999px;
    border: 2px solid rgb(var(--c-slate-950));
}
::-webkit-scrollbar-thumb:hover { background: rgb(var(--c-slate-500)); }
.no-scrollbar::-webkit-scrollbar { display: none; }
.no-scrollbar { scrollbar-width: none; }

/* --------------------------------------------------- answer status states */
.status-correct {
    border-color: rgb(var(--c-good-400)) !important;
    background-color: rgb(var(--c-good-500) / .12) !important;
    box-shadow: 0 0 0 1px rgb(var(--c-good-400) / .35), 0 10px 30px -14px rgb(var(--c-good-400) / .5);
}

.status-wrong {
    border-color: rgb(var(--c-bad-500)) !important;
    animation: shake .4s cubic-bezier(.36, .07, .19, .97) both;
}

.option-selected {
    border-color: rgb(var(--c-primary-400)) !important;
    background-color: rgb(var(--c-primary-500) / .16) !important;
}

@keyframes shake {
    10%, 90% { transform: translate3d(-1px, 0, 0); }
    20%, 80% { transform: translate3d(2px, 0, 0); }
    30%, 50%, 70% { transform: translate3d(-4px, 0, 0); }
    40%, 60% { transform: translate3d(4px, 0, 0); }
}

/* ------------------------------------------------------------- hint chip */
/* Small enough to sit inside the answer box without crowding the text. */
.hint-chip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 8px;
    color: rgb(var(--c-hint-400));
    background: rgb(var(--c-hint-500) / .12);
    border: 1px solid rgb(var(--c-hint-500) / .28);
    transition: background-color .15s ease, color .15s ease, opacity .15s ease;
    flex: 0 0 auto;
}
.hint-chip:hover:not(:disabled) { background: rgb(var(--c-hint-500) / .24); color: rgb(var(--c-hint-300)); }
.hint-chip:disabled { opacity: .25; cursor: not-allowed; }

/* the chip floats inside the right edge of a text answer field */
.answer-wrap { position: relative; }
.answer-wrap .hint-chip {
    position: absolute;
    top: 50%;
    right: 10px;
    transform: translateY(-50%);
}
.answer-wrap input { padding-right: 44px; }

/* ------------------------------------------------------- floating XP text */
.float-xp {
    position: absolute;
    color: rgb(var(--c-xp-400));
    font-family: 'Space Grotesk', ui-monospace, monospace;
    font-weight: 700;
    font-size: 1.35rem;
    pointer-events: none;
    text-shadow: 0 4px 14px rgb(var(--c-xp-500) / .45);
    animation: floatUp 1s ease-out forwards;
    z-index: 50;
}

@keyframes floatUp {
    0%   { opacity: 1; transform: translateY(0) scale(1); }
    100% { opacity: 0; transform: translateY(-50px) scale(1.5); }
}

/* ---------------------------------------------------------- theme toggle */
.theme-toggle .icon-dark { display: none; }
html[data-theme="light"] .theme-toggle .icon-dark { display: block; }
html[data-theme="light"] .theme-toggle .icon-light { display: none; }

/* -------------------------------------------------------------- a11y bits */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
    }
}

/* ------------------------------------------------------ content protection */
/* Deterrents only — see assets/js/protect.js for what these can and cannot do. */

/* Selection is killed on the paper, but never on the fields a candidate uses. */
body.is-protected .glass-card,
body.is-protected h1,
body.is-protected h2,
body.is-protected h3 {
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
}

body.is-protected input,
body.is-protected textarea {
    -webkit-user-select: text;
    -moz-user-select: text;
    user-select: text;
}

body.is-protected img { -webkit-user-drag: none; user-drag: none; pointer-events: none; }

/* Tiled identity stamp. Sits above the paper, below the modals, and never
   intercepts a click. */
.watermark-layer {
    position: fixed;
    inset: 0;
    z-index: 30;
    pointer-events: none;
    background-repeat: repeat;
    color: rgb(var(--c-slate-400));
    opacity: .11;
    mix-blend-mode: normal;
}

/* Content is obscured whenever the window is not focused. Does not stop a
   screenshot — makes lining one up in another app harder, and keeps the paper
   off screen shares. */
body.is-shielded #partContentContainer,
body.is-shielded header {
    filter: blur(11px);
    transition: filter .12s ease;
}

body.is-shielded::after {
    content: "Paused — return to this window to continue";
    position: fixed;
    inset: 0;
    z-index: 60;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: 'Plus Jakarta Sans', sans-serif;
    font-weight: 700;
    font-size: 15px;
    color: rgb(var(--c-slate-300));
    background: rgb(var(--c-slate-950) / .82);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
}

/* Ctrl+P is intercepted in JS, but the browser menu still offers Print.
   This makes the printed result worthless rather than a clean copy. */
@media print {
    body.is-protected * { visibility: hidden !important; }
    body.is-protected::before {
        content: "This paper cannot be printed.";
        visibility: visible !important;
        display: block;
        margin: 40mm auto;
        text-align: center;
        font-family: sans-serif;
        font-size: 14pt;
        color: #000;
    }
}


/* --------------------------------------------------- per-part colour keys */
/* Each part owns a hue, so moving between them reads as moving somewhere new. */
/* --part-text is the 300 shade: lighter in dark mode, darker in light mode, so
   it clears AA even when it sits on one of the tinted fills below.
   --part (400) is for borders, dots and solid fills; --part-deep (500) tints. */
[data-part="1"] { --part-text: var(--c-cyan-300);    --part: var(--c-cyan-400);    --part-deep: var(--c-cyan-500); }
[data-part="2"] { --part-text: var(--c-rose-300);    --part: var(--c-rose-400);    --part-deep: var(--c-rose-500); }
[data-part="3"] { --part-text: var(--c-amber-300);   --part: var(--c-amber-400);   --part-deep: var(--c-amber-500); }
[data-part="4"] { --part-text: var(--c-emerald-300); --part: var(--c-emerald-400); --part-deep: var(--c-emerald-500); }
[data-part="5"] { --part-text: var(--c-purple-300);  --part: var(--c-purple-400);  --part-deep: var(--c-purple-500); }

/* Every tab shows its colour as a dot, so all five hues are visible at once
   without the whole strip shouting. */
.part-tab[data-part] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .55rem;
}
.part-tab[data-part]::before {
    content: "";
    width: 8px;
    height: 8px;
    flex: 0 0 auto;
    border-radius: 50%;
    background: rgb(var(--part));
    opacity: .5;
    transition: opacity .15s ease, box-shadow .15s ease;
}
.part-tab[data-part][aria-current="true"]::before {
    opacity: 1;
    box-shadow: 0 0 0 3px rgb(var(--part) / .22);
}

.part-tab[data-part][aria-current="true"] {
    background-color: rgb(var(--part-deep) / .18) !important;
    border-color: rgb(var(--part) / .5) !important;
    color: rgb(var(--part-text)) !important;
    box-shadow: 0 6px 20px -12px rgb(var(--part) / .8) !important;
}
.part-tab[data-part]:not([aria-current="true"]):hover {
    color: rgb(var(--part-text)) !important;
    border-color: rgb(var(--part) / .28) !important;
}
.part-tab[data-part]:not([aria-current="true"]):hover::before { opacity: .85; }

/* question cards take a tint and a coloured top sheen from their part */
#partContentContainer[data-part] .glass-card::before {
    background: linear-gradient(90deg, transparent, rgb(var(--part) / .75), transparent);
    height: 2px;
}
#partContentContainer[data-part] .glass-card {
    border-color: rgb(var(--part) / .20);
}
#partContentContainer[data-part] .glass-card:hover {
    border-color: rgb(var(--part) / .38);
}

/* the question-number badge and the passage icon follow the part hue too */
#partContentContainer[data-part] .q-badge {
    color: rgb(var(--part-text));
    background-color: rgb(var(--part-deep) / .12);
    border-color: rgb(var(--part) / .28);
}
#partContentContainer[data-part] .stim-icon {
    color: rgb(var(--part-text));
    background-color: rgb(var(--part-deep) / .16);
    border-color: rgb(var(--part) / .30);
}

/* a correct answer still wins over the part tint */
#partContentContainer[data-part] .glass-card.status-correct { border-color: rgb(var(--c-emerald-400)) !important; }
#partContentContainer[data-part] .glass-card.status-wrong   { border-color: rgb(var(--c-rose-500)) !important; }

/* progress bar sweeps the full set of part hues */
#progressBar {
    background-image: linear-gradient(90deg,
        rgb(var(--c-cyan-400)),
        rgb(var(--c-rose-400)),
        rgb(var(--c-amber-400)),
        rgb(var(--c-emerald-400)),
        rgb(var(--c-purple-400))) !important;
}


/* ------------------------------------------- colour on interactive surfaces */
/* The first version carried colour on every control. This keeps that feel but
   at lower saturation: hue on the accents, neutral on the large surfaces. */

/* answer cards take a faint wash of their part's hue */
#partContentContainer[data-part] .glass-card {
    background-image: linear-gradient(rgb(var(--part) / .045), rgb(var(--part) / .015));
}

/* option letter chips carry the part hue instead of flat grey */
#partContentContainer[data-part] .opt-key {
    color: rgb(var(--part-text));
    background-color: rgb(var(--part-deep) / .13);
    border-color: rgb(var(--part) / .30);
}

/* hover and selection follow the part, not one global accent */
#partContentContainer[data-part] .opt-row:hover:not(:disabled) {
    border-color: rgb(var(--part) / .55) !important;
    background-color: rgb(var(--part-deep) / .10) !important;
}
#partContentContainer[data-part] .option-selected {
    border-color: rgb(var(--part)) !important;
    background-color: rgb(var(--part-deep) / .17) !important;
}
#partContentContainer[data-part] .option-selected .opt-key {
    color: rgb(var(--c-ink));
    background-color: rgb(var(--part));
    border-color: rgb(var(--part));
}

/* text answer focus ring picks up the part hue */
#partContentContainer[data-part] .answer-wrap input:focus {
    border-color: rgb(var(--part)) !important;
    box-shadow: 0 0 0 3px rgb(var(--part) / .18);
}

/* a hairline of all five part hues across the top of the header */
.masthead-rule {
    height: 2px;
    border-radius: 2px;
    background: linear-gradient(90deg,
        rgb(var(--c-cyan-400)),
        rgb(var(--c-rose-400)),
        rgb(var(--c-amber-400)),
        rgb(var(--c-emerald-400)),
        rgb(var(--c-purple-400)));
    opacity: .75;
}

/* HUD chips read a little warmer than flat neutral */
#hudXpContainer, #hudStreakContainer { transition: transform .15s ease, box-shadow .15s ease; }
#hudXpContainer     { box-shadow: 0 4px 16px -10px rgb(var(--c-amber-400) / .9); }
#hudStreakContainer { box-shadow: 0 4px 16px -10px rgb(var(--c-rose-400) / .9); }

/* ================================================================ login page */
/* Sized in rem so the form scales as one unit. Centred with margin:auto inside
   a flex parent, because grid/flex centring clips the top once the card is
   taller than the viewport — which happens on a short landscape phone. */

.login-page {
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    padding: clamp(1rem, 4vw, 2.5rem);
}

.login-shell {
    margin: auto;
    width: 100%;
    max-width: 23.5rem;
}

.login-card {
    width: 100%;
    border-radius: clamp(1.25rem, 3vw, 1.5rem);
    padding: clamp(1.75rem, 6vw, 2.25rem);
}

/* ------------------------------------------------------------------ head */
/* Centred crest and wordmark: reads as an institutional sign-in rather than
   a row of unrelated elements. */
.login-head {
    text-align: center;
    padding-bottom: 1.5rem;
    margin-bottom: 1.5rem;
    border-bottom: 1px solid rgb(var(--c-slate-700));
}

/* The crest is RGB with no alpha and solid white corners, so it is clipped to
   the ellipse the artwork already draws. aspect-ratio matches the file. */
.login-crest {
    width: clamp(4rem, 17vw, 4.75rem);
    height: auto;
    aspect-ratio: 256 / 284;
    margin: 0 auto .875rem;
    border-radius: 50%;
    background: #fff;
    object-fit: cover;
    box-shadow: 0 .375rem 1rem -.5rem rgba(0, 0, 0, .5);
}

.login-head h1 {
    font-size: clamp(1.375rem, 5.5vw, 1.625rem);
    font-weight: 800;
    line-height: 1.1;
    letter-spacing: -.02em;
    color: rgb(var(--c-fg-strong));
}

.login-sub {
    margin-top: .375rem;
    font-size: .75rem;
    font-weight: 500;
    letter-spacing: .01em;
    color: rgb(var(--c-slate-400));
}

/* ----------------------------------------------------------------- facts */
/* A definition list, so the numbers are labelled rather than floating. */
.login-facts {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    margin-bottom: 1.5rem;
    border: 1px solid rgb(var(--c-slate-700));
    border-radius: .875rem;
    overflow: hidden;
    background: rgb(var(--c-slate-800) / .45);
}
.login-facts > div {
    padding: .6875rem .25rem .75rem;
    text-align: center;
    min-width: 0;
}
.login-facts > div + div { border-left: 1px solid rgb(var(--c-slate-700)); }

.login-facts dt {
    font-size: .625rem;
    font-weight: 700;
    letter-spacing: .11em;
    text-transform: uppercase;
    color: rgb(var(--c-slate-400));
}
.login-facts dd {
    margin: .125rem 0 0;
    font-family: 'Space Grotesk', monospace;
    font-size: 1.125rem;
    font-weight: 700;
    line-height: 1.1;
    color: rgb(var(--c-indigo-300));
}
.login-facts > div:nth-child(2) dd { color: rgb(var(--c-amber-300)); }
.login-facts > div:nth-child(3) dd { color: rgb(var(--c-emerald-300)); }

/* ---------------------------------------------------------------- fields */
.login-field { margin-bottom: .875rem; }

.login-field label {
    display: block;
    font-size: .6875rem;
    font-weight: 700;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: rgb(var(--c-slate-400));
    margin-bottom: .4375rem;
}

.login-field input {
    display: block;
    width: 100%;
    /* never below 1rem: iOS zooms the viewport on focus for anything smaller */
    font-size: 1rem;
    font-weight: 500;
    color: rgb(var(--c-slate-200));
    background: rgb(var(--c-slate-950) / .55);
    border: 1px solid rgb(var(--c-slate-700));
    border-radius: .75rem;
    padding: .8125rem .875rem;
    transition: border-color .15s ease, box-shadow .15s ease, background-color .15s ease;
}
.login-field input::placeholder {
    color: rgb(var(--c-slate-500));
    font-weight: 400;
}
.login-field input:hover { border-color: rgb(var(--c-slate-600)); }
.login-field input:focus {
    outline: none;
    background: rgb(var(--c-slate-950) / .8);
    border-color: rgb(var(--c-indigo-400));
    box-shadow: 0 0 0 3px rgb(var(--c-indigo-500) / .16);
}
.login-field input[aria-invalid="true"] {
    border-color: rgb(var(--c-rose-500));
    box-shadow: 0 0 0 3px rgb(var(--c-rose-500) / .14);
}

.login-error {
    font-size: .8125rem;
    font-weight: 500;
    line-height: 1.45;
    color: rgb(var(--c-rose-300));
    background: rgb(var(--c-rose-500) / .1);
    border: 1px solid rgb(var(--c-rose-500) / .28);
    border-radius: .625rem;
    padding: .5625rem .75rem;
    margin-bottom: .875rem;
}

/* ---------------------------------------------------------------- submit */
.login-submit {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .5rem;
    width: 100%;
    font-size: .875rem;
    font-weight: 700;
    letter-spacing: .01em;
    color: rgb(var(--c-ink));
    background: rgb(var(--c-indigo-400));
    border: 0;
    border-radius: .75rem;
    padding: .875rem 1.25rem;
    margin-top: 1.125rem;
    cursor: pointer;
    transition: background-color .15s ease, transform .1s ease, box-shadow .15s ease;
    box-shadow: 0 .5rem 1.25rem -.75rem rgb(var(--c-indigo-400));
}
.login-submit svg {
    width: 1.0625rem;
    height: 1.0625rem;
    transition: transform .15s ease;
}
.login-submit:hover {
    background: rgb(var(--c-indigo-300));
    box-shadow: 0 .625rem 1.5rem -.75rem rgb(var(--c-indigo-400));
}
.login-submit:hover svg { transform: translateX(2px); }
.login-submit:active { transform: translateY(1px); }

.login-note {
    margin-top: .875rem;
    font-size: .6875rem;
    line-height: 1.5;
    text-align: center;
    color: rgb(var(--c-slate-500));
}

/* ------------------------------------------------------------ theme toggle */
.login-theme {
    position: fixed;
    top: 1.125rem;
    right: 1.125rem;
    width: 2.375rem;
    height: 2.375rem;
    display: grid;
    place-items: center;
    color: rgb(var(--c-slate-400));
    background: rgb(var(--c-slate-800) / .7);
    border: 1px solid rgb(var(--c-slate-700));
    border-radius: .625rem;
    cursor: pointer;
    z-index: 10;
    transition: color .15s ease, border-color .15s ease;
}
.login-theme:hover {
    color: rgb(var(--c-fg-strong));
    border-color: rgb(var(--c-slate-600));
}
.login-theme svg { width: 1rem; height: 1rem; }

/* Short landscape screens: drop the decorative rows so the form still fits. */
@media (max-height: 34rem) {
    .login-facts, .login-note { display: none; }
    .login-head { padding-bottom: 1rem; margin-bottom: 1rem; }
    .login-crest { width: 3rem; }
}

/* Very narrow phones: tighten the fact strip rather than letting the three
   columns collide, and claw back some card padding. */
@media (max-width: 20rem) {
    .login-card { padding: 1.25rem; }
    .login-facts dt { font-size: .5625rem; letter-spacing: .05em; }
    .login-facts dd { font-size: 1rem; }
    .login-field label { letter-spacing: .06em; }
}

/* ===================================================== part navigation */

/* Pull-to-refresh is the direct cause of the "my answers vanished" reports:
   a swipe-up overscroll at the top of the page reloads it. Containing the
   overscroll stops the browser gesture without affecting normal scrolling. */
html, body { overscroll-behavior-y: contain; }

/* per-part tally on each tab */
.tab-count {
    display: inline-block;
    margin-left: .5rem;
    padding: .0625rem .375rem;
    border-radius: .375rem;
    font-family: 'Space Grotesk', monospace;
    font-size: .625rem;
    font-weight: 700;
    letter-spacing: 0;
    color: rgb(var(--c-slate-300));
    background: rgb(var(--c-slate-950) / .55);
    border: 1px solid rgb(var(--c-slate-700));
}
.tab-count.is-done {
    color: rgb(var(--c-emerald-300));
    background: rgb(var(--c-emerald-500) / .12);
    border-color: rgb(var(--c-emerald-500) / .35);
}

/* the way forward at the end of every part */
.part-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-top: .5rem;
    margin-bottom: 3rem;
    padding-top: 1.25rem;
    border-top: 1px solid rgb(var(--c-slate-700));
}

.part-footer__status {
    margin: 0;
    font-size: .8125rem;
    font-weight: 500;
    color: rgb(var(--c-slate-400));
}
.part-footer__status .is-done { color: rgb(var(--c-emerald-300)); font-weight: 700; }
.part-footer__status .is-todo { color: rgb(var(--c-amber-300)); font-weight: 700; }

.part-footer__actions {
    display: flex;
    align-items: center;
    gap: .625rem;
    margin-left: auto;
}

.part-move {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    font-size: .8125rem;
    font-weight: 700;
    color: rgb(var(--c-slate-300));
    background: rgb(var(--c-slate-800));
    border: 1px solid rgb(var(--c-slate-700));
    border-radius: .75rem;
    padding: .6875rem 1rem;
    cursor: pointer;
    transition: color .15s ease, border-color .15s ease, background-color .15s ease;
}
.part-move:hover {
    color: rgb(var(--c-fg-strong));
    border-color: rgb(var(--c-slate-600));
    background: rgb(var(--c-slate-700));
}

/* the primary action: keep moving through the paper */
.part-move--next,
.part-move--finish {
    color: rgb(var(--c-ink));
    background: rgb(var(--c-indigo-400));
    border-color: transparent;
    box-shadow: 0 .5rem 1.25rem -.75rem rgb(var(--c-indigo-400));
}
.part-move--next:hover,
.part-move--finish:hover {
    color: rgb(var(--c-ink));
    background: rgb(var(--c-indigo-300));
    border-color: transparent;
}
.part-move--finish {
    background: rgb(var(--c-emerald-400));
    box-shadow: 0 .5rem 1.25rem -.75rem rgb(var(--c-emerald-400));
}
.part-move--finish:hover { background: rgb(var(--c-emerald-300)); }

@media (max-width: 30rem) {
    .part-footer__actions { width: 100%; }
    .part-move--next, .part-move--finish { flex: 1; justify-content: center; }
}

/* ----------------------------------------------- unfinished-paper dialog */
.confirm-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: .375rem;
}
.confirm-list li {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .625rem .75rem;
    border-radius: .625rem;
    background: rgb(var(--c-slate-950) / .5);
    border: 1px solid rgb(var(--c-slate-700));
}
.confirm-part {
    flex: 1;
    min-width: 0;
    font-size: .8125rem;
    font-weight: 600;
    color: rgb(var(--c-slate-200));
}
.confirm-count {
    font-family: 'Space Grotesk', monospace;
    font-size: .75rem;
    font-weight: 700;
    color: rgb(var(--c-amber-300));
}
.confirm-go {
    font-size: .6875rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: rgb(var(--c-ink));
    background: rgb(var(--c-indigo-400));
    border: 0;
    border-radius: .5rem;
    padding: .3125rem .625rem;
    cursor: pointer;
}
.confirm-go:hover { background: rgb(var(--c-indigo-300)); }

/* ============================================ review fixes: a11y, mobile, perf */

.visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* -------------------------------------------------- reading passage (mobile) */
/* Above the two-column breakpoint the passage sits beside the questions, as
   before. Below it, stacking meant scrolling back up to re-read for every
   question, so it becomes a sticky bar that opens and closes with one tap. */

.passage { height: fit-content; }
.passage__card { overflow: hidden; }

.passage__toggle {
    display: flex;
    align-items: center;
    gap: .75rem;
    width: 100%;
    text-align: left;
    padding: 1.25rem 1.5rem;
    background: none;
    border: 0;
    cursor: pointer;
}
.passage__title {
    flex: 1;
    min-width: 0;
    font-size: 1.05rem;
    font-weight: 800;
    letter-spacing: -.01em;
    color: rgb(var(--c-fg-strong));
}
.passage__chevron {
    flex: 0 0 auto;
    color: rgb(var(--c-slate-400));
    transition: transform .25s ease;
}
.passage.is-collapsed .passage__chevron { transform: rotate(180deg); }

.passage__body {
    padding: 0 1.5rem 1.75rem;
    overflow-y: auto;
    scrollbar-width: none;
}
.passage__body::-webkit-scrollbar { display: none; }

@media (min-width: 1024px) {
    /* desktop: always open, sticky beside the questions, no toggle needed */
    .passage {
        position: sticky;
        top: var(--header-h, 7rem);
    }
    .passage__card { max-height: calc(100vh - var(--header-h, 7rem) - 2rem); display: flex; flex-direction: column; }
    .passage__body { flex: 1; min-height: 0; }
    .passage__toggle { cursor: default; pointer-events: none; }
    .passage__chevron { display: none; }
}

@media (max-width: 1023.98px) {
    .passage {
        position: sticky;
        top: var(--header-h, 6rem);
        z-index: 20;
        margin-bottom: 1.25rem;
    }
    .passage__toggle { padding: .9375rem 1.125rem; }
    .passage__title { font-size: .9375rem; }
    .passage__body { max-height: 38vh; padding: 0 1.125rem 1.25rem; }
    .passage.is-collapsed .passage__body { display: none; }
}

/* ------------------------------------------------------------ staggered entry */
/* transform + opacity only, so this stays on the compositor. */
.card-enter {
    animation: cardIn .45s cubic-bezier(.22, .68, .36, 1) backwards;
    animation-delay: calc(var(--i, 0) * .045s);
}
@keyframes cardIn {
    from { opacity: 0; transform: translate3d(0, 14px, 0); }
    to   { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------- performance */
/* backdrop-filter costs a compositing pass per element, and a part renders up
   to eight cards at once. Desktop absorbs it; a mid-range phone does not. */
@media (max-width: 1023.98px) {
    .glass-card {
        -webkit-backdrop-filter: blur(9px);
        backdrop-filter: blur(9px);
    }
}
@media (pointer: coarse) and (max-width: 640px) {
    /* smallest touch screens: drop the blur entirely and go opaque */
    .glass-card, .glass-panel {
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
        background: rgb(var(--c-slate-900) / .94);
    }
}

/* The toast used Tailwind's animate-bounce, which never stops. */
#toastMessage { animation: none !important; }

/* The colour fields drift at a quarter of scroll speed (see initParallax).
   translate3d promotes the layer on its own; no will-change needed on an
   element this large. */
body::before {
    transform: translate3d(0, var(--parallax, 0px), 0);
}

/* ======================================================= mobile masthead */
/* The header wrapped into three rows on a phone - brand, HUD chips, controls -
   and it is sticky, so roughly a third of the viewport was permanently gone
   while answering. It now starts compact and collapses to a single row of
   essentials as soon as the student scrolls. Everything hidden comes back by
   scrolling to the top; nothing is removed. */

@media (max-width: 767.98px) {
    #masthead { top: .5rem; }

    .masthead__bar {
        padding: .5rem .75rem .625rem;
        gap: .5rem .625rem;
        border-radius: 1.5rem;
        border-width: 1px;
    }

    .masthead__mark { width: 2.25rem; height: 2.25rem; border-radius: .875rem; border-width: 1px; }
    .masthead__mark svg { width: 1.125rem; height: 1.125rem; }
    .masthead__brand h1 { font-size: .9375rem; }
    .masthead__badge { display: none; }
    /* the candidate's own name is not what they need while answering */
    #candidateLine { display: none; }

    .masthead__hud { gap: .375rem; }
    .hud-chip {
        padding: .3125rem .5rem;
        font-size: .75rem;
        border-width: 1px;
        border-radius: .625rem;
    }
    .hud-chip svg { width: .875rem; height: .875rem; }

    .masthead__controls { gap: .375rem; }
    .masthead__controls button { border-width: 1px; }
    #themeToggleBtn, #audioToggleBtn { width: 2rem; height: 2rem; border-radius: .625rem; }
    .masthead__controls button:last-child { padding: .5rem .75rem; border-radius: .625rem; }

    #progressStrip { margin-top: .75rem; margin-bottom: .25rem; }

    /* --- collapsed: only what a student needs mid-question --- */
    body.is-scrolled .masthead__id,
    body.is-scrolled .hud-chip--streak,
    body.is-scrolled .hud-chip--hints,
    body.is-scrolled #themeToggleBtn,
    body.is-scrolled #audioToggleBtn { display: none; }

    body.is-scrolled .masthead__bar { padding: .375rem .625rem; }
    body.is-scrolled .masthead__mark { width: 1.875rem; height: 1.875rem; }
}

/* narrow phones: the Finish button keeps its icon and drops its label */
@media (max-width: 24.5rem) {
    body.is-scrolled .finish-label { display: none; }
}

/* the bar resizes, so anything sticking to it animates rather than jumps */
.masthead__bar,
.masthead__mark { transition: padding .18s ease, width .18s ease, height .18s ease; }
