/*
 * CT Quest styles, built on the Slate theme contract (slate/docs/adr/0002).
 *
 * Every colour and font below is a contract name. The values are Sidecar's
 * own cool palette (docs/adr/0002, amended 2026-11-05), not Slate's house
 * palette: cards are white on a pale blue page, so a card is the lighter
 * thing on the page. Retinting the whole app is an edit to this one :root
 * block and nothing else. Light only:
 * there is no dark palette, no toggle and no prefers-color-scheme rule.
 *
 * The four --status-* tones are the contract's optional extension for "the
 * state of a thing": correct/incorrect answers, late attempts, answers
 * waiting to be marked, released results. Destructive controls (reset an
 * attempt, log out) are app-local and draw on --status-critical, because the
 * contract has no destructive colour (slate/docs/adr/0021).
 *
 * The component classes (.card, .btn, .field, .tag, .section-heading,
 * .error-text, .progress, .concept-tags) are plain-CSS ports of the Slate
 * registry components of the same names. See
 * docs/adr/0002-adopt-slate-visual-conventions.md.
 */

:root {
  color-scheme: light;

  /* Neutrals (7) */
  --color-ink: #101B33;
  --color-ink-soft: #34405C;
  --color-muted: #525E7A;
  --color-surface: #FFFFFF;
  --color-surface-2: #F6F8FD;
  --color-line: #DCE3F1;
  --color-line-strong: #8D9AB8;

  /* Background (1) and accent trio (3) */
  --color-bg: #EEF3FB;
  --color-accent: #1F55D6;
  --color-accent-deep: #163FA6;
  --color-accent-soft: #DDE7FB;

  /* Fonts (3): Slate's system stacks, no web font to download. Headings use
     --font-sans; --font-serif stays in the contract for prose that wants it. */
  --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
    Helvetica, Arial, sans-serif;
  --font-serif: "Iowan Old Style", "Palatino Linotype", Palatino,
    "Book Antiqua", Georgia, "Liberation Serif", "DejaVu Serif", serif;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Code", Consolas, monospace;

  /* Status tones (optional extension, ADR 0002 amendment). Each clears
     4.5:1 on both --color-bg and --color-surface, so they are safe for
     small text. */
  --status-positive: #14684A;
  --status-neutral: #4B5670;
  --status-warning: #855200;
  --status-critical: #B3261E;

  /* Sizes (ADR 0002, amended 2026-11-06). Not colours or fonts, so the
     theme contract is unchanged. The student page reads at 17px with a 14px
     floor; body.teacher below puts the teacher page back to 15px. */

  /* Type: seven sizes. rem on a 16px root. */
  --fs-sm: 0.875rem;    /* 14px  meta lines, tags, captions. Nothing smaller. */
  --fs-body: 1.0625rem; /* 17px  body, row titles, buttons, inputs, options */
  --fs-code: 0.9375rem; /* 15px  monospace, code blocks and inline code */
  --fs-h4: 1.125rem;    /* 18px  subheads inside a reading (Markdown ##) */
  --fs-h3: 1.25rem;     /* 20px  card titles, day and step titles, the question prompt */
  --fs-h2: 1.5rem;      /* 24px  the screen title */
  --fs-hero: 1.875rem;  /* 30px  one element per screen; 26px under 600px */
  --lh-sm: 1.45;
  --lh-body: 1.55;

  /* Space: 4-based, seven steps. */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;
  --sp-7: 48px;

  /* Radius: three values. */
  --r-card: 12px;
  --r-ctl: 8px;
  --r-pill: 999px;

  /* Controls and containers. */
  --ctl-h: 44px;           /* buttons and inputs: a finger-sized target */
  --ctl-h-sm: 36px;        /* the compact button, where one is unavoidable */
  --ctl-pad-x: var(--sp-5);
  --card-pad: var(--sp-5);
  --page-pad: var(--sp-4);
}

@media (max-width: 599.98px) {
  :root {
    --fs-hero: 1.625rem;
    --card-pad: var(--sp-4);
    --page-pad: var(--sp-3);
  }
}

/* The teacher page is a dense tool, not a reading page: 15px body, 13px
   meta and 36px controls. It shares the spacing and radius tokens. */
body.teacher {
  --fs-sm: 0.8125rem;
  --fs-body: 0.9375rem;
  --fs-code: 0.875rem;
  --fs-h4: 1rem;
  --fs-h3: 1.05rem;
  --fs-h2: 1.3rem;
  --fs-hero: 1.5rem;
  --lh-body: 1.5;
  --ctl-h: 2.25rem;
  --ctl-h-sm: 1.875rem;
  --ctl-pad-x: var(--sp-3);
  --card-pad: var(--sp-4);
  --page-pad: var(--sp-4);
}

@media (max-width: 640px) {
  body.teacher {
    --card-pad: var(--sp-3);
    --page-pad: var(--sp-3);
  }
}

/* ---------- Base ---------- */

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  min-height: 100svh;
  background: var(--color-bg);
  color: var(--color-ink-soft);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Weights: 400 for text, 600 for titles, labels and buttons, 700 for the
   screen title (h2) and the hero. */
h1,
h2,
h3,
h4 {
  margin: 0;
  font-family: var(--font-sans);
  color: var(--color-ink);
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.01em;
}

h2 {
  font-size: var(--fs-h2);
  font-weight: 700;
  line-height: 1.25;
}

h3 {
  font-size: var(--fs-h3);
}

h4 {
  font-size: var(--fs-h4);
}

/* The one most important line on a screen, such as a result headline. At
   most one per screen. */
.hero-text {
  margin: 0;
  color: var(--color-ink);
  font-size: var(--fs-hero);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.015em;
}

p {
  margin: 0;
}

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

code,
pre,
kbd {
  font-family: var(--font-mono);
}

code,
kbd {
  font-size: var(--fs-code);
}

strong {
  color: var(--color-ink);
  font-weight: 600;
}

button:not(:disabled) {
  cursor: pointer;
}

:focus-visible {
  outline: 2px solid var(--color-accent-deep);
  outline-offset: 2px;
}

::placeholder {
  color: var(--color-muted);
  opacity: 1;
}

.hidden {
  display: none !important;
}

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

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

/* ---------- Icons (the sprite in index.html, SidecarUI.icon) ---------- */

/* Stroke icons from one sprite, coloured by the text around them, sized to
   it. An icon always sits beside words and is hidden from screen readers. */
.icon {
  display: inline-block;
  width: 1.25em;
  height: 1.25em;
  flex: none;
  vertical-align: -0.25em;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.icon--sm {
  width: 1em;
  height: 1em;
  vertical-align: -0.125em;
}

.icon--lg {
  width: 24px;
  height: 24px;
}

.icon.icon--positive {
  color: var(--status-positive);
}

.icon.icon--critical {
  color: var(--status-critical);
}

.icon.icon--warning {
  color: var(--status-warning);
}

.icon.icon--neutral {
  color: var(--status-neutral);
}

.icon.icon--accent {
  color: var(--color-accent-deep);
}

.icon.icon--muted {
  color: var(--color-muted);
}

/* ---------- Page shell (PageShell) ---------- */

.bar {
  border-bottom: 1px solid var(--color-line);
  background: var(--color-surface-2);
}

.bar__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  max-width: 1120px;
  min-height: 56px;
  margin: 0 auto;
  padding: 0 var(--page-pad);
}

body.teacher .bar__inner {
  max-width: 1200px;
  min-height: 0;
  padding: var(--sp-2) var(--page-pad);
}

.bar__title {
  min-width: 0;
  font-size: var(--fs-h4);
  font-weight: 700;
}

.bar__context {
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  font-weight: 400;
  color: var(--color-muted);
  letter-spacing: 0;
}

.bar__link {
  font-size: var(--fs-sm);
  color: var(--color-accent-deep);
  text-underline-offset: 3px;
}

/* The student's identity, right of the title once they are logged in:
   their codename and Log out (app.js showIdentity). */
.bar__who {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  min-width: 0;
  color: var(--color-ink-soft);
  font-size: var(--fs-sm);
}

.bar__name {
  overflow: hidden;
  color: var(--color-ink);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A header text button with a 44px hit area. */
.bar__btn {
  min-height: var(--ctl-h);
  padding: 0 var(--sp-2);
  border: 0;
  border-radius: var(--r-ctl);
  background: none;
  color: var(--color-accent-deep);
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.bar__btn:hover {
  background: var(--color-accent-soft);
}

.bar__who[hidden],
.bar__link[hidden] {
  display: none;
}

/* During a check the only way off the page is on purpose. */
body.in-attempt .bar__link {
  display: none;
}

.wrap {
  max-width: 1120px;
  margin: 0 auto;
  padding: var(--sp-5) var(--page-pad) var(--sp-7);
}

body.teacher .wrap {
  padding: var(--page-pad) var(--page-pad) var(--sp-6);
}

.wrap--wide {
  max-width: 1200px;
}

/* A grid's children default to min-width: auto, so one wide line (a code
   block) would widen the column past the card and get clipped. min-width: 0
   keeps them to the column, and the wide thing scrolls in its own box. */
.stack {
  display: grid;
  gap: var(--sp-4);
}

.stack > * {
  min-width: 0;
}

.stack--tight {
  gap: var(--sp-2);
}

.row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
}

.row--between {
  justify-content: space-between;
}

.muted {
  color: var(--color-muted);
}

/* Meta lines only: timestamps, counts, captions. Not for text a student
   has to read to act. */
.small {
  font-size: var(--fs-sm);
  line-height: var(--lh-sm);
}

.mono {
  font-family: var(--font-mono);
}

.mt-s {
  margin-top: var(--sp-2);
}

.mt-m {
  margin-top: var(--sp-3);
}

.stack > .section-heading {
  margin-bottom: 0;
}

/* ---------- Card ---------- */

/* Two levels of container: the page, then the card. Inside a card only code
   blocks, reading panels and answer boxes have a fill, and nothing but form
   controls has a border; rows are separated by dividers (.rows). */
.card {
  overflow: hidden;
  padding: var(--card-pad);
  border: 1px solid var(--color-line);
  border-radius: var(--r-card);
  background: var(--color-surface);
  box-shadow: 0 1px 2px color-mix(in srgb, var(--color-ink) 8%, transparent);
}

.card--flush {
  padding: 0;
}

.card__pad {
  padding: var(--sp-3) var(--sp-3) 0;
}

p.card__pad {
  padding-bottom: var(--sp-3);
}

.card--raised {
  background: var(--color-surface-2);
}

.card--accent {
  background: var(--color-accent-soft);
}

/* ---------- SectionHeading (no eyebrow) ---------- */

.section-heading {
  display: grid;
  gap: var(--sp-1);
  margin-bottom: var(--sp-3);
}

.section-heading p {
  color: var(--color-ink-soft);
  max-width: 65ch;
}

/* An in-card group label: small, bold, sentence case. */
.group-label {
  margin: var(--sp-5) 0 var(--sp-2);
  color: var(--color-ink-soft);
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: 0;
}

.group-label:first-child {
  margin-top: 0;
}

/* ---------- Button ---------- */

/* One filled button per screen, .btn--accent, for the thing to do next.
   Everything else is .btn--secondary (filled with the surface, outlined) or
   .btn--ghost. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--ctl-h);
  padding: var(--sp-2) var(--ctl-pad-x);
  border: 1px solid transparent;
  border-radius: var(--r-ctl);
  font: inherit;
  font-size: var(--fs-body);
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  user-select: none;
  transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}

.btn:not(:disabled):active {
  transform: translateY(1px);
}

.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* The registry's accent button fills with --color-accent, which is under
   4.5:1 against light text at this size (ADR 0002's contrast table), so this
   app fills with the deep shade instead. On the student page .btn--primary
   is the same button, so "go forward" has one colour; the teacher page keeps
   its ink primary. */
.btn--accent,
.btn--primary {
  background: var(--color-accent-deep);
  border-color: var(--color-accent-deep);
  color: var(--color-bg);
}

.btn--accent:not(:disabled):hover,
.btn--primary:not(:disabled):hover {
  background: color-mix(in srgb, var(--color-accent-deep) 80%, var(--color-ink));
  border-color: color-mix(in srgb, var(--color-accent-deep) 80%, var(--color-ink));
}

body.teacher .btn--primary {
  background: var(--color-ink);
  border-color: var(--color-ink);
}

body.teacher .btn--primary:not(:disabled):hover {
  background: var(--color-ink-soft);
  border-color: var(--color-ink-soft);
}

.btn--secondary {
  background: var(--color-surface);
  border-color: var(--color-line-strong);
  color: var(--color-ink);
}

.btn--secondary:not(:disabled):hover {
  background: var(--color-surface-2);
  border-color: var(--color-ink-soft);
}

.btn--ghost {
  background: transparent;
  color: var(--color-ink-soft);
}

.btn--ghost:not(:disabled):hover {
  background: var(--color-surface-2);
}

/* App-local destructive control (ADR 0021): outline only, so a row of them
   does not shout, in the critical tone. */
.btn--destructive {
  background: transparent;
  border-color: color-mix(in srgb, var(--status-critical) 45%, transparent);
  color: var(--status-critical);
}

.btn--destructive:not(:disabled):hover {
  background: color-mix(in srgb, var(--status-critical) 8%, transparent);
  border-color: var(--status-critical);
}

/* Compact: 36px at 15px on the student page, where a full button does not
   fit. The teacher page keeps 30px at 13px. */
.btn--sm {
  min-height: var(--ctl-h-sm);
  padding: var(--sp-1) var(--sp-3);
  font-size: var(--fs-code);
}

body.teacher .btn--sm {
  font-size: var(--fs-sm);
}

.btn--block {
  width: 100%;
}

/* The one button that moves a screen forward is never a sliver. */
.btn--wide {
  min-width: 160px;
}

/* ---------- Field ---------- */

.field {
  display: block;
  min-width: 0;
}

.field__label,
.field > label,
.legend {
  display: block;
  margin-bottom: var(--sp-1);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--color-ink);
}

.field__hint {
  font-size: var(--fs-sm);
  font-weight: 400;
  color: var(--color-muted);
}

.input,
input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="search"],
input[type="datetime-local"],
select,
textarea {
  width: 100%;
  min-height: var(--ctl-h);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--color-line-strong);
  border-radius: var(--r-ctl);
  background: var(--color-surface);
  color: var(--color-ink);
  font: inherit;
  font-size: var(--fs-body);
}

textarea {
  line-height: var(--lh-body);
  resize: vertical;
}

input:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px var(--color-accent-soft);
}

input[type="radio"],
input[type="checkbox"] {
  width: 1.125rem;
  height: 1.125rem;
  min-height: 0;
  margin: 0;
  flex: none;
  accent-color: var(--color-accent-deep);
}

body.teacher input[type="radio"],
body.teacher input[type="checkbox"] {
  width: 1rem;
  height: 1rem;
}

input[type="radio"]:focus-visible,
input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--color-accent-deep);
  outline-offset: 2px;
  box-shadow: none;
}

.check {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--color-ink);
  cursor: pointer;
}

.check--disabled {
  color: var(--color-muted);
  cursor: default;
}

fieldset {
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

legend {
  padding: 0;
}

/* ---------- ErrorText and notices ---------- */

.error-text {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--status-critical);
}

/* A message about the page: a tinted panel with no border, in the tone's
   colour mixed into the surface. The words carry the meaning. */
.notice {
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-ctl);
  background: var(--color-surface-2);
  color: var(--color-ink);
  font-size: var(--fs-body);
}

body.teacher .notice {
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-sm);
}

.notice--warning {
  background: color-mix(in srgb, var(--status-warning) 10%, var(--color-surface));
}

.notice--critical {
  background: color-mix(in srgb, var(--status-critical) 8%, var(--color-surface));
  color: var(--status-critical);
}

.notice--positive {
  background: color-mix(in srgb, var(--status-positive) 10%, var(--color-surface));
}

/* ---------- Tag, status and ConceptTags ---------- */

.tag {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: 2px var(--sp-2);
  border: 1px solid var(--color-line-strong);
  border-radius: var(--r-pill);
  background: var(--color-surface-2);
  color: var(--color-ink-soft);
  font-size: var(--fs-sm);
  font-weight: 600;
  line-height: var(--lh-sm);
}

.tag--accent {
  border-color: color-mix(in srgb, var(--color-accent) 30%, transparent);
  background: var(--color-accent-soft);
  color: var(--color-accent-deep);
}

.tag--code {
  white-space: nowrap;
  font-family: var(--font-mono);
  letter-spacing: 0.04em;
}

/* A status is a tag with a dot. A hollow dot means "not known yet"
   (--status-neutral, ADR 0002): an answer still being marked. */
.status::before {
  content: "";
  width: 7px;
  height: 7px;
  border: 1.5px solid currentColor;
  border-radius: 50%;
  background: currentColor;
}

.status--positive {
  color: var(--status-positive);
  border-color: color-mix(in srgb, var(--status-positive) 40%, transparent);
}

.status--neutral,
.status--pending {
  color: var(--status-neutral);
}

.status--pending::before {
  background: transparent;
}

.status--warning {
  color: var(--status-warning);
  border-color: color-mix(in srgb, var(--status-warning) 40%, transparent);
}

.status--critical {
  color: var(--status-critical);
  border-color: color-mix(in srgb, var(--status-critical) 40%, transparent);
}

.tone-positive {
  color: var(--status-positive);
}

.tone-critical {
  color: var(--status-critical);
}

.tone-warning {
  color: var(--status-warning);
}

.tone-neutral {
  color: var(--status-neutral);
}

.concept-tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-1);
}

.concept-tag {
  display: inline-block;
  padding: 0 var(--sp-2);
  border: 1px solid var(--color-line-strong);
  border-radius: var(--r-ctl);
  color: var(--color-ink-soft);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.08em;
  line-height: 1.6;
  text-transform: uppercase;
}

/* The teacher page keeps its small uppercase level and topic labels. */
body.teacher .concept-tag {
  font-size: 0.6875rem;
}

/* ---------- Shared patterns (web/ui.js) ---------- */

/* Rows: a list inside a card where each row is one whole-row button (or
   link), separated by dividers, with no box of its own. Leading icon, a
   17px/600 title, a 14px meta line, a trailing chevron; 56px minimum. */
.rows {
  margin: 0;
  padding: 0;
  list-style: none;
}

.rows__item + .rows__item {
  border-top: 1px solid var(--color-line);
}

.rows__btn,
.rows__line {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  min-height: 56px;
  margin: 0;
  padding: var(--sp-3) var(--sp-2);
  border: 0;
  border-radius: var(--r-ctl);
  background: none;
  color: var(--color-ink);
  font: inherit;
  text-align: left;
  text-decoration: none;
}

.rows__btn:not(:disabled):hover {
  background: var(--color-surface-2);
}

.rows__btn:focus-visible {
  outline-offset: -2px;
}

.rows__btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.rows__icon {
  width: 24px;
  height: 24px;
  color: var(--color-ink-soft);
}

.rows__text {
  display: grid;
  flex: 1 1 auto;
  gap: 2px;
  min-width: 0;
}

.rows__title {
  color: var(--color-ink);
  font-size: var(--fs-body);
  font-weight: 600;
  line-height: 1.3;
}

.rows__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-1) var(--sp-3);
  color: var(--color-muted);
  font-size: var(--fs-sm);
  line-height: var(--lh-sm);
}

.rows__end {
  flex: none;
  color: var(--color-ink-soft);
  font-size: var(--fs-sm);
  font-weight: 600;
}

.rows__chevron {
  color: var(--color-muted);
}

.rows__btn:not(:disabled):hover .rows__chevron {
  color: var(--color-accent-deep);
}

/* Pips: step dots for up to 8 steps, with "1 of 3" beside them. Done is
   filled, to do is a ring, current is a ring in the accent. Over 8 steps,
   a bar the width of its container. */
.pips {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--color-ink-soft);
  font-size: var(--fs-sm);
}

.pips__dots {
  display: inline-flex;
  gap: var(--sp-1);
}

.pips__dot {
  width: 8px;
  height: 8px;
  border: 1.5px solid var(--color-line-strong);
  border-radius: 50%;
}

.pips__dot--done {
  border-color: var(--color-accent-deep);
  background: var(--color-accent-deep);
}

.pips__dot--current {
  border-color: var(--color-accent-deep);
  box-shadow: 0 0 0 2px var(--color-accent-soft);
}

.pips--bar {
  display: flex;
  width: 100%;
}

.pips__bar {
  flex: 1 1 auto;
  height: 8px;
  border: 0;
  border-radius: var(--r-pill);
  overflow: hidden;
  background: var(--color-line);
  appearance: none;
  -webkit-appearance: none;
}

.pips__bar::-webkit-progress-bar {
  background: var(--color-line);
}

.pips__bar::-webkit-progress-value {
  border-radius: var(--r-pill);
  background: var(--color-accent-deep);
}

.pips__bar::-moz-progress-bar {
  border-radius: var(--r-pill);
  background: var(--color-accent-deep);
}

.pips__text {
  flex: none;
  font-variant-numeric: tabular-nums;
}

/* The hero card: the one thing to do next, at the hero size, with one
   accent button. The only accent-soft card on its screen. */
.hero {
  background: var(--color-accent-soft);
  border-color: color-mix(in srgb, var(--color-accent) 25%, var(--color-line));
}

.hero__title {
  color: var(--color-ink);
  font-size: var(--fs-hero);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.015em;
}

.hero__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1) var(--sp-4);
  margin-top: var(--sp-2);
  color: var(--color-ink-soft);
  font-size: var(--fs-sm);
}

.hero__meta-item {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
}

.hero > p:not(.hero__meta) {
  margin-top: var(--sp-3);
  max-width: 66ch;
  color: var(--color-ink);
}

.hero__action {
  margin-top: var(--sp-5);
}

@media (max-width: 599.98px) {
  .hero__action .btn {
    width: 100%;
  }
}

/* ---------- Student: join ---------- */

.join {
  max-width: 400px;
  margin: var(--sp-2) auto 0;
}

.join__rules {
  margin: var(--sp-3) 0 0;
  padding-left: var(--sp-4);
  color: var(--color-ink-soft);
  font-size: var(--fs-sm);
}

.join__rules li + li {
  margin-top: 2px;
}

/* ---------- Student: progress strip (ProgressNav, laid flat) ---------- */

.q-strip {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-4);
  /* Out to the edges of .wrap, whose top padding is --sp-5. */
  margin: calc(-1 * var(--sp-5)) calc(-1 * var(--page-pad)) var(--sp-4);
  padding: var(--sp-2) var(--page-pad);
  border-bottom: 1px solid var(--color-line);
  background: color-mix(in srgb, var(--color-bg) 94%, transparent);
  backdrop-filter: blur(6px);
}

.q-strip__count {
  font-weight: 600;
  color: var(--color-ink);
}

.q-strip__event {
  margin-left: auto;
  color: var(--color-muted);
  font-size: var(--fs-sm);
}

/* The right end of the strip: the timer pill and Review and submit. */
.q-strip__end {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-4);
  margin-left: auto;
}

.q-strip__review {
  min-height: var(--ctl-h);
  font-size: var(--fs-sm);
  font-weight: 600;
}

.progress {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

.progress__dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: 1.5px solid var(--color-ink-soft);
  border-radius: 50%;
  color: var(--color-muted);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
}

.progress__dot--done {
  border-color: var(--color-accent-deep);
  background: var(--color-accent-deep);
  color: var(--color-bg);
}

.progress__dot--current {
  box-shadow: 0 0 0 2px var(--color-bg), 0 0 0 4px var(--color-accent);
}

/* Skipped: a dashed ring, so it reads differently from answered without
   relying on colour. */
.progress__dot--skipped {
  border-style: dashed;
  border-color: var(--color-ink-soft);
}

/* Locked (committed, can't be changed): square corners. */
.progress__dot--locked {
  border-radius: 4px;
}

/* In order: not reached yet. */
.progress__dot--ahead {
  border-color: var(--color-line-strong);
  color: var(--color-line-strong);
}

/* Free navigation: each 36px dot sits in a 44px button that jumps to its
   question. The dot keeps its size; the button is the hit area. */
.progress--jump {
  gap: 0;
  min-width: 0;
  max-width: 100%;
}

.progress__jump {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: var(--r-ctl);
  background: none;
  cursor: pointer;
}

.progress__jump:hover .progress__dot:not(.progress__dot--done) {
  border-color: var(--color-accent-deep);
  color: var(--color-ink);
}

.progress__jump:focus-visible {
  outline-offset: -2px;
}

/* The timer: a pill with a clock icon. Under 2 minutes it takes the
   warning tone (app.js updateTimerPill). */
.timer {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  min-height: var(--ctl-h-sm);
  padding: 0 var(--sp-3);
  border: 1px solid var(--color-line);
  border-radius: var(--r-pill);
  background: var(--color-surface);
  color: var(--color-ink);
  font-size: var(--fs-sm);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.timer--low {
  border-color: var(--status-warning);
  background: color-mix(in srgb, var(--status-warning) 12%, var(--color-surface));
  color: var(--status-warning);
}

/* ---------- Student: question ---------- */

.q-layout {
  display: grid;
  gap: var(--sp-3);
  align-items: start;
}

@media (min-width: 900px) {
  .q-layout {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

.q-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--sp-3);
}

.q-points {
  flex: none;
  font-size: var(--fs-sm);
  color: var(--color-muted);
}

.prompt-text {
  margin-top: var(--sp-3);
  color: var(--color-ink);
  font-size: var(--fs-body);
  white-space: pre-wrap;
}

/* The student page: the question's title is a small line, and the prompt
   under it is the heading, at 20px. */
.q-head--student {
  align-items: baseline;
}

.q-title {
  color: var(--color-ink-soft);
  font-size: var(--fs-sm);
  font-weight: 600;
}

.prompt-text--lead {
  margin-top: var(--sp-2);
  font-size: var(--fs-h3);
  font-weight: 400;
  line-height: 1.45;
  letter-spacing: 0;
}

.code-label {
  margin: var(--sp-3) 0 var(--sp-1);
  color: var(--color-ink-soft);
  font-size: var(--fs-sm);
  font-weight: 600;
}

pre {
  margin: var(--sp-3) 0 0;
  padding: var(--sp-3) var(--sp-4);
  overflow: auto;
  border: 0;
  border-radius: var(--r-ctl);
  background: var(--color-surface-2);
  color: var(--color-ink);
  font-size: var(--fs-code);
  line-height: 1.5;
}

pre code {
  font-size: inherit;
}

.code-label + pre {
  margin-top: 0;
}

.answer-label {
  display: block;
  margin-bottom: var(--sp-2);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--color-ink);
}

/* An answer label that is the instruction itself ("Type exactly what it
   prints"), at body size. */
.answer-label--lead {
  font-size: var(--fs-body);
}

.options {
  display: grid;
  gap: var(--sp-2);
}

.opt {
  display: flex;
  gap: var(--sp-3);
  align-items: flex-start;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--color-line-strong);
  border-radius: var(--r-ctl);
  background: var(--color-bg);
  color: var(--color-ink);
  cursor: pointer;
}

.opt:hover {
  background: var(--color-surface-2);
}

.opt:has(input:checked) {
  border-color: var(--color-accent-deep);
  background: var(--color-accent-soft);
  box-shadow: inset 0 0 0 1px var(--color-accent-deep);
}

.opt input {
  margin-top: var(--sp-1);
}

.opt__text {
  line-height: 1.45;
  /* An option can be multi-line printed output (RGS-S1-04). */
  white-space: pre-line;
}

.q-nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-4);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--color-line);
}

.q-nav .row {
  margin-left: auto;
}

.q-status {
  margin-top: var(--sp-3);
}

.q-hint {
  margin-top: var(--sp-3);
  color: var(--color-ink-soft);
  font-size: var(--fs-sm);
}

.answer-locked {
  opacity: 0.85;
}

.answer-locked .opt,
.answer-locked .pa-handle {
  cursor: default;
}

/* A committed answer cannot be changed, so the panels lose their emphasis. */
.answer-locked .pa-workspace {
  border-color: var(--color-line-strong);
}

/* While a commit is in flight the answer cannot be edited (the area is
   inert); dimmed so that shows. */
.answer-area--busy {
  opacity: 0.6;
}

.q-change {
  margin-top: var(--sp-3);
}

.q-feedback {
  margin-top: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--color-line);
  border-radius: var(--r-ctl);
  background: var(--color-surface-2);
}

.q-feedback__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: 2px;
  color: var(--color-ink);
  font-weight: 600;
}

/* ---------- Student: check start screen ---------- */

.check-start {
  max-width: 640px;
  margin: 0 auto;
}

.check-start__points {
  display: grid;
  gap: var(--sp-2);
  margin: var(--sp-5) 0 0;
  padding-left: var(--sp-5);
  color: var(--color-ink);
}

.check-start__clock {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
  color: var(--color-ink-soft);
  font-size: var(--fs-sm);
}

.check-start__home {
  margin-top: var(--sp-4);
}

.check-start__home .link-btn {
  min-height: var(--ctl-h);
}

/* ---------- Student: review ---------- */

.review .rows {
  margin-top: var(--sp-4);
}

/* Submit beside the summary at the top of a long review. */
.review__top {
  margin-top: var(--sp-3);
}

.review .notice {
  margin-top: var(--sp-3);
}

.review-state {
  color: var(--color-muted);
}

.review-state--warning {
  color: var(--status-warning);
  font-weight: 600;
}

/* ---------- Student: results ---------- */

/* A row of ticks and crosses, one per question: the check at a glance. */
.tally {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1);
  margin-top: var(--sp-3);
}

.results__head .notice {
  margin-top: var(--sp-3);
}

.results__leave {
  justify-content: flex-start;
}

.breakdown {
  margin: 0;
  padding: 0;
  list-style: none;
}

.result-row {
  display: flex;
  gap: var(--sp-3);
  padding: var(--sp-4) 0;
  border-bottom: 1px solid var(--color-line);
}

.result-row:last-child {
  border-bottom: 0;
}

.results__answers .breakdown {
  margin-top: var(--sp-2);
}

/* One answer: the mark, the title, then "Your answer" and "Correct answer"
   as labelled boxes, side by side from 700px. A wrong answer's box is tinted
   neutral, like its cross; the correct answer's box is tinted positive. */
.answer__mark {
  display: grid;
  flex: none;
  justify-items: center;
  align-content: start;
  gap: 2px;
  width: 24px;
}

.answer__status {
  color: var(--color-ink-soft);
  font-size: var(--fs-sm);
  font-weight: 600;
}

.answer__body {
  flex: 1 1 auto;
  min-width: 0;
}

.answer__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-1) var(--sp-3);
}

.answer__points {
  margin-left: auto;
}

.answer__title {
  color: var(--color-ink);
  font-size: var(--fs-body);
  font-weight: 600;
}

.answer__points {
  color: var(--color-muted);
  font-size: var(--fs-sm);
}

.answer__boxes {
  display: grid;
  gap: var(--sp-2);
  margin-top: var(--sp-2);
}

@media (min-width: 700px) {
  .answer__boxes--two {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

.answer__box {
  min-width: 0;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-ctl);
  background: var(--color-surface-2);
}

.answer__box--wrong {
  background: color-mix(in srgb, var(--status-neutral) 8%, var(--color-surface));
}

.answer__box--right {
  background: color-mix(in srgb, var(--status-positive) 8%, var(--color-surface));
}

.answer__label {
  color: var(--color-ink-soft);
  font-size: var(--fs-sm);
  font-weight: 600;
}

.answer__text {
  margin-top: 2px;
  color: var(--color-ink);
  font-family: var(--font-mono);
  font-size: var(--fs-code);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.answer__code {
  margin: 2px 0 0;
  padding: 0;
  background: none;
  white-space: pre-wrap;
}

.answer__why,
.answer__note {
  margin-top: var(--sp-2);
  max-width: 66ch;
  color: var(--color-ink);
}

.answer__note {
  color: var(--color-ink-soft);
}

.backup summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--color-ink);
}

.codebox {
  max-height: 5.5rem;
  margin-top: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  overflow: auto;
  border: 1px solid var(--color-line);
  border-radius: var(--r-ctl);
  background: var(--color-bg);
  color: var(--color-ink-soft);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  word-break: break-all;
}

/* ---------- Question types (web/types/) ---------- */

/* Code trace: the program with line numbers, and the output box. */
.ct-code code {
  display: block;
}

.ct-line {
  display: block;
  white-space: pre;
}

.ct-line__no {
  display: inline-block;
  width: 2em;
  margin-right: var(--sp-3);
  text-align: right;
  color: var(--color-muted);
  user-select: none;
  -webkit-user-select: none;
}

/* On a phone a check's or a Try it's code wraps rather than scrolling out of
   sight. A numbered line is a row: the number, then the text, which wraps
   under itself, so the numbers still mark where each real line starts. */
@media (max-width: 599.98px) {
  .q-card pre,
  .q-bare pre {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
  }

  .q-card .ct-line,
  .q-bare .ct-line {
    display: flex;
    white-space: normal;
  }

  /* The narrowest gutter that still reads: two digits and one space. */
  .q-card .ct-line__no,
  .q-bare .ct-line__no {
    flex: none;
    width: 2ch;
    margin-right: 1ch;
  }

  .q-card .ct-line__text,
  .q-bare .ct-line__text {
    flex: 1 1 auto;
    min-width: 0;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    /* Hanging indent: a wrapped part sits two characters right of where its
       line's own text starts (--ind spaces in), so it reads as a continuation. */
    padding-left: calc((var(--ind, 0) + 2) * 1ch);
    text-indent: calc((var(--ind, 0) + 2) * -1ch);
  }

}

/* A code block that still scrolls sideways (a reading step's, an answer's)
   fades at its right edge while there is more to see. after-render.js sets
   data-overflow after each draw and clears it once the block is scrolled to
   its end. */
pre[data-overflow="right"] {
  -webkit-mask-image: linear-gradient(to right, var(--color-ink) calc(100% - var(--sp-6)), transparent);
  mask-image: linear-gradient(to right, var(--color-ink) calc(100% - var(--sp-6)), transparent);
}

.ct-output,
textarea.open-response {
  min-height: 8rem;
}

.ct-output {
  font-family: var(--font-mono);
  font-size: var(--fs-code);
}

.open-response__hint {
  margin-top: var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--color-muted);
}

/* Between "Type exactly what it prints" and the box. */
.ct-hint {
  margin: 0 0 var(--sp-2);
  color: var(--color-ink-soft);
  font-size: var(--fs-sm);
}

/* Python (python-run): the setup shown read-only, the editor, the Run bar,
   and each check's result with the printed output under it. */
.py-setup {
  margin-bottom: var(--sp-3);
  color: var(--color-ink-soft);
}

.py-code {
  min-height: 14rem;
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  white-space: pre;
  overflow-x: auto;
  tab-size: 4;
}

.py-hint {
  margin-top: var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--color-muted);
}

.py-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
  margin-top: var(--sp-3);
}

.py-status {
  font-size: var(--fs-sm);
  color: var(--color-muted);
}

.py-output:not(:empty) {
  margin-top: var(--sp-3);
}

.py-stale,
.py-summary,
.py-error {
  margin-bottom: var(--sp-2);
}

.py-error {
  white-space: pre-wrap;
}

.py-checks {
  display: grid;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.py-check {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-1) var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--color-line);
  border-left-width: 3px;
  border-radius: 0 var(--r-ctl) var(--r-ctl) 0;
  background: var(--color-surface-2);
}

.py-check--pass {
  border-left-color: var(--status-positive);
}

.py-check--fail {
  border-left-color: var(--status-critical);
}

.py-check__name {
  color: var(--color-ink);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
}

.py-check__why {
  flex-basis: 100%;
  color: var(--color-ink-soft);
  font-size: var(--fs-sm);
}

.py-stdout {
  max-height: 14rem;
  white-space: pre-wrap;
  font-size: var(--fs-sm);
}

.py-switched,
.py-draft {
  margin-bottom: var(--sp-3);
}

.py-draft pre {
  max-height: 12rem;
  font-size: var(--fs-sm);
}

/* Parsons: the program being built and the pool of unused lines. */
.pa-help {
  margin: var(--sp-3) 0 var(--sp-1);
  font-size: var(--fs-sm);
  color: var(--color-ink-soft);
}

.pa-instruction {
  margin: var(--sp-1) 0 var(--sp-3);
  font-size: var(--fs-sm);
  color: var(--color-ink-soft);
}

/* The workspace is the main area: heavier border, tall, and a big dashed
   drop zone while it is empty. */
.pa-workspace {
  padding: var(--sp-3) var(--sp-3) var(--sp-3);
  border: 2px solid var(--color-accent);
  border-radius: var(--r-ctl);
  background: var(--color-surface-2);
  min-height: 12rem;
  transition: box-shadow 120ms ease, background-color 120ms ease;
}

.pa-panel-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2);
  margin-bottom: var(--sp-2);
}

.pa-panel-title {
  margin: 0;
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--color-ink);
}

.pa-count {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--color-accent-deep);
}

.pa-dropzone {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 8rem;
  margin: 0;
  padding: var(--sp-4);
  border: 2px dashed var(--color-accent);
  border-radius: var(--r-ctl);
  background: color-mix(in srgb, var(--color-accent-soft) 45%, transparent);
  color: var(--color-accent-deep);
  font-size: var(--fs-body);
  text-align: center;
}

/* The tray is recessed: blocks sit in it waiting to be used. */
.pa-tray {
  margin-top: var(--sp-4);
  padding: var(--sp-3) var(--sp-3) var(--sp-3);
  border: 1px solid var(--color-line-strong);
  border-radius: var(--r-ctl);
  background: var(--color-surface);
  box-shadow: inset 0 2px 5px color-mix(in srgb, var(--color-ink) 16%, transparent);
}

.pa-tray .pa-panel-title {
  margin-bottom: var(--sp-2);
}

.pa-drop-active {
  box-shadow: 0 0 0 4px var(--color-accent-soft), 0 0 0 6px var(--color-accent-deep);
}

.pa-tray.pa-drop-active {
  box-shadow: inset 0 2px 5px color-mix(in srgb, var(--color-ink) 16%, transparent), 0 0 0 4px var(--color-accent-soft), 0 0 0 6px var(--color-accent-deep);
}

.pa-list {
  min-height: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

.pa-line {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: var(--sp-2) 0;
  padding: var(--sp-1) var(--sp-2);
  border: 1px solid var(--color-line-strong);
  border-radius: var(--r-ctl);
  background: var(--color-bg);
  box-shadow: 0 1px 2px color-mix(in srgb, var(--color-ink) 18%, transparent);
}

.pa-line--dragging {
  outline: 2px solid var(--color-accent-deep);
  opacity: 0.9;
}

.pa--dragging,
.pa--dragging * {
  user-select: none;
  -webkit-user-select: none;
  cursor: grabbing;
}

/* touch-action: none only on the handle, so the rest of the page still
   scrolls with a finger. */
.pa-handle {
  flex: none;
  padding: var(--sp-2) 2px;
  color: var(--color-muted);
  letter-spacing: -3px;
  cursor: grab;
  touch-action: none;
}

.pa-text {
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  white-space: pre;
  color: var(--color-ink);
  font-size: var(--fs-sm);
}

.pa-controls {
  display: flex;
  flex: none;
  gap: var(--sp-1);
}

.pa-btn {
  min-width: 2.25rem;
  min-height: 2.25rem;
  padding: 2px var(--sp-2);
}

/* Add is the one used most, so it is bold; the Next button stays the one
   accent button on the screen. */
.pa-btn.pa-add {
  padding: 2px var(--sp-4);
  font-weight: 700;
}

/* Fingers need bigger targets than a mouse does. */
@media (pointer: coarse) {
  .pa-btn {
    min-width: 2.75rem;
    min-height: 2.75rem;
  }
}

.pa-program .pa-add,
.pa-pool .pa-up,
.pa-pool .pa-down,
.pa-pool .pa-remove {
  display: none;
}

.pa-empty {
  margin-top: var(--sp-1);
  color: var(--color-muted);
  font-size: var(--fs-sm);
}

.pa-empty[hidden] {
  display: none;
}

.pa-status {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* Code reading: dotted glossary words in the code, the note they open,
   and the two parts of the answer. The code block itself is the code-trace
   one (.ct-code, .ct-line). */
.cr-term {
  margin: 0;
  padding: 0;
  border: 0;
  border-bottom: 1px dotted var(--color-accent-deep);
  border-radius: 0;
  background: none;
  color: inherit;
  font: inherit;
  line-height: inherit;
  cursor: help;
}

.cr-term:hover,
.cr-term[aria-expanded="true"] {
  background: var(--color-accent-soft);
}

.cr-hint {
  margin-top: var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--color-muted);
}

.cr-note {
  margin: var(--sp-2) 0 0;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--color-line-strong);
  border-left: 3px solid var(--color-accent-deep);
  border-radius: var(--r-ctl);
  background: var(--color-surface-2);
  color: var(--color-ink);
  font-size: var(--fs-sm);
}

.cr-part + .cr-part {
  margin-top: var(--sp-4);
}

.cr-part__points {
  margin-left: var(--sp-1);
  color: var(--color-muted);
  font-size: var(--fs-sm);
  font-weight: 400;
}

.cr-line-pick {
  display: flex;
  flex-wrap: wrap;
  gap: 2px var(--sp-2);
  align-items: baseline;
}

.cr-line-pick__no {
  flex: none;
  color: var(--color-muted);
  font-size: var(--fs-sm);
}

.cr-line-pick code {
  font-size: var(--fs-sm);
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}

/* ---------- Student: question types at reading size ----------
   On the student page the answer options, hints and controls of every
   type are at body size with 44px targets (52px for an option). The
   teacher page keeps the denser rules above. */

body:not(.teacher) .opt {
  align-items: center;
  min-height: 52px;
  padding: var(--sp-3) var(--sp-4);
  background: var(--color-surface);
  font-size: var(--fs-body);
}

body:not(.teacher) .opt input {
  flex: none;
  width: 20px;
  height: 20px;
  margin: 0;
}

body:not(.teacher) .ct-hint,
body:not(.teacher) .open-response__hint,
body:not(.teacher) .py-hint,
body:not(.teacher) .cr-hint,
body:not(.teacher) .pa-help,
body:not(.teacher) .pa-instruction,
body:not(.teacher) .bk-help,
body:not(.teacher) .bk-goal {
  color: var(--color-ink-soft);
  font-size: var(--fs-body);
}

body:not(.teacher) .ct-hint {
  margin-top: var(--sp-3);
}

body:not(.teacher) .py-code,
body:not(.teacher) .pa-text,
body:not(.teacher) .cr-line-pick code,
body:not(.teacher) .py-check__name,
body:not(.teacher) .py-stdout {
  font-size: var(--fs-code);
}

body:not(.teacher) .cr-note,
body:not(.teacher) .py-check__why,
body:not(.teacher) .py-status {
  font-size: var(--fs-body);
}

body:not(.teacher) .pa-btn,
body:not(.teacher) .pa-handle {
  min-width: var(--ctl-h);
  min-height: var(--ctl-h);
}

/* On a phone, Add is a "+" alone (its label still says Add to screen
   readers), so the line of code keeps the width. */
@media (max-width: 599.98px) {
  body:not(.teacher) .pa-add__word {
    display: none;
  }

  body:not(.teacher) .pa-btn.pa-add {
    padding: 0;
  }

  body:not(.teacher) .pa-handle {
    min-width: var(--sp-6);
  }
}

body:not(.teacher) .pa-handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

body:not(.teacher) .bk-view summary {
  min-height: var(--ctl-h-sm);
}

/* ---------- Teacher: dashboard ---------- */

.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
  font-size: 0.875rem;
}

.dash {
  display: grid;
  gap: var(--sp-3);
  align-items: start;
}

.dash__side,
.dash__main {
  display: grid;
  gap: var(--sp-3);
  min-width: 0;
}

/* The sidebar holds only the event list; the main area shows the chosen
   event or the new-event form. */
.side-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
}

.side-head h2 {
  font-size: 1.125rem;
}

.dash__back {
  justify-self: start;
}

/* A sidebar link to a page that is not an event, such as the question bank. */
.side-link {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2);
  width: 100%;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--color-line);
  border-left: 3px solid transparent;
  border-radius: var(--r-ctl);
  background: var(--color-surface);
  color: var(--color-ink);
  font: inherit;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.side-link:hover {
  background: var(--color-surface-2);
}

.side-link--active {
  border-left-color: var(--color-accent-deep);
  background: var(--color-accent-soft);
}

.bank__filters {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: var(--sp-2) var(--sp-2);
  margin-bottom: var(--sp-2);
}

.bank__count {
  margin-bottom: var(--sp-1);
}

.form-actions--start {
  justify-content: flex-start;
}

/* On a phone or narrow window the list comes first. Opening an event or the
   form replaces it, and "All events" brings it back. */
@media (max-width: 999.98px) {
  .dash--open .dash__side,
  .dash:not(.dash--open) .dash__main {
    display: none;
  }
}

@media (min-width: 1000px) {
  .dash {
    grid-template-columns: 20rem minmax(0, 1fr);
  }

  .dash__side {
    position: sticky;
    top: 12px;
    max-height: calc(100vh - 24px);
    overflow-y: auto;
  }

  .dash__back {
    display: none;
  }

  /* The form has the main area's width: four short fields to a row, the two
     settings side by side, and the quick setup cards in two columns. */
  .dash__main .form-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  .dash__main .form-grid .setting {
    grid-column: span 2;
  }

  .dash__main .quick__list {
    grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
    align-items: start;
  }
}

.form-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--sp-2);
}

.form-grid .field--full {
  grid-column: 1 / -1;
}

.form-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--sp-3);
}

/* Event list */
.event-list {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--color-line);
}

.event-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 2px var(--sp-2);
  width: 100%;
  padding: var(--sp-2) var(--sp-2);
  border: 0;
  border-bottom: 1px solid var(--color-line);
  border-left: 3px solid transparent;
  background: transparent;
  color: var(--color-ink);
  font: inherit;
  text-align: left;
}

.event-card:hover {
  background: var(--color-surface-2);
}

.event-card--active {
  border-left-color: var(--color-accent-deep);
  background: var(--color-accent-soft);
}

.event-card--active:hover {
  background: var(--color-accent-soft);
}

.event-card__title {
  font-weight: 600;
  overflow-wrap: anywhere;
}

.event-card__meta {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 2px var(--sp-2);
  color: var(--color-muted);
  font-size: 0.8125rem;
}

.event-card__fact {
  white-space: nowrap;
}

.event-card__title .tag {
  margin-left: var(--sp-1);
  vertical-align: 1px;
}

.event-card__owner {
  color: var(--color-ink-soft);
  font-weight: 600;
  overflow-wrap: anywhere;
}

/* An admin's event filter (ADR 0004): All teachers / Mine, then a teacher. */
.event-filter {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-1);
  padding-bottom: var(--sp-2);
}

.segmented {
  display: inline-flex;
  flex: none;
  border: 1px solid var(--color-line-strong);
  border-radius: var(--r-ctl);
  overflow: hidden;
}

.segmented__btn {
  min-height: 1.875rem;
  padding: var(--sp-1) var(--sp-2);
  border: 0;
  background: var(--color-bg);
  color: var(--color-ink-soft);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 500;
}

.segmented__btn + .segmented__btn {
  border-left: 1px solid var(--color-line-strong);
}

.segmented__btn:hover {
  background: var(--color-surface-2);
}

.segmented__btn[aria-pressed="true"] {
  background: var(--color-accent-soft);
  color: var(--color-accent-deep);
  font-weight: 600;
}

.segmented__btn:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

.event-filter select {
  flex: 1 1 9rem;
  width: auto;
  min-width: 0;
  min-height: 1.875rem;
  padding: var(--sp-1) var(--sp-2);
  font-size: 0.8125rem;
}

/* Results */
.results-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  margin-bottom: var(--sp-2);
}

.attempts {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--color-line);
}

.attempt {
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--color-line);
}

.attempt__row {
  display: grid;
  grid-template-columns: minmax(8rem, 1.4fr) minmax(0, 2fr) auto auto;
  align-items: center;
  gap: var(--sp-1) var(--sp-3);
}

.attempt__who {
  min-width: 0;
  overflow-wrap: anywhere;
}

.attempt__score {
  font-family: var(--font-mono);
  font-weight: 600;
  color: var(--color-ink);
  text-align: right;
}

.attempt__actions {
  display: flex;
  gap: var(--sp-1);
  justify-content: flex-end;
}

@media (max-width: 640px) {
  .attempt__row {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .attempt__row .row {
    grid-column: 1 / -1;
    grid-row: 2;
  }

  .attempt__row .attempt__actions {
    grid-column: 1 / -1;
    grid-row: 3;
    justify-self: end;
  }
}

/* Event facts, settings edit and history */
.results-facts {
  margin: 0 0 var(--sp-2);
  overflow-wrap: anywhere;
}

/* An admin reading another teacher's event (ADR 0004). */
.read-only {
  margin: 0 0 var(--sp-2);
  overflow-wrap: anywhere;
}

.edit-settings {
  margin: 0 0 var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--color-line-strong);
  border-radius: var(--r-ctl);
  background: var(--color-surface-2);
}

.edit-settings .notice {
  margin: var(--sp-2) 0 var(--sp-2);
  background: var(--color-bg);
}

.edit-settings .form-actions {
  gap: var(--sp-2);
}

.history {
  margin: 0 0 var(--sp-2);
}

.history summary {
  cursor: pointer;
  font-weight: 600;
  font-size: 0.875rem;
  color: var(--color-ink);
}

.history__list {
  margin: var(--sp-1) 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--color-line);
}

.history__item {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px var(--sp-2);
  padding: var(--sp-1) 0;
  border-bottom: 1px solid var(--color-line);
  font-size: 0.875rem;
  overflow-wrap: anywhere;
}

/* AI answer review */
.ai-review {
  margin: var(--sp-2) 0 0 var(--sp-3);
  padding: var(--sp-2) var(--sp-2);
  border: 1px solid var(--color-line);
  border-left: 3px solid var(--color-line-strong);
  border-radius: 0 var(--r-ctl) var(--r-ctl) 0;
  background: var(--color-surface-2);
}

.ai-review--attention {
  border-left-color: var(--status-warning);
}

.ai-review__answer {
  margin: var(--sp-1) 0;
  color: var(--color-ink);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.ai-review__form {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: end;
  gap: var(--sp-2);
  margin-top: var(--sp-2);
}

.ai-review__score {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
}

.ai-review__score input {
  width: 4.5rem;
}

.ai-review textarea {
  min-height: 2.25rem;
  height: 2.25rem;
}

@media (max-width: 640px) {
  .ai-review {
    margin-left: 0;
  }

  .ai-review__form {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .ai-review__form .field--feedback {
    grid-column: 1 / -1;
    grid-row: 1;
  }

  .ai-review textarea {
    height: 4.5rem;
  }
}

/* Per-outcome summary */
.table-wrap {
  overflow-x: auto;
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}

.data-table caption {
  padding: var(--sp-3) 0 var(--sp-1);
  text-align: left;
  font-family: var(--font-sans);
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-ink);
}

.data-table th,
.data-table td {
  padding: var(--sp-1) var(--sp-2);
  border-bottom: 1px solid var(--color-line);
  text-align: right;
  vertical-align: top;
}

.data-table th:first-child,
.data-table td:first-child {
  padding-left: 0;
  text-align: left;
}

.data-table thead th {
  border-bottom-color: var(--color-line-strong);
  color: var(--color-muted);
  font-size: 0.8125rem;
  font-weight: 600;
  white-space: nowrap;
}

.data-table td {
  font-variant-numeric: tabular-nums;
}

.data-table td:first-child {
  color: var(--color-ink);
}

.data-table .indent {
  padding-left: var(--sp-4);
}

.sort-btn {
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-decoration: underline dotted;
  text-underline-offset: 3px;
}

.sort-btn:hover {
  color: var(--color-ink);
}

/* ---------- Teacher: quick setup cards and event settings ---------- */

.quick {
  display: grid;
  gap: var(--sp-2);
  /* A fieldset is as wide as its min-content by default, so the question
     preview's code lines would push it past the column. */
  min-width: 0;
}

.quick > * {
  min-width: 0;
}

.quick__list {
  display: grid;
  gap: var(--sp-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

.preset {
  border: 1px solid var(--color-line);
  border-left: 3px solid transparent;
  border-radius: var(--r-ctl);
  background: var(--color-bg);
}

.preset--chosen {
  border-color: var(--color-line-strong);
  border-left-color: var(--color-accent-deep);
  background: var(--color-accent-soft);
}

.preset__pick {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 2px var(--sp-2);
  align-items: start;
  padding: var(--sp-1) var(--sp-2);
  cursor: pointer;
}

.preset__pick input {
  margin: var(--sp-1) 0 0;
  accent-color: var(--color-accent-deep);
}

.preset__text {
  display: grid;
  min-width: 0;
}

.preset__label {
  font-weight: 600;
  color: var(--color-ink);
  overflow-wrap: anywhere;
}

.preset__desc {
  font-size: 0.8125rem;
  color: var(--color-muted);
}

.preset__count {
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
  color: var(--color-ink-soft);
  white-space: nowrap;
}

.quick__knobs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
  gap: var(--sp-2);
  padding: 0 var(--sp-2) var(--sp-2) var(--sp-6);
}

.quick__who {
  grid-column: 1 / -1;
}

.setting {
  display: grid;
  align-content: start;
  gap: var(--sp-1);
}

.setting__options {
  display: grid;
  gap: var(--sp-1);
}

.setting__options .check input {
  accent-color: var(--color-accent-deep);
}

/* ---------- Teacher: advanced picker ---------- */

#advancedPicker {
  grid-column: 1 / -1;
  border: 1px solid var(--color-line-strong);
  border-radius: var(--r-ctl);
  background: var(--color-surface-2);
}

#advancedPicker > summary {
  padding: var(--sp-2) var(--sp-2);
  cursor: pointer;
  font-weight: 600;
  color: var(--color-ink);
}

.picker {
  display: grid;
  gap: var(--sp-3);
  padding: 0 var(--sp-2) var(--sp-2);
}

@media (min-width: 1000px) {
  .picker {
    grid-template-columns: minmax(0, 1fr) 22rem;
    align-items: start;
  }

  .picker__preview {
    position: sticky;
    top: 12px;
  }
}

.picker__filters {
  display: grid;
  gap: var(--sp-3);
}

.picker__row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-5);
}

.picker__hint {
  margin-top: var(--sp-1);
  font-size: 0.8125rem;
  color: var(--color-muted);
}

.picker__difficulty {
  display: flex;
  gap: var(--sp-2);
}

.picker__difficulty input {
  width: 5rem;
}

.picker__preview {
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--color-line);
  border-radius: var(--r-ctl);
  background: var(--color-bg);
  font-size: 0.875rem;
}

.preview-facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2px var(--sp-2);
  margin: var(--sp-2) 0 0;
  font-size: 0.8125rem;
}

.preview-facts dt {
  color: var(--color-muted);
}

.preview-facts dd {
  margin: 0;
  color: var(--color-ink);
}

.preview-list {
  max-height: 24rem;
  margin: var(--sp-2) 0 0;
  padding: 0 0 0 var(--sp-4);
  overflow: auto;
  font-size: 0.8125rem;
  color: var(--color-ink);
}

.preview-list li {
  padding: 1px 0;
}

.outcome-groups {
  display: grid;
  gap: var(--sp-2);
}

@media (min-width: 700px) {
  .outcome-groups {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.outcome-group {
  padding: var(--sp-1) var(--sp-2);
  border: 1px solid var(--color-line);
  border-radius: var(--r-ctl);
  background: var(--color-bg);
}

.outcome-group legend {
  padding: 0 var(--sp-1);
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--color-ink);
}

.outcome-option {
  display: flex;
  gap: var(--sp-2);
  align-items: flex-start;
  padding: var(--sp-1) 0;
  color: var(--color-ink);
  font-size: 0.8125rem;
  cursor: pointer;
}

.outcome-option input {
  margin-top: 2px;
}

.tree {
  padding: var(--sp-1) var(--sp-2);
  border: 1px solid var(--color-line);
  border-radius: var(--r-ctl);
  background: var(--color-bg);
}

.tree-branch > summary {
  display: flex;
  align-items: center;
  list-style: none;
  cursor: pointer;
}

.tree-branch > summary::-webkit-details-marker {
  display: none;
}

.tree-branch > summary::before {
  content: "\25b8";
  flex: none;
  width: 1em;
  color: var(--color-muted);
}

.tree-branch[open] > summary::before {
  content: "\25be";
}

.tree-children,
.tree-leaf {
  margin-left: 1.2em;
}

.tree-row {
  display: flex;
  gap: var(--sp-2);
  align-items: center;
  padding: 2px 0;
  color: var(--color-ink);
  font-size: 0.8125rem;
  cursor: pointer;
}

.tree-count {
  color: var(--color-muted);
  font-size: 0.75rem;
}

/* ---------- Teacher: compact question preview ---------- */

.qp__controls {
  display: flex;
  gap: var(--sp-2);
  margin: var(--sp-2) 0;
}

.qp-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* A column flex item's default min-width is content-based, not 0, so a wide
   descendant (the rubric table, say) would otherwise stretch the whole list
   to fit it instead of wrapping or scrolling inside its own block. */
.qp-list > li {
  min-width: 0;
}

.qp-row {
  min-width: 0;
  border: 1px solid var(--color-line);
  border-radius: var(--r-ctl);
  background: var(--color-surface-2);
}

.qp-row > summary {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-1) var(--sp-2);
  padding: var(--sp-1) var(--sp-2);
  cursor: pointer;
  list-style: none;
}

.qp-row > summary::-webkit-details-marker {
  display: none;
}

.qp-row__num {
  flex: none;
  color: var(--color-muted);
  font-family: var(--font-mono);
  font-size: 0.75rem;
}

.qp-row__title {
  flex: 1 1 160px;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-weight: 600;
  color: var(--color-ink);
}

.qp-row__body {
  padding: var(--sp-1) var(--sp-2) var(--sp-3);
  border-top: 1px solid var(--color-line);
}

.qp-row__body .prompt-text {
  margin-top: var(--sp-2);
}

.qp-row__body .pa-line {
  justify-content: space-between;
}

.qp-row__body .pa-list {
  padding: 2px 0;
}

.qp-teacher {
  margin-top: var(--sp-3);
  padding: var(--sp-2) var(--sp-2);
  border: 1px dashed var(--color-line-strong);
  border-radius: var(--r-ctl);
  background: var(--color-bg);
}

.qp-teacher__label {
  margin: 0 0 var(--sp-1);
}

/* Same look as a real MCQ option, plus room to wrap onto a second line for
   the "Correct" tag on narrow screens, which the interactive .opt never
   needs. */
.qp-opt {
  flex-wrap: wrap;
  cursor: default;
}

.qp-opt .opt__text {
  flex: 1 1 0;
  min-width: 0;
}

/* The preview shows a question whole: no height cap on its code, grids and
   programs, unlike the short code boxes elsewhere on the page. */
.qp-row__body .codebox {
  max-height: none;
}

.qp-teacher p + p {
  margin-top: var(--sp-1);
}

.qp-rubric td:nth-child(2) {
  text-align: left;
}

.qp-glossary {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 2px var(--sp-3);
  margin: var(--sp-1) 0 0;
  font-size: 0.8125rem;
}

.qp-glossary dd {
  margin: 0;
}

/* Block grids side by side where they fit, each scrolling on its own if a
   grid is wider than the screen. */
.qp-stages {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
}

.qp-stage {
  min-width: 0;
  max-width: 100%;
}

.qp-grid {
  width: max-content;
  max-width: 100%;
  overflow-x: auto;
  line-height: 1.2;
  letter-spacing: 0.2em;
}

/* ---------- Phone ---------- */

@media (max-width: 640px) {

  .q-strip__event {
    display: none;
  }

  /* A long row of question buttons scrolls sideways rather than taking
     several lines of the sticky strip. */
  .progress--jump {
    flex: 1 1 auto;
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: thin;
  }

  .progress--jump li {
    flex: none;
  }

  .form-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ---------- Question bank review workbench ---------- */

/* The index of every question beside the review pane; stacked on a narrow
   screen. The list scrolls on its own so the pane stays in reach. */
.qr {
  display: grid;
  gap: var(--sp-3);
  align-items: start;
}

.qr-index,
.qr-pane {
  min-width: 0;
}

/* Stacked, the list is capped so the pane is not a long scroll away. */
.qr-index {
  max-height: 18rem;
  overflow-y: auto;
}

.qr-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

.qr-row {
  display: grid;
  gap: var(--sp-1);
  width: 100%;
  padding: var(--sp-1) var(--sp-2);
  border: 1px solid var(--color-line);
  border-radius: var(--r-ctl);
  background: var(--color-surface-2);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.qr-row:hover {
  border-color: var(--color-line-strong);
}

.qr-row[aria-current="true"] {
  border-color: var(--color-accent-deep);
  background: var(--color-accent-soft);
  box-shadow: inset 3px 0 0 var(--color-accent-deep);
}

/* A retired question stays in the list, dimmed; its badge says why. */
.qr-row--retired .qr-row__title {
  color: var(--color-muted);
  text-decoration: line-through;
}

.qr-row__title {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-weight: 600;
  color: var(--color-ink);
}

.qr-row__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-1) var(--sp-2);
}

.qr-pane {
  display: grid;
  gap: var(--sp-3);
}

.qr-student {
  display: grid;
  gap: var(--sp-3);
}

.qr-practice {
  margin-bottom: var(--sp-2);
}

.qr-key__body {
  margin-top: var(--sp-2);
  border: 1px solid var(--color-line);
  border-radius: var(--r-ctl);
  background: var(--color-surface-2);
}

.qr-key__body[hidden] {
  display: none;
}

.qr-empty {
  padding: var(--sp-5) var(--sp-2);
  text-align: center;
}

@media (min-width: 1000px) {
  .qr {
    grid-template-columns: 17rem minmax(0, 1fr);
  }

  .qr-index {
    position: sticky;
    top: 12px;
    max-height: calc(100vh - 24px);
    overflow-y: auto;
  }
}

/* Event composition: the count by response type and a difficulty histogram
   (one series, so one colour and no legend). Columns are at most 24px wide
   with a 4px rounded top and a square base, the count sits on the cap. */
.composition {
  margin: 0 0 var(--sp-3);
}

.composition__types {
  margin: 0 0 var(--sp-2);
}

.dh {
  margin: 0;
}

.dh__caption {
  margin-bottom: var(--sp-1);
  font-size: 0.8125rem;
  color: var(--color-ink-soft);
}

.dh__cols {
  display: flex;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.dh__col {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 32px;
}

.dh__count,
.dh__level {
  font-size: 0.75rem;
  color: var(--color-muted);
}

.dh__slot {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  width: 100%;
  height: 56px;
  border-bottom: 1px solid var(--color-line-strong);
}

.dh__bar {
  width: 24px;
  border-radius: var(--r-ctl) var(--r-ctl) 0 0;
  background: var(--color-accent);
}


/* ==========================================================================
   Loading cues (student side): skeleton, spinner, busy button.
   Reusable: put .skeleton on any block, .spinner inline, aria-busy="true" on
   a .btn. Motion is switched off under prefers-reduced-motion.
   ========================================================================== */

.skeleton {
  display: block;
  border-radius: var(--r-ctl);
  background: linear-gradient(
    90deg,
    var(--color-surface) 0%,
    var(--color-surface-2) 50%,
    var(--color-surface) 100%
  );
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.4s ease-in-out infinite;
}

.skeleton--line {
  height: 0.875rem;
  margin: var(--sp-3) 0;
}

.skeleton--title {
  width: 55%;
  height: 1.5rem;
  margin: var(--sp-1) 0 var(--sp-4);
}

.skeleton--short {
  width: 40%;
}

.skeleton--block {
  height: 2.5rem;
  margin: var(--sp-3) 0;
}

@keyframes skeleton-shimmer {
  from { background-position: 100% 0; }
  to { background-position: -100% 0; }
}

.spinner {
  display: inline-block;
  flex: none;
  width: 1em;
  height: 1em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  vertical-align: -0.125em;
  animation: spinner-turn 0.7s linear infinite;
}

@keyframes spinner-turn {
  to { transform: rotate(360deg); }
}

.btn[aria-busy="true"] {
  opacity: 0.7;
  cursor: progress;
  pointer-events: none;
}

.btn[aria-busy="true"]::before {
  content: "";
  width: 1em;
  height: 1em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spinner-turn 0.7s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .skeleton {
    animation: none;
    background: var(--color-surface);
  }

  /* A spinner that does not turn is a broken ring; show a static dotted
     ring instead. The button keeps its word ("Starting...") either way. */
  .spinner,
  .btn[aria-busy="true"]::before {
    animation: none;
    border-style: dotted;
    border-right-color: currentColor;
  }
}


/* ---------- Admin loading (Task B) ----------
   Loading cues for the teacher pages. Self-contained on purpose: names carry
   a -b suffix or sk- prefix so they can be merged with the student-side
   skeleton/spinner styles later. */

/* Thin indeterminate bar under the header, shown while requests are in
   flight (body[data-loading], set by admin.js after a short delay). Sticky
   with zero layout height, so it hugs the top of the viewport. */
.load-bar {
  position: sticky;
  top: 0;
  z-index: 50;
  height: 3px;
  margin-bottom: calc(-1 * var(--sp-1));
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s;
}

body[data-loading] .load-bar {
  opacity: 1;
}

.load-bar::before {
  content: "";
  display: block;
  width: 40%;
  height: 100%;
  background: var(--color-accent);
  animation: load-bar-b 1.1s ease-in-out infinite;
}

@keyframes load-bar-b {
  from { transform: translateX(-100%); }
  to { transform: translateX(250%); }
}

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

/* Shimmering placeholder block. */
.skeleton-b {
  border-radius: var(--r-ctl);
  background: linear-gradient(
    90deg,
    var(--color-surface-2) 25%,
    var(--color-line) 50%,
    var(--color-surface-2) 75%
  );
  background-size: 200% 100%;
  animation: skeleton-b-shimmer 1.4s linear infinite;
}

@keyframes skeleton-b-shimmer {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}

.sk-main,
.sk-side,
.sk-attempts,
.sk-list {
  display: grid;
  gap: var(--sp-2);
}

.sk-main {
  position: relative;
  padding: var(--sp-1) 0;
}

.sk-side {
  padding: var(--sp-3);
}

.sk-head { height: 28px; width: 55%; }
.sk-subhead { height: 14px; width: 80%; }
.sk-row { height: 40px; }
.sk-list .sk-row { height: 28px; }
.sk-chart { height: 120px; }

/* Busy button: a spinner ahead of the label. */
.btn[aria-busy="true"] {
  cursor: progress;
  opacity: 0.85;
}

.btn[aria-busy="true"]::before {
  content: "";
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  margin-right: 0.5em;
  vertical-align: -0.12em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spinner-b 0.7s linear infinite;
}

@keyframes spinner-b {
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .skeleton-b {
    animation: none;
    background: var(--color-line);
  }

  .load-bar::before {
    width: 100%;
    animation: none;
    opacity: 0.6;
  }

  .btn[aria-busy="true"]::before {
    animation: none;
    border-right-color: currentColor;
    border-style: dotted;
  }
}

/* ==========================================================================
   Cohort results: summary tiles, collapsible sections, charts (web/charts.js)
   and the per-student dialog. Charts take every colour from the tokens:
   bars in --color-accent, a bar below 50% in --status-critical with the
   words "below 50%" beside it, the class-average tick in --color-ink.
   ========================================================================== */

.stat-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(118px, 1fr));
  gap: var(--sp-2);
  margin: 0 0 var(--sp-2);
  padding: 0;
  list-style: none;
}

.stat-tile {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  padding: var(--sp-2) var(--sp-2);
  border: 1px solid var(--color-line);
  border-radius: var(--r-ctl);
  background: var(--color-bg);
}

.stat-tile__label {
  font-size: 0.8125rem;
  color: var(--color-muted);
}

.stat-tile__value {
  font-size: 1.5rem;
  font-weight: 600;
  line-height: 1.2;
  color: var(--color-ink);
}

.stat-tile__value.tone-critical {
  color: var(--status-critical);
}

.stat-tile__value.tone-warning {
  color: var(--status-warning);
}

.stat-tile__note {
  font-size: 0.75rem;
  overflow-wrap: anywhere;
}

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

.link-btn:focus-visible {
  outline: 2px solid var(--color-accent-deep);
  outline-offset: 2px;
}

.section-controls {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-1);
  margin: 0 0 var(--sp-1);
}

.results-section {
  border-top: 1px solid var(--color-line);
}

.results-section > summary {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px var(--sp-2);
  padding: var(--sp-2) 0;
  cursor: pointer;
  list-style: none;
}

.results-section > summary::-webkit-details-marker {
  display: none;
}

.results-section > summary::before {
  content: "";
  flex: none;
  width: 0;
  height: 0;
  margin-right: 2px;
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
  border-left: 7px solid var(--color-ink-soft);
  transition: transform 0.15s ease;
}

.results-section[open] > summary::before {
  transform: rotate(90deg);
}

.results-section > summary:focus-visible {
  outline: 2px solid var(--color-accent-deep);
  outline-offset: 2px;
}

.results-section__title {
  font-family: var(--font-sans);
  font-size: 1.0625rem;
  font-weight: 600;
  color: var(--color-ink);
}

.results-section__body {
  padding: 0 0 var(--sp-3);
  min-width: 0;
}

.results-section__body > p:first-child {
  margin-top: 0;
}

.sub-section {
  margin: var(--sp-2) 0 0;
}

.sub-section .table-wrap {
  margin-top: var(--sp-1);
}

/* Charts */
.chart {
  margin: 0 0 var(--sp-2);
  min-width: 0;
}

.chart__svg {
  display: block;
  overflow: visible;
}

.chart__label {
  fill: var(--color-ink);
  font-family: var(--font-sans);
  font-size: 13px;
}

.chart__note,
.chart__axis-label {
  fill: var(--color-muted);
  font-family: var(--font-sans);
  font-size: 11.5px;
}

.chart__value {
  fill: var(--color-ink);
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  /* A halo in the surface colour keeps the value readable where it crosses
     the class-average tick or the 50% line. */
  paint-order: stroke;
  stroke: var(--color-surface);
  stroke-width: 3px;
  stroke-linejoin: round;
}

.student-dialog .chart__value {
  stroke: var(--color-bg);
}

.chart__value--none {
  fill: var(--color-muted);
  font-weight: 400;
}

.chart__flag {
  fill: var(--status-critical);
}

.chart__track {
  fill: color-mix(in srgb, var(--color-line) 55%, transparent);
}

.chart__bar {
  fill: var(--color-accent);
}

.chart__bar--below {
  fill: var(--status-critical);
}

.chart__row:hover .chart__bar {
  opacity: 0.82;
}

.chart__compare {
  fill: var(--color-ink);
}

.chart__threshold,
.chart__baseline {
  stroke: var(--color-line-strong);
  stroke-width: 1;
}

.chart__legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-1) var(--sp-1);
  margin: 0 0 var(--sp-1);
  color: var(--color-ink-soft);
}

.chart__key {
  display: inline-block;
  flex: none;
  margin-left: var(--sp-1);
}

.chart__key:first-child {
  margin-left: 0;
}

.chart__key--bar,
.chart__key--below {
  width: 12px;
  height: 8px;
  border-radius: 2px;
  background: var(--color-accent);
}

.chart__key--below {
  background: var(--status-critical);
}

.chart__key--compare {
  width: 2px;
  height: 12px;
  background: var(--color-ink);
}

.chart__key--threshold {
  width: 1px;
  height: 12px;
  background: var(--color-line-strong);
}

.chart-empty {
  margin: var(--sp-1) 0 var(--sp-2);
}

/* Per-student dialog */
.student-dialog {
  width: min(760px, calc(100vw - 16px));
  max-width: none;
  max-height: calc(100dvh - 24px);
  padding: 0;
  overflow: auto;
  border: 1px solid var(--color-line-strong);
  border-radius: var(--r-ctl);
  background: var(--color-bg);
  color: var(--color-ink-soft);
  box-shadow: 0 8px 28px color-mix(in srgb, var(--color-ink) 22%, transparent);
}

.student-dialog::backdrop {
  background: color-mix(in srgb, var(--color-ink) 45%, transparent);
}

.student-dialog__head {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--color-line);
  background: var(--color-surface);
}

.student-dialog__who {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-1) var(--sp-2);
  min-width: 0;
}

.student-dialog__who h2 {
  margin: 0;
  overflow-wrap: anywhere;
}

/* Its own stacking layer under the sticky head, so nothing in the body
   paints over the name and Close button while scrolling. */
.student-dialog__body {
  position: relative;
  z-index: 0;
  padding: var(--sp-3) var(--sp-4) var(--sp-4);
}

.student-dialog__h {
  margin: var(--sp-4) 0 var(--sp-1);
  font-size: 1rem;
}

.student-dialog__body > .notice {
  margin-top: 0;
}

.q-strip {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1);
  margin: 0 0 var(--sp-2);
  padding: 0;
  list-style: none;
}

.q-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 52px;
  min-height: 44px;
  padding: var(--sp-1) var(--sp-1);
  border: 1px solid currentColor;
  border-radius: var(--r-ctl);
  background: color-mix(in srgb, currentColor 10%, var(--color-bg));
  font: inherit;
  font-size: 0.75rem;
  line-height: 1.3;
  cursor: pointer;
}

.q-cell:focus-visible {
  outline: 2px solid var(--color-accent-deep);
  outline-offset: 2px;
}

.q-cell__num {
  font-family: var(--font-mono);
  font-weight: 700;
  color: var(--color-ink);
}

.q-cell--correct { color: var(--status-positive); }
.q-cell--partial { color: var(--status-warning); }
.q-cell--wrong { color: var(--status-critical); }

/* Not known yet, or nothing given: hollow, like the pending status dot. */
.q-cell--unmarked,
.q-cell--skipped {
  color: var(--status-neutral);
  background: var(--color-bg);
}

.q-cell--unmarked {
  border-style: dashed;
}

.answer-rows {
  margin-top: var(--sp-1);
}

.answer-row .answer-text {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.answer-row .ai-review {
  margin-top: var(--sp-2);
}

/* Cohorts and codenames (teacher page). The slip sheet prints on its own:
   while body.print-slips is set, everything else is hidden from print. */
.cohort {
  padding-top: var(--sp-3);
  border-top: 1px solid var(--color-line);
}

.cohort__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2);
}

.cohort__spares {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
}

.cohort__spares input {
  width: 5rem;
}

.cohort-students {
  width: 100%;
  border-collapse: collapse;
}

.cohort-students th,
.cohort-students td {
  padding: var(--sp-1) var(--sp-2);
  border-bottom: 1px solid var(--color-line);
  text-align: left;
}

.cohort-students__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-2);
}

.slips {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
  gap: var(--sp-2);
}

.slip {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.15rem var(--sp-3);
  padding: var(--sp-3);
  border: 1px dashed var(--color-line-strong);
  color: var(--color-ink);
  background: var(--color-bg);
  break-inside: avoid;
}

.slip__label {
  color: var(--color-muted);
  font-size: 0.85em;
}

.slip__value {
  font-weight: 600;
}

@media print {
  body.print-slips * {
    visibility: hidden;
  }

  body.print-slips #slipSheet,
  body.print-slips #slipSheet * {
    visibility: visible;
  }

  body.print-slips #slipSheet {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
  }
}
