/* ===== Design tokens =====
   Dark is the default everywhere (:root). Every surface token is paired
   with a foreground token guaranteed to sit on it — never mix a surface
   from one pairing with a foreground from another. `body.paper` (the
   write screen only, see the Paper mode section) redefines every one of
   these to the light "paper" equivalents; nothing outside this block and
   the .paper block below may declare a raw hex color. */
:root {
  --bg-base: #10151c;
  --bg-surface: #171e27;
  --bg-raised: #1e2732;
  --bg-inset: #0c1117;

  --fg-primary: #e8edf3;
  --fg-secondary: #a8b5c4;
  --fg-muted: #74838f;

  --border-subtle: #232d3a;
  --border-strong: #33404f;

  --accent: #ffb23e;
  --accent-fg: #10151c;
  --accent-dim: #3a2f1a;

  --opponent: #62b6c4;
  --opponent-fg: #10151c;
  --opponent-dim: #1c333a;

  --positive: #5fc98a;
  --positive-fg: #10151c;
  --caution: #f0b849;
  --caution-fg: #10151c;
  --caution-dim: #3a2f14;
  --danger: #ff7a6b;
  --danger-fg: #10151c;

  /* Test panel fixes spec, section 2: "flagged" needs a colour genuinely
     distinct from "answered" (--accent) — --caution was an almost
     pixel-identical amber (#f0b849 vs #ffb23e, #3a2f14 vs #3a2f1a) and is
     exactly why the bug existed. Same purple family as --chart-4, already
     proven legible on this dark background. */
  --warning: #b18cf0;
  --warning-fg: #10151c;
  --warning-dim: #2a2340;

  /* Chart series — kept as these names so dashboard.js/progress.js don't
     need touching, just repointed to dark-mode-safe hues. --chart-4 has
     no equivalent in the given palette, added per the token rule ("if a
     component needs a colour that doesn't exist as a token, add it"). */
  --chart-total: var(--accent);
  --chart-readiness: var(--opponent);
  --chart-3: var(--positive);
  --chart-4: #b18cf0;
  --chart-5: var(--caution);

  --font-serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --font-mono: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;

  --radius: 10px;
}

/* Paper mode — the writing screen only. Same token names, light values,
   so every component below keeps working unchanged under either mode. */
body.paper {
  --bg-base: #f4f2ea;
  --bg-surface: #ffffff;
  --bg-raised: #ffffff;
  --bg-inset: #eeece3;

  --fg-primary: #1d2530;
  --fg-secondary: #5b6470;
  --fg-muted: #85909b;

  --border-subtle: #dcd8c8;
  --border-strong: #c2bda8;

  --accent: #b5501f;
  --accent-fg: #fff8ef;
  --accent-dim: #f3ded2;

  --positive: #2f6b3a;
  --positive-fg: #ffffff;
  --caution: #9a6a2c;
  --caution-fg: #ffffff;
  --danger: #b3271a;
  --danger-fg: #ffffff;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  min-height: 100%;
}

body {
  font-family: var(--font-sans);
  background: var(--bg-base);
  color: var(--fg-primary);
  transition: background-color 0.3s ease, color 0.3s ease;
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* Dark is the default (no class needed — :root already is the dark
   palette). body.paper is the one deliberate light exception, scoped to
   the writing screen — see the Paper mode token block above. */

:focus-visible {
  outline: 3px solid var(--border-strong);
  outline-offset: 2px;
}

button {
  font-family: inherit;
  cursor: pointer;
}
button:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}

.app {
  max-width: 880px;
  margin: 0 auto;
  padding: 24px 20px 64px;
  min-height: 100vh;
}

/* Writing/test screen layout spec: one shared centering wrapper — every
   .screen (index.html, reading/verbal/maths/quant.html, test-sitting.html;
   nowhere else uses this class, confirmed) centers itself, rather than
   each screen declaring its own max-width and drifting over time.
   Split-pane screens opt into .screen-wide for the taller cap. Requires
   .app.practice-app (below) to have already removed the outer .app's own
   width cap — otherwise a .screen still centers, but inside a box that's
   itself capped and left-aligned, which looks identical to the original
   left-hugging bug. */
.screen {
  display: none;
  max-width: 720px;
  margin: 0 auto;
  width: 100%;
}
.screen.active {
  display: block;
}
.screen.screen-wide {
  max-width: 1400px;
}
/* maths.html/quant.html's screen-session mixes the shared split-pane
   test-taking UI (#test-session-mount, wants the wide cap) with an older,
   simple single-column drill UI (#topic-drill-block/#format-drill-block)
   as siblings in the same .screen-wide section — this keeps the older UI
   visually narrow even though its parent section is now wide. */
#topic-drill-block,
#format-drill-block {
  max-width: 720px;
  margin: 0 auto;
}
/* The outer shell-provided .app is shared by every role (admin/parent/
   student) via .shell-main .app { max-width:1000px; margin:0; ... } —
   deliberately NOT touched globally, since admin/parent list-heavy pages
   are out of this spec's scope and may genuinely want that width. Only
   the six practice/test pages that opt in with .practice-app have their
   cap removed, so their own .screen can reach the true available width. */
.shell-main .app.practice-app {
  max-width: none;
  margin: 0;
  padding: 0;
}

/* ===== Typography ===== */
h1,
h2,
h3 {
  font-family: var(--font-sans);
  font-weight: 700;
  line-height: 1.2;
  margin: 0 0 0.5em;
}

.brand {
  font-size: 1.1rem;
  letter-spacing: 0.02em;
  font-weight: 700;
  margin-bottom: 24px;
  opacity: 0.85;
}
.brand .dot {
  color: var(--accent);
}
/* GoApexam wordmark: "Apex" carries the accent so the Apex/Exam blend (the
   shared "ex") reads as a deliberate name, not a plain string. Shared by
   .brand (login, pre-session) and .top-nav .nav-brand (every other page). */
.brand-apex,
.nav-brand .brand-apex {
  color: var(--accent);
  font-weight: 800;
}

.prose,
.essay-prompt,
.transcript {
  font-family: var(--font-serif);
}

.mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* ===== Setup screen ===== */
.setup-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: 24px;
  margin-bottom: 20px;
}

.field-label {
  display: block;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--fg-muted);
  margin-bottom: 10px;
}

.field-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 12px;
  margin: 12px 0;
}

.field-grid input,
.field-grid select {
  width: 100%;
  margin-top: 4px;
}

.missing-chip {
  display: inline-block;
  margin: 2px 4px 2px 0;
  padding: 2px 10px;
  border-radius: 12px;
  border: 1px solid var(--border-strong);
  background: var(--bg-raised);
  color: var(--fg-primary);
  cursor: pointer;
  font-size: 0.85rem;
}

.missing-chip:hover {
  border-color: var(--accent);
}

select.topic-select {
  width: 100%;
  padding: 12px 14px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: var(--bg-base);
  color: var(--fg-primary);
  font-size: 1rem;
  font-family: var(--font-serif);
}

.option-row {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

/* Authoring-fixes spec, 1d: the question/option-editing form (distinct
   from .option-row above, which renders student-facing option BUTTONS
   side by side — the two were sharing one class, which is why an
   authoring row was being flex-wrapped like a button row and broke with
   longer content). Each label sits directly above its own full-width
   field, one option per row, option text and its misconception stacked
   within that row — holds together at 380px and with multi-line content
   in either field since nothing here is constrained to share a row. */
.option-author-row {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
}

.mini-chart-pair {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
}

.mini-chart {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  height: 120px;
  padding-top: 8px;
}

.mini-chart-bar-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  width: 44px;
}

.mini-chart-bar-track {
  display: flex;
  align-items: flex-end;
  height: 90px;
  width: 100%;
}

.mini-chart-bar {
  width: 100%;
  min-height: 4px;
  background: var(--border-strong);
  border-radius: 3px 3px 0 0;
}

.mini-chart-bar-highlight {
  background: var(--accent);
}

.mini-chart-bar-value {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--fg-primary);
}

.mini-chart-bar-label {
  font-size: 0.7rem;
  color: var(--fg-muted);
  text-align: center;
}

.option-btn {
  flex: 1 1 140px;
  padding: 12px 14px;
  border-radius: 8px;
  border: 1px solid var(--border-subtle);
  background: var(--bg-base);
  color: var(--fg-primary);
  text-align: left;
  transition: border-color 0.15s ease, background 0.15s ease;
}
/* Direct children only (">"): the option body can contain KaTeX (whose
   layout is built from nested spans) and **bold** (a nested <strong>). A
   descendant selector here turned every KaTeX span into a block — "$x = 4$"
   rendered as x, =, 4 stacked vertically — and would give bold option
   text the letter-badge styling. */
.option-btn > strong {
  display: block;
  font-size: 0.95rem;
}
.option-btn > span {
  display: block;
  font-size: 0.8rem;
  color: var(--fg-muted);
  margin-top: 2px;
}
.option-btn.selected {
  border-color: var(--accent);
  background: rgba(255, 178, 62, 0.12);
}
.option-btn.selected > strong {
  color: var(--accent);
}

.toggle-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.toggle-row input[type="checkbox"] {
  width: 20px;
  height: 20px;
}

/* Voice input record buttons (public/shared.js#attachVoiceInput) */
button.listening {
  background: var(--accent);
  color: var(--accent-fg, #1a1200);
  border-color: var(--accent);
}

/* Shared question-type renderer (public/questionRenderer.js) */
.answer-input {
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: var(--bg-base);
  color: var(--fg-primary);
  font-family: var(--font-sans);
  font-size: 0.95rem;
}
.answer-input.answer-blank {
  display: inline-block;
  width: 140px;
  margin: 0 4px;
}
.answer-input.answer-numeric,
.answer-input.answer-short {
  width: 100%;
  max-width: 320px;
}
.stem-with-blanks {
  line-height: 2.1;
}

.order-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.order-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid var(--border-subtle);
  background: var(--bg-base);
  cursor: grab;
}
.order-item.dragging {
  opacity: 0.5;
}
.order-item-body {
  flex: 1;
}
.order-item-controls {
  display: flex;
  gap: 4px;
}
.order-item-controls button {
  padding: 4px 8px;
}

.match-columns {
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
}
.match-col {
  flex: 1 1 200px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.match-item {
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid var(--border-subtle);
  background: var(--bg-base);
  color: var(--fg-primary);
  text-align: left;
}
.match-item.match-selected {
  border-color: var(--accent);
  background: rgba(255, 178, 62, 0.12);
}
.match-item.match-paired {
  border-color: var(--positive, var(--accent));
}
.match-pair-tag {
  font-size: 0.75rem;
  color: var(--fg-muted);
  margin-left: 6px;
}

.start-btn {
  width: 100%;
  padding: 16px;
  font-size: 1.05rem;
  font-weight: 700;
  border-radius: 8px;
  border: none;
  background: var(--accent);
  color: var(--accent-fg);
  margin-top: 8px;
}
.start-btn:hover:not(:disabled) {
  filter: brightness(1.08);
}

/* A genuine end-of-flow CTA (vocabulary practice's "Done" screen) — block,
   centered, full emphasis, the single action on that screen. */
.home-start-btn {
  display: block;
  text-align: center;
  text-decoration: none;
  font-size: 1.15rem;
  padding: 20px;
  margin-bottom: 24px;
}

/* B1: a nav-adjacent CTA (Home's "Start practice", the "pick up where you
   left off" link) sits on the same line as its heading, small, not a
   full-width block — the heading row itself is a flex line. */
.page-heading-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.page-heading-row h1 {
  margin: 0;
}
/* Parent Children page: "X of Y children" beside Add a child (child limit). */
.child-limit-actions {
  display: flex;
  align-items: center;
  gap: 12px;
}
.child-limit-at {
  color: var(--warning);
  font-weight: 600;
}
.heading-action-btn {
  display: inline-block;
  text-decoration: none;
  white-space: nowrap;
  padding: 10px 18px;
  font-size: 0.9rem;
  font-weight: 700;
  border-radius: 8px;
  border: none;
  background: var(--accent);
  color: var(--accent-fg);
}
.heading-action-btn:hover {
  filter: brightness(1.08);
}
.secondary-btn {
  display: inline-block;
  white-space: nowrap;
  padding: 10px 18px;
  font-size: 0.9rem;
  font-weight: 700;
  border-radius: 8px;
  border: 1px solid var(--border-subtle);
  background: var(--bg-surface);
  color: var(--fg-primary);
}
.secondary-btn:hover {
  border-color: var(--fg-muted);
}

.setup-note {
  font-size: 0.85rem;
  color: var(--fg-secondary);
  margin-top: 8px;
}

.setup-note code {
  word-break: break-word;
  white-space: normal;
}

/* ===== Debate screen ===== */
.debate-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  margin-bottom: 16px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--border-subtle);
}
.debate-topic {
  font-size: 1.1rem;
  font-family: var(--font-serif);
  max-width: 60ch;
}
.debate-meta {
  text-align: right;
  flex-shrink: 0;
}
.clock {
  font-size: 1.8rem;
  font-weight: 700;
}
.clock.low {
  color: var(--danger);
}
.round-indicator {
  font-size: 0.8rem;
  color: var(--fg-muted);
  margin-top: 4px;
}

.transcript {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-height: 200px;
  margin-bottom: 16px;
}

.turn {
  max-width: 78%;
  padding: 12px 16px;
  border-radius: 12px;
  font-size: 1.02rem;
  line-height: 1.5;
}
.turn.mine {
  align-self: flex-end;
  background: var(--accent-dim);
  border: 1px solid var(--accent);
  color: var(--fg-primary);
  border-bottom-right-radius: 3px;
}
.turn.theirs {
  align-self: flex-start;
  background: var(--opponent-dim);
  border: 1px solid var(--opponent);
  color: var(--fg-primary);
  border-bottom-left-radius: 3px;
}
.turn-label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  opacity: 0.75;
  margin-bottom: 4px;
}

.turn.thinking {
  opacity: 0.6;
  font-style: italic;
}

.composer {
  position: sticky;
  bottom: 0;
  background: var(--bg-base);
  padding-top: 10px;
  border-top: 1px solid var(--border-subtle);
}
.composer-row {
  display: flex;
  gap: 8px;
  align-items: flex-end;
}
.composer textarea {
  flex: 1;
  resize: none;
  min-height: 52px;
  max-height: 160px;
  padding: 12px 14px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: var(--bg-surface);
  color: var(--fg-primary);
  font-family: var(--font-serif);
  font-size: 1rem;
}
.icon-btn {
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: var(--bg-surface);
  color: var(--fg-primary);
  font-size: 1.2rem;
  display: flex;
  align-items: center;
  justify-content: center;
}
.icon-btn.listening {
  background: var(--accent);
  color: var(--accent-fg);
  border-color: var(--accent);
  animation: icon-btn-pulse 1.4s ease-in-out infinite;
}
.icon-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  animation: none;
}
@keyframes icon-btn-pulse {
  0%, 100% { box-shadow: 0 0 0 0 var(--accent-dim); }
  50% { box-shadow: 0 0 0 6px var(--accent-dim); }
}
.icon-btn.send {
  background: var(--accent);
  color: var(--accent-fg);
  border-color: var(--accent);
}

.voice-status {
  font-size: 0.8rem;
  color: var(--fg-muted);
  min-height: 1.2em;
  margin-top: 6px;
}

.debate-footer {
  display: flex;
  justify-content: flex-end;
  margin-top: 12px;
}
.continue-btn {
  padding: 12px 20px;
  border-radius: 8px;
  border: 1px solid var(--accent);
  background: transparent;
  color: var(--accent);
  font-weight: 600;
}

/* ===== Write screen ===== */
.write-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  margin-bottom: 20px;
}
.write-meta {
  text-align: right;
}
.write-meta .clock {
  color: var(--accent);
}
.write-meta .clock.low {
  color: var(--danger);
}

.essay-prompt {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: 18px 20px;
  font-size: 1.1rem;
  margin-bottom: 18px;
}

.essay-textarea {
  width: 100%;
  min-height: 46vh;
  padding: 20px;
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  background: var(--bg-surface);
  color: var(--fg-primary);
  font-family: var(--font-serif);
  font-size: 1.08rem;
  line-height: 1.6;
  resize: vertical;
}

.write-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 14px;
}
.word-count {
  font-size: 0.9rem;
  color: var(--fg-muted);
}
.word-count.enough {
  color: var(--accent);
}

.hand-in-btn {
  padding: 14px 24px;
  border-radius: 8px;
  border: none;
  background: var(--accent);
  color: var(--accent-fg);
  font-weight: 700;
  font-size: 1rem;
}

/* ===== Report screen ===== */
.report-score {
  display: flex;
  align-items: baseline;
  gap: 16px;
  margin-bottom: 6px;
}
.report-total {
  font-size: 3rem;
  font-weight: 700;
  font-family: var(--font-mono);
}
.report-band {
  font-size: 1.2rem;
  font-weight: 600;
  color: var(--accent);
}

.examiner-note {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-left: 4px solid var(--accent);
  border-radius: var(--radius);
  padding: 18px 20px;
  font-family: var(--font-serif);
  font-size: 1.05rem;
  line-height: 1.6;
  margin: 20px 0;
}

.criteria-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 14px;
  margin-bottom: 24px;
}
.criterion {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: 16px;
}
.criterion-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 6px;
}
.criterion-name {
  font-weight: 700;
  font-size: 0.88rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.criterion-score {
  font-family: var(--font-mono);
  font-size: 1.1rem;
  color: var(--accent);
}
.criterion-comment {
  font-family: var(--font-serif);
  font-size: 0.98rem;
  color: var(--fg-secondary);
  line-height: 1.5;
}

.report-block {
  margin-bottom: 20px;
}
.report-block h3 {
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--fg-muted);
  margin-bottom: 6px;
}
.report-block p {
  font-family: var(--font-serif);
  font-size: 1.02rem;
  line-height: 1.55;
  margin: 0;
}

.report-actions {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 28px;
}
.report-actions button {
  padding: 12px 20px;
  border-radius: 8px;
  border: 1px solid var(--fg-primary);
  background: transparent;
  color: var(--fg-primary);
  font-weight: 600;
}
.report-actions .primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-fg);
}

/* Model answer panel */
.model-answer-panel {
  display: none;
  margin-top: 20px;
  background: var(--bg-raised);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: 20px;
}
.model-answer-panel.active {
  display: block;
}

/* ===== Modal (D, E2) ===== */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 40px 16px;
  overflow-y: auto;
  z-index: 100;
}
.modal {
  background: var(--bg-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  padding: 24px;
  max-width: 640px;
  width: 100%;
  outline: none;
}
.modal-wide {
  max-width: 920px;
}
/* Test preview (public/testPreview.js) is a working tool, not a
   document: it spans from the side navigation's edge (240px, fixed above
   900px — see .shell-sidebar) to the viewport edge, capped at the student
   sitting's own .screen.screen-wide width so the split pane renders at
   the same size the student gets. Below 900px the sidebar is off-canvas,
   so the overlay's normal 16px gutter applies; a page with no side
   navigation at all (testAuthoring.html) just gets the full viewport. */
.modal-overlay.modal-overlay-full {
  padding: 24px 16px;
}
.modal-full {
  max-width: 1400px;
}
.modal-full .modal-scroll-body {
  max-height: calc(100vh - 200px);
}
@media (min-width: 901px) {
  .modal-overlay.modal-overlay-full {
    padding: 24px 28px;
  }
  body:has(.shell-sidebar) .modal-overlay.modal-overlay-full {
    padding-left: calc(240px + 28px);
  }
}
/* Preview modal (public/topicPreview.js, public/testPreview.js) — the
   only modals tall enough to need their own internal scroll rather than
   growing the whole page, since they can hold a topic map, a full
   transcript turn, and a band-scale table all at once. */
.modal-scroll-body {
  max-height: 65vh;
  overflow-y: auto;
  margin-top: 16px;
}
.band-scale-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 10px;
}
.band-scale-cell {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: 8px;
  padding: 10px;
}
.band-scale-cell p {
  margin: 6px 0 0;
  font-size: 0.85rem;
  color: var(--fg-secondary);
}
.modal-close-btn {
  float: right;
  background: transparent;
  border: none;
  color: var(--fg-muted);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  padding: 4px 8px;
}
.modal-close-btn:hover {
  color: var(--fg-primary);
}
.modal-section {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--border-subtle);
}
.modal-section h4 {
  margin: 0 0 8px;
  font-size: 0.95rem;
}
.idea-list {
  margin: 0;
  padding-left: 20px;
}
.idea-list li {
  margin-bottom: 4px;
}
.tabs {
  display: flex;
  gap: 8px;
  margin-bottom: 16px;
}
.tab-btn {
  padding: 8px 16px;
  border-radius: 20px;
  border: 1px solid var(--border-subtle);
  background: transparent;
  color: var(--fg-primary);
  font-size: 0.9rem;
  font-weight: 600;
}
.tab-btn.active {
  background: var(--fg-primary);
  color: var(--bg-base);
}
.model-answer-body {
  font-family: var(--font-serif);
  font-size: 1.02rem;
  line-height: 1.7;
  white-space: pre-wrap;
}
.model-answer-body .section-label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  color: var(--accent);
  margin-top: 1.4em;
  margin-bottom: 0.3em;
}
.model-answer-loading {
  color: var(--fg-muted);
  font-style: italic;
}

/* ===== Dry run banner (Preview and dry run spec, section 2) =====
   Prepended to <body> itself (public/shared.js#showDryRunBanner), so it
   sits above the shell/content entirely and survives every screen-switch.
   Deliberately NOT position: sticky — .shell-sidebar is already sticky at
   top: 0, and a second sticky element at the same top would fight it for
   the same spot while scrolling a long screen (e.g. the report). Static
   flow instead: it's the first thing on every screen, pushes everything
   else down once, and never overlaps the sidebar. --caution, not
   --danger: this is a notice, not an error. */
.dry-run-banner {
  background: var(--caution);
  color: var(--caution-fg);
  text-align: center;
  font-weight: 600;
  font-size: 0.9rem;
  padding: 10px 16px;
}

/* ===== Utility ===== */
.error-banner {
  background: var(--bg-surface);
  border: 1px solid var(--danger);
  color: var(--danger);
  padding: 12px 16px;
  border-radius: 8px;
  margin-bottom: 16px;
  font-size: 0.9rem;
}
.readonly-banner {
  background: var(--bg-surface);
  border: 1px solid var(--accent);
  color: var(--accent);
  padding: 12px 16px;
  border-radius: 8px;
  margin-bottom: 16px;
  font-size: 0.9rem;
}
.hidden {
  display: none !important;
}

/* ===== Auth / nav (Phase 2) ===== */
.app-narrow {
  max-width: 420px;
}

.login-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: 28px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.login-card h1 {
  margin-bottom: 12px;
}
.login-card .field-label {
  color: var(--fg-muted);
  margin-top: 14px;
}
.login-card input {
  width: 100%;
  padding: 12px 14px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: var(--bg-base);
  color: var(--fg-primary);
  font-family: var(--font-sans);
  font-size: 1rem;
}
.login-submit-btn {
  margin-top: 20px;
  width: 100%;
}
/* Admin two-step sign-in (two-step.html). The QR code keeps a white
   background in the dark theme: phone cameras read dark-on-light best. */
.two-step-qr {
  align-self: center;
  width: 220px;
  margin: 8px 0;
  padding: 10px;
  background: #ffffff;
  border-radius: 8px;
}
.two-step-qr svg {
  display: block;
  width: 100%;
  height: auto;
}
.two-step-key {
  font-family: var(--font-mono, monospace);
  font-size: 1rem;
  letter-spacing: 0.04em;
  word-break: break-all;
  color: var(--fg-primary);
}
.two-step-codes {
  margin: 12px 0;
  padding: 14px 14px 14px 52px;
  font-family: var(--font-mono, monospace);
  font-size: 1.05rem;
  line-height: 1.7;
  columns: 2;
}
.two-step-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 12px;
}
.two-step-confirm {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 16px;
  color: var(--fg-secondary);
}
.two-step-confirm input {
  width: auto;
}
.login-register-note {
  margin-top: 18px;
  text-align: center;
}
.credentials-once-box {
  background: var(--bg-inset);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  padding: 14px 16px;
  margin: 12px 0;
}
.credentials-once-box .credential-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-family: var(--font-mono);
  font-size: 1rem;
  padding: 4px 0;
}

.top-nav {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 14px 20px;
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border-subtle);
  font-family: var(--font-sans);
}
.top-nav .nav-brand {
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--fg-primary);
  text-decoration: none;
}
.top-nav .nav-brand .dot {
  color: var(--accent);
}
.nav-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  margin-left: 4px;
  border-radius: 8px;
  background: var(--accent);
  color: var(--accent-fg);
  font-family: var(--font-mono);
  font-size: 0.65rem;
  font-weight: 700;
  vertical-align: middle;
}
.top-nav .nav-links {
  display: flex;
  gap: 16px;
  flex: 1;
}
.top-nav .nav-links a {
  color: var(--fg-muted);
  text-decoration: none;
  font-size: 0.92rem;
  font-weight: 600;
  padding: 4px 0;
  border-bottom: 2px solid transparent;
}
/* Scoped to exactly this selector — .current must never affect anything
   outside a nav link (this is what the earlier greyed-logo bug came from:
   an unscoped active-state rule bleeding onto unrelated elements). */
.top-nav .nav-links a.current {
  color: var(--fg-primary);
  border-bottom-color: var(--accent);
}
.top-nav .nav-user {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 0.88rem;
  color: var(--fg-muted);
}
.nav-logout-btn {
  padding: 6px 12px;
  border-radius: 6px;
  border: 1px solid var(--border-subtle);
  background: transparent;
  color: var(--fg-primary);
  font-size: 0.85rem;
}

/* ===== Admin (Phase 2) ===== */
.tab-panel {
  display: none;
}
.tab-panel.active {
  display: block;
}

.admin-row {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: 14px 16px;
  margin-bottom: 10px;
}
.admin-row-summary {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.admin-row-stem {
  flex: 1 1 240px;
  color: var(--fg-muted);
  font-size: 0.9rem;
}
/* Whole-row-clickable rows (vocabulary word list opening the word-detail
   modal) — a visible hover/focus state is the whole point, since nothing
   inside the row looks like a button on its own. */
.word-row {
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.word-row:hover,
.word-row:focus-visible {
  border-color: var(--border-strong);
  background: var(--bg-raised);
}

/* ===== Review later ===== */
.review-toggle-btn {
  margin-top: 10px;
}
.review-status {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 10px;
}
.review-remove-btn {
  background: transparent;
  border: none;
  color: var(--fg-muted);
  font-size: 0.82rem;
  text-decoration: underline;
  padding: 2px 0;
}
.review-remove-btn:hover {
  color: var(--danger);
}
.admin-row-summary strong {
  margin-right: 4px;
}

/* Subject chooser (Practice) + subject rows (Home, parent dashboard) — a
   coming-soon subject reuses .setup-card/.admin-row structure, just dimmed,
   so it never looks like real content or implies data/progress exists. */
.subject-card-muted,
.subject-row-muted {
  opacity: 0.65;
}
.admin-row-inline {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: 8px;
  margin-bottom: 8px;
}

.tag {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  padding: 3px 8px;
  border-radius: 12px;
  background: var(--bg-base);
  border: 1px solid var(--border-subtle);
  color: var(--fg-muted);
}
.tag-active {
  color: var(--positive);
  border-color: var(--positive);
}
.tag-inactive {
  color: var(--caution);
  border-color: var(--caution);
}
.tag-danger {
  color: var(--danger);
  border-color: var(--danger);
}
.answer-locked {
  pointer-events: none;
  opacity: 0.7;
}
.solution-steps {
  margin: 8px 0 0;
  padding-left: 20px;
  color: var(--fg-secondary);
}
.solution-steps li {
  margin-bottom: 4px;
}
.katex-inline {
  cursor: default;
}
.stimulus-card {
  background: var(--bg-inset);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: 14px 16px;
  margin: 10px 0 16px;
}
.stimulus-title {
  margin: 0 0 8px;
}
.stimulus-table {
  border-collapse: collapse;
  width: 100%;
  margin-bottom: 8px;
}
.stimulus-table th,
.stimulus-table td {
  border: 1px solid var(--border-subtle);
  padding: 6px 10px;
  text-align: left;
  font-size: 0.9rem;
}
.stimulus-matrix td {
  text-align: center;
  font-family: var(--font-mono);
}
.stimulus-chart {
  width: 100%;
  max-width: 360px;
  height: auto;
  color: var(--fg-secondary);
}
.stimulus-chart-pie {
  max-width: 160px;
}
.stimulus-alt-text {
  color: var(--fg-secondary);
  font-size: 0.85rem;
  margin: 8px 0 0;
}
.stimulus-caption {
  color: var(--fg-muted);
  font-size: 0.8rem;
  font-style: italic;
  margin: 4px 0 0;
}
.stimulus-fallback {
  white-space: pre-wrap;
  font-size: 0.8rem;
  background: var(--bg-base);
  padding: 8px;
  border-radius: 6px;
  overflow-x: auto;
}
.stimulus-venn {
  margin-bottom: 8px;
}
.cost-warning {
  color: var(--caution);
}
.storage-caution {
  color: var(--caution);
}
.storage-warning {
  color: var(--danger);
}
.storage-table-wrap {
  overflow-x: auto;
  margin-top: 12px;
}
table.storage-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}
table.storage-table th,
table.storage-table td {
  text-align: left;
  padding: 8px 12px;
  border-bottom: 1px solid var(--border-subtle);
  white-space: nowrap;
}
table.storage-table th {
  color: var(--fg-muted);
  font-weight: 600;
}

.admin-edit-form,
.admin-edit-form-inline,
.inline-form-column {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 14px;
}
.admin-edit-form textarea,
.admin-edit-form-inline input,
.admin-edit-form-inline select,
.inline-form-column input,
.inline-form-column textarea,
.inline-form-column select,
.admin-edit-form input,
.admin-edit-form select {
  width: 100%;
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: var(--bg-base);
  color: var(--fg-primary);
  font-family: var(--font-sans);
  font-size: 0.95rem;
}
/* The blanket width:100%/padding rule above is meant for text-shaped
   inputs — a checkbox/radio inside one of these forms (e.g. the question
   authoring "Correct" toggle) must not inherit it, or it collapses to a
   near-invisible sliver. Higher-specificity override, same 20px sizing as
   .toggle-row's existing checkbox rule. */
.admin-edit-form-inline input[type="checkbox"],
.admin-edit-form-inline input[type="radio"],
.inline-form-column input[type="checkbox"],
.inline-form-column input[type="radio"],
.admin-edit-form input[type="checkbox"],
.admin-edit-form input[type="radio"] {
  width: 20px;
  height: 20px;
  padding: 0;
}
.admin-edit-form button,
.admin-edit-form-inline button,
.inline-form-column button {
  align-self: flex-start;
  padding: 8px 16px;
  border-radius: 8px;
  border: none;
  background: var(--accent);
  color: var(--accent-fg);
  font-weight: 600;
}

.inline-form {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 16px;
}
.inline-form input {
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: var(--bg-surface);
  color: var(--fg-primary);
}
.inline-form button {
  padding: 10px 16px;
  border-radius: 8px;
  border: none;
  background: var(--accent);
  color: var(--accent-fg);
  font-weight: 600;
}

.level-description {
  color: var(--fg-secondary);
  font-size: 0.92rem;
  margin: 8px 0 0;
}

.criterion-fieldset {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: 14px 16px;
  margin: 0 0 14px;
}
.criterion-fieldset legend {
  font-weight: 700;
  padding: 0 6px;
}
.criterion-fieldset textarea {
  width: 100%;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--border-strong);
  background: var(--bg-base);
  color: var(--fg-primary);
  font-family: var(--font-sans);
  font-size: 0.9rem;
}

/* ===== Plan screen (Phase 2) ===== */
.plan-form {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 20px;
}
.plan-form .field-label {
  margin-top: 14px;
}
.plan-form input {
  width: 100%;
  padding: 12px 14px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: var(--bg-surface);
  color: var(--fg-primary);
  font-family: var(--font-serif);
  font-size: 1rem;
}

.plan-recap {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-left: 4px solid var(--accent);
  border-radius: var(--radius);
  padding: 16px 20px;
  margin-bottom: 18px;
}
.plan-recap h3 {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--fg-muted);
  margin-bottom: 10px;
}
.plan-recap dl {
  margin: 0;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 4px 16px;
}
.plan-recap dt {
  font-weight: 700;
  font-size: 0.85rem;
  color: var(--fg-muted);
}
.plan-recap dd {
  margin: 0;
  font-family: var(--font-serif);
}

/* ===== Marking screen (Phase 2) ===== */
#screen-marking {
  text-align: center;
  padding-top: 15vh;
}
.marking-status {
  font-size: 1.1rem;
  color: var(--fg-muted);
  margin-top: 12px;
}
#marking-error {
  margin-top: 24px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

/* ===== Version comparison (4.6) ===== */
.comparison-row {
  display: flex;
  justify-content: space-between;
  padding: 8px 0;
  border-bottom: 1px solid var(--border-subtle);
  font-size: 0.95rem;
}
.comparison-scores {
  font-family: var(--font-mono);
}
.delta-up {
  color: var(--positive);
}
.delta-down {
  color: var(--danger);
}
.delta-flat {
  color: var(--fg-muted);
}
.comparison-meta {
  font-size: 0.85rem;
  color: var(--fg-muted);
  margin-top: 10px;
}
.comparison-fix-status {
  margin-top: 10px;
  padding: 10px 14px;
  border-radius: 8px;
  border: 1px solid;
  font-size: 0.92rem;
}
.fix-yes {
  background: var(--bg-surface);
  border-color: var(--positive);
  color: var(--positive);
}
.fix-no {
  background: var(--bg-surface);
  border-color: var(--danger);
  color: var(--danger);
}

.history-link {
  padding: 12px 20px;
  border-radius: 8px;
  border: 1px solid var(--fg-primary);
  color: var(--fg-primary);
  text-decoration: none;
  font-weight: 600;
  font-size: 0.95rem;
}

/* ===== Dashboard (Phase 2) ===== */
.trend-svg,
.bar-svg {
  width: 100%;
  height: auto;
  display: block;
}
.chart-empty {
  color: var(--fg-secondary);
  font-style: italic;
}
.chart-legend {
  display: flex;
  gap: 18px;
  margin-top: 8px;
}
.legend-item {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.85rem;
  color: var(--fg-muted);
}
.legend-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  display: inline-block;
}
.criteria-chart-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 16px;
  margin-top: 10px;
}
.criterion-chart h4 {
  font-size: 0.85rem;
  color: var(--fg-muted);
  margin-bottom: 6px;
}
.recurring-fix-list {
  margin: 0;
  padding-left: 20px;
}
.recurring-fix-list li {
  margin-bottom: 6px;
  font-family: var(--font-serif);
}
#student-picker-card select {
  width: 100%;
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: var(--bg-surface);
  color: var(--fg-primary);
}

/* ===== Phase 3: goal banner, promotion screen, level journey ===== */

.goal-banner {
  border-left: 4px solid var(--accent);
}
.goal-banner h3 {
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--fg-muted);
  margin-bottom: 6px;
}
.goal-banner p {
  font-family: var(--font-serif);
  font-size: 1.02rem;
  line-height: 1.55;
  margin: 0;
}
.goal-banner.goal-repeat {
  border-left-color: var(--chart-readiness);
}

.promotion-reminder {
  font-size: 0.9rem;
  color: var(--fg-secondary);
  font-style: italic;
  margin: 10px 0 0;
}

.promotion-warning {
  background: var(--bg-raised);
  border: 1px solid var(--caution);
  border-radius: var(--radius);
  padding: 16px;
}

#promotion-qualifying-list {
  margin: 8px 0 0;
  padding-left: 20px;
  font-family: var(--font-serif);
}
#promotion-qualifying-list li {
  margin-bottom: 4px;
}

.level-journey {
  list-style: none;
  margin: 0;
  padding: 0;
}
.level-journey li {
  padding: 10px 0;
  border-bottom: 1px solid var(--border-subtle);
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.level-journey li:last-child {
  border-bottom: none;
}

/* Report screen's readiness block reuses .report-block spacing; the score
   line itself reuses .report-score sizing rules, scaled down slightly. */
#report-readiness-block .report-score {
  margin-bottom: 8px;
}
#report-readiness-block .report-total {
  font-size: 1.6rem;
}

/* ===== Vocabulary practice (Phase 3 remediation) =====
   Mobile-first: this is where most vocabulary practice actually happens,
   per the spec. Large touch targets, single column, nothing hover-only. */
.vocab-card {
  background: var(--bg-raised);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: 32px 24px;
  text-align: center;
  max-width: 480px;
  margin: 12px auto 0;
}
.vocab-card-front {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  margin-bottom: 24px;
}
.vocab-word {
  font-size: 2rem;
  font-weight: 700;
  font-family: var(--font-serif);
}
.vocab-pos {
  color: var(--fg-muted);
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.vocab-card-back {
  text-align: left;
  margin-bottom: 24px;
}
.vocab-definition {
  font-family: var(--font-serif);
  font-size: 1.15rem;
  line-height: 1.5;
  margin: 0 0 8px;
}
.vocab-phonetic {
  color: var(--fg-muted);
  font-family: var(--font-mono);
  font-size: 0.9rem;
  margin: 0 0 16px;
}
.vocab-examples {
  border-top: 1px solid var(--border-subtle);
  padding-top: 14px;
}
.vocab-examples p {
  font-family: var(--font-serif);
  color: var(--fg-secondary);
  line-height: 1.5;
  margin: 0 0 10px;
}
/* E5: the info icon sits above the grade row, right-aligned, subtle. */
.vocab-grades-row {
  display: flex;
  justify-content: flex-end;
  position: relative;
}
.grade-info-btn {
  background: transparent;
  border: none;
  color: var(--fg-muted);
  font-size: 1.1rem;
  cursor: pointer;
  padding: 6px 8px;
}
.grade-info-btn:hover {
  color: var(--fg-secondary);
}
.grade-info-popover {
  position: absolute;
  bottom: 100%;
  right: 0;
  margin-bottom: 8px;
  background: var(--bg-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  padding: 14px 16px;
  width: 260px;
  max-width: 80vw;
  font-size: 0.85rem;
  line-height: 1.5;
  z-index: 50;
}
.grade-info-popover p {
  margin: 0 0 6px;
}
.grade-info-popover p:last-child {
  margin-bottom: 0;
}

.vocab-grades {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}
.vocab-grade-btn {
  padding: 18px 10px;
  border-radius: 8px;
  border: none;
  font-weight: 700;
  font-size: 1rem;
  color: var(--bg-base);
}
.vocab-grade-btn.again {
  background: var(--danger);
}
.vocab-grade-btn.hard {
  background: var(--caution);
}
.vocab-grade-btn.good {
  background: var(--positive);
}
.vocab-grade-btn.easy {
  background: var(--opponent);
}

/* ===== Responsive =====
   Vocabulary practice is mostly a phone surface, so the nav gets real
   design attention below 640px, not just a wrap: nav-links become a fixed
   bottom tab bar (the primary navigation surface); brand + logout stay in
   normal flow as a slim top strip, out of the bar entirely. */
@media (max-width: 640px) {
  .top-nav {
    flex-wrap: wrap;
    gap: 8px;
    padding: 10px 14px;
  }
  .top-nav .nav-user {
    font-size: 0.8rem;
  }
  .top-nav .nav-links {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 10;
    display: flex;
    gap: 0;
    background: var(--bg-surface);
    border-top: 1px solid var(--border-subtle);
    padding: 6px 4px calc(6px + env(safe-area-inset-bottom, 0px));
  }
  .top-nav .nav-links a {
    flex: 1;
    text-align: center;
    padding: 8px 2px;
    font-size: 0.72rem;
    border-bottom: none;
    border-radius: 8px;
  }
  .top-nav .nav-links a.current {
    background: var(--bg-raised);
  }
  .app {
    padding: 16px 14px 88px;
  }
  .turn {
    max-width: 90%;
  }
  .debate-header,
  .write-header {
    flex-direction: column;
  }
  .debate-meta,
  .write-meta {
    text-align: left;
  }
  .report-total {
    font-size: 2.2rem;
  }
  .vocab-card {
    padding: 24px 18px;
    margin-top: 0;
  }
  .vocab-word {
    font-size: 1.6rem;
  }
  .vocab-grades {
    grid-template-columns: 1fr 1fr;
  }
  .vocab-grade-btn {
    padding: 20px 8px;
    font-size: 0.95rem;
  }
}

/* ===== Sidebar shell (admin, parent and student panels) =====
   Persistent left sidebar + main content area, shared between all three
   multi-page panels. Every colour here is a token from :root — no raw hex.
   Below 900px the sidebar becomes a slide-in drawer behind a hamburger
   button; above it, always visible, hamburger hidden. */
.shell {
  display: flex;
  min-height: 100vh;
  align-items: flex-start;
}

.shell-sidebar {
  width: 240px;
  flex: 0 0 240px;
  min-height: 100vh;
  background: var(--bg-surface);
  border-right: 1px solid var(--border-subtle);
  display: flex;
  flex-direction: column;
  padding: 18px 14px;
  position: sticky;
  top: 0;
}

.shell-logo {
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--fg-primary);
  text-decoration: none;
  padding: 6px 10px 18px;
  display: block;
}
.shell-logo .brand-apex {
  color: var(--accent);
  font-weight: 800;
}
.shell-logo .dot {
  color: var(--accent);
}

.shell-top-slot:empty {
  display: none;
}

.shell-nav {
  flex: 1;
  overflow-y: auto;
}

.shell-nav-heading {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--fg-muted);
  padding: 16px 10px 6px;
}
.shell-nav-heading:first-child {
  padding-top: 4px;
}

.shell-nav-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 9px 10px;
  border-radius: 8px;
  color: var(--fg-secondary);
  text-decoration: none;
  font-size: 0.92rem;
  margin-bottom: 2px;
}
.shell-nav-link:hover {
  background: var(--bg-raised);
  color: var(--fg-primary);
}
.shell-nav-link.is-active {
  background: var(--accent-dim);
  color: var(--fg-primary);
  font-weight: 700;
}
.shell-nav-link.is-disabled {
  color: var(--fg-muted);
  pointer-events: none;
  opacity: 0.6;
}

.shell-nav-group {
  margin-bottom: 2px;
}

.shell-nav-group-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  padding: 16px 10px 6px;
  background: none;
  border: none;
  cursor: pointer;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--fg-muted);
  font-family: inherit;
}
.shell-nav-group:first-child .shell-nav-group-toggle {
  padding-top: 4px;
}
.shell-nav-group-toggle:hover {
  color: var(--fg-secondary);
}
.shell-nav-group-items {
  overflow: hidden;
}
.shell-nav-group.is-collapsed .shell-nav-group-items {
  display: none;
}

.shell-nav-badge {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  padding: 1px 7px;
  border-radius: 10px;
  background: var(--accent);
  color: var(--accent-fg);
}

.shell-sidebar-footer {
  border-top: 1px solid var(--border-subtle);
  padding-top: 12px;
  margin-top: 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  font-size: 0.85rem;
  color: var(--fg-muted);
}

/* Accordion sidebar (every panel — renderShell always adds this class).
   Sidebar is exactly one viewport tall: logo/child selector at the top,
   the menu scrolls on its own in the middle, and the name/Log out footer
   is always visible at the bottom. dvh so a phone's browser toolbar
   can't push the footer off-screen (vh line is the fallback). */
.shell--accordion .shell-sidebar {
  height: 100vh;
  height: 100dvh;
  min-height: 0;
}
.shell--accordion .shell-logo,
.shell--accordion .shell-top-slot,
.shell--accordion .shell-sidebar-footer {
  flex-shrink: 0;
}
.shell--accordion .shell-nav {
  min-height: 0;
  overscroll-behavior: contain;
}
.shell--accordion .shell-sidebar-footer .shell-user-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.shell--accordion .shell-nav-group-toggle,
.shell--accordion .shell-nav-group:first-child .shell-nav-group-toggle {
  min-height: 44px;
  padding: 8px 6px 8px 10px;
  margin-top: 2px;
  border-radius: 8px;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--fg-secondary);
  text-align: left;
}
.shell--accordion .shell-nav-group-toggle:hover {
  background: var(--bg-raised);
  color: var(--fg-primary);
}
.shell--accordion .shell-nav-group-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.shell--accordion .shell-nav-group-toggle[aria-expanded="true"] {
  color: var(--fg-primary);
}
.shell--accordion .shell-nav-group-label {
  flex: 1;
}
.shell--accordion .shell-nav-group-chevron {
  flex: 0 0 22px;
  width: 22px;
  height: 22px;
  color: var(--fg-primary);
  transition: transform 0.2s ease;
}
.shell--accordion .shell-nav-group-toggle[aria-expanded="true"] .shell-nav-group-chevron {
  transform: rotate(90deg);
  color: var(--accent);
}
.shell--accordion .shell-nav-group:not(.is-collapsed) .shell-nav-group-badge {
  display: none;
}
.shell--accordion .shell-nav-group-items {
  padding-left: 8px;
}

.shell-main {
  flex: 1;
  min-width: 0;
  padding: 24px 28px 64px;
}
.shell-main .app {
  max-width: 1000px;
  margin: 0;
  padding: 0;
  min-height: 0;
}

.hamburger-btn {
  display: none;
  position: fixed;
  top: 14px;
  left: 14px;
  z-index: 40;
  width: 40px;
  height: 40px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: var(--bg-surface);
  color: var(--fg-primary);
  font-size: 1.1rem;
}

.shell-overlay {
  display: none;
}

/* ===== Landing cards (admin/parent overview pages) ===== */
.landing-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 14px;
  margin-bottom: 24px;
}
.landing-card {
  display: block;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: 16px;
  text-decoration: none;
  color: inherit;
}
.landing-card:hover {
  border-color: var(--border-strong);
}
.landing-card-label {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--fg-muted);
  margin-bottom: 8px;
}
.landing-card-value {
  font-family: var(--font-mono);
  font-size: 1.6rem;
  color: var(--fg-primary);
}
.landing-card-note {
  font-size: 0.82rem;
  color: var(--fg-secondary);
  margin-top: 6px;
}
.landing-card.landing-card-warning .landing-card-value {
  color: var(--caution);
}

/* ===== Motivation card (gallery list, Home teaser, full reading modal,
   admin preview — public/motivationCard.js, one shared component) =====
   Image left, content right; image never stretches or distorts (capped
   by max-width/max-height with width/height left auto, not object-fit,
   so a portrait photo shrinks to fit both caps instead of just one).
   No raw colour values below — every one is a token, and `color` is set
   explicitly wherever a card sits inside a plain <button> (the Home
   teaser, the gallery card): a <button> does NOT reliably inherit the
   page's text colour the way a <div> does — some browsers apply their
   own default form-control text colour instead, unreadable against this
   dark surface, unless an author style says otherwise. */
/* "Inspiration of the week" — inside the Home card, above its content. */
.motivation-card-heading {
  margin: 0 0 12px;
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--accent);
}
/* Responsive grid: 1 column under 768px, 3 from 768px, 4 only from
   1600px (see the revision note below). Viewport breakpoints. The gallery
   page lifts the 1000px reading-width cap (.app-gallery-wide, set by
   motivationCard.js#renderMotivationGallery) so columns get the full
   content width. */
.shell-main .app.app-gallery-wide {
  max-width: none;
}
.motivation-gallery-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: flex-end;
}
.motivation-gallery-filters .field-label {
  flex: 1 1 220px;
}
.motivation-gallery-list {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 16px;
  margin-bottom: 24px;
}
/* Revised after the first pass: 5-6 desktop columns left each card's
   image ~60px wide — too small to make out a face or a scene. Desktop is
   now 3 (4 only on very wide screens), and 1024-1199px stays at 3 too
   (the sidebar is visible there, so 4 columns would be narrower still). */
@media (min-width: 768px) {
  .motivation-gallery-list {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
@media (min-width: 1600px) {
  .motivation-gallery-list {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}
/* Inside a grid column the card keeps its two-column layout, sized to the
   column: the image takes up to 40% of the card (never more than its own
   300px cap), the text the rest. */
.motivation-gallery-card {
  height: 100%;
  padding: 12px;
}
.motivation-gallery-list .motivation-card {
  gap: 10px;
}
.motivation-gallery-list .motivation-card-media {
  flex: 0 1 40%;
  max-width: 300px;
}
.motivation-gallery-list .motivation-card-media img {
  max-width: 100%;
  max-height: 260px;
}
.motivation-gallery-list .motivation-card-content h3 {
  font-size: 0.95rem;
  margin: 0 0 6px;
  overflow-wrap: anywhere;
}
.motivation-gallery-list .motivation-card-content .setup-note,
.motivation-gallery-list .motivation-card-readmore {
  font-size: 0.8rem;
  margin: 0 0 6px;
}
.motivation-home-card,
.motivation-gallery-card {
  display: block;
  width: 100%;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: 16px;
  text-decoration: none;
  color: var(--fg-primary);
  font: inherit;
}
.motivation-gallery-card:hover,
.motivation-home-card:hover {
  border-color: var(--border-strong);
}
.motivation-card {
  display: flex;
  gap: 16px;
  align-items: flex-start;
}
.motivation-card-media {
  flex: 0 0 auto;
}
.motivation-card-media img {
  display: block;
  max-width: 300px;
  max-height: 400px;
  width: auto;
  height: auto;
  border-radius: 8px;
}
.motivation-card-content {
  flex: 1 1 auto;
  min-width: 0;
}
.motivation-card-readmore {
  color: var(--accent);
  text-decoration: underline;
}
.motivation-card-readmore:hover {
  filter: brightness(1.15);
}
@media (max-width: 768px) {
  .motivation-card {
    flex-direction: column;
  }
  .motivation-card-media img {
    max-width: 100%;
    max-height: 220px;
    margin: 0 auto;
  }
}

/* Inline markdown links (public/questionRenderer.js#renderInlineContentHtml)
   — used anywhere the shared content renderer runs (motivation cards,
   passages, question stems/options), so a link looks and behaves the
   same everywhere it appears: underlined, accent-coloured, opens
   externally with a visible ↗ indicator. */
.content-md-link {
  color: var(--accent);
  text-decoration: underline;
}
.content-md-link:hover {
  filter: brightness(1.15);
}
.content-md-link-icon {
  margin-left: 0.15em;
  font-size: 0.85em;
}

/* ===== Child selector (parent panel) ===== */
.child-selector {
  margin-bottom: 14px;
}
.child-selector select {
  width: 100%;
}
.child-selector-label {
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--fg-primary);
  padding: 8px 10px 14px;
}

@media (max-width: 900px) {
  .hamburger-btn {
    display: block;
  }
  .shell-sidebar {
    position: fixed;
    left: 0;
    top: 0;
    height: 100vh;
    z-index: 41;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    box-shadow: 2px 0 12px rgba(0, 0, 0, 0.3);
  }
  .shell-sidebar.is-open {
    transform: translateX(0);
  }
  .shell-overlay.is-open {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 40;
  }
  .shell-main {
    padding: 64px 16px 48px;
    width: 100%;
  }
}

/* ===== Data table (shared component, public/dataTable.js) =====
   One implementation for every admin/parent list screen. Compact rows,
   sticky header within its own scroll area, mobile stacked-card fallback
   below 768px (a horizontally-scrolling table is unusable on a phone). */
.dt-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 12px;
}
.dt-search {
  flex: 1 1 220px;
  min-width: 160px;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: var(--bg-base);
  color: var(--fg-primary);
  font-size: 0.9rem;
}
.dt-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.dt-filter-select,
.dt-bulk-select {
  padding: 7px 10px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: var(--bg-base);
  color: var(--fg-primary);
  font-size: 0.85rem;
}

.dt-bulk-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 10px 14px;
  margin-bottom: 10px;
  background: var(--accent-dim);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
}
.dt-bulk-count {
  font-weight: 700;
  font-size: 0.88rem;
  color: var(--fg-primary);
}
.dt-bulk-action-group {
  display: inline-flex;
  gap: 6px;
  align-items: center;
}
.dt-bulk-btn {
  padding: 6px 12px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: var(--bg-surface);
  color: var(--fg-primary);
  font-size: 0.85rem;
}
.dt-bulk-btn-danger {
  border-color: var(--danger);
  color: var(--danger);
}

.dt-table-wrap {
  max-height: 68vh;
  overflow: auto;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
}
.dt-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.88rem;
}
.dt-table thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border-strong);
  text-align: left;
  padding: 0;
  white-space: nowrap;
}
.dt-th-sort {
  width: 100%;
  padding: 9px 12px;
  background: none;
  border: none;
  color: var(--fg-secondary);
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  text-align: left;
  cursor: pointer;
}
.dt-table thead th:not(:has(.dt-th-sort)) {
  padding: 9px 12px;
  color: var(--fg-muted);
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.dt-th-select,
.dt-td-select {
  width: 34px;
  text-align: center;
}
.dt-th-actions {
  width: 1%;
}
.dt-table tbody tr {
  background: var(--bg-base);
  border-bottom: 1px solid var(--border-subtle);
}
.dt-table tbody tr:nth-child(even) {
  background: var(--bg-inset);
}
.dt-table td {
  padding: 7px 12px;
  color: var(--fg-primary);
  vertical-align: middle;
}
.dt-td-truncate {
  max-width: 260px;
}
.dt-cell-text {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dt-td-actions {
  text-align: right;
  white-space: nowrap;
}
.dt-actions-wrap {
  display: inline-flex;
  gap: 4px;
  position: relative;
}

.dt-icon-btn {
  width: 28px;
  height: 28px;
  border-radius: 6px;
  border: 1px solid var(--border-subtle);
  background: var(--bg-surface);
  color: var(--fg-primary);
  font-size: 0.9rem;
  line-height: 1;
  cursor: pointer;
}
.dt-icon-btn:hover {
  border-color: var(--border-strong);
}
.dt-icon-btn-danger {
  color: var(--danger);
  border-color: var(--danger);
}
.dt-icon-btn-primary {
  color: var(--accent);
  border-color: var(--accent);
}

.dt-overflow-menu {
  position: absolute;
  right: 0;
  top: 100%;
  z-index: 5;
  min-width: 140px;
  background: var(--bg-raised);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
  padding: 4px;
  display: flex;
  flex-direction: column;
}
.dt-overflow-item {
  padding: 7px 10px;
  border: none;
  background: none;
  color: var(--fg-primary);
  text-align: left;
  font-size: 0.85rem;
  border-radius: 6px;
  cursor: pointer;
}
.dt-overflow-item:hover {
  background: var(--bg-raised);
  filter: brightness(1.2);
}
.dt-overflow-item-danger {
  color: var(--danger);
}

.dt-inline-edit {
  padding: 4px 6px;
  border-radius: 6px;
  border: 1px solid var(--border-subtle);
  background: var(--bg-surface);
  color: var(--fg-primary);
  font-size: 0.85rem;
}
.dt-inline-edit-number {
  width: 70px;
}
.dt-saved-flash {
  outline: 2px solid var(--positive);
  outline-offset: 1px;
  transition: outline-color 1s ease;
}

.dt-no-rows {
  padding: 24px 12px;
  text-align: center;
  color: var(--fg-muted);
}

.dt-pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 10px;
  font-size: 0.85rem;
  color: var(--fg-muted);
}
.dt-pagination-controls {
  display: flex;
  gap: 8px;
}
.dt-page-btn {
  padding: 6px 12px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: var(--bg-surface);
  color: var(--fg-primary);
  font-size: 0.85rem;
}
.dt-page-btn:disabled {
  opacity: 0.5;
}

.dt-empty-state {
  padding: 32px 16px;
  text-align: center;
  color: var(--fg-secondary);
  background: var(--bg-surface);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
}

.dt-skeleton {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.dt-skeleton-row {
  display: flex;
  gap: 12px;
  padding: 10px 12px;
  background: var(--bg-surface);
  border-radius: 8px;
}
.dt-skeleton-block {
  height: 14px;
  border-radius: 4px;
  background: var(--bg-inset);
  animation: dt-pulse 1.3s ease-in-out infinite;
}
@keyframes dt-pulse {
  0%, 100% { opacity: 0.5; }
  50% { opacity: 1; }
}

.dt-mobile-list {
  display: none;
}

@media (max-width: 768px) {
  .dt-table-wrap {
    display: none;
  }
  .dt-mobile-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
  .dt-mobile-card {
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius);
    padding: 12px 14px;
  }
  .dt-mobile-card-head {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
    font-size: 0.95rem;
  }
  .dt-mobile-field {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    padding: 4px 0;
    border-top: 1px solid var(--border-subtle);
    font-size: 0.85rem;
  }
  .dt-mobile-field:first-child {
    border-top: none;
  }
  .dt-mobile-field-label {
    color: var(--fg-muted);
    flex: 0 0 auto;
  }
  .dt-mobile-field-value {
    text-align: right;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .dt-mobile-card-actions {
    display: flex;
    gap: 6px;
    justify-content: flex-end;
    margin-top: 10px;
    padding-top: 8px;
    border-top: 1px solid var(--border-subtle);
  }
}

/* ===== Extraction review: reconciliation panel + staged-edit save bar =====
   public/parent/extraction-review.js — the panel sits above the rows grid
   and never changes height when its counts change, so updating it can't
   itself cause a scroll jump the way a full grid re-render would. */
.recon-panel {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: 8px;
  padding: 14px 16px;
  margin-bottom: 14px;
}
.recon-panel h3 {
  margin-top: 0;
}
.recon-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  margin-bottom: 8px;
}
.recon-stat {
  font-size: 0.9rem;
  color: var(--fg-muted);
}
.recon-stat strong {
  color: var(--fg-primary);
  font-size: 1.05rem;
}
.recon-action-card {
  margin-top: 10px;
}
.recon-unanswered {
  margin-top: 10px;
  font-size: 0.85rem;
  color: var(--fg-muted);
}
.recon-unanswered ul {
  margin: 8px 0 0;
  padding-left: 18px;
}

.save-bar {
  position: sticky;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--bg-raised);
  border: 1px solid var(--caution);
  border-radius: 8px;
  padding: 10px 16px;
  margin-top: 10px;
  box-shadow: 0 -4px 14px rgba(0, 0, 0, 0.25);
}
.save-bar span {
  font-size: 0.9rem;
  color: var(--caution);
  font-weight: 600;
}

/* ===== Topic map (public/topicMap.js) =====
   SVG scales via viewBox alone — no separate mobile layout. */
.topic-map-svg {
  width: 100%;
  height: auto;
  max-width: 720px;
  display: block;
  margin: 0 auto;
}
.topic-map-label {
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.topic-map-input,
.topic-map-leaf-input {
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  border: none;
  background: transparent;
  color: var(--fg-primary);
  font-family: inherit;
  font-size: 12px;
  padding: 0;
}
.topic-map-leaf-input {
  font-size: 10px;
}
.topic-map-input:focus,
.topic-map-leaf-input:focus {
  outline: 1px solid var(--accent);
  outline-offset: 1px;
}
.topic-map-static-text {
  font-size: 12px;
  color: var(--fg-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.topic-map-leaf-static {
  font-size: 10px;
  color: var(--fg-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.topic-map-toggle {
  width: 100%;
  height: 100%;
  border: none;
  background: none;
  color: var(--fg-muted);
  cursor: pointer;
  font-size: 11px;
  padding: 0;
}
.topic-map-vocab-strip {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border-subtle);
  font-size: 0.8rem;
  color: var(--fg-muted);
  text-align: center;
}
.topic-map-vocab-label {
  font-weight: 600;
  margin-right: 6px;
}
.topic-map-vocab-chip {
  display: inline-block;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: 10px;
  padding: 2px 8px;
  margin: 2px;
  font-family: var(--font-mono);
  font-size: 0.72rem;
}
.stimulus-row {
  margin-bottom: 14px;
}
/* Distinct panel above the stimulus items, shown verbatim — never a
   plain inline paragraph easy to skim past, since misreading the exact
   wording costs marks (2026-09-12 pass). */
.stimulus-instruction-panel {
  border: 1px solid var(--border-subtle);
  background: var(--bg-surface-alt, var(--bg-surface));
  border-left: 3px solid var(--fg-primary);
  border-radius: 8px;
  padding: 10px 14px;
  margin-bottom: 12px;
}
.stimulus-instruction-label {
  display: block;
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--fg-muted);
  margin-bottom: 4px;
}
.stimulus-instruction-panel p {
  margin: 0;
  font-size: 0.9rem;
}
.stimulus-heading {
  font-size: 0.85rem;
  font-weight: 700;
  margin: 0 0 8px;
}
/* Stacked vertically, full width, one below the other — never side by
   side (that read as clutter and hid how many items there were). */
.stimulus-stack {
  display: flex;
  flex-direction: column;
  width: 100%;
}
.stimulus-panel {
  border: 1px solid var(--border-subtle);
  background: var(--bg-surface);
  border-radius: 8px;
  padding: 10px 14px;
  font-size: 0.88rem;
  width: 100%;
}
.stimulus-type-label {
  display: inline-block;
  border: 1px solid var(--border-subtle);
  border-radius: 10px;
  padding: 1px 8px;
  margin-bottom: 6px;
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--fg-muted);
}
.stimulus-body {
  white-space: pre-wrap;
}
.stimulus-panel img,
.stimulus-image-slot img {
  max-width: 100%;
  border-radius: 6px;
  margin-bottom: 4px;
}
.stimulus-panel select {
  margin-top: 6px;
  width: 100%;
  font-size: 0.78rem;
}
/* The connector — the clearest way to tell a 13-year-old whether they
   need both stimulus items or just one — sits between panels as its own
   small centred element, visually distinct from either panel's content. */
.stimulus-connector {
  display: flex;
  justify-content: center;
  margin: 2px 0;
}
.stimulus-connector span {
  background: var(--fg-primary);
  color: var(--bg-base);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  border-radius: 12px;
  padding: 2px 12px;
}
.map-comparison-row {
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
}
.map-comparison-col {
  flex: 1 1 300px;
  min-width: 0;
}
.map-comparison-col h4 {
  text-align: center;
  margin-bottom: 6px;
}

/* ===== Practice-session minimal header =====
   The one exception to "every page has the sidebar": an active debate/
   write/timed-question-set screen hides the sidebar and hamburger
   entirely and shows this fixed bar instead — a timer and an exit
   control, nothing that invites navigating away. Toggled by
   shared.js#setShellPracticeMode. */
.shell-minimal-header {
  display: none;
}
/* Dry run mark for a practice/sitting screen — see
   shared.js#showDryRunBanner's comment for why this rides on the fixed
   minimal header rather than the plain .dry-run-banner (which is covered
   once practice mode makes the header position: fixed). Selector matches
   .shell.practice-mode .shell-minimal-header's own specificity (3 simple
   selectors) plus .dry-run-active, rather than relying on source order,
   since a rule of equal-or-lower specificity later in the file still
   loses to that one. */
.shell.practice-mode .shell-minimal-header.dry-run-active {
  background: var(--caution);
  border-bottom-color: var(--caution);
}
.shell-minimal-header.dry-run-active .shell-minimal-header-title,
.shell-minimal-header.dry-run-active .shell-minimal-timer {
  color: var(--caution-fg);
}
.shell-minimal-header.dry-run-active .shell-minimal-header-title::before {
  content: "🧪 DRY RUN — ";
}
.shell.practice-mode .shell-sidebar,
.shell.practice-mode .hamburger-btn {
  display: none;
}
.shell.practice-mode .shell-minimal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 20px;
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border-subtle);
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 30;
}
.shell.practice-mode .shell-main {
  padding-top: 64px;
}
.shell-minimal-header-title {
  font-weight: 700;
  color: var(--fg-primary);
  font-size: 0.95rem;
}
.shell-minimal-timer {
  font-family: var(--font-mono);
  font-size: 1.1rem;
  color: var(--fg-primary);
}
.shell-minimal-timer.low {
  color: var(--danger);
}
.shell-minimal-exit-btn {
  padding: 6px 14px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: transparent;
  color: var(--fg-primary);
  font-size: 0.85rem;
  cursor: pointer;
}
.shell-minimal-exit-btn:hover {
  border-color: var(--danger);
  color: var(--danger);
}

/* ===== Shared test-taking interface (public/testSession.js) =====
   One component for Reading/Verbal/Maths/Quant/mock tests — see the
   spec's own token list (--surface-1/2, --border-accent, --bg-accent,
   --fill-accent, --on-accent, --bg-warning/--border-warning/--text-warning,
   --text-success/--text-danger) mapped onto this app's actual token
   vocabulary rather than added as a second, parallel set:
   --surface-1→--bg-raised, --surface-2→--bg-surface, --bg-accent/
   --bg-accent-muted→--accent-dim, --border-accent/--text-accent→--accent,
   --fill-accent→--accent, --on-accent→--accent-fg, --bg-warning→
   --caution-dim (new, added above alongside --accent-dim/--opponent-dim),
   --border-warning/--text-warning→--caution, --text-success→--positive,
   --text-danger→--danger. See docs/DECISIONS.md. */

/* Equal inset on both sides of the question column, so stem, options,
   header and action bar never touch an edge — including in full-screen
   mode (testSplitPane.js), where no page padding surrounds them. */
.test-session {
  padding-inline: var(--test-session-gutter, 16px);
}
/* Header + progress bar, pinned as one block. --test-session-sticky-top is
   the height of any fixed bar above it (the practice-mode minimal header on
   the real sitting — measured by testSplitPane.js, never hard-coded); 0 in
   preview and in full-screen mode. Before this, the header stuck at top:0
   directly underneath that fixed bar, so it vanished as soon as the student
   scrolled. The negative margin lets the band span the gutters so scrolled
   content never shows beside it. */
.test-session-topbar {
  position: sticky;
  top: var(--test-session-sticky-top, 0px);
  z-index: 5;
  margin-inline: calc(-1 * var(--test-session-gutter, 16px));
  padding-inline: var(--test-session-gutter, 16px);
  background: var(--bg-base);
}
.test-session-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 0 8px;
  border-bottom: 1px solid var(--border-subtle);
}
.test-session-header-left {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.test-session-title {
  font-size: 0.85rem;
  color: var(--fg-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.test-session-progress {
  font-size: 1rem;
  font-weight: 600;
  color: var(--fg-primary);
}
.test-session-header-right {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: none;
}
.test-session-clock-block {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}
.test-session-header-actions:empty {
  display: none;
}
.test-session-expand-btn {
  padding: 6px 10px;
  font-size: 0.8rem;
  white-space: nowrap;
}
.test-session-clock {
  font-size: 1.3rem;
  font-weight: 600;
  color: var(--fg-primary);
}
.test-session-clock.warning {
  color: var(--caution);
}
.test-session-clock.danger {
  color: var(--danger);
}
.test-session-clock-label {
  font-size: 0.7rem;
  color: var(--fg-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.test-session-progress-bar {
  height: 4px;
  background: var(--border-subtle);
  border-radius: 2px;
  overflow: hidden;
}
.test-session-progress-fill {
  height: 100%;
  background: var(--accent);
  transition: width 0.2s ease;
}

.test-session-body {
  padding: 20px 0 12px;
}
.test-session-stem {
  font-size: 17px;
  line-height: 1.6;
  color: var(--fg-primary);
}
.test-session-stimulus {
  margin: 16px 0;
  padding: 14px;
  border-radius: var(--radius);
  background: var(--bg-raised);
  border: 1px solid var(--border-subtle);
}
.test-session-stimulus.hidden,
.test-session-answer-review-stimulus-slot:empty {
  display: none;
}
.test-session-options {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 16px;
}
.test-session-select-all-note {
  margin: 12px 0 0;
}

/* Full-width tappable rows with a circular letter badge, restyling the
   existing .option-btn markup questionRenderer.js already produces — not a
   new renderer, see testSession.js's comment. */
.test-session-options .option-btn {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: none;
  width: 100%;
  padding: 14px 16px;
  border-radius: var(--radius);
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.test-session-options .option-btn:hover {
  border-color: var(--border-strong);
  background: var(--bg-raised);
}
.test-session-options .option-btn > strong {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--bg-inset);
  color: var(--fg-secondary);
  font-size: 0.85rem;
  margin: 0;
}
.test-session-options .option-btn > span {
  font-size: 15px;
  color: var(--fg-primary);
  margin: 0;
}
.test-session-options .option-btn.selected {
  border-color: var(--accent);
  background: var(--accent-dim);
}
.test-session-options .option-btn.selected > strong {
  background: var(--accent);
  color: var(--accent-fg);
}

.link-btn {
  background: none;
  border: none;
  padding: 0;
  color: var(--fg-secondary);
  font-size: 0.85rem;
  text-decoration: underline;
  cursor: pointer;
}
.link-btn:hover {
  color: var(--fg-primary);
}
.test-session-clear-btn {
  margin-top: 12px;
}

.test-session-scratch-block {
  margin-top: 20px;
  border-top: 1px solid var(--border-subtle);
  padding-top: 12px;
}
.test-session-scratch-toggle {
  background: none;
  border: none;
  padding: 0;
  color: var(--fg-secondary);
  font-size: 0.85rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 4px;
}
.test-session-scratch-caret {
  display: inline-block;
}
.test-session-scratch-block.collapsed .test-session-scratch {
  display: none;
}
.test-session-scratch {
  width: 100%;
  margin-top: 8px;
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid var(--border-subtle);
  background: var(--bg-base);
  color: var(--fg-primary);
  font-family: var(--font-mono);
  font-size: 0.9rem;
  resize: vertical;
}

.test-session-action-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--border-subtle);
}
.test-session-action-bar-right {
  display: flex;
  gap: 10px;
}
.test-session-flag-btn {
  padding: 8px 14px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: transparent;
  color: var(--fg-secondary);
  cursor: pointer;
}
.test-session-flag-btn.flagged {
  border-color: var(--caution);
  color: var(--caution);
  background: var(--caution-dim);
}
.test-session-back-btn,
.test-session-next-btn {
  padding: 10px 18px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: transparent;
  color: var(--fg-primary);
  cursor: pointer;
}
.test-session-back-btn:disabled {
  opacity: 0.4;
  cursor: default;
}
.test-session-next-btn.primary,
.test-session-submit-btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-fg);
  font-weight: 600;
}

/* ===== Palette ===== */
.test-session-palette-card {
  margin-top: 24px;
  padding: 16px;
  border-radius: var(--radius);
  background: var(--bg-raised);
  border: 1px solid var(--border-subtle);
}
.test-session-palette-header {
  margin-bottom: 12px;
}
.test-session-palette-counts {
  font-size: 0.85rem;
  color: var(--fg-secondary);
}
.test-session-palette-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(38px, 1fr));
  gap: 8px;
}
.test-session-palette-btn {
  aspect-ratio: 1;
  min-width: 38px;
  border-radius: 8px;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  color: var(--fg-muted);
}
/* Section boundary (createTestSession's sectionBoundaryAfter option) — a
   visible gap after the last question of a section, not just another grid
   cell, so the palette itself shows where one section's questions end and
   the next begins. */
.test-session-palette-btn.test-session-palette-section-end {
  margin-right: 10px;
  border-right: 2px solid var(--accent);
}
/* Split-view spec, section 1: the section title spans the whole palette
   grid width (grid-column: 1/-1) so it reads as a group header, not
   another cell competing with the question buttons. */
.test-session-palette-section-title {
  grid-column: 1 / -1;
  margin-top: 6px;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--fg-muted);
}
.test-session-palette-section-title:first-child {
  margin-top: 0;
}
.test-session-palette-btn.not-seen {
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  color: var(--fg-muted);
}
.test-session-palette-btn.skipped {
  background: var(--bg-surface);
  border: 1px dashed var(--border-strong);
  color: var(--fg-secondary);
}
.test-session-palette-btn.answered {
  background: var(--accent-dim);
  border: 1px solid var(--accent);
  color: var(--accent);
}
.test-session-palette-btn.flagged {
  background: var(--warning-dim);
  border: 1px solid var(--warning);
  color: var(--warning);
}
.test-session-palette-btn.current {
  background: var(--accent);
  border: 1px solid var(--accent);
  color: var(--accent-fg);
}
.test-session-palette-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--border-subtle);
  font-size: 0.78rem;
  color: var(--fg-secondary);
}
.palette-legend-item {
  display: flex;
  align-items: center;
  gap: 6px;
}
.palette-swatch {
  width: 14px;
  height: 14px;
  border-radius: 4px;
  display: inline-block;
}
.palette-swatch.not-seen {
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
}
.palette-swatch.skipped {
  background: var(--bg-surface);
  border: 1px dashed var(--border-strong);
}
.palette-swatch.answered {
  background: var(--accent-dim);
  border: 1px solid var(--accent);
}
.palette-swatch.flagged {
  background: var(--warning-dim);
  border: 1px solid var(--warning);
}
.palette-swatch.current {
  background: var(--accent);
  border: 1px solid var(--accent);
}

/* ===== Review before submit ===== */
.test-session-review-screen.hidden {
  display: none;
}
.test-session-review-counts {
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
  margin: 12px 0 20px;
}
.review-count-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 70px;
}
.review-count-item strong {
  font-size: 1.4rem;
  color: var(--fg-primary);
}
.review-count-item span {
  font-size: 0.75rem;
  color: var(--fg-muted);
}
.test-session-review-lists {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin: 20px 0;
}
.test-session-review-list h3 {
  font-size: 0.9rem;
  margin-bottom: 6px;
}
.test-session-review-list ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.85rem;
  color: var(--fg-secondary);
}
.test-session-submit-btn {
  margin-top: 12px;
  padding: 12px 24px;
}

/* ===== Answer review (post-submission) ===== */
.test-session-easy-missed {
  margin-bottom: 16px;
  padding: 14px;
  border-radius: var(--radius);
  background: var(--bg-raised);
  border: 1px solid var(--border-subtle);
}
.test-session-easy-missed p {
  margin: 0 0 6px;
  font-size: 0.9rem;
  color: var(--fg-secondary);
}
.test-session-easy-missed p:last-child {
  margin-bottom: 0;
}
.test-session-review-filters {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}
.test-session-filter-btn {
  padding: 6px 14px;
  border-radius: 999px;
  border: 1px solid var(--border-strong);
  background: transparent;
  color: var(--fg-secondary);
  font-size: 0.82rem;
  cursor: pointer;
}
.test-session-filter-btn.active {
  background: var(--accent-dim);
  border-color: var(--accent);
  color: var(--accent);
}
.test-session-flagged-tag {
  color: var(--caution);
  font-size: 0.8rem;
  font-weight: 400;
}
.answer-review-tag {
  font-size: 0.78rem;
  font-weight: 600;
}
.answer-review-tag.answer-review-correct {
  color: var(--positive);
}
.answer-review-tag.answer-review-wrong {
  color: var(--danger);
}
.answer-review-option.is-correct .criterion-name {
  color: var(--positive);
}
.answer-review-option.is-wrong-choice .criterion-name {
  color: var(--danger);
}
.test-session-misconception {
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--caution-dim);
  border-left: 3px solid var(--caution);
  font-size: 0.9rem;
  color: var(--fg-primary);
}

@media (max-width: 480px) {
  .test-session-header-left {
    max-width: 60%;
  }
  .test-session-palette-grid {
    grid-template-columns: repeat(auto-fill, minmax(34px, 1fr));
  }
  .test-session-action-bar {
    flex-wrap: wrap;
  }
  .test-session-action-bar-right {
    width: 100%;
    justify-content: space-between;
  }
}

/* ===== Prompt editor (prompt management spec) ===== */
.prompt-hotpath-note {
  background: var(--bg-surface);
  border: 1px solid var(--danger);
  color: var(--danger);
  padding: 10px 14px;
  border-radius: 8px;
  font-size: 0.85rem;
  margin-bottom: 16px;
}
.prompt-editor-grid {
  display: grid;
  grid-template-columns: 1fr 280px;
  gap: 16px;
  align-items: start;
}
.prompt-body-textarea {
  width: 100%;
  min-height: 480px;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  line-height: 1.5;
  padding: 12px;
  border-radius: 8px;
  border: 1px solid var(--border-subtle);
  background: var(--bg-surface);
  color: var(--fg-primary);
  resize: vertical;
  tab-size: 2;
}
.prompt-var-panel {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: 8px;
  padding: 12px;
  max-height: 480px;
  overflow-y: auto;
}
.prompt-var-panel h4 {
  margin: 0 0 8px;
  font-size: 0.85rem;
}
.prompt-var-item {
  width: 100%;
  text-align: left;
  background: var(--bg-base);
  border: 1px solid var(--border-subtle);
  border-radius: 6px;
  padding: 6px 8px;
  margin-bottom: 6px;
  cursor: pointer;
  color: var(--fg-primary);
}
.prompt-var-item:hover {
  border-color: var(--fg-muted);
}
.prompt-var-item code {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--positive);
}
.prompt-var-item p {
  margin: 2px 0 0;
  font-size: 0.75rem;
  color: var(--fg-muted);
}
.prompt-save-row {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  margin-top: 12px;
  flex-wrap: wrap;
}
.prompt-change-note {
  flex: 1;
  min-width: 240px;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--border-subtle);
  background: var(--bg-surface);
  color: var(--fg-primary);
}
.prompt-warning-list {
  background: var(--caution-dim);
  border: 1px solid var(--caution);
  color: var(--caution);
  padding: 10px 14px;
  border-radius: 8px;
  font-size: 0.85rem;
  margin-top: 12px;
}
.prompt-warning-list ul {
  margin: 4px 0 0;
  padding-left: 18px;
}
.prompt-diff {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  border: 1px solid var(--border-subtle);
  border-radius: 8px;
  overflow: hidden;
}
.prompt-diff-line {
  padding: 1px 10px;
  white-space: pre-wrap;
  word-break: break-word;
}
.prompt-diff-same {
  background: var(--bg-surface);
  color: var(--fg-secondary);
}
.prompt-diff-added {
  background: #123420;
  color: var(--positive);
}
.prompt-diff-removed {
  background: #3a1a16;
  color: var(--danger);
}
.prompt-version-row {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border-subtle);
}
.prompt-version-meta {
  font-size: 0.8rem;
  color: var(--fg-muted);
}
.prompt-test-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}
.prompt-test-pane {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: 8px;
  padding: 12px;
}
.prompt-test-pane h4 {
  margin: 0 0 8px;
  font-size: 0.85rem;
}
.prompt-test-output {
  white-space: pre-wrap;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  line-height: 1.5;
  max-height: 400px;
  overflow-y: auto;
}
.prompt-params-textarea {
  width: 100%;
  min-height: 140px;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  padding: 10px;
  border-radius: 8px;
  border: 1px solid var(--border-subtle);
  background: var(--bg-surface);
  color: var(--fg-primary);
}
@media (max-width: 900px) {
  .prompt-editor-grid,
  .prompt-test-columns {
    grid-template-columns: 1fr;
  }
}

/* ===== Study planner, time-scheduled (design rule: no overdue state,
   ever — nothing here turns red or says "missed"; an unstarted item
   quietly becomes a plain question, never a judgement) ===== */
.planner-week-nav {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
}
.planner-week-nav-btn {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  color: var(--fg-primary);
  border-radius: 8px;
  padding: 6px 12px;
  cursor: pointer;
}
.planner-week-nav-btn:hover {
  border-color: var(--fg-muted);
}
.planner-week-label {
  font-weight: 700;
  font-size: 1rem;
}
.planner-weeks-remaining {
  font-size: 0.8rem;
  color: var(--fg-muted);
}
.planner-note-input {
  width: 100%;
  min-height: 32px;
  padding: 6px 8px;
  font-size: 0.8rem;
  border-radius: 6px;
  border: 1px solid var(--border-subtle);
  background: var(--bg-base);
  color: var(--fg-primary);
  margin-top: 6px;
}

/* Guidance card — a deliberate, persistent product feature (not one-time
   help text), shown until dismissed and reachable afterwards. */
.planner-guidance-card {
  background: var(--accent-dim);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: 16px 18px;
  margin-bottom: 16px;
}
.planner-guidance-card h4 {
  margin: 12px 0 4px;
}
.planner-guidance-card h4:first-child {
  margin-top: 0;
}
.planner-guidance-card p {
  margin: 0 0 4px;
  font-size: 0.88rem;
  color: var(--fg-secondary);
}
.planner-guidance-dismiss-row {
  display: flex;
  justify-content: flex-end;
  margin-top: 8px;
}
.planner-guidance-link {
  font-size: 0.82rem;
}

/* Load summary — a plain per-day total, never a judgement; .heavy adds a
   quiet note, not a colour warning. */
.planner-load-row {
  display: flex;
  gap: 6px;
  margin-bottom: 10px;
  flex-wrap: wrap;
}
.planner-load-day {
  flex: 1;
  min-width: 64px;
  text-align: center;
  font-size: 0.72rem;
  color: var(--fg-muted);
  padding: 4px;
  border-radius: 6px;
}
.planner-load-day.heavy {
  background: var(--caution-dim);
  color: var(--caution);
}
.planner-load-day strong {
  display: block;
  font-size: 0.8rem;
  color: var(--fg-primary);
}

/* Reschedule / proposal banners — plain questions, never statements about
   the child (spec section 4). */
.planner-prompt-card {
  border: 1px solid var(--border-subtle);
  border-radius: 8px;
  padding: 10px 14px;
  margin-bottom: 8px;
  background: var(--bg-surface);
}
.planner-prompt-card p {
  margin: 0 0 8px;
}
.planner-prompt-slots {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.planner-repeated-note {
  font-size: 0.78rem;
  color: var(--fg-muted);
  font-style: italic;
  margin-top: 2px;
}

/* ---------- Calendar grid ---------- */
.planner-calendar-wrap {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--bg-surface);
}
.planner-calendar-header {
  display: grid;
  grid-template-columns: 56px repeat(7, 1fr);
  border-bottom: 1px solid var(--border-subtle);
}
.planner-day-header {
  padding: 8px 4px;
  text-align: center;
  font-size: 0.78rem;
  border-left: 1px solid var(--border-subtle);
}
.planner-day-header strong {
  display: block;
  font-size: 0.85rem;
}
.planner-calendar-scroll {
  max-height: 620px;
  overflow-y: auto;
}
.planner-calendar-body {
  display: grid;
  grid-template-columns: 56px repeat(7, 1fr);
  position: relative;
}
.planner-time-gutter {
  position: relative;
  border-right: 1px solid var(--border-subtle);
}
.planner-time-label {
  position: absolute;
  right: 6px;
  transform: translateY(-50%);
  font-size: 0.68rem;
  color: var(--fg-muted);
}
.planner-day-col {
  position: relative;
  border-left: 1px solid var(--border-subtle);
  background-image: repeating-linear-gradient(to bottom, transparent, transparent 59px, var(--border-subtle) 60px);
}
.planner-block {
  position: absolute;
  left: 2px;
  right: 2px;
  border-radius: 6px;
  padding: 3px 6px;
  font-size: 0.72rem;
  overflow: hidden;
  cursor: grab;
  border: 1px solid var(--border-subtle);
  background: var(--accent-dim);
  color: var(--fg-primary);
  touch-action: none;
  user-select: none;
}
.planner-block-title {
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.planner-block-time {
  font-size: 0.66rem;
  color: var(--fg-muted);
}
.planner-block.item-practice {
  background: var(--accent-dim);
  border-color: var(--accent);
}
.planner-block.item-school_homework {
  background: var(--opponent-dim);
  border-color: var(--opponent);
}
.planner-block.item-tuition_homework {
  background: var(--caution-dim);
  border-color: var(--caution);
}
.planner-block.item-activity,
.planner-block.item-other {
  background: var(--bg-raised);
  border-color: var(--border-strong);
}
.planner-block.is-done {
  opacity: 0.8;
  border-left: 3px solid var(--positive);
  background: var(--bg-raised);
}
.planner-calendar-readonly .planner-block {
  cursor: pointer;
}
.planner-block.is-dragging {
  opacity: 0.7;
  z-index: 5;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
}
.planner-block-commitment {
  position: absolute;
  left: 2px;
  right: 2px;
  border-radius: 6px;
  padding: 3px 6px;
  font-size: 0.72rem;
  background-image: repeating-linear-gradient(45deg, var(--bg-inset), var(--bg-inset) 4px, var(--bg-raised) 4px, var(--bg-raised) 8px);
  color: var(--fg-muted);
  border: 1px dashed var(--border-strong);
  cursor: default;
}
.planner-resize-handle {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 6px;
  cursor: ns-resize;
}
.planner-conflict-toast {
  background: var(--caution-dim);
  color: var(--caution);
  border-radius: 6px;
  padding: 8px 12px;
  font-size: 0.8rem;
  margin-bottom: 10px;
}
.planner-commitment-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 12px;
  border: 1px solid var(--border-subtle);
  border-radius: 8px;
  margin-bottom: 6px;
  font-size: 0.85rem;
}
.planner-suggest-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--border-subtle);
  border-radius: 8px;
  margin-bottom: 8px;
}
.planner-suggest-reason {
  font-size: 0.78rem;
  color: var(--fg-muted);
}
.planner-suggest-slot {
  font-size: 0.78rem;
  color: var(--fg-secondary);
  margin-top: 2px;
}

/* Today list (Home card + student Today view) — a short, plain, time-
   ordered list; the one place a fresh reader gets no calendar at all. */
.planner-today-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border-subtle);
}
.planner-today-row:last-child {
  border-bottom: none;
}
.planner-today-time {
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
  color: var(--fg-muted);
  font-size: 0.82rem;
  width: 46px;
}
.planner-today-title {
  font-weight: 600;
}
.planner-today-title.is-done {
  opacity: 0.6;
  text-decoration: line-through;
}
.planner-today-row-clickable {
  cursor: pointer;
  border-radius: 6px;
}
.planner-today-row-clickable:hover {
  background: var(--bg-surface);
}

/* Today / week-calendar toggle (student's own page) */
.planner-view-tabs {
  display: flex;
  gap: 4px;
  margin: 10px 0 16px;
  border-bottom: 1px solid var(--border-subtle);
}
.planner-view-tab {
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 8px 4px;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--fg-muted);
  cursor: pointer;
}
.planner-view-tab.active {
  color: var(--fg-primary);
  border-bottom-color: var(--accent);
}

/* Single day-at-a-time fallback (student, narrow viewports) — same block
   renderer as the 7-day grid, one column instead of seven. */
.planner-calendar-day .planner-calendar-header {
  display: none;
}
.planner-calendar-body-single {
  grid-template-columns: 56px 1fr;
}

@media (max-width: 700px) {
  .planner-calendar-wrap {
    overflow-x: auto;
  }
  /* The single-day fallback is deliberately narrow-friendly (a 56px
     gutter plus one flexible column) — the forced 640px scroll width
     below is only for the 7-day grid, never the day view. */
  .planner-calendar-wrap:not(.planner-calendar-day) .planner-calendar-header,
  .planner-calendar-wrap:not(.planner-calendar-day) .planner-calendar-body {
    min-width: 640px;
  }
}

/* ===== Writing screen split-pane (writing and test screen layout spec) =====
   Below 1024px: everything below is a plain block-flow stack in source
   order (prompt, stimuli, map, textarea) — no split, by design. */
.write-left-pane-inner > * + * {
  margin-top: 16px;
}
.write-pane-collapse-btn {
  display: none;
}
.write-prompt-panel {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: 16px;
}
.write-map-section {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: 12px 16px;
}
.write-map-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}
.write-map-header h3 {
  margin: 0;
}
.write-map-header button {
  background: transparent;
  border: 1px solid var(--border-subtle);
  border-radius: 6px;
  color: var(--fg-primary);
  cursor: pointer;
  padding: 2px 10px;
  line-height: 1.6;
}
.write-map-section.collapsed .write-map-body {
  display: none;
}
.write-divider {
  display: none;
}

@media (min-width: 1024px) {
  .write-split {
    display: flex;
    align-items: stretch;
    gap: 0;
    min-height: 70vh;
  }
  .write-left-pane {
    flex: 0 0 var(--write-left-pane-width, 38%);
    max-width: var(--write-left-pane-width, 38%);
    min-width: 220px;
    position: relative;
    display: flex;
    flex-direction: column;
    padding-right: 20px;
  }
  .write-left-pane-inner {
    display: flex;
    flex-direction: column;
    min-height: 0;
    flex: 1;
  }
  .write-prompt-panel {
    position: sticky;
    top: 0;
    z-index: 2;
    flex-shrink: 0;
  }
  .write-map-section {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }
  .write-map-section.collapsed {
    flex: 0 0 auto;
  }
  .write-map-body {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
  }
  .write-divider {
    display: block;
    flex: 0 0 6px;
    cursor: col-resize;
    background: var(--border-subtle);
    border-radius: 3px;
    margin: 0 8px;
  }
  .write-divider:hover,
  .write-divider:focus-visible {
    background: var(--accent);
    outline: none;
  }
  .write-right-pane {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
  }
  .write-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-shrink: 0;
  }
  .write-meta {
    display: flex;
    gap: 16px;
    align-items: center;
  }
  #screen-write .essay-textarea {
    flex: 1;
    min-height: 0;
    height: 100%;
    resize: none;
  }
  .write-pane-collapse-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    top: 8px;
    right: -14px;
    z-index: 3;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: 1px solid var(--border-subtle);
    background: var(--bg-surface);
    color: var(--fg-primary);
    cursor: pointer;
    padding: 0;
  }
  .write-left-pane.collapsed {
    flex: 0 0 32px;
    max-width: 32px;
    min-width: 32px;
    overflow: hidden;
    padding-right: 0;
  }
  .write-left-pane.collapsed .write-left-pane-inner {
    display: none;
  }
  .write-left-pane.collapsed .write-pane-collapse-btn {
    right: auto;
    left: 2px;
  }
  .write-left-pane.collapsed + .write-divider {
    display: none;
  }
}

/* ===== Test-taking split pane (reading passage + shared question mount;
   also any per-question stimulus inside testSession.js's own markup) ===== */
.test-session-split-layout {
  display: block;
}
.test-session-passage-section-name {
  margin: 0 0 8px;
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--fg-muted);
}
.test-session-resize-handle {
  display: none;
}
@media (min-width: 1024px) {
  /* No split when there's no passage to show (e.g. a non-passage test
     set) — a 3-column grid with only the mount column live would leave
     empty tracks instead of the question using the full width. */
  .test-session-split-layout:has(.test-session-passage-pane.hidden) {
    display: block;
  }
  .test-session-split-layout:has(.test-session-passage-pane.hidden) .test-session-resize-handle {
    display: none;
  }
  /* Middle track is the draggable divider; the left pane's own width is
     a CSS variable so JS can persist and restore it per student (spec
     section 1: "Pane width remembered per student, divider draggable") —
     the 260-600px clamp matches the JS drag/keyboard handler's own clamp
     in public/app/test-sitting.js. */
  .test-session-split-layout {
    display: grid;
    /* minmax(0, 1fr), not 1fr: a bare 1fr can't shrink below its content's
       min-content width, so a long unbreakable header line (a long test
       name beside the clock and the full-screen button) pushed the whole
       page into horizontal overflow at 1024px. */
    grid-template-columns: minmax(260px, var(--test-session-pane-width, 380px)) 10px minmax(0, 1fr);
    gap: 0 14px;
    align-items: start;
  }
  /* Explicit placement, not auto-flow: reading.html's split layout has no
     resize handle, so with auto-flow its question mount fell into the
     10px divider track — Reading practice on desktop rendered the whole
     question one word per line (introduced when the divider track was
     added on 2026-09-20; caught 2026-09-25). Pane, handle and question
     area now always land in their own tracks whether a handle exists. */
  .test-session-split-layout > .test-session-passage-pane {
    grid-column: 1;
  }
  .test-session-split-layout > .test-session-resize-handle {
    grid-column: 2;
  }
  .test-session-split-layout > :not(.test-session-passage-pane):not(.test-session-resize-handle) {
    grid-column: 3;
  }
  .test-session-split-layout .test-session-passage-pane {
    position: sticky;
    top: calc(var(--test-session-sticky-top, 0px) + 16px);
    /* Fixed bar + 16px above, and the page space below the layout
       (--test-session-below, measured by testSplitPane.js) — otherwise, at
       the bottom of the page, the end of the layout pushes the pane up
       under the fixed bar. */
    max-height: calc(100vh - var(--test-session-sticky-top, 0px) - 16px - var(--test-session-below, 16px));
    /* .setup-card's bottom margin counts against where a sticky box may
       sit — it pushed the pane 20px up under the fixed bar at the page
       bottom — and does nothing else in a grid row. */
    margin-bottom: 0;
    overflow-y: auto;
  }
  .test-session-resize-handle {
    display: block;
    align-self: stretch;
    position: relative;
    cursor: col-resize;
  }
  .test-session-resize-handle::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 4px;
    width: 2px;
    border-radius: 1px;
    background: var(--border-subtle);
  }
  .test-session-resize-handle:hover::after,
  .test-session-resize-handle:focus-visible::after {
    background: var(--accent);
  }
}

.test-session-body {
  display: block;
}
@media (min-width: 1024px) {
  .test-session-body.has-stimulus {
    display: grid;
    grid-template-columns: minmax(240px, 1fr) minmax(320px, 1.4fr);
    gap: 24px;
    align-items: start;
  }
  .test-session-body.has-stimulus .test-session-stimulus {
    position: sticky;
    top: 16px;
    max-height: calc(100vh - 32px);
    overflow-y: auto;
  }
}

/* ===== Report: exam prompt at the top ===== */
.report-prompt-panel {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 20px;
}
.report-prompt-panel h3 {
  margin: 0 0 6px;
  font-size: 0.85rem;
  color: var(--fg-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.report-prompt-panel p {
  margin: 0;
}

/* ===== Rich question content (markdown editor, image picker, rendered
   content) — spec: "GoApexam — rich question content and AI-assisted
   authoring" ===== */
.content-editor {
  border: 1px solid var(--border-subtle);
  border-radius: 8px;
  overflow: hidden;
  background: var(--bg-base);
}
.content-editor-toolbar {
  display: flex;
  gap: 4px;
  padding: 6px;
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border-subtle);
}
.content-editor-toolbar button {
  background: transparent;
  border: 1px solid transparent;
  border-radius: 6px;
  color: var(--fg-primary);
  padding: 4px 8px;
  cursor: pointer;
  font-size: 0.85rem;
}
.content-editor-toolbar button:hover {
  border-color: var(--border-subtle);
  background: var(--bg-raised);
}
.content-editor-preview-toggle {
  margin-left: auto;
}
.content-editor-katex-ref {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  padding: 8px 10px;
  background: var(--accent-dim);
  font-size: 0.75rem;
  border-bottom: 1px solid var(--border-subtle);
}
.content-editor-katex-ref-item code {
  background: var(--bg-inset);
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 0.72rem;
}
.content-editor-textarea {
  display: block;
  width: 100%;
  border: none;
  background: transparent;
  color: var(--fg-primary);
  padding: 10px;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  resize: vertical;
  box-sizing: border-box;
}
.content-editor-textarea:focus {
  outline: none;
}
.content-editor-preview {
  padding: 10px 12px;
  min-height: 60px;
}
.content-editor-preview p:first-child {
  margin-top: 0;
}

/* Rendered content — student interface, authoring preview, admin review,
   answer review all share this (public/questionRenderer.js). */
.content-md-image {
  max-width: 100%;
  height: auto;
  display: block;
  margin: 6px 0;
  border-radius: 6px;
}
.content-md-table-wrap {
  overflow-x: auto;
  margin: 8px 0;
}
.content-md-table {
  border-collapse: collapse;
  font-size: 0.85rem;
}
.content-md-table th,
.content-md-table td {
  border: 1px solid var(--border-subtle);
  padding: 4px 8px;
  text-align: left;
}
.content-md-table th {
  background: var(--bg-surface);
}
.katex-block {
  display: block;
  overflow-x: auto;
  margin: 10px 0;
  text-align: center;
}

/* Image picker modal */
.content-image-picker-tabs {
  display: flex;
  gap: 6px;
  margin-bottom: 12px;
  border-bottom: 1px solid var(--border-subtle);
}
.content-image-picker-tab {
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 6px 4px;
  color: var(--fg-muted);
  cursor: pointer;
  font-weight: 600;
}
.content-image-picker-tab.active {
  color: var(--fg-primary);
  border-bottom-color: var(--accent);
}
.content-image-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 10px;
  max-height: 360px;
  overflow-y: auto;
}
.content-image-grid-item {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: 8px;
  padding: 6px;
  cursor: pointer;
  text-align: left;
}
.content-image-grid-item:hover {
  border-color: var(--accent);
}
.content-image-grid-item.content-image-used-here {
  border-color: var(--positive);
}
.content-image-grid-item img {
  width: 100%;
  height: 80px;
  object-fit: cover;
  border-radius: 4px;
  display: block;
  margin-bottom: 4px;
}
.content-image-grid-caption {
  display: block;
  font-size: 0.72rem;
  color: var(--fg-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* AI-assisted authoring: every pre-filled field stays visually marked
   until the author edits or confirms it (spec section 2) — never saved
   as authored content without being seen. */
.ai-suggested-field {
  position: relative;
  border-left: 3px solid var(--opponent) !important;
  background: var(--opponent-dim);
}
.ai-suggested-label {
  display: inline-block;
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--opponent);
  background: var(--opponent-dim);
  border-radius: 4px;
  padding: 1px 6px;
  margin-left: 6px;
}

/* Verification gate results */
.gate-results-panel {
  border: 1px solid var(--border-subtle);
  border-radius: 8px;
  padding: 12px 14px;
  margin: 10px 0;
  background: var(--bg-surface);
}
.gate-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 4px 0;
  font-size: 0.85rem;
}
.gate-badge {
  flex-shrink: 0;
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  border-radius: 4px;
  padding: 2px 6px;
}
.gate-badge.pass {
  background: var(--positive);
  color: var(--positive-fg);
}
.gate-badge.flagged,
.gate-badge.fail {
  background: var(--caution);
  color: var(--caution-fg);
}
.gate-badge.not_applicable,
.gate-badge.manual_required {
  background: var(--bg-inset);
  color: var(--fg-muted);
}
.gate-acknowledge-row {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border-subtle);
}

/* Preview mounts the same split pane inside a modal's own scroll body —
   the pinned pane's max height follows the modal, not the viewport. */
@media (min-width: 1024px) {
  .modal-scroll-body .test-session-split-layout .test-session-passage-pane {
    top: 0;
    max-height: 70vh;
  }
  .modal-full .modal-scroll-body .test-session-split-layout .test-session-passage-pane {
    max-height: calc(100vh - 220px);
  }
}

/* Review-before-submit: the stem excerpt is rendered markdown/math now
   (not an escaped 80-char slice), so it's clamped to one line visually. */
.test-session-review-stem-excerpt {
  display: inline-block;
  max-width: 100%;
  vertical-align: bottom;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.test-session-review-stem-excerpt img {
  display: none;
}
.test-session-answer-review-stem {
  margin: 0 0 8px;
}

/* LaTeX readability in authoring: the question stem and option-body
   editors (public/testAuthoring.js — #editor-stem, #editor-opt-A..E) are
   where math is typed, and 0.85rem mono made \frac{}{} / ^{} source hard
   to read. Larger mono for just these fields (and their rendered
   preview), not the whole form — misconception/explanation editors keep
   the default size. */
#editor-stem .content-editor-textarea,
.option-author-row > [id^="editor-opt-"]:not([id$="-misconception"]) .content-editor-textarea {
  font-size: 1.05rem;
  line-height: 1.6;
  letter-spacing: 0.01em;
}
#editor-stem .content-editor-preview,
.option-author-row > [id^="editor-opt-"]:not([id$="-misconception"]) .content-editor-preview {
  font-size: 1.1rem;
}
#editor-stem .content-editor-preview .katex,
.option-author-row > [id^="editor-opt-"]:not([id$="-misconception"]) .content-editor-preview .katex {
  font-size: 1.15em;
}

/* Image plus text (public/contentEditor.js#mountImageDescriptionField) —
   two clearly separate inputs: the image the student sees, and the text
   description AI reads. */
.image-plus-text {
  border: 1px solid var(--border-subtle);
  border-radius: 8px;
  padding: 10px 14px 12px;
  margin: 8px 0;
  display: grid;
  gap: 10px;
}
.image-plus-text legend {
  font-weight: 600;
  padding: 0 6px;
}
.image-plus-text-part {
  display: grid;
  gap: 6px;
}
.image-plus-text-part .setup-note {
  margin: 0;
}
.image-plus-text-description {
  width: 100%;
  box-sizing: border-box;
}
.image-plus-text-ai-note {
  margin: 0;
  padding: 8px 10px;
  border-left: 3px solid var(--accent);
  background: var(--warning-dim);
  border-radius: 4px;
  font-size: 0.9rem;
}
.image-plus-text-status {
  margin: 0;
  font-size: 0.85rem;
  color: var(--fg-muted);
}
.image-plus-text-status.warning {
  color: var(--danger);
  font-weight: 600;
}

/* Bulk JSON test import (public/testImport.js). */
.test-import-summary p {
  margin: 6px 0;
}
.test-import-checklist {
  list-style: none;
  padding-left: 0;
}
.test-import-checklist label {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--fg-primary);
}
.test-import-diff-list {
  display: grid;
  gap: 8px;
  margin-bottom: 16px;
}
.test-import-group {
  margin-top: 10px;
}
.test-import-group > summary {
  cursor: pointer;
}
.test-import-group textarea,
.test-import-group input[type="text"] {
  width: 100%;
}
.test-import-question {
  border-top: 1px solid var(--border-subtle);
  padding: 10px 0;
}
.test-import-question-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  margin-bottom: 6px;
}
.test-import-options {
  display: grid;
  gap: 6px;
  margin-top: 6px;
}
.test-import-option {
  display: flex;
  align-items: center;
  gap: 8px;
}
.test-import-actions {
  display: flex;
  gap: 12px;
  align-items: center;
  margin-top: 14px;
}
.error-banner ul,
.test-import-result ul {
  margin: 4px 0 0;
  padding-left: 18px;
}

/* Below the split breakpoint the passage stacks ABOVE the questions, so a
   header pinned inside the question column would sit below a long passage
   — off screen, timer included, for as long as the student reads it. Here
   the question column's wrappers dissolve (display: contents) and the
   header block moves to the top of the whole layout (order: -1), so it's
   pinned across the passage and the questions alike. Scoped to
   testSplitPane.js's own layout (.test-split-pane) — reading.html's split
   layout has different children. */
@media (max-width: 1023px) {
  .test-split-pane {
    display: flex;
    flex-direction: column;
  }
  .test-split-pane > .test-session-mount,
  .test-split-pane > .test-session-mount > .test-session {
    display: contents;
  }
  .test-split-pane .test-session-topbar {
    order: -1;
    margin-inline: 0;
    padding-inline: 0;
  }
}

/* ===== Full-screen test view (public/testSplitPane.js) =====
   A CSS takeover rather than the browser Fullscreen API: it works inside
   the preview modal and on iPads (whose Fullscreen API support is
   partial), and Escape stays ours to handle. The split layout covers the
   whole viewport — side navigation, practice-mode bar, modal chrome — and
   becomes its own scroller, so the pinned header sits at its top. */
body.test-session-expanded {
  overflow: hidden;
}
.test-session-split-layout.is-expanded {
  position: fixed;
  inset: 0;
  z-index: 1000;
  overflow-y: auto;
  margin: 0;
  max-width: none;
  padding: 0 24px 24px;
  background: var(--bg-base);
  --test-session-sticky-top: 0px;
}
@media (max-width: 600px) {
  .test-session-split-layout.is-expanded {
    padding: 0 12px 16px;
  }
}
@media (min-width: 1024px) {
  /* Wins over the preview modal's own pane rules (top:0 / 70vh). */
  .test-session-split-layout.is-expanded .test-session-passage-pane,
  .modal-scroll-body .test-session-split-layout.is-expanded .test-session-passage-pane,
  .modal-full .modal-scroll-body .test-session-split-layout.is-expanded .test-session-passage-pane {
    top: 16px;
    max-height: calc(100vh - 32px);
  }
  .test-session-split-layout.is-expanded > .test-session-passage-pane {
    margin-top: 16px;
  }
}

/* Batch image conversion (public/convertImages.js, and the "From converted
   images" tab in public/contentEditor.js's image picker). Refused and
   low-confidence results carry a coloured left edge and chip so they
   stand out at the top of the holding list. */
.convert-usage { color: var(--fg-secondary); font-size: 0.9rem; }
.convert-limit-form { align-items: flex-end; }
.convert-limit-form input { width: 120px; }
.convert-limit-status { align-self: center; color: var(--fg-secondary); font-size: 0.85rem; }
.convert-lanes {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
  margin: 16px 0 32px;
}
@media (max-width: 900px) {
  .convert-lanes { grid-template-columns: minmax(0, 1fr); }
}
.convert-lane {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: 10px;
  padding: 16px;
  min-width: 0;
}
.convert-lane h2 { margin-top: 0; font-size: 1.1rem; }
.convert-dropzone {
  display: block;
  border: 2px dashed var(--border-strong);
  border-radius: 10px;
  padding: 20px 12px;
  text-align: center;
  color: var(--fg-secondary);
  cursor: pointer;
}
.convert-dropzone.is-dragover { border-color: var(--accent); background: var(--accent-dim); }
.convert-dropzone input { display: block; margin: 0 auto 8px; max-width: 100%; }
.convert-selected { list-style: none; padding: 0; margin: 12px 0 0; display: grid; gap: 6px; max-height: 280px; overflow-y: auto; }
.convert-selected li { display: flex; align-items: center; gap: 10px; min-width: 0; }
.convert-selected img { width: 48px; height: 36px; object-fit: contain; background: #fff; border-radius: 4px; flex: none; }
.convert-selected-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.85rem; }
.convert-selected .secondary-btn { padding: 4px 10px; font-size: 0.8rem; }
.convert-estimate { font-size: 0.85rem; color: var(--fg-secondary); min-height: 1em; }
.convert-warning { color: var(--caution); }
.convert-lane-actions, .convert-item-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.convert-progress { margin-top: 8px; font-size: 0.85rem; color: var(--fg-secondary); }
.convert-progress.convert-error { color: var(--danger); }
.convert-holding-header { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }
.convert-unused-toggle { font-size: 0.9rem; color: var(--fg-secondary); }
.convert-items { list-style: none; padding: 0; margin: 0; display: grid; gap: 12px; }
.convert-item {
  display: grid;
  grid-template-columns: 160px minmax(0, 1fr);
  gap: 14px;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-left: 4px solid var(--border-strong);
  border-radius: 10px;
  padding: 12px;
}
.convert-item-refused { border-left-color: var(--danger); }
.convert-item-low { border-left-color: var(--caution); }
.convert-item-medium { border-left-color: var(--opponent); }
.convert-item-high { border-left-color: var(--positive); }
@media (max-width: 640px) {
  .convert-item { grid-template-columns: minmax(0, 1fr); }
}
.convert-item-thumb img { width: 100%; max-height: 140px; object-fit: contain; background: #fff; border-radius: 6px; }
.convert-item-body { min-width: 0; display: grid; gap: 8px; }
.convert-item-body textarea { width: 100%; box-sizing: border-box; font-family: ui-monospace, Consolas, monospace; font-size: 0.85rem; }
.convert-item-meta { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.convert-item-name { font-size: 0.8rem; color: var(--fg-muted); overflow-wrap: anywhere; }
.convert-chip {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--bg-raised);
  color: var(--fg-secondary);
}
.convert-chip-refused { background: var(--danger); color: var(--danger-fg); }
.convert-chip-low { background: var(--caution); color: var(--caution-fg); }
.convert-chip-medium { background: var(--opponent-dim); color: var(--opponent); }
.convert-chip-high { background: var(--bg-raised); color: var(--positive); }
.convert-chip-used { background: var(--accent-dim); color: var(--accent); }
.convert-item-reason { margin: 0; color: var(--caution); font-size: 0.85rem; }
.convert-item-refused .convert-item-reason { color: var(--danger); }
.convert-item-hint { margin: 0; font-size: 0.85rem; color: var(--opponent); }
.convert-item-render { font-size: 1.15rem; overflow-x: auto; }
.convert-item-status { font-size: 0.85rem; color: var(--fg-secondary); }
.converted-picker-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 8px; max-height: 360px; overflow-y: auto; }
.converted-picker-item {
  display: flex;
  gap: 10px;
  align-items: center;
  width: 100%;
  text-align: left;
  background: var(--bg-surface);
  color: var(--fg-primary);
  border: 1px solid var(--border-subtle);
  border-radius: 8px;
  padding: 8px;
}
.converted-picker-item:hover { border-color: var(--accent); }
.converted-picker-item img { width: 72px; height: 54px; object-fit: contain; background: #fff; border-radius: 4px; flex: none; }
.converted-picker-text { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; min-width: 0; font-size: 0.9rem; }
.converted-picker-value { flex-basis: 100%; overflow-wrap: anywhere; }
.converted-picker-preview { max-width: 100%; max-height: 200px; object-fit: contain; background: #fff; border-radius: 6px; align-self: flex-start; }
/* Unreviewed diagram results (migration 076): mandatory review before use. */
.convert-item-review { border-left-color: var(--caution); }
.convert-chip-review { background: var(--caution-dim); color: var(--caution); }
.convert-item-review-note { margin: 0; font-size: 0.85rem; color: var(--caution); }
.converted-picker-item:disabled { opacity: 0.6; cursor: not-allowed; }
.converted-picker-item:disabled:hover { border-color: var(--border-subtle); }
.convert-item-mark-reviewed {
  padding: 10px 18px;
  font-size: 0.9rem;
  font-weight: 700;
  border-radius: 8px;
  border: none;
  background: var(--accent);
  color: var(--accent-fg);
  cursor: pointer;
}

/* ===== Manual test result entry (public/manualResultEntry.js) =====
   A compact spreadsheet-style grid; it scrolls sideways inside its card
   on a narrow screen rather than squashing the inputs. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
.manual-grid-wrap { overflow-x: auto; margin: 12px 0; }
.manual-grid { border-collapse: collapse; width: 100%; font-size: 0.9rem; }
.manual-grid th {
  text-align: left;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--fg-muted);
  padding: 4px 6px;
  border-bottom: 1px solid var(--border-subtle);
  white-space: nowrap;
}
.manual-grid td { padding: 2px 3px; border-bottom: 1px solid var(--border-subtle); vertical-align: middle; }
.manual-grid input {
  width: 100%;
  box-sizing: border-box;
  padding: 5px 6px;
  font: inherit;
  background: var(--bg-inset);
  color: var(--fg-primary);
  border: 1px solid var(--border-subtle);
  border-radius: 4px;
}
.manual-grid input:focus { border-color: var(--accent); outline: none; }
.manual-grid tr.manual-row-error td { background: rgba(255, 122, 107, 0.06); }
.manual-grid .manual-derived { white-space: nowrap; }
.cell-error,
.manual-grid input.cell-error,
.field-grid input.cell-error {
  border-color: var(--danger);
  box-shadow: 0 0 0 1px var(--danger);
}
.manual-remove-btn {
  width: 28px;
  height: 28px;
  border-radius: 6px;
  border: 1px solid var(--border-subtle);
  background: var(--bg-surface);
  color: var(--fg-muted);
  font-size: 1rem;
  line-height: 1;
}
.manual-remove-btn:hover { color: var(--danger); border-color: var(--danger); }
.manual-computed { margin-top: 4px; padding: 8px 0; font-size: 1rem; color: var(--fg-primary); text-transform: none; letter-spacing: 0; }
.manual-note { border: 1px solid var(--caution); border-radius: 8px; padding: 8px 14px; margin-bottom: 12px; font-size: 0.9rem; color: var(--caution); }
.manual-note p { margin: 4px 0; }
.manual-note-error { border-color: var(--danger); color: var(--danger); }
.manual-save-bar { border-color: var(--border-strong); }
/* Summary panel controls and the entry screen's own buttons, matching the
   app's form inputs (.inline-form-column) instead of browser defaults. */
.manual-entry .field-grid input,
.manual-entry .field-grid select {
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: var(--bg-base);
  color: var(--fg-primary);
  font-family: var(--font-sans);
  font-size: 0.95rem;
  box-sizing: border-box;
}
.manual-entry .field-grid input[type="date"] { color-scheme: dark; }
#manual-save-btn,
#manual-confirm-btn,
#manual-test-continue {
  padding: 10px 18px;
  border-radius: 8px;
  border: none;
  background: var(--accent);
  color: var(--accent-fg);
  font-weight: 700;
}
/* The grid scrolls sideways inside its card; inline-size containment
   stops its width leaking up through the shell's flex layout and
   widening the whole page on a phone. */
.manual-entry { min-width: 0; }
/* position: relative keeps the absolutely positioned visually-hidden
   header label inside the scroll container; without it the label escapes
   the clipping and widens the page to the full grid width. */
.manual-grid-wrap { position: relative; max-width: 100%; contain: inline-size; }
.manual-entry select, #manual-test-select { max-width: 100%; }
#manual-section { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
#manual-section.hidden { display: none; }
#manual-section p { flex-basis: 100%; margin: 0; }

/* Settings -> Models by job, and the prompt editor's Model field (AI
   branding & model provider spec, Part B). */
table.storage-table.model-table td {
  white-space: normal;
  vertical-align: top;
}
.model-table a,
.model-links a {
  color: var(--accent);
}
select.model-select {
  padding: 6px 10px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: var(--bg-base);
  color: var(--fg-primary);
  font-family: var(--font-sans);
  font-size: 0.9rem;
  max-width: 100%;
}

/* ===== Messaging (public/messages.js) ===== */
.message-inbox { display: flex; flex-direction: column; gap: 8px; }
.message-inbox-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  background: var(--bg-surface);
  color: var(--fg-primary);
  text-decoration: none;
}
.message-inbox-row:hover { border-color: var(--border-strong); }
.message-inbox-name { font-weight: 600; flex: 1; }
.message-inbox-meta { color: var(--fg-muted); font-size: 0.85rem; }
.message-inbox-unread {
  min-width: 22px;
  padding: 2px 7px;
  border-radius: 11px;
  background: var(--accent);
  color: var(--accent-fg);
  font-size: 0.8rem;
  font-weight: 700;
  text-align: center;
}
.message-thread {
  display: flex;
  flex-direction: column;
  height: calc(100vh - 120px);
  min-height: 420px;
  max-width: 760px;
}
.message-thread-header h1 { margin: 4px 0 12px; }
.message-back-link { color: var(--fg-secondary); font-size: 0.9rem; }
.message-list {
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  background: var(--bg-inset);
}
.message { display: flex; flex-direction: column; max-width: 78%; }
.message.mine { align-self: flex-end; align-items: flex-end; }
.message.theirs { align-self: flex-start; align-items: flex-start; }
.message-bubble {
  padding: 9px 13px;
  border-radius: 14px;
  background: var(--bg-raised);
  color: var(--fg-primary);
  overflow-wrap: anywhere;
}
.message.mine .message-bubble { background: var(--accent-dim); }
.message-text { white-space: pre-wrap; }
.message-meta, .message-seen { color: var(--fg-muted); font-size: 0.75rem; margin-top: 3px; }
.message-seen { font-weight: 600; }
.message-image-btn { padding: 0; border: 0; background: none; cursor: zoom-in; display: block; }
.message-image { display: block; max-width: 260px; max-height: 260px; border-radius: 8px; }
.message-image-full { display: block; max-width: 100%; max-height: 80vh; margin: 0 auto; }
.message-voice { display: flex; align-items: center; gap: 8px; }
.message-voice audio { max-width: 240px; min-width: 0; height: 36px; }
.message-voice-duration { color: var(--fg-secondary); font-size: 0.85rem; font-variant-numeric: tabular-nums; white-space: nowrap; }
.message-bubble:has(.message-image-btn) { padding: 4px; }
.message-typing { color: var(--fg-secondary); font-size: 0.85rem; font-style: italic; padding: 6px 4px 0; }
.message-emoji-panel {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(40px, 1fr));
  gap: 4px;
  margin-top: 8px;
  padding: 8px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  background: var(--bg-surface);
}
.message-emoji { font-size: 1.3rem; padding: 4px; border: 0; background: none; cursor: pointer; }
.message-emoji:hover { background: var(--bg-raised); border-radius: 6px; }
.message-recording {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 8px;
  padding: 8px 12px;
  border-radius: var(--radius);
  background: var(--bg-surface);
  color: var(--fg-primary);
}
.message-recording-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--danger);
  animation: icon-btn-pulse 1.4s ease-in-out infinite;
}
.message-composer { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.message-composer textarea {
  width: 100%;
  resize: none;
  min-height: 44px;
  max-height: 160px;
  padding: 10px 12px;
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  background: var(--bg-surface);
  color: var(--fg-primary);
  font: inherit;
}
.message-composer-actions { display: flex; gap: 8px; justify-content: flex-end; }
.message-composer-actions .icon-btn { width: 42px; height: 42px; font-size: 1.05rem; }
@media (max-width: 600px) {
  .message { max-width: 90%; }
  .message-thread { height: calc(100vh - 90px); }
  .message-image { max-width: 200px; }
  .message-voice audio { max-width: 190px; }
}

/* Skill taxonomy: the chosen tag's description under each picker, and the
   label on Year 9 questions (spec section 2: beyond the selective exam). */
.tag-description {
  display: block;
  margin-top: 4px;
  font-weight: 400;
}
.stretch-label {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--warning);
  background: var(--warning-dim);
  border-radius: 4px;
  padding: 2px 6px;
  white-space: nowrap;
}

/* Admin Tags page (skill taxonomy) */
.tag-tree {
  list-style: none;
  margin: 6px 0;
  padding-left: 12px;
}
.tag-tree li {
  padding: 4px 0;
}
.tag-tree-retired {
  opacity: 0.6;
}
.tag-tree-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 10px;
}
.tag-tree-actions {
  margin-left: auto;
  white-space: nowrap;
}
.tag-tree-children {
  padding-left: 24px;
}
.tag-tree-category {
  margin-bottom: 10px;
}

/* ---------- Question editor (public/testAuthoring.js) ----------
   Question editor redesign spec: the test authoring screen only, full
   browser width, two panes that each scroll on their own so a save never
   moves the page. Every other admin/parent page keeps .app's 880px cap. */
.app.qe-app {
  max-width: none;
  margin: 0;
  padding: 0;
  min-height: 0;
}
.qe {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
}
.qe button {
  padding: 7px 12px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: var(--bg-raised);
  color: var(--fg-primary);
  font-size: 0.875rem;
  line-height: 1.3;
}
.qe button:hover:not(:disabled) {
  border-color: var(--fg-muted);
}
.qe button:focus-visible,
.qe select:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.qe button.qe-primary,
.qe .qe-form-actions button[type="submit"] {
  background: var(--accent);
  color: var(--accent-fg);
  border-color: var(--accent);
  font-weight: 700;
}
.qe button.qe-danger {
  color: var(--danger);
}
.qe button.link-btn {
  padding: 0;
  border: none;
  background: none;
  color: var(--accent);
}

/* Top bar */
.qe-topbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px 16px;
  padding: 10px 20px;
  border-bottom: 1px solid var(--border-subtle);
  background: var(--bg-surface);
}
.qe-topbar-main {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 14px;
  min-width: 0;
}
.qe-back {
  color: var(--fg-secondary);
  text-decoration: none;
  font-size: 0.875rem;
}
.qe-topbar h1 {
  margin: 0;
  font-size: 1.25rem;
}
.qe-topbar-summary {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 12px;
  color: var(--fg-secondary);
  font-size: 0.875rem;
}
.qe-topbar-summary > span + span::before {
  content: "·";
  margin-right: 12px;
  color: var(--fg-muted);
}
.qe-status.is-published {
  color: var(--positive);
  font-weight: 700;
}
.qe-status.is-draft {
  color: var(--caution);
  font-weight: 700;
}
.qe-topbar-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.qe-attention {
  position: relative;
}
.qe-chip {
  display: inline-block;
  padding: 5px 12px;
  border-radius: 999px;
  font-size: 0.8125rem;
  font-weight: 600;
}
.qe .qe-chip.is-attention {
  background: var(--caution-dim);
  border: 1px solid var(--caution);
  color: var(--caution);
  border-radius: 999px;
}
.qe-chip.is-complete {
  color: var(--positive);
  border: 1px solid var(--border-subtle);
}
.qe-attention-list {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  z-index: 30;
  width: min(440px, 90vw);
  max-height: 60vh;
  overflow-y: auto;
  padding: 8px 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--bg-raised);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}
.qe-attention-list ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.qe-attention-list li + li {
  border-top: 1px solid var(--border-subtle);
}
.qe .qe-attention-list .link-btn {
  display: block;
  width: 100%;
  padding: 8px 0;
  text-align: left;
  color: var(--fg-primary);
}
.qe .qe-attention-list .link-btn:hover {
  color: var(--accent);
}
.qe-readonly-note {
  margin: 8px 20px 0;
  padding: 8px 14px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
}
.qe-test-panel {
  padding: 14px 20px;
  border-bottom: 1px solid var(--border-subtle);
  background: var(--bg-surface);
}
.qe-test-form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 12px 20px;
}
.qe-test-shuffle {
  flex-basis: 100%;
  margin: 0;
  padding: 0;
  border: none;
}
.qe-check {
  display: flex;
  align-items: center;
  gap: 8px;
}
.qe > .error-banner {
  margin: 8px 20px 0;
}

/* Body: list | selected question */
.qe-body {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 280px minmax(0, 1fr);
}
.qe-list {
  overflow-y: auto;
  border-right: 1px solid var(--border-subtle);
  background: var(--bg-surface);
  padding: 10px 10px 40px;
}
.qe-list-tools {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 8px;
}
.qe .qe-list-tools button {
  padding: 5px 9px;
  font-size: 0.8125rem;
}
.qe-list-status p {
  margin: 4px 0 8px;
}
.qe-group {
  margin-bottom: 10px;
}
.qe-group-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 2px 4px;
  position: sticky;
  top: -10px;
  z-index: 2;
  background: var(--bg-surface);
}
.qe .qe-group-title {
  flex: 1;
  min-width: 0;
  padding: 0;
  border: none;
  background: none;
  text-align: left;
  font-weight: 700;
  font-size: 0.8125rem;
  color: var(--fg-secondary);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.qe-group-count {
  color: var(--fg-muted);
  font-size: 0.75rem;
}
.qe-split-chip {
  padding: 1px 6px;
  border-radius: 999px;
  border: 1px solid var(--warning);
  color: var(--warning);
  font-size: 0.6875rem;
}
.qe .qe-icon-btn {
  padding: 0 8px;
  min-height: 28px;
  line-height: 1;
  font-size: 1rem;
}
.qe-run {
  min-height: 6px;
}
.qe-run-empty {
  min-height: 34px;
  border: 1px dashed var(--border-strong);
  border-radius: 8px;
}
.qe-row {
  display: flex;
  align-items: center;
  gap: 2px;
  border-radius: 8px;
  border: 1px solid transparent;
}
.qe-row:hover {
  background: var(--bg-raised);
}
.qe-row.is-selected {
  background: var(--accent-dim);
  border-color: var(--accent);
}
.qe-row-ghost {
  opacity: 0.4;
}
.qe-drag {
  cursor: grab;
  padding: 8px 4px;
  color: var(--fg-muted);
  font-size: 0.75rem;
  letter-spacing: -2px;
  user-select: none;
}
.qe .qe-row-btn {
  flex: 1;
  min-width: 0;
  display: flex;
  gap: 8px;
  align-items: baseline;
  padding: 7px 4px;
  border: none;
  background: none;
  text-align: left;
}
.qe-num {
  min-width: 1.75em;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--fg-secondary);
}
.qe-stem {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.875rem;
}
.qe-markers {
  display: flex;
  gap: 4px;
  padding-right: 6px;
  font-size: 0.8125rem;
}
.qe-marker-warn {
  color: var(--caution);
}
.qe-marker-dirty {
  color: var(--accent);
}
.qe .qe-new-q {
  margin: 4px 0 0 30px;
  font-size: 0.8125rem;
}
.qe-list-empty {
  padding: 8px;
}

/* Section menu (appended to <body>, outside .qe) */
.qe-menu {
  position: fixed;
  z-index: 40;
  min-width: 220px;
  max-width: 320px;
  padding: 6px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--bg-raised);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}
.qe-menu-title {
  margin: 4px 8px 6px;
  font-weight: 700;
  font-size: 0.875rem;
}
.qe-menu-title .setup-note {
  display: block;
  font-weight: 400;
}
.qe-menu button {
  display: block;
  width: 100%;
  padding: 8px 10px;
  border: none;
  border-radius: 6px;
  background: none;
  color: var(--fg-primary);
  text-align: left;
  font: inherit;
  font-size: 0.875rem;
  cursor: pointer;
}
.qe-menu button:hover,
.qe-menu button:focus-visible {
  background: var(--bg-surface);
  outline: none;
}

/* Right pane */
.qe-main {
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.qe-jump-wrap {
  display: none;
}
.qe-pane {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}
.qe-pane-head {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
  padding: 10px 24px;
  border-bottom: 1px solid var(--border-subtle);
  background: var(--bg-base);
}
.qe-pane-title {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  flex: 1 1 240px;
}
.qe-pane-title h2 {
  margin: 0;
  font-size: 1.0625rem;
}
.qe-pane-section {
  margin-left: 8px;
  font-weight: 400;
  font-size: 0.875rem;
  color: var(--fg-secondary);
}
.qe .qe-list-toggle {
  display: none;
}
.qe-pane-nav,
.qe-pane-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.qe-save-status {
  font-size: 0.8125rem;
  color: var(--fg-secondary);
}
.qe-save-status.is-saved {
  color: var(--positive);
}
.qe-save-status.is-error {
  color: var(--danger);
}
.qe-pane-body {
  padding: 20px 24px 64px;
  max-width: 1100px;
}
.qe-empty {
  padding: 40px 0;
}
.qe-missing {
  margin-bottom: 18px;
  padding: 10px 14px;
  border: 1px solid var(--caution);
  border-radius: var(--radius);
  background: var(--caution-dim);
}
.qe-missing h3 {
  margin: 0 0 4px;
  font-size: 0.9375rem;
}
.qe-missing ul {
  margin: 0;
  padding-left: 18px;
}
.qe-suggestion {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin-bottom: 18px;
  padding: 10px 14px;
  border: 1px solid var(--warning);
  border-radius: var(--radius);
  background: var(--warning-dim);
}
.qe-suggestion p {
  margin: 0;
  flex: 1 1 300px;
}
.qe-context {
  margin-bottom: 18px;
  padding: 10px 14px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  background: var(--bg-surface);
}
.qe-context summary {
  cursor: pointer;
  font-weight: 700;
}
.qe-context .model-answer-body {
  max-height: 45vh;
  overflow-y: auto;
  margin-top: 8px;
}
.qe-stem-view {
  font-size: 1.0625rem;
  line-height: 1.55;
}
.qe-options-view {
  list-style: none;
  margin: 16px 0 20px;
  padding: 0;
  display: grid;
  gap: 8px;
}
.qe-options-view li {
  display: flex;
  gap: 12px;
  padding: 10px 14px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
}
.qe-options-view li.is-correct {
  border-color: var(--positive);
}
.qe-opt-label {
  font-weight: 700;
  color: var(--fg-secondary);
}
.qe-opt-body {
  min-width: 0;
  flex: 1;
}
.qe-opt-body > p:first-child {
  margin-top: 0;
}
.qe-correct-chip {
  display: inline-block;
  margin-top: 4px;
  color: var(--positive);
  font-weight: 700;
  font-size: 0.8125rem;
}
.qe-misconception {
  margin: 4px 0 0;
  color: var(--fg-secondary);
  font-size: 0.875rem;
}
.qe-misconception span {
  color: var(--fg-muted);
}
.qe-view-heading {
  margin: 0 0 6px;
  font-size: 0.9375rem;
}
.qe-explanation {
  line-height: 1.55;
}
.qe-meta {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 10px 20px;
  margin: 20px 0;
  padding: 12px 14px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
}
.qe-meta dt {
  font-size: 0.75rem;
  color: var(--fg-muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.qe-meta dd {
  margin: 2px 0 0;
}
.qe-view-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 10px;
  margin-bottom: 20px;
}
.qe-inline-label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.875rem;
  color: var(--fg-secondary);
}
.qe-form-actions {
  display: flex;
  gap: 8px;
  margin-top: 8px;
}

/* Narrow screens: the list collapses into a dropdown above the question;
   "☰ List" opens it in full for sections and new questions. Drag-and-drop
   is off here — "Move to position…" does the job. */
@media (max-width: 899px) {
  .qe {
    height: auto;
  }
  .qe-topbar,
  .qe-pane-head,
  .qe-pane-body,
  .qe-test-panel {
    padding-left: 16px;
    padding-right: 16px;
  }
  .qe-body {
    display: block;
  }
  .qe-list {
    display: none;
    border-right: none;
    border-bottom: 1px solid var(--border-subtle);
    max-height: 70vh;
  }
  .qe.is-list-open .qe-list {
    display: block;
  }
  .qe-drag {
    display: none;
  }
  .qe-jump-wrap {
    display: block;
    padding: 10px 16px 0;
  }
  .qe-jump-wrap select {
    width: 100%;
    max-width: 100%;
  }
  .qe .qe-list-toggle {
    display: inline-block;
  }
  .qe-pane {
    overflow: visible;
  }
  .qe-pane-head {
    position: static;
  }
}
.qe-opt-body p,
.qe-explanation p:last-child,
.qe-stem-view p:last-child {
  margin-bottom: 0;
}
.qe-opt-body p {
  margin-top: 0;
}
.qe-flat .qe-group-head {
  display: none;
}
/* Keep "Edit test" and the status chip at the top right, on the name's
   line; only narrower screens let the actions wrap underneath. */
@media (min-width: 1100px) {
  .qe-topbar {
    flex-wrap: nowrap;
  }
  .qe-topbar-main {
    flex: 1 1 auto;
  }
  .qe-topbar-actions {
    flex: none;
  }
}
.qe-jump-wrap select {
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: var(--bg-raised);
  color: var(--fg-primary);
  font-size: 0.9375rem;
}
.qe-attention-list-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 2px 0 6px;
  border-bottom: 1px solid var(--border-subtle);
}
.qe .qe-attention-list .qe-attention-list-head .link-btn {
  display: inline;
  width: auto;
  padding: 0;
  color: var(--accent);
}
.qe-menu .qe-menu-close {
  margin-top: 4px;
  border-top: 1px solid var(--border-subtle);
  border-radius: 0 0 6px 6px;
  color: var(--fg-secondary);
}

/* One-question-at-a-time review (content protection spec, Phase 3; public/reviewPager.js) */
.review-pager-nav {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 12px 0;
}
.review-pager-where {
  flex: 1;
  text-align: center;
  color: var(--text-muted, inherit);
}
.test-session-reconnecting {
  margin: 8px 0;
  border-left: 4px solid var(--warning, #d4a017);
}

/* Content protection spec, Phase 4: watermark on the mock-test review and
   the test preview (public/watermark.js). Above the content but faint,
   and never in the way of a click, a selection or a screen reader. */
.has-watermark {
  position: relative;
}
.content-watermark {
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  user-select: none;
  opacity: 0.09;
  background-repeat: repeat;
}
@media print {
  .content-watermark {
    opacity: 0.12;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
}

/* Content protection spec, Phase 4: admin Access alerts and Settings'
   Content limits card. */
.access-alerts-table td {
  vertical-align: top;
}
.access-level {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.85em;
  font-weight: 600;
}
.access-level-over {
  background: var(--danger);
  color: var(--danger-fg);
}
.access-level-near {
  background: var(--warning-dim);
  color: var(--warning);
}
.content-limit-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 110px;
  gap: 4px 12px;
  align-items: center;
  margin-bottom: 12px;
}
.content-limit-row small {
  display: block;
}
.content-limit-row > small {
  grid-column: 2;
}

/* ===== Question bank coverage (admin) =====
   Plain HTML/CSS charts: a bar per tree/skill row (width set inline, as a
   share of the largest row) and a one-colour heatmap on the grid. --heat
   is set inline per cell (18–100%); cells past ~55% switch to dark text so
   the number stays readable on the light end of the colour. A zero cell is
   unshaded with a dashed outline, so gaps read as gaps. */
.cov-filters { margin-bottom: 12px; }
.cov-totals {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 8px;
  margin: 8px 0;
}
.cov-total {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 12px;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
}
.cov-total-value { font-size: 1.4rem; font-weight: 700; }
.cov-total-value .cov-count { font-size: inherit; font-weight: inherit; color: var(--fg-primary); }
.cov-total-label { font-size: 0.8rem; color: var(--fg-secondary); }
.cov-columns {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 24px;
  align-items: start;
}
.cov-table { width: 100%; border-collapse: collapse; }
.cov-table th, .cov-table td { padding: 4px 8px; border-bottom: 1px solid var(--border-subtle); text-align: left; font-weight: normal; }
.cov-table thead th { font-size: 0.8rem; color: var(--fg-secondary); }
.cov-table .cov-num { text-align: right; width: 5rem; font-variant-numeric: tabular-nums; }
.cov-table .cov-bar-col { width: 40%; }
.cov-strand th, .cov-strand td { font-weight: 700; background: var(--bg-raised); }
.cov-subtopic th { padding-left: 24px; }
.cov-table tr.is-gap th { color: var(--fg-muted); }
.cov-count { font-size: 0.95rem; color: var(--fg-primary); }
.cov-count.is-zero { color: var(--fg-muted); }
.cov-bar-track {
  display: block;
  height: 10px;
  background: var(--bg-inset);
  border-radius: 5px;
  overflow: hidden;
}
.cov-bar { display: block; height: 100%; background: var(--accent); border-radius: 5px; }
.cov-grid-wrap { overflow: auto; max-width: 100%; border: 1px solid var(--border-subtle); border-radius: var(--radius); }
.cov-grid { border-collapse: separate; border-spacing: 2px; }
.cov-grid thead th { vertical-align: bottom; padding: 4px 2px; font-weight: normal; font-size: 0.78rem; color: var(--fg-secondary); }
.cov-grid thead th span { writing-mode: vertical-rl; transform: rotate(180deg); display: inline-block; max-height: 11rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cov-grid tbody th { text-align: left; font-weight: normal; padding: 2px 8px; white-space: nowrap; position: sticky; left: 0; background: var(--bg-base); z-index: 1; }
.cov-grid-corner { position: sticky; left: 0; background: var(--bg-base); z-index: 2; text-align: left; }
.cov-grid tbody .cov-grid-strand th { font-weight: 700; padding-top: 10px; color: var(--fg-primary); }
.cov-cell {
  min-width: 2.4rem;
  height: 1.9rem;
  text-align: center;
  border-radius: 4px;
  background: color-mix(in srgb, var(--opponent) var(--heat, 0%), var(--bg-surface));
}
.cov-cell .cov-count { text-decoration: none; font-variant-numeric: tabular-nums; }
.cov-cell.is-strong .cov-count { color: var(--opponent-fg); font-weight: 700; }
.cov-cell.is-zero { background: transparent; outline: 1px dashed var(--border-strong); outline-offset: -2px; }
.cov-retired { margin-top: 16px; }
.cov-drill-list { list-style: none; padding: 0; max-height: 60vh; overflow: auto; }
.cov-drill-list li { padding: 8px 0; border-bottom: 1px solid var(--border-subtle); }
