/* ---------- fonts ----------
   Self-hosted rather than pulled from Google's CDN: no third-party request on
   the critical path, nothing to go wrong on a slow connection, and no student
   traffic leaking to fonts.gstatic.com.

   Both are variable fonts, so one file per subset covers every weight we use.
   The VIETNAMESE subset is not optional here — the whole interface is Vietnamese
   and latin-ext stops short of U+1EA0–1EF9, which is where most of the tone
   marks live (ạ ả ấ ầ ộ ướ). Note this is Archivo variable at weight 900, NOT
   the separate "Archivo Black" family: that one ships no Vietnamese subset at
   all, so every heading with a tone mark would have fallen back to a system
   font mid-word. Same design, correct coverage.

   font-display: swap — text is readable in a fallback face immediately and
   reflows when the real font lands, rather than sitting invisible. */

@font-face {
  font-family: 'Archivo'; font-style: normal; font-weight: 400 900;
  font-display: swap; src: url('/static/fonts/archivo-vietnamese.woff2') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1,
                 U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
                 U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Archivo'; font-style: normal; font-weight: 400 900;
  font-display: swap; src: url('/static/fonts/archivo-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Archivo'; font-style: normal; font-weight: 400 900;
  font-display: swap; src: url('/static/fonts/archivo-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191,
                 U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 100 900;
  font-display: swap; src: url('/static/fonts/inter-vietnamese.woff2') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1,
                 U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
                 U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 100 900;
  font-display: swap; src: url('/static/fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 100 900;
  font-display: swap; src: url('/static/fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191,
                 U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* Typography. The pairing theieltsordinary uses: one very heavy, slightly
     wide grotesk carrying every heading and number, and one quiet humanist sans
     carrying the reading text. Contrast comes from weight and scale, not from
     mixing personalities. */
  --font-display: 'Archivo', 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --font-body: 'Inter', 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  /* The wordmark SVG is live text whose ghost offsets were hand-placed against
     this exact stack — it stays pinned so the logo does not shift. */
  --font-wordmark: 'Segoe UI', Roboto, Helvetica, Arial, ui-sans-serif, system-ui, sans-serif;

  --weight-body: 400;
  --weight-medium: 500;
  --weight-strong: 600;
  --weight-display: 800;
  --weight-black: 900;

  /* Reading sizes stay in rem so they honour the browser's font-size setting. */
  --text-micro: 0.7rem;     /* uppercase tracked labels */
  --text-xs: 0.78rem;
  --text-sm: 0.87rem;
  --text-base: 0.95rem;
  --text-lg: 1.1rem;
  --text-xl: 1.45rem;
  --text-2xl: 2rem;
  --text-display: 2.6rem;   /* the big band readout */

  --leading-tight: 1.15;
  --leading-snug: 1.4;
  --leading-body: 1.65;     /* Vietnamese stacks diacritics; it needs the room */
  --tracking-label: 0.11em;

  /* Greyscale palette. Replaces the old navy as the dominant colour across the
     login backdrop and the working screens. The names stay --navy/--navy-dark
     because they are referenced in ~60 places and renaming them would be a
     refactor, not a recolour; read them as "the dominant dark". */
  --ink-950: #0A0A0A;
  --ink-900: #161616;
  --ink-800: #1D1C1C;
  --ink-700: #232323;
  --ink-600: #2C2C2C;
  --ink-500: #363636;
  --ink-400: #454444;

  --navy: #1D1C1C;
  --navy-dark: #0A0A0A;
  --crimson: #E31837;
  --crimson-dark: #b8112b;

  /* The neutral ramp, de-tinted to match the palette above. These were a cool
     blue-grey (slate), which read as a blue cast once the dominant colour went
     neutral black. Each step keeps its old lightness, so contrast ratios and
     every layout decision built on them are unchanged — only the hue is gone.
     The names stay for the same reason --navy does: they are used everywhere. */
  --offwhite: #F5F5F5;
  --slate-800: #232323;
  --slate-700: #363636;
  --slate-600: #454444;
  --slate-500: #6b6b6b;
  --slate-400: #9a9a9a;
  --slate-300: #d2d2d2;
  --slate-200: #e6e6e6;
  --slate-100: #f3f3f3;
  --slate-50: #fafafa;
  --amber-100: #fef3c7;
  --amber-200: #fde68a;
  --amber-300: #fcd34d;
  --amber-700: #b45309;
  --amber-800: #92400e;
  --blue-100: #dbeafe;
  --blue-200: #bfdbfe;
  --blue-300: #93c5fd;
  --blue-700: #1d4ed8;
  --blue-800: #1e40af;
  --emerald-50: #ecfdf5;
  --emerald-100: #d1fae5;
  --emerald-600: #059669;
  --emerald-700: #047857;
  --radius: 8px;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--offwhite);
  color: var(--slate-800);
  font-family: var(--font-body);
  font-weight: var(--weight-body);
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
}
.container { max-width: 1280px; margin: 0 auto; padding: 0 24px; }
/* Above this the 1280px cap was leaving ~340px of empty gutter on each side
   while the student edited a whole paragraph through a 449px-wide box. The
   extra width goes mostly to the sidebar, which is the actual working surface. */
@media (min-width: 1500px) {
  .container { max-width: 1560px; }
}

/* ---------- header ---------- */
header.top {
  background: var(--navy);
  border-bottom: 4px solid var(--crimson);
}
.header-inner {
  display: flex;
  flex-direction: column;
  gap: 16px;
  /* Carries .container's 24px horizontal padding rather than zeroing it. This
     element has BOTH classes, and .header-inner wins on source order, so
     `padding: 16px 0` was silently dropping the gutter: the wordmark sat hard
     against the screen edge on anything under 1280px, and sat 24px left of the
     body content above it. */
  padding: 16px 24px;
}
@media (min-width: 1024px) {
  .header-inner { flex-direction: row; align-items: center; justify-content: space-between; }
}
.logo-row { display: flex; align-items: center; gap: 12px; }
.logo-text p:first-child {
  font-family: var(--font-display);
  color: #fff; font-weight: var(--weight-black); font-size: 20px; margin: 0;
  line-height: var(--leading-tight); letter-spacing: -0.015em;
}
.logo-text p:last-child {
  color: var(--slate-300); font-size: 11px; margin: 3px 0 0; line-height: 1.2;
  letter-spacing: 0.08em; text-transform: uppercase; font-weight: var(--weight-strong);
}

.summary-card {
  background: #fff; border-radius: 10px; box-shadow: 0 10px 25px rgba(0,0,0,0.15);
  padding: 16px 20px; display: flex; align-items: center; gap: 20px; position: relative;
}
@media (min-width: 1024px) { .summary-card { min-width: 440px; } }
.band-eyebrow { font-size: 10px; font-weight: 700; letter-spacing: 0.08em; color: var(--slate-400); text-transform: uppercase; margin: 0; }
/* The band score is the number the whole screen is about — display face, and
   tabular figures so it does not shuffle sideways when it changes. */
.band-eyebrow-vn { font-size: 10px; letter-spacing: 0.02em; color: var(--slate-400); margin: 0 0 4px; }
.band-number-row { display: flex; align-items: baseline; gap: 4px; }
.band-number { font-family: var(--font-display); font-size: 38px; font-weight: var(--weight-black); letter-spacing: -0.02em; color: var(--navy); font-variant-numeric: tabular-nums; display: inline-block; transition: transform .15s ease; }
.band-number.pop { animation: popOnce 600ms ease-out; }
.band-max { font-size: 12px; font-weight: 600; color: var(--slate-400); }
.delta-toast {
  position: absolute; top: -8px; right: -12px; background: var(--emerald-600); color: #fff;
  font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 999px; box-shadow: 0 4px 10px rgba(0,0,0,.15);
  opacity: 0; transform: translateY(-4px); transition: opacity .3s ease, transform .3s ease; pointer-events: none;
}
.delta-toast.show { opacity: 1; transform: translateY(0); }
.divider-v { height: 40px; width: 1px; background: var(--slate-200); flex-shrink: 0; }
.chips-row { display: flex; flex-wrap: wrap; gap: 4px 16px; margin-bottom: 8px; }
.chip { font-size: 12px; }
.chip .label { color: var(--slate-400); }
.chip .value { font-weight: 700; color: var(--navy); font-variant-numeric: tabular-nums; }
.gauge-track { position: relative; height: 8px; border-radius: 999px; background: var(--slate-200); }
.gauge-fill { position: absolute; inset: 0 auto 0 0; border-radius: 999px; background: rgba(29,28,28,0.22); transition: width 700ms cubic-bezier(.22,1,.36,1); }
.gauge-marker {
  position: absolute; top: -6px; width: 16px; height: 16px; border-radius: 999px;
  background: var(--crimson); border: 2px solid #fff; box-shadow: 0 2px 6px rgba(0,0,0,.25);
  transition: left 700ms cubic-bezier(.22,1,.36,1);
}
.gauge-scale { display: flex; justify-content: space-between; font-size: 9px; color: var(--slate-400); margin-top: 4px; font-variant-numeric: tabular-nums; }

/* ---------- round stepper ---------- */
.stepper { display: flex; align-items: center; gap: 12px; margin-bottom: 24px; font-size: 14px; flex-wrap: wrap; }
.step { display: flex; align-items: center; gap: 8px; }
.step-dot { width: 20px; height: 20px; border-radius: 999px; display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; background: var(--slate-200); color: var(--slate-500); }
.step-dot.on { background: var(--navy); color: #fff; }
.step-dot.done { background: var(--emerald-600); color: #fff; }
.step-label { color: var(--slate-500); font-weight: var(--weight-medium); }
.step-label.on { font-family: var(--font-display); color: var(--navy); font-weight: var(--weight-display); }
.step-label.done { color: var(--emerald-700); font-weight: var(--weight-strong); }
.step-rule { width: 32px; height: 1px; background: var(--slate-300); }
.restart-link { margin-left: auto; background: none; border: 0; font-size: 12px; font-weight: 600; color: var(--slate-400); cursor: pointer; padding: 4px 0; }
.restart-link:hover { color: var(--navy); }

/* ---------- layout ---------- */
.layout { display: grid; grid-template-columns: 1fr; gap: 24px; padding-bottom: 40px; }
@media (min-width: 1024px) { .layout { grid-template-columns: 7fr 5fr; } }
/* The essay is read, the sidebar is worked in — so past a point the essay column
   stops growing (a 850px measure is already past comfortable line length) and
   the rest of the width goes to the editor. */
@media (min-width: 1500px) {
  .layout { grid-template-columns: minmax(0, 760px) 1fr; }
}

.card { background: #fff; border-radius: 10px; border: 1px solid var(--slate-200); box-shadow: 0 1px 2px rgba(0,0,0,0.04); margin-bottom: 24px; }
.essay-head { padding: 20px 24px 16px; border-bottom: 1px solid var(--slate-100); }
.essay-eyebrow { font-family: var(--font-display); font-size: 11px; font-weight: var(--weight-display); letter-spacing: 0.08em; color: var(--crimson); text-transform: uppercase; margin: 0; }
.essay-meta { font-size: 12px; color: var(--slate-400); margin: 4px 0 0; }
/* The student's own essay, in English — Inter at reading size. */
.essay-body { padding: 24px; font-size: 15px; line-height: 1.9; }
.essay-body p { margin: 0 0 20px; white-space: pre-wrap; }
.essay-body p:last-child { margin-bottom: 0; }
.essay-legend { padding: 12px 24px; border-top: 1px solid var(--slate-100); display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--slate-400); flex-wrap: wrap; }
.legend-swatch { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }

.highlight-btn {
  background: transparent; border: 0; padding: 0; margin: 0; font: inherit; color: inherit;
  cursor: default; display: inline; text-align: left;
}
.highlight-btn.active {
  cursor: pointer; border-radius: 3px; padding: 0.02em 0.15em; margin: 0 -0.02em;
  text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 3px;
  transition: background-color .15s ease, box-shadow .15s ease;
}
.highlight-btn.active.logic { background: var(--amber-100); text-decoration-color: var(--amber-700); box-shadow: inset 0 -1px 0 var(--amber-300); }
.highlight-btn.active.logic:hover { background: var(--amber-200); }
.highlight-btn.active.language { background: var(--blue-100); text-decoration-color: var(--blue-700); box-shadow: inset 0 -1px 0 var(--blue-300); }
.highlight-btn.active.language:hover { background: var(--blue-200); }
/* Every remaining misspelling is marked at once and each is its own click
   target, so the student fixes them where they sit in the essay. */
.highlight-btn.active.spelling {
  background: #fdeceb; text-decoration-color: var(--crimson);
  box-shadow: inset 0 -1px 0 var(--crimson); cursor: pointer;
}
.highlight-btn.active.spelling:hover { background: #f7c9c4; }
.highlight-btn.resolved { color: var(--slate-500); }

/* Anchored to the word rather than parked in the sidebar: the student is
   looking at the typo, so the box to retype it belongs there. */
.spell-pop {
  position: absolute; z-index: 60; width: 260px;
  background: #fff; border: 2.5px solid #0A0A0A; border-radius: 4px;
  box-shadow: 5px 5px 0 #0A0A0A; padding: 14px 16px;
}
.spell-pop-title {
  margin: 0 0 2px; font-family: var(--font-display);
  font-size: 0.7rem; font-weight: var(--weight-display);
  letter-spacing: var(--tracking-label); text-transform: uppercase;
  color: var(--crimson);
}
.spell-pop-hint { margin: 0 0 8px; font-size: 0.78rem; color: var(--slate-600); }
.spell-pop-input {
  width: 100%; padding: 8px 10px; font: inherit; font-size: 0.9rem; font-weight: 600;
  color: var(--navy); background: var(--slate-50);
  border: 2px solid #0A0A0A; border-radius: 3px;
}
.spell-pop-input:focus { outline: none; background: #fff; box-shadow: 3px 3px 0 var(--crimson); }
.spell-pop-msg {
  margin: 8px 0 0; font-size: 0.76rem; line-height: var(--leading-snug);
}
.spell-pop-msg.wrong { color: var(--crimson-dark); }
.spell-pop-actions {
  margin-top: 10px; display: flex; justify-content: flex-end; gap: 8px; align-items: center;
}
.spell-pop-cancel {
  background: none; border: 0; cursor: pointer;
  font-size: 0.74rem; font-weight: 600; color: var(--slate-500);
}
.spell-pop-cancel:hover { color: var(--navy); }
.spell-pop-submit {
  background: var(--crimson); color: #fff; border: 0; border-radius: 4px;
  padding: 7px 14px; font: inherit; font-size: 0.78rem; font-weight: 700; cursor: pointer;
}
.spell-pop-submit:hover:not(:disabled) { background: var(--crimson-dark); }
.spell-pop-submit:disabled { opacity: 0.5; cursor: not-allowed; }
.resolved-check { color: var(--emerald-600); font-size: 12px; vertical-align: super; }

aside.sidebar-col .sticky-wrap { position: static; }
@media (min-width: 1024px) { aside.sidebar-col .sticky-wrap { position: sticky; top: 24px; } }
.sidebar-card { border-left: 4px solid var(--crimson); overflow: hidden; }
.sidebar-head { background: var(--navy); padding: 16px 20px; }
.sidebar-head p:first-child { font-family: var(--font-display); color: #fff; font-weight: var(--weight-display); font-size: 15px; letter-spacing: -0.01em; margin: 0; }
.sidebar-head p:last-child { color: var(--slate-300); font-size: 12px; margin: 4px 0 0; }
.sidebar-body { padding: 20px; min-height: 280px; }

.panel-empty { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 40px 0; color: var(--slate-400); }
.panel-empty svg { width: 40px; height: 40px; margin-bottom: 12px; color: var(--slate-300); }
.panel-empty .msg1 { font-size: 14px; font-weight: 500; color: var(--slate-500); margin: 0; }
.panel-empty .msg2 { font-size: 12px; color: var(--slate-400); margin: 4px 0 0; max-width: 220px; }

/* The label carries the model's own name for the flaw, which for logic errors
   runs long ("Mâu thuẫn logic / Thiếu nhất quán giữa Mở bài và Thân bài" — 434px
   of a 500px card). Uppercase with tracking was sized for a one-word chip, so
   long names get sentence case, normal tracking and room to wrap onto a second
   line instead of straining against the card edge. */
.tag {
  display: inline-block; font-family: var(--font-display);
  font-size: 10px; font-weight: var(--weight-display); letter-spacing: 0.05em;
  text-transform: uppercase; padding: 4px 8px; border-radius: 4px;
  max-width: 100%; line-height: 1.45; overflow-wrap: anywhere;
}
.tag.long {
  text-transform: none; letter-spacing: 0.01em; font-size: 11px;
}
.tag.logic { background: var(--amber-100); color: var(--amber-800); }
.tag.language { background: var(--blue-100); color: var(--blue-800); }
.quote-block { font-size: 14px; font-style: italic; color: var(--slate-600); padding-left: 12px; margin: 12px 0 0; }
.quote-block.logic { border-left: 2px solid var(--amber-300); }
.quote-block.language { border-left: 2px solid var(--blue-300); }
/* Vietnamese teaching text: diacritics stack above and below, so it gets more
   leading than the English essay body does. */
.question-text { font-size: 14px; color: var(--slate-700); line-height: var(--leading-body); margin: 16px 0 0; }
.incorrect-verdict {
  font-size: 13px; color: var(--crimson-dark); background: #fdeceb; border: 1px solid #f7c9c4;
  border-radius: 6px; padding: 10px 12px; margin: 16px 0 0; line-height: 1.5;
}
textarea.correction-input {
  width: 100%; margin-top: 12px; font-size: 14px; border: 1px solid var(--slate-300); border-radius: 6px;
  padding: 8px 12px; resize: vertical; font-family: inherit; color: inherit;
}
textarea.correction-input:focus { outline: none; border-color: var(--navy); box-shadow: 0 0 0 2px rgba(29,28,28,0.25); }
textarea.correction-input::placeholder { color: var(--slate-400); }
.fix-toolbar { display: flex; justify-content: space-between; align-items: center; margin-top: 8px; gap: 8px; flex-wrap: wrap; }
.fix-toolbar span { font-size: 12px; color: var(--slate-400); }
.btn-submit {
  background: var(--crimson); color: #fff; font-size: 14px; font-weight: 600;
  padding: 10px 20px; border-radius: 6px; border: 0; cursor: pointer; transition: background-color .15s ease;
}
.btn-submit:hover:not(:disabled) { background: var(--crimson-dark); }
.btn-submit:disabled { opacity: 0.4; cursor: not-allowed; }
.btn-advance {
  margin-top: 16px; width: 100%; background: var(--navy); color: #fff; font-size: 14px; font-weight: 600;
  padding: 10px 0; border-radius: 6px; border: 0; cursor: pointer; transition: background-color .15s ease;
}
.btn-advance:hover { background: var(--navy-dark); }
.btn-ghost {
  margin-top: 20px; background: transparent; border: 1px solid var(--navy); color: var(--navy);
  font-size: 14px; font-weight: 600; padding: 8px 16px; border-radius: 6px; cursor: pointer;
  transition: background-color .15s ease, color .15s ease;
}
.btn-ghost:hover { background: var(--navy); color: #fff; }
.btn-ghost:disabled { opacity: 0.4; cursor: not-allowed; }
.btn-ghost:disabled:hover { background: transparent; color: var(--navy); }

/* Deleting the highlighted span. Sits in the same row as "Nộp bài sửa" but must
   not read as the primary action: cutting text is the rarer, heavier choice, so
   it stays outlined while the submit button keeps the crimson fill. The shared
   .btn-ghost rule carries a 20px top margin for the standalone buttons it was
   written for; inside the toolbar that would break the row alignment. */
.fix-toolbar .btn-ghost { margin-top: 0; padding: 9px 14px; font-size: 13px; }

.delete-preview {
  margin-top: 16px; border: 1px solid var(--amber-300); background: var(--amber-100);
  border-radius: 6px; padding: 12px 14px;
}
.delete-preview-title { font-size: 12px; font-weight: 700; color: var(--amber-800); margin: 0 0 8px; }
/* The result of the cut, shown in the essay's own typeface: this is English
   prose about to be spliced in, not Vietnamese teaching text. */
.delete-preview-after {
  font-size: 14px; line-height: var(--leading-body); color: var(--slate-800);
  background: #fff; border: 1px solid var(--amber-200); border-radius: 4px;
  padding: 10px 12px; margin: 0;
}
.delete-preview-note {
  font-size: 12px; line-height: 1.5; color: var(--crimson-dark); margin: 10px 0 0;
}
.delete-preview-actions { display: flex; justify-content: flex-end; align-items: center; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
.delete-preview-actions .btn-ghost { margin-top: 0; padding: 9px 14px; font-size: 13px; }
.delete-preview-actions .btn-submit { padding: 9px 16px; font-size: 13px; }

/* Shown only when a quote could not be located in the essay, so the student is
   told why nothing is marked instead of being asked to rewrite an invisible
   span. */
.highlight-warning {
  margin: 0; padding: 12px 24px; font-size: 13px; line-height: 1.5;
  color: var(--amber-800); background: var(--amber-100);
  border-top: 1px solid var(--amber-200);
}

.success-block { text-align: center; padding: 16px 0; }
.success-icon { width: 44px; height: 44px; border-radius: 999px; background: var(--emerald-100); color: var(--emerald-600); display: flex; align-items: center; justify-content: center; margin: 0 auto 12px; }
.success-icon svg { width: 20px; height: 20px; }
.success-title { font-weight: 600; color: var(--navy); font-size: 14px; margin: 0; }
.success-quote {
  font-size: 13px; color: var(--slate-600); background: var(--slate-50); border: 1px solid var(--slate-100);
  border-radius: 6px; padding: 10px 12px; margin-top: 12px; text-align: left; font-style: italic; line-height: 1.5;
}
.success-note { font-size: 14px; color: var(--slate-600); margin-top: 16px; }

/* ---------- per-fix band credit ---------- */
.delta-note {
  display: flex; align-items: flex-start; gap: 8px; text-align: left;
  margin-top: 12px; padding: 10px 12px; border-radius: 6px;
  background: var(--emerald-50, #ecfdf5); border: 1px solid var(--emerald-100);
}
.delta-note-badge {
  flex: none; font-size: 11px; font-weight: 700; letter-spacing: 0.02em;
  padding: 3px 8px; border-radius: 999px; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.delta-note-badge.earned { background: var(--emerald-600); color: #fff; }
.delta-note-badge.flat { background: var(--slate-100); color: var(--slate-600); }
.delta-note-text { font-size: 12px; color: var(--slate-600); line-height: 1.5; }

/* ---------- provisional vs verified scoring ---------- */
.band-state {
  display: inline-block; font-size: 9px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.06em; padding: 2px 6px; border-radius: 4px; margin-top: 2px;
}
.band-state.estimated { background: var(--amber-100, #fef3c7); color: #92400e; }
.band-state.verified { background: var(--emerald-100); color: var(--emerald-700); }
.chip.estimated { border-style: dashed; opacity: 0.85; }
.chip.unmeasured { opacity: 0.45; }

.banner-info {
  background: #eff6ff; border: 1px solid #bfdbfe; color: #1e3a5f;
  padding: 10px 14px; border-radius: 8px; font-size: 13px; line-height: 1.55; margin-bottom: 16px;
}

#student-select, #student-other {
  width: 100%; padding: 10px 12px; font-size: 0.95rem; font-family: inherit;
  color: var(--navy); background: #fff;
  border: 1px solid var(--slate-200, #e6e6e6); border-radius: 6px;
}
#student-select:focus, #student-other:focus {
  outline: 2px solid var(--navy); outline-offset: -1px;
}

/* ---------- spelling checklist ---------- */
/* Every misspelling at once. Typos are too cheap to spend a Socratic round on
   each, so the whole set is cleared in one pass before grammar begins. */
.spelling-panel {
  background: var(--amber-100); border: 1px solid var(--amber-300);
  border-left: 4px solid var(--amber-700); border-radius: var(--radius);
  padding: 14px 18px; margin-bottom: 16px;
}
.spelling-head { display: flex; align-items: baseline; gap: 10px; }
.spelling-title {
  font-family: var(--font-display);
  font-size: 0.75rem; font-weight: var(--weight-display); letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--amber-800);
}
.spelling-count { font-size: 0.75rem; font-weight: 600; color: var(--amber-700); }
.spelling-note { margin: 6px 0 10px; font-size: 0.8rem; color: var(--slate-600); line-height: var(--leading-body); }
.spelling-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.spelling-list li {
  background: #fff; border: 1px solid var(--amber-300); border-radius: 999px;
  padding: 4px 12px; font-size: 0.82rem; color: var(--slate-700);
}
.spelling-list .wrong { color: var(--crimson-dark); text-decoration: line-through; font-weight: 600; }
.spelling-list .right { color: var(--emerald-700); font-weight: 700; }
.spelling-list li.fixed { opacity: 0.55; }
.spelling-list li.fixed .wrong { text-decoration: line-through; }

.cycle-hint {
  margin: 12px 0 0; font-size: 0.76rem; line-height: 1.6; color: var(--slate-500);
  text-align: left;
}
.cycle-hint strong { color: var(--navy); }

/* Sits on the navy header, so it cannot inherit the navy-on-white button
   colours — those rendered as an invisible button on an invisible label. */
#auth-indicator { display: flex; align-items: center; gap: 12px; margin-left: auto; }
#auth-name { font-size: 0.82rem; font-weight: 600; color: #fff; }
#logout-btn {
  padding: 6px 12px; font-size: 0.8rem;
  background: transparent; color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.45);
}
#logout-btn:hover { background: rgba(255, 255, 255, 0.12); border-color: #fff; }

/* ---------- login (comic energy, greyscale palette) ---------- */
/* Halftone dots over a greyscale field. Same two-layer construction as before,
   recoloured to the #0A0A0A–#454444 ramp: the light now blooms out of the black
   the way it does in the smoke reference, instead of glowing crimson and blue.
   The white card and its crimson shadow still read hard against it. */
/* Full-bleed: the login owns the whole viewport rather than sitting as a panel
   on a white page. The header floats over it transparently (see .login-mode). */
#login-section {
  position: fixed; inset: 0; z-index: 1;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.login-stage {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  padding: 96px 16px 32px;
}
/* Both layers sit oversized so the parallax shift never exposes an edge.
   --mx/--my are set from the pointer in app.js and range about -1 .. 1. */
.login-stage::before,
.login-stage::after {
  content: ""; position: absolute; inset: -60px; z-index: 0;
  will-change: transform;
}
.login-stage::before {
  background:
    radial-gradient(circle at 22% 18%, rgba(69,68,68,0.95), transparent 46%),
    radial-gradient(circle at 82% 78%, rgba(54,54,54,0.85), transparent 50%),
    radial-gradient(circle at 62% 30%, rgba(44,44,44,0.70), transparent 40%),
    linear-gradient(150deg, var(--ink-800) 0%, var(--ink-900) 55%, var(--ink-950) 100%);
  transform: translate(calc(var(--mx, 0) * 18px), calc(var(--my, 0) * 18px));
}
/* The halftone layer. Two offset dot grids read as printed comic tone, and it
   drifts less than the colour behind it so the two separate into depth. */
.login-stage::after {
  background-image:
    radial-gradient(rgba(255,255,255,0.17) 1.1px, transparent 1.2px),
    radial-gradient(rgba(255,255,255,0.09) 1.1px, transparent 1.2px);
  background-size: 13px 13px, 13px 13px;
  background-position: 0 0, 6.5px 6.5px;
  mask-image: radial-gradient(circle at 50% 45%, #000 30%, transparent 72%);
  -webkit-mask-image: radial-gradient(circle at 50% 45%, #000 30%, transparent 72%);
  transform: translate(calc(var(--mx, 0) * -8px), calc(var(--my, 0) * -8px));
}
/* The blob chases the pointer on a spring rather than tracking it exactly, so
   it drifts in a beat behind the cursor. Sits above the backdrop but below the
   card, and never eats a click. */
.cursor-blob {
  position: absolute; top: 0; left: 0; z-index: 0;
  width: 460px; height: 460px; margin: -230px 0 0 -230px;
  pointer-events: none;
  border-radius: 50%;
  background: radial-gradient(circle,
    rgba(255, 255, 255, 0.30) 0%,
    rgba(226, 226, 226, 0.17) 38%,
    rgba(180, 180, 180, 0.07) 62%,
    transparent 72%);
  filter: blur(28px);
  opacity: 0;
  transition: opacity 0.5s ease;
  will-change: transform;
}
.cursor-blob.live { opacity: 1; }

/* Over the full-bleed gradient the solid navy bar would read as a stripe, so
   the header goes transparent and just floats its wordmark. */
body.login-mode { overflow: hidden; }
body.login-mode header.top {
  position: relative; z-index: 10;
  background: transparent; border-bottom: none;
}
body.login-mode .foot { display: none; }

.login-card {
  position: relative; z-index: 1;
  width: 100%; max-width: 400px; padding: 34px 30px 26px;
  background: #fff;
  border: 3px solid #0A0A0A;
  border-radius: 4px;
  /* Hard offset shadow = ink-on-newsprint panel, not a soft web card. */
  box-shadow: 9px 9px 0 var(--crimson), 9px 9px 0 3px #0A0A0A;
}

/* Comic starburst sticker, tilted so it reads as slapped onto the panel. */
.login-badge {
  position: absolute; top: -30px; right: -26px;
  width: 92px; height: 92px;
  transform: rotate(11deg);
  filter: drop-shadow(3px 3px 0 rgba(11, 18, 32, 0.85));
}
/* Pinned, not inherited. Both SVGs are LIVE TEXT whose x/y offsets — the
   cyan/pink misregistration split, the halftone strip, the badge fitting inside
   the starburst's spikes — were placed by hand against the old system stack.
   Swapping the body font would silently shift all of it. */
.login-badge text { text-anchor: middle; font-family: var(--font-wordmark); font-style: italic; }
/* Sized to the burst's inner radius so no letter crosses a spike. */
.badge-by {
  font-size: 12px; font-weight: 700; fill: #7c4a03; letter-spacing: 0.04em;
}
.badge-name {
  font-size: 17px; font-weight: 900; fill: #0A0A0A; letter-spacing: -0.02em;
}

/* v1.2.1 wordmark, recoloured. Every internal proportion is the original —
   only the CSS width grew, so the logo still outranks the heading below it. */
.login-logo { display: block; width: 286px; height: auto; margin: 0 0 4px -6px; }
.login-logo text { font-family: var(--font-wordmark); font-style: italic; }
/* Deep electric blue, not azure: the lighter tone read as washed out next to
   the crimson. Heavier weight for the same reason. */
.logo-the {
  font-size: 22px; font-weight: 900; fill: #1240E5;
  letter-spacing: 0.02em;
}
.logo-word text { font-size: 47px; font-weight: 900; letter-spacing: -0.03em; }
/* The misregistered-print split, unchanged from the original. */
.ghost-cyan { fill: #2ad4e8; opacity: 0.9; }
.ghost-pink { fill: #ff2e88; opacity: 0.9; }
/* No stroke. An outline on letters this size fills the counters and muddies
   the shapes — the offset cyan/pink ghosts already give the edges definition,
   which is how the film titles do it. */
.logo-main { fill: var(--crimson); }

/* Deliberately smaller than the wordmark: this labels the form, it is not the
   brand. Both words now sit on one line at this size. */
/* The card keeps its comic palette and ink border; only the type changes, so
   the login screen and the working screens read as one product. */
.login-title {
  font-family: var(--font-display);
  margin: 2px 0 0; font-size: var(--text-xl); font-weight: var(--weight-black);
  line-height: var(--leading-tight);
  letter-spacing: -0.02em; color: var(--navy); font-style: italic;
}
.login-title span { color: var(--crimson); margin-left: 0.3em; }
.login-sub {
  margin: 12px 0 22px; font-size: 0.83rem; color: var(--slate-500);
  padding-bottom: 16px; border-bottom: 2px dashed var(--slate-200);
}

.login-label {
  display: block; margin-bottom: 6px;
  font-family: var(--font-display);
  font-size: var(--text-micro); font-weight: var(--weight-display);
  letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--slate-600);
}
#login-username, #login-password {
  width: 100%; padding: 12px 14px; margin-bottom: 16px;
  font-size: 0.95rem; font-family: inherit; font-weight: 600;
  color: var(--navy); background: var(--slate-50);
  border: 2.5px solid #0A0A0A; border-radius: 3px;
  transition: box-shadow 0.12s ease, background 0.12s ease;
}
#login-username::placeholder, #login-password::placeholder {
  color: var(--slate-400); font-weight: 500;
}
#login-username:focus, #login-password:focus {
  outline: none; background: #fff;
  box-shadow: 4px 4px 0 var(--crimson);
}

/* The password field carries the input's old bottom margin so the reveal button
   can be absolutely positioned against the field itself. */
.password-field { position: relative; margin-bottom: 16px; }
.password-field #login-password { margin-bottom: 0; padding-right: 46px; }

/* Sits INSIDE the 2.5px ink border. The focus state on these inputs is a 4px
   offset hard shadow rather than a ring, so anything hanging off the right edge
   would be cut by it. */
.password-toggle {
  position: absolute; top: 2.5px; right: 2.5px; bottom: 2.5px;
  display: flex; align-items: center; justify-content: center;
  width: 40px; padding: 0; cursor: pointer;
  background: none; border: 0; border-radius: 2px;
  color: var(--slate-500);
  transition: color 0.12s ease, background 0.12s ease;
}
.password-toggle:hover { color: var(--navy); background: var(--slate-100); }
.password-toggle[aria-pressed="true"] { color: var(--crimson); }
.password-toggle:focus-visible { outline: 2px solid var(--navy); outline-offset: -2px; }
.password-toggle svg { width: 19px; height: 19px; }

.login-error {
  margin: -4px 0 14px; padding: 9px 12px;
  background: #fff1f2; border-left: 4px solid var(--crimson);
  color: var(--crimson-dark); font-size: 0.83rem; font-weight: 600;
}

.login-btn {
  width: 100%; padding: 13px 18px; cursor: pointer;
  background: var(--crimson); color: #fff;
  font-family: var(--font-display); font-size: var(--text-base); font-weight: var(--weight-black);
  letter-spacing: 0.06em; text-transform: uppercase; font-style: italic;
  border: 2.5px solid #0A0A0A; border-radius: 3px;
  box-shadow: 5px 5px 0 #0A0A0A;
  transition: transform 0.1s ease, box-shadow 0.1s ease, background 0.15s ease;
}
.login-btn:hover:not(:disabled) { background: var(--crimson-dark); }
/* Press = the panel physically sinks onto its shadow. */
.login-btn:active:not(:disabled) {
  transform: translate(5px, 5px); box-shadow: 0 0 0 #0A0A0A;
}
.login-btn:focus-visible { outline: 3px solid var(--amber-300); outline-offset: 3px; }
.login-btn:disabled { opacity: 0.6; cursor: not-allowed; }

.login-note {
  margin: 16px 0 0; font-size: 0.72rem; line-height: var(--leading-body); color: var(--slate-500);
}

@media (max-width: 520px) {
  .login-title { font-size: 2.1rem; }
  .login-card { padding: 28px 22px 22px; box-shadow: 6px 6px 0 var(--crimson), 6px 6px 0 3px #0A0A0A; }
  .login-badge { right: -8px; }
}
#student-other { margin-top: 8px; }
.consent-note {
  font-size: 11px; color: var(--slate-400); line-height: 1.5; margin: 8px 0 0;
}

.edit-hint {
  font-size: 11px; color: var(--slate-400); line-height: 1.5; margin: 12px 0 6px;
}

.scope-note {
  font-size: 11px; color: var(--slate-400); margin: 0 0 8px;
  padding-bottom: 8px; border-bottom: 1px dashed var(--slate-100);
}

.stepper-actions { margin-left: auto; display: flex; align-items: center; gap: 12px; }
.btn-end-session {
  background: var(--navy); color: #fff; border: 0; border-radius: 6px;
  font-size: 12px; font-weight: 600; padding: 6px 12px; cursor: pointer;
}
.btn-end-session:hover { background: var(--ink-600); }

.cycle-actions { display: flex; flex-direction: column; gap: 8px; width: 100%; margin-top: 16px; }

/* ---------- final report ---------- */
.report { text-align: left; }
.report-overall { text-align: center; padding: 12px 0 16px; border-bottom: 1px solid var(--slate-100); }
.report-overall-label {
  font-size: 10px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--slate-400); margin: 0 0 6px;
}
.report-overall-scores {
  margin: 0; display: flex; align-items: center; justify-content: center; gap: 8px;
  font-family: var(--font-display); letter-spacing: -0.02em;
  font-size: 28px; font-weight: var(--weight-black); color: var(--navy); font-variant-numeric: tabular-nums;
}
.report-overall-scores .was { color: var(--slate-400); font-size: 20px; }
.report-overall-scores .arrow { color: var(--slate-400); font-size: 16px; }

.report-row { padding: 12px 0; border-bottom: 1px solid var(--slate-100); }
.report-row-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.report-crit { font-family: var(--font-display); font-size: 12px; font-weight: var(--weight-display); color: var(--navy); }
.report-scores {
  display: flex; align-items: baseline; gap: 5px; font-size: 13px;
  font-weight: 700; color: var(--navy); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.report-scores .was { color: var(--slate-400); font-weight: 600; }
.report-scores .arrow { color: var(--slate-400); font-size: 11px; }
.report-comment { font-size: 12px; color: var(--slate-600); line-height: var(--leading-body); margin: 6px 0 0; }

.report-delta { font-size: 10px; font-weight: 700; padding: 1px 5px; border-radius: 4px; }
.report-delta.up { background: var(--emerald-100); color: var(--emerald-700); }
.report-delta.down { background: #fee2e2; color: #b91c1c; }
.report-delta.flat { background: var(--slate-100); color: var(--slate-600); }

.report-block { margin-top: 16px; }
.report-block-title {
  font-size: 10px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--slate-400); margin: 0 0 4px;
}
.report-block p:last-child { font-size: 13px; color: var(--slate-600); line-height: var(--leading-body); margin: 0; }
.report-ceiling-value {
  margin: 0; font-family: var(--font-display); font-size: 36px;
  font-weight: var(--weight-black); letter-spacing: -0.02em; color: var(--navy);
  line-height: 1.1; font-variant-numeric: tabular-nums;
}
.report-ceiling-reason {
  margin: 8px 0 0; font-size: 12px; color: var(--slate-600); line-height: var(--leading-body); text-align: left;
}
.report-scores .ceiling { color: var(--slate-400); font-weight: 600; font-size: 11px; }

.report-block.ceiling {
  background: var(--slate-50); border-left: 3px solid var(--navy);
  padding: 10px 12px; border-radius: 0 6px 6px 0;
}

.report-block.focus {
  background: var(--slate-50); border-left: 3px solid var(--crimson);
  padding: 10px 12px; border-radius: 0 6px 6px 0;
}
.report-warn {
  margin-top: 12px; font-size: 12px; color: #92400e;
  background: var(--amber-100, #fef3c7); padding: 8px 10px; border-radius: 6px; line-height: 1.5;
}
.report-meta { margin-top: 12px; font-size: 11px; color: var(--slate-400); text-align: center; }

/* ---------- essay submission card ---------- */
#essay-input-section textarea#essay-input,
#essay-input-section textarea#topic-input {
  width: 100%; font-family: inherit; font-size: 0.95rem; padding: 0.75rem; border-radius: 8px;
  border: 1px solid var(--slate-300); background: var(--slate-50); color: var(--slate-800); resize: vertical;
}
.input-label { display: block; font-size: 0.85rem; font-weight: 600; color: var(--navy); margin-bottom: 6px; }
.required-star { color: var(--crimson); }
.topic-meta { margin-top: 6px; }
.topic-meta span { font-size: 0.8rem; color: var(--slate-400); }
.topic-banner {
  padding: 12px 24px; border-bottom: 1px solid var(--slate-100); background: var(--slate-50);
  font-size: 13px; color: var(--slate-600); line-height: 1.5;
}
.topic-banner strong { color: var(--navy); font-weight: 700; display: block; font-size: 11px;
  text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 4px; }
.essay-toolbar {
  display: flex; justify-content: space-between; align-items: center; margin-top: 0.75rem; flex-wrap: wrap; gap: 0.5rem;
}
.essay-actions { display: flex; gap: 0.5rem; }
#word-count { color: var(--slate-400); font-size: 0.85rem; }
.btn { border: none; border-radius: 8px; padding: 0.6rem 1.1rem; font-size: 0.9rem; cursor: pointer; font-weight: 600; }
.btn-primary { background: var(--navy); color: #fff; }
.btn-primary:hover { background: var(--navy-dark); }
.btn-secondary { background: transparent; color: var(--navy); border: 1px solid var(--navy); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.field-error { color: var(--crimson-dark); font-size: 0.85rem; margin-top: 0.5rem; }

.banner { padding: 0.85rem 1rem; border-radius: 8px; font-size: 0.9rem; margin-bottom: 16px; }
.banner-error { background: #fdeceb; color: var(--crimson-dark); }

footer.foot { padding: 8px 0 40px; }
footer.foot p { font-size: 11px; color: var(--slate-400); margin: 0; }

.hidden { display: none !important; }

@keyframes popOnce { 0% { transform: scale(1); } 30% { transform: scale(1.12); } 100% { transform: scale(1); } }
@keyframes fadeScaleIn { from { opacity: 0; transform: translateY(4px) scale(0.98); } to { opacity: 1; transform: translateY(0) scale(1); } }
.fade-in { animation: fadeScaleIn 220ms ease-out; }

::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: var(--slate-300); border-radius: 8px; }
::-webkit-scrollbar-track { background: transparent; }

:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; }

.loading-overlay {
  position: fixed; inset: 0; background: rgba(0, 12, 38, 0.55);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 20px; color: #fff; z-index: 50;
}

/* Two crimson arcs turning at different speeds — reads as motion at a glance
   even in peripheral vision, which a single thin arc does not. */
.spinner {
  position: relative; width: 54px; height: 54px; border-radius: 50%;
  /* Without this the column flex parent squashes it to border height on a
     short viewport, and it stops reading as a circle. */
  flex: none;
  border: 4px solid rgba(255, 255, 255, 0.16);
  border-top-color: var(--crimson);
  animation: spin 0.85s cubic-bezier(0.62, 0.15, 0.4, 0.9) infinite;
}
.spinner::after {
  content: ""; position: absolute; inset: 5px; border-radius: 50%;
  border: 3px solid transparent; border-bottom-color: rgba(227, 24, 55, 0.55);
  animation: spin 1.5s linear infinite reverse;
}
@keyframes spin { to { transform: rotate(360deg); } }

.loading-line {
  margin: 0; display: flex; align-items: baseline; gap: 1px;
  font-size: 14px; font-weight: 600; letter-spacing: 0.06em; color: #fff;
}
.loading-dots { display: inline-flex; gap: 2px; margin-left: 2px; }
.loading-dots i {
  width: 3px; height: 3px; border-radius: 50%; background: var(--crimson);
  align-self: flex-end; margin-bottom: 3px; opacity: 0.25;
  animation: dotPulse 1.2s ease-in-out infinite;
}
.loading-dots i:nth-child(2) { animation-delay: 0.16s; }
.loading-dots i:nth-child(3) { animation-delay: 0.32s; }
@keyframes dotPulse {
  0%, 70%, 100% { opacity: 0.25; transform: translateY(0); }
  35% { opacity: 1; transform: translateY(-2px); }
}

