/* ==========================================================================
   AgLE Online Review — stylesheet
   Themes are driven by [data-theme] on <html>.
   ========================================================================== */

:root {
  --sb-w: 244px;
  --r-sm: 6.5px;
  --r-md: 10px;
  --r-lg: 13.5px;
  --r-xl: 16.5px;
  --ease: cubic-bezier(.4, 0, .2, 1);
  --font: "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
}

/* ---------- Theme: Default (Dark) ---------- */
:root, [data-theme="dark"] {
  --bg:            #07090b;
  --bg-glow:       rgba(34, 197, 94, .07);
  --sidebar:       #0b0e11;
  --panel:         #0e1216;
  --panel-2:       #12171c;
  --panel-hover:   #161c22;
  --line:          #1c2229;
  --line-soft:     #161b21;
  --text:          #e8edf2;
  --text-dim:      #8c98a4;
  --text-mute:     #6a747f;
  --accent:        #22c55e;
  --accent-ink:    #04140a;
  --accent-soft:   rgba(34, 197, 94, .12);
  --shadow:        0 15px 33px rgba(0, 0, 0, .45);
  --ok:            #22c55e;
  --bad:           #ef4444;
  --warn:          #f59e0b;
  /* "In Progress" status badge. A blue, distinct from --ok/--warn/--bad and
     from every theme accent, so the five card states stay tellable apart. */
  --info:          #60a5fa;
  color-scheme: dark;
}
[data-theme="ocean"] {
  --bg: #05090f; --bg-glow: rgba(56,140,255,.10);
  --sidebar: #080d15; --panel: #0b1220; --panel-2: #0f1728; --panel-hover: #131d31;
  --line: #1a2740; --line-soft: #142033;
  --text: #e6eefb; --text-dim: #8ea4c2; --text-mute: #6d84a3;
  --accent: #38bdf8; --accent-ink: #04121b; --accent-soft: rgba(56,189,248,.13);
  --shadow: 0 15px 33px rgba(0,0,0,.5);
  --ok:#22c55e; --bad:#f87171; --warn:#fbbf24; --info:#7dd3fc;
  color-scheme: dark;
}
[data-theme="forest"] {
  --bg: #050b07; --bg-glow: rgba(34,197,94,.12);
  --sidebar: #071009; --panel: #09150d; --panel-2: #0d1c12; --panel-hover: #112418;
  --line: #17301f; --line-soft: #13271a;
  --text: #e7f4ea; --text-dim: #8fae99; --text-mute: #6f8d79;
  --accent: #34d399; --accent-ink: #04140c; --accent-soft: rgba(52,211,153,.13);
  --shadow: 0 15px 33px rgba(0,0,0,.5);
  --ok:#34d399; --bad:#f87171; --warn:#fbbf24; --info:#60a5fa;
  color-scheme: dark;
}
[data-theme="sunset"] {
  --bg: #0c0703; --bg-glow: rgba(249,115,22,.12);
  --sidebar: #120a04; --panel: #170d05; --panel-2: #1e1208; --panel-hover: #26170b;
  --line: #35200f; --line-soft: #2b1a0c;
  --text: #fbecdd; --text-dim: #bd9c7f; --text-mute: #997c62;
  --accent: #fb923c; --accent-ink: #1a0b02; --accent-soft: rgba(251,146,60,.14);
  --shadow: 0 15px 33px rgba(0,0,0,.5);
  --ok:#4ade80; --bad:#f87171; --warn:#fbbf24; --info:#7dd3fc;
  color-scheme: dark;
}
[data-theme="royal"] {
  --bg: #08050f; --bg-glow: rgba(168,85,247,.12);
  --sidebar: #0c0817; --panel: #110b1e; --panel-2: #171026; --panel-hover: #1d1531;
  --line: #2a1c45; --line-soft: #221737;
  --text: #efe9fb; --text-dim: #a596c4; --text-mute: #8477a3;
  --accent: #a855f7; --accent-ink: #14061f; --accent-soft: rgba(168,85,247,.14);
  --shadow: 0 15px 33px rgba(0,0,0,.5);
  --ok:#4ade80; --bad:#f87171; --warn:#fbbf24; --info:#7dd3fc;
  color-scheme: dark;
}
[data-theme="light"] {
  --bg: #f4f6f8; --bg-glow: rgba(22,163,74,.08);
  --sidebar: #ffffff; --panel: #ffffff; --panel-2: #f7f9fb; --panel-hover: #eef2f6;
  --line: #e0e6ec; --line-soft: #eaeff4;
  --text: #14202b; --text-dim: #5b6b7a; --text-mute: #7a8896;
  --accent: #16a34a; --accent-ink: #ffffff; --accent-soft: rgba(22,163,74,.10);
  --shadow: 0 10px 25px rgba(15, 35, 55, .10);
  --ok:#16a34a; --bad:#dc2626; --warn:#d97706; --info:#2563eb;
  color-scheme: light;
}

/* ---------- Reset ---------- */
* { box-sizing: border-box; }

/* The UA rule for [hidden] is display:none, but ANY author `display` on the
   same element outranks it. Several components here set display and are also
   toggled via el.hidden, so make the attribute authoritative once, globally. */
[hidden] { display: none !important; }
html, body { height: 100%; }
body {
  margin: 0;
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
body::before {
  content: "";
  position: fixed; inset: 0;
  background:
    radial-gradient(753px 352px at 62% -12%, var(--bg-glow), transparent 70%),
    radial-gradient(586px 318px at 8% 108%, var(--bg-glow), transparent 70%);
  pointer-events: none; z-index: 0;
}
h1, h2, h3, h4, p, ul, ol, figure { margin: 0; }
ul, ol { padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: 0; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 5px; }

.ico {
  width: 16.5px; height: 16.5px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.9;
  stroke-linecap: round; stroke-linejoin: round;
}

/* ---------- Layout ----------
   No z-index here on purpose: it would create a stacking context and trap the
   mobile drawer (z-80) beneath the body-level scrim (z-70). */
.layout { position: relative; display: flex; min-height: 100vh; }
.main {
  flex: 1; min-width: 0; display: flex; flex-direction: column;
  padding: 22px 28px 0;
}

/* ---------- Sidebar ---------- */
.sidebar {
  width: var(--sb-w); flex: none;
  background: var(--sidebar);
  border-right: 1px solid var(--line);
  padding: 20px 16.5px;
  display: flex; flex-direction: column; gap: 22px;
  position: sticky; top: 0; height: 100vh; overflow-y: auto;
}
/* Two classes so this beats .icon-btn's display:grid, which is declared later. */
.sidebar__close.icon-btn { display: none; position: absolute; top: 13.5px; right: 13.5px; }
.brand { display: flex; align-items: center; gap: 11.5px; padding: 2px 3.5px; }
.brand__mark {
  width: 49px; height: 49px; flex: none;
  display: grid; place-items: center;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 40%, var(--accent-soft), transparent 70%);
}
.brand__mark svg { width: 45px; height: 45px; }
.brand__mark .wreath { fill: none; stroke: var(--accent); stroke-width: 3; stroke-linecap: round; }
.brand__mark .wreath-leaf { fill: var(--accent); }
.brand__mark .sprout { stroke: #d8b24a; stroke-width: 3; stroke-linecap: round; }
.brand__mark .sprout-leaf { fill: #eef3ee; }
.brand__text strong { display: block; font-size: 25px; letter-spacing: 0.5px; line-height: 1; }
.brand__text small { display: block; font-size: 9px; letter-spacing: 2px; color: var(--text-dim); margin-top: 4px; }

.nav { display: flex; flex-direction: column; gap: 3.5px; }
.nav__item {
  display: flex; align-items: center; gap: 11.5px;
  padding: 11px 12.5px; border-radius: var(--r-md);
  color: var(--text-dim); font-size: 12.5px; font-weight: 500;
  border: 1px solid transparent;
  transition: background .18s var(--ease), color .18s var(--ease), border-color .18s var(--ease);
}
.nav__item:hover { background: var(--panel-hover); color: var(--text); }
.nav__item.is-active {
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  color: var(--text);
}
.nav__item.is-active .ico { color: var(--accent); }
.nav__badge {
  margin-left: auto; font-style: normal; font-size: 9px; font-weight: 700;
  background: var(--accent); color: var(--accent-ink);
  padding: 2px 6.5px; border-radius: 999px;
}
.nav__sep { border: 0; border-top: 1px solid var(--line); margin: 11.5px 5px; }

.promo {
  margin-top: auto; text-align: left; width: 100%;
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 15px;
  transition: border-color .2s var(--ease), transform .2s var(--ease);
}
.promo:hover { border-color: color-mix(in srgb, var(--accent) 50%, transparent); transform: translateY(-2px); }
.promo__head { display: flex; align-items: center; gap: 10px; }
.promo__trophy svg { width: 25px; height: 25px; fill: none; stroke: var(--warn); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.promo__title { font-weight: 700; font-size: 12.5px; line-height: 1.25; }
.promo__body { color: var(--text-dim); font-size: 11px; line-height: 1.45; margin-top: 7px; }
.promo__pct { font-size: 12.5px; font-weight: 700; color: var(--accent); margin-top: 10px; }
.promo__bar { height: 6px; border-radius: 999px; background: var(--line); margin-top: 7px; overflow: hidden; }
.promo__bar i { display: block; height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--accent) 45%, #ffd166)); transition: width .5s var(--ease); }
/* Streak and best subject: one per line, so neither is lost in a run-on. */
.promo__note { display: flex; flex-direction: column; gap: 3px; margin-top: 9px; font-size: 10.5px; font-weight: 600; color: var(--text-dim); }

/* ---------- Topbar ---------- */
.topbar { padding-bottom: 16.5px; }
.topbar__row { display: flex; align-items: flex-start; gap: 20px; flex-wrap: wrap; }
.topbar__titles { flex: 1; min-width: 218px; }
.topbar__titles h1 { font-size: 25px; font-weight: 700; letter-spacing: -.4px; line-height: 1.2; }
.topbar__titles p { color: var(--text-dim); margin-top: 6.5px; font-size: 12.5px; }
.topbar__actions { display: flex; gap: 10px; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7.5px;
  padding: 10px 16.5px; border-radius: var(--r-md);
  font-size: 12px; font-weight: 600; white-space: nowrap;
  border: 1px solid var(--line); background: var(--panel);
  transition: background .18s var(--ease), border-color .18s var(--ease), color .18s var(--ease), transform .12s var(--ease);
}
.btn:hover { background: var(--panel-hover); border-color: color-mix(in srgb, var(--accent) 55%, transparent); }
.btn:active { transform: translateY(1px); }
.btn--primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.btn--primary:hover { background: color-mix(in srgb, var(--accent) 85%, #fff); border-color: var(--accent); color: var(--accent-ink); }
.btn--danger { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 45%, transparent); }
.btn--danger:hover { background: color-mix(in srgb, var(--bad) 14%, transparent); border-color: var(--bad); }
.btn--sm { padding: 6.5px 11.5px; font-size: 11px; }
.btn[disabled] { opacity: .42; pointer-events: none; }

.icon-btn {
  width: 32px; height: 32px; display: grid; place-items: center;
  border-radius: 8.5px; color: var(--text-dim);
  transition: background .18s var(--ease), color .18s var(--ease);
}
.icon-btn:hover { background: var(--panel-hover); color: var(--text); }

/* ---------- Theme picker ---------- */
.topbar__theme { display: flex; align-items: center; justify-content: flex-end; gap: 11.5px; margin-top: 15px; }
.theme__label { color: var(--text-dim); font-size: 12.5px; }
.dropdown { position: relative; }
/* Do NOT transition the background here. It is fed by var(--panel), which is
   re-pointed on every theme switch, and a background transition driven by a
   changing custom property latches the OLD colour — which left this button
   dark-on-dark in Light Mode (and white-on-white switching back). Border still
   animates; the fill swaps instantly and therefore always matches the theme.
   Same class of bug as the Bloom tiles further down. */
.theme__btn {
  display: flex; align-items: center; gap: 13.5px;
  padding: 9px 13.5px; border-radius: var(--r-md);
  border: 1px solid var(--line); background-color: var(--panel); color: var(--text);
  transition: border-color .18s var(--ease);
}
.theme__btn:hover { border-color: color-mix(in srgb, var(--accent) 55%, transparent); background-color: var(--panel-hover); }
.theme__btn .chev { width: 15px; height: 15px; transition: transform .2s var(--ease); }
.theme__btn[aria-expanded="true"] .chev { transform: rotate(180deg); }
.theme__menu {
  position: absolute; right: 0; top: calc(100% + 8.5px); z-index: 40;
  min-width: 198px; padding: 8.5px;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--shadow);
  animation: pop .16s var(--ease);
}
@keyframes pop { from { opacity: 0; transform: translateY(-6px); } }
.theme__opt {
  width: 100%; display: flex; align-items: center; gap: 11.5px;
  padding: 9px 10px; border-radius: 8.5px; font-size: 12.5px; text-align: left;
}
.theme__opt:hover { background: var(--panel-hover); }
.theme__opt .swatch { width: 12.5px; height: 12.5px; border-radius: 50%; flex: none; box-shadow: 0 0 0 1px rgba(255,255,255,.14) inset; }
.theme__opt .tick { margin-left: auto; width: 15px; height: 15px; color: var(--accent); opacity: 0; }
.theme__opt.is-active .tick { opacity: 1; }

/* ---------- Layout (view) picker ---------- */
.viewctl { margin-right: auto; }
.viewctl__btn { gap: 9px; }
.viewctl__btn .ico { width: 16px; height: 16px; color: var(--accent); }
.viewctl__btn .chev { color: inherit; }
.viewctl__text { font-size: 12.5px; font-weight: 600; }
.viewctl__menu { min-width: 232px; }
.view__opt .ico { width: 16px; height: 16px; flex: none; color: var(--text-dim); }
.view__opt.is-active .ico { color: var(--accent); }
.view__opt-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.view__opt-text small { color: var(--text-mute); font-size: 10.5px; }

/* ---------- Content shell ---------- */
.content { flex: 1; padding-bottom: 28px; }
.view { animation: fade .25s var(--ease); }
@keyframes fade { from { opacity: 0; transform: translateY(6.5px); } }

.footer {
  display: flex; align-items: center; justify-content: space-between; gap: 15px; flex-wrap: wrap;
  border-top: 1px solid var(--line-soft);
  padding: 15px 0 18.5px; color: var(--text-mute); font-size: 11.5px;
}
.footer__links { display: flex; align-items: center; gap: 8.5px; }
.footer__links a:hover { color: var(--accent); }
.footer__links .dot { opacity: .5; }
.footer__tag { display: inline-flex; align-items: center; gap: 6.5px; }
.footer__tag .leaf { width: 15px; height: 15px; color: var(--accent); fill: currentColor; stroke: none; }

/* ---------- Subject list (dashboard) ---------- */
/* Cards are compact and hug their content rather than spanning the full
   content column: a fixed ceiling keeps all six the same width so their
   icons, arrows and START buttons line up down the page. */
.subjects { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; }
.subject {
  --c: var(--accent);
  display: flex; align-items: center; gap: 14px;
  /* Wide enough for the row layout to hold icon, text column, status badge and
     action button without crowding; 700px predated the badge and the progress
     line and left the text column too narrow once they arrived. */
  width: 100%; max-width: 880px; text-align: left;
  padding: 13px 16px;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-lg);
  transition: border-color .2s var(--ease), background .2s var(--ease), transform .2s var(--ease), box-shadow .2s var(--ease);
}
.subject:hover {
  border-color: color-mix(in srgb, var(--c) 55%, transparent);
  background: var(--panel-2);
  transform: translateY(-2px);
  box-shadow: 0 8.5px 25px color-mix(in srgb, var(--c) 12%, transparent);
}
.subject__icon {
  width: 44px; height: 44px; flex: none; display: grid; place-items: center;
  border-radius: 50%; border: 2px solid var(--c); color: var(--c);
  background: color-mix(in srgb, var(--c) 10%, transparent);
}
.subject__icon svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.subject__body { flex: 1; min-width: 0; }
.subject__name { font-size: 15px; font-weight: 700; letter-spacing: 0.5px; text-transform: uppercase; }
.subject__desc { color: var(--text-dim); font-size: 11.5px; margin-top: 4px; }
.subject__meta { display: flex; gap: 6.5px; flex-wrap: wrap; margin-top: 7px; }

/* Icon and status badge share the card's first row, so the badge is the first
   thing read on every card and always in the same place.

   This pairing only makes sense in the COLUMN layouts (grid, and the Final
   Examination panel). In the row layouts the card is already a single line of
   icon | body | badge | button, and a full-width wrapper inside that row would
   take the whole line and crush the body to one word per line — which is
   exactly what it did. There, the wrapper dissolves with `display: contents`
   and its two children become direct flex items of the row, ordered below. */
.subject__top { display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%; }
.subjects--list .subject__top,
.subjects--compact .subject__top { display: contents; }
/* With the wrapper gone the DOM order is icon, badge, body, button; the badge
   belongs after the text it describes, so the four are ordered explicitly. */
.subjects--list .subject__icon,
.subjects--compact .subject__icon { order: 0; }
.subjects--list .subject__body,
.subjects--compact .subject__body { order: 1; }
.subjects--list .statusbadge,
.subjects--compact .statusbadge { order: 2; }
.subjects--list .subject__start,
.subjects--compact .subject__start { order: 3; }

/* ---------- Status badge ----------
   ONE badge per card. It replaced a stack of four overlapping indicators (an
   inline padlock, a "Coming Soon" chip, an internal "no questions uploaded yet"
   note and a second padlock) that all said the same thing in different words.
   The state also decides the single action button beneath it, so the two can
   never disagree. */
.statusbadge {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  font-size: 9.5px; font-weight: 700; letter-spacing: .6px; text-transform: uppercase;
  padding: 4px 9px; border-radius: 999px; border: 1px solid transparent;
}
.statusbadge .ico { width: 11px; height: 11px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.statusbadge--available { color: var(--ok);       border-color: color-mix(in srgb, var(--ok) 45%, transparent);       background: color-mix(in srgb, var(--ok) 12%, transparent); }
.statusbadge--progress  { color: var(--info, #3b82f6); border-color: color-mix(in srgb, var(--info, #3b82f6) 45%, transparent); background: color-mix(in srgb, var(--info, #3b82f6) 12%, transparent); }
.statusbadge--done      { color: var(--accent);   border-color: color-mix(in srgb, var(--accent) 45%, transparent);   background: color-mix(in srgb, var(--accent) 12%, transparent); }
.statusbadge--soon      { color: var(--warn);     border-color: color-mix(in srgb, var(--warn) 45%, transparent);     background: color-mix(in srgb, var(--warn) 12%, transparent); }
.statusbadge--locked    { color: var(--text-dim); border-color: color-mix(in srgb, var(--text-dim) 40%, transparent); background: color-mix(in srgb, var(--text-dim) 12%, transparent); }

/* ---------- Card statistics ----------
   Same two lines on every open card, in the same order, so a column of cards
   can be compared by eye instead of each one carrying whichever numbers
   happened to apply to it. */
.subject__stats { font-size: 11.5px; font-weight: 600; color: var(--text); margin-top: 8px; }
.subject__stats--sub { font-weight: 500; color: var(--text-dim); margin-top: 2px; }
.subject__stats--note { font-weight: 500; color: var(--text-dim); }
.subject__sub { font-size: 10.5px; color: var(--text-mute); margin-top: 5px; }
.subject__bar {
  height: 5px; border-radius: 999px; background: var(--line);
  margin-top: 7px; overflow: hidden;
}
.subject__bar i {
  display: block; height: 100%; border-radius: 999px;
  background: var(--c, var(--accent));
  transition: width .5s var(--ease);
}
.chip {
  font-size: 9.5px; font-weight: 600; letter-spacing: 0.5px;
  padding: 3.5px 8.5px; border-radius: 999px;
  border: 1px solid var(--line); color: var(--text-dim); background: var(--panel-2);
}
.chip--accent { color: var(--c); border-color: color-mix(in srgb, var(--c) 45%, transparent); background: color-mix(in srgb, var(--c) 10%, transparent); }
.subject__arrow { flex: none; color: var(--c); display: grid; place-items: center; transition: transform .2s var(--ease); }
.subject__arrow svg { width: 32px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }
.subject:hover .subject__arrow { transform: translateX(5px); }
.subject__start {
  flex: none; padding: 9px 20px; border-radius: 8.5px;
  border: 2px solid var(--c); color: var(--c);
  font-weight: 700; font-size: 11px; letter-spacing: 1px;
  transition: background .18s var(--ease), color .18s var(--ease);
}
.subject__start:hover { background: var(--c); color: var(--accent-ink); }

/* Layout variants. The markup is identical in all three, so every subject
   stays rendered and reachable no matter which one is selected — only the
   arrangement and the density of the secondary text change. */

/* Grid: cards stack vertically and sit side by side. The column count is set
   per breakpoint rather than left to auto-fit, so a wide monitor fills its
   width with four cards instead of stretching three across it. */
.subjects--grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  align-items: stretch;
  gap: 12px;
}
@media (min-width: 1480px) { .subjects--grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 1120px) { .subjects--grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.subjects--grid .subject {
  flex-direction: column; align-items: flex-start;
  max-width: none; height: 100%;
  gap: 9px; padding: 15px;
}
/* The body takes the slack, which pins the action button to the bottom edge of
   every card whatever its text runs to — so the buttons line up across a row. */
.subjects--grid .subject__body { flex: 1 1 auto; width: 100%; }
.subjects--grid .subject__arrow { display: none; }
.subjects--grid .subject__start { width: 100%; padding: 10px 0; text-align: center; margin-top: auto; }
.subjects--grid .subject:hover { transform: translateY(-3px); }

/* Compact: one dense row per subject, so all six fit without scrolling. */
.subjects--compact { gap: 6.5px; }
.subjects--compact .subject { padding: 8.5px 13.5px; gap: 11.5px; }
.subjects--compact .subject__icon { width: 33px; height: 33px; border-width: 1.5px; }
.subjects--compact .subject__icon svg { width: 17px; height: 17px; }
.subjects--compact .subject__name { font-size: 13px; }
.subjects--compact .subject__desc { display: none; }
.subjects--compact .subject__meta { margin-top: 4px; }
.subjects--compact .subject__arrow { display: none; }
.subjects--compact .subject__start { padding: 6.5px 15px; font-size: 10px; }
.subjects--compact .subject:hover { transform: none; }
/* Dense means dense: the badge and the percentage already carry the state, so
   the progress bar and the last-activity line come out and the two statistic
   lines collapse onto one row. */
.subjects--compact .subject__bar,
.subjects--compact .subject__sub { display: none; }
.subjects--compact .subject__stats { display: inline; font-size: 10.5px; margin-top: 0; }
.subjects--compact .subject__stats--sub::before { content: ' · '; }

/* List: a single wide row per subject. The body holds the text column, the
   progress bar is capped so it does not stretch the width of the card, and the
   badge and button stay on the right at their natural size. */
.subjects--list .subject__bar { max-width: 340px; }
.subjects--list .subject__stats { margin-top: 6px; }
.subjects--list .statusbadge,
.subjects--list .subject__start { flex: none; }

/* ---------- Topic picker (shown when a subject is opened) ---------- */
.topicpick { display: flex; flex-direction: column; gap: 10px; }
.topicpick__row { display: flex; align-items: stretch; gap: 10px; }
/* Each card is a solid swatch carrying its own ink (--tc / --ti, set inline).
   Every colour inside the card derives from --ti rather than from the theme, so
   the card reads the same in all six themes. No background transition here: the
   fill is fixed per card, and animating it would reintroduce the latching bug
   documented on .theme__btn. */
/* Two classes deep on purpose: .card also sets `background`, is defined later
   in this file and would otherwise win at equal specificity. */
.topicpick .topicpick__card {
  flex: 1; display: flex; align-items: center; gap: 13.5px;
  text-align: left; padding: 14px 16px; cursor: pointer;
  background-color: var(--tc, var(--panel));
  color: var(--ti, var(--text));
  border-color: color-mix(in srgb, var(--ti, var(--line)) 22%, transparent);
  transition: border-color .2s var(--ease), transform .2s var(--ease), box-shadow .2s var(--ease);
}
.topicpick .topicpick__card:hover {
  border-color: color-mix(in srgb, var(--ti) 55%, transparent);
  transform: translateY(-2px);
  box-shadow: 0 8.5px 25px color-mix(in srgb, var(--tc) 45%, transparent);
}
.topicpick__mark {
  width: 11px; height: 11px; flex: none; border-radius: 50%;
  border: 2.5px solid var(--ti); background: color-mix(in srgb, var(--ti) 25%, transparent);
}
.topicpick__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.topicpick__name { font-size: 14px; font-weight: 700; letter-spacing: .3px; color: var(--ti); }
/* 88%, not less: the secondary line is 11.5px, and on the darker swatches a
   weaker ink drops it below the 4.5:1 contrast floor. */
.topicpick__meta { color: color-mix(in srgb, var(--ti) 88%, transparent); font-size: 11.5px; }
.topicpick__go { flex: none; color: var(--ti); display: grid; place-items: center; transition: transform .2s var(--ease); }
.topicpick__go svg { width: 28px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }
.topicpick__card:hover .topicpick__go { transform: translateX(5px); }
.topicpick .topicpick__card--all { border-style: dashed; border-width: 2px; }
/* Chip sits on the card's own fill, so it takes the card ink rather than the
   subject accent, which would be invisible on several of the swatches. */
.topicpick__tag {
  margin-left: 7px; vertical-align: middle;
  color: var(--ti); border-color: color-mix(in srgb, var(--ti) 45%, transparent);
  background: color-mix(in srgb, var(--ti) 12%, transparent);
}
/* An added topic with no questions yet cannot start a session, but stays
   visible so the admin can find it and fill it. */
.topicpick .topicpick__card[disabled] { opacity: .5; cursor: default; }
/* A topic still awaiting its questions. Red so it reads as outstanding work
   rather than as a neutral label, and it overrides the per-topic ink colour
   the sibling tag uses. */
.topicpick__tag--ongoing {
  color: var(--bad) !important;
  border-color: color-mix(in srgb, var(--bad) 50%, transparent) !important;
  background: color-mix(in srgb, var(--bad) 14%, transparent) !important;
  font-weight: 700; letter-spacing: .02em;
}
/* The disabled card is dimmed, which would mute the badge with it. */
.topicpick .topicpick__card[disabled] .topicpick__tag--ongoing { opacity: 1; }
.topicpick .topicpick__card[disabled]:hover { transform: none; box-shadow: none; border-color: color-mix(in srgb, var(--ti) 22%, transparent); }
.topicpick .topicpick__card[disabled] .topicpick__go { opacity: .4; }
.topicpick__manage { flex: none; align-self: center; }

/* ---------- Locks (reader access control) ----------
   A lock is shown wherever something exists but may not be opened: a subject or
   topic with no questions yet, or one whose questions have not been released.
   The dimming is deliberately mild — a locked card still has to be legible,
   because it tells the reader what is coming rather than hiding it. */
.lockglyph { width: 13px; height: 13px; stroke-width: 2; }
.chip .lockglyph { margin-right: 4px; vertical-align: -2px; }
.btn .lockglyph { width: 14px; height: 14px; margin-right: 5px; vertical-align: -2.5px; }

.chip--lock {
  color: var(--text-dim);
  border-color: color-mix(in srgb, var(--text-dim) 40%, transparent);
  background: color-mix(in srgb, var(--text-dim) 12%, transparent);
}
.chip--open {
  color: var(--ok);
  border-color: color-mix(in srgb, var(--ok) 45%, transparent);
  background: color-mix(in srgb, var(--ok) 12%, transparent);
}
/* Inline beside a title. Sits on the baseline of the heading it annotates. */
.lockmark { display: inline-flex; margin-left: 7px; color: var(--text-mute); vertical-align: -2px; }
.lockmark .lockglyph { width: 14px; height: 14px; }

/* "Available Soon": shut by the calendar rather than by a switch, so it reads
   as a pending release (warn) instead of a refusal (dim grey). */
.chip--soon {
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 45%, transparent);
  background: color-mix(in srgb, var(--warn) 12%, transparent);
}

/* The Final Examination card. It is a .subject in every dimension — the class
   carries the width, padding, radius and all three layout variants — so the
   only thing added here is the hairline that marks it as the scheduled paper
   rather than one of the six subjects above it. */
.subject--final { border-style: solid; box-shadow: inset 3px 0 0 color-mix(in srgb, var(--c) 60%, transparent); }
.subjects--grid .subject--final { box-shadow: inset 0 3px 0 color-mix(in srgb, var(--c) 60%, transparent); }
.subjects--grid .subject--final:hover {
  box-shadow: inset 0 3px 0 color-mix(in srgb, var(--c) 60%, transparent),
              0 8.5px 25px color-mix(in srgb, var(--c) 12%, transparent);
}
.subject--final:hover {
  box-shadow: inset 3px 0 0 color-mix(in srgb, var(--c) 60%, transparent),
              0 8.5px 25px color-mix(in srgb, var(--c) 12%, transparent);
}

/* The free-of-charge promise (FINAL_EXAM.freeNote). Sits between the description and
   the badges, in the card's own accent so it reads as a promise rather than as
   more description. Its own line: it must not be mistaken for a status badge,
   which is what everything below it is. */
.subject__free {
  margin-top: 6px;
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.3px;
  color: var(--c);
}
.subjects--compact .subject__free { display: none; }

.notice--free {
  border-color: color-mix(in srgb, var(--ok) 45%, transparent);
  background: color-mix(in srgb, var(--ok) 8%, transparent);
}

/* A card whose subject is not open yet: no hover lift, no pointer, nothing to
   click — but NOT dimmed. Its whole job is to tell the reader what the subject
   covers and when it opens, and a card at 72% opacity could not be read. Only
   the action button is visually disabled; the content stays at full contrast. */
.subject--locked { border-style: dashed; }
.subject--locked:hover {
  transform: none; box-shadow: none;
  background: var(--panel); border-color: var(--line);
}
.subject__lock { flex: none; color: var(--text-mute); display: grid; place-items: center; }
.subject__lock .lockglyph { width: 22px; height: 22px; stroke-width: 1.7; }
.subject__start--locked {
  border-color: color-mix(in srgb, var(--text-dim) 40%, transparent);
  color: var(--text-mute); cursor: default;
  background: color-mix(in srgb, var(--text-dim) 7%, transparent);
}
.subject__start--locked:hover {
  background: color-mix(in srgb, var(--text-dim) 7%, transparent);
  color: var(--text-mute);
}

/* ---------- Filter Subjects ----------
   Replaces the topbar control labelled only "View", which said nothing about
   what it changed. That dropdown still exists and now reads "Layout"; this bar
   is the filter, and it sits with the cards it filters. */
.filterbar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-bottom: 13px;
}
.filterbar__label { font-size: 11px; font-weight: 700; letter-spacing: .7px; text-transform: uppercase; color: var(--text-dim); }
.filterbar__opts { display: flex; gap: 5px; flex-wrap: wrap; }
.filterbar__opt {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--panel);
  color: var(--text-dim); font-size: 11px; font-weight: 600; cursor: pointer;
  transition: border-color .18s var(--ease), color .18s var(--ease), background .18s var(--ease);
}
.filterbar__opt:hover { border-color: color-mix(in srgb, var(--accent) 50%, transparent); color: var(--text); }
.filterbar__opt.is-on {
  border-color: color-mix(in srgb, var(--accent) 60%, transparent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
}
.filterbar__n {
  font-size: 9.5px; font-weight: 700; min-width: 16px; text-align: center;
  padding: 1px 5px; border-radius: 999px;
  background: color-mix(in srgb, currentColor 15%, transparent);
}

/* ---------- Continue Reviewing ----------
   One click back into the last session, above the grid so resuming does not
   start with hunting for the subject that was already open. */
.resume {
  --c: var(--accent);
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 14px 16px; margin-bottom: 13px;
  background: var(--panel); border: 1px solid var(--line);
  border-left: 3px solid var(--c);
  border-radius: var(--r-lg);
}
.resume__body { flex: 1 1 260px; min-width: 0; }
.resume__eyebrow { font-size: 10px; font-weight: 700; letter-spacing: .8px; text-transform: uppercase; color: var(--c); }
.resume__title { font-size: 14px; font-weight: 700; margin-top: 3px; }
.resume__meta { font-size: 11.5px; color: var(--text-dim); margin-top: 3px; }
.resume .subject__bar { max-width: 320px; }
.resume__go { flex: none; }

/* ---------- Final Examination section ----------
   Below the six subjects and outside their grid. It is not a seventh subject:
   it is a scheduled event with a date, a countdown, an eligibility rule and six
   papers behind it, and inside the grid it read as just another locked card. */
.sectitle {
  font-size: 13px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase;
  color: var(--text-dim); margin: 26px 0 11px;
  display: flex; align-items: center; gap: 12px;
}
.sectitle::after { content: ''; flex: 1; height: 1px; background: var(--line); }
.finalex {
  --c: var(--accent);
  display: flex; align-items: stretch; gap: 20px; flex-wrap: wrap;
  padding: 18px 20px;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: inset 3px 0 0 color-mix(in srgb, var(--c) 60%, transparent);
}
.finalex__main { flex: 1 1 340px; min-width: 0; }
.finalex__name { font-size: 16px; font-weight: 700; letter-spacing: .4px; margin-top: 11px; }
.finalex__desc { color: var(--text-dim); font-size: 12px; margin-top: 5px; }
.finalex__sect { margin-top: 13px; }
.finalex__lbl {
  display: block; font-size: 10px; font-weight: 700; letter-spacing: .8px;
  text-transform: uppercase; color: var(--text-mute);
}
.finalex__note { font-size: 11.5px; color: var(--text-dim); margin-top: 4px; max-width: 62ch; }
.finalex__side {
  flex: 0 1 240px; display: flex; flex-direction: column; justify-content: center; gap: 6px;
  padding: 14px 16px; border-radius: var(--r-lg);
  background: color-mix(in srgb, var(--c) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--c) 25%, transparent);
  text-align: center;
}
/* Tabular figures so the seconds place does not jog the layout every tick. */
.finalex__big {
  font-size: 24px; font-weight: 700; color: var(--c); line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.finalex__when { font-size: 11px; color: var(--text-dim); }
.finalex__go { margin-top: 9px; width: 100%; justify-content: center; }
@media (max-width: 760px) { .finalex__side { flex: 1 1 100%; } }

/* Locked topic card. The swatch stays, so the topic keeps its identity in the
   list, but the card is flattened and the arrow becomes a padlock. */
.topicpick .topicpick__card--locked { opacity: .6; cursor: default; }
.topicpick .topicpick__card--locked:hover {
  transform: none; box-shadow: none;
  border-color: color-mix(in srgb, var(--ti) 22%, transparent);
}
.topicpick__mark--lock {
  border: 0; background: none; width: auto; height: auto;
  display: grid; place-items: center; color: var(--ti);
}
.topicpick__mark--lock .lockglyph { width: 17px; height: 17px; }
.topicpick__card--locked .topicpick__go .lockglyph { width: 18px; height: 18px; }
/* Badges keep full strength on a dimmed card: the lock state is the one thing
   that must stay readable there. */
.topicpick .topicpick__card--locked .topicpick__tag { opacity: 1; }
.topicpick__tag--locked {
  color: var(--ti) !important;
  border-color: color-mix(in srgb, var(--ti) 45%, transparent) !important;
  background: color-mix(in srgb, var(--ti) 16%, transparent) !important;
  font-weight: 700;
}
.topicpick__tag--open {
  color: var(--ok) !important;
  border-color: color-mix(in srgb, var(--ok) 55%, transparent) !important;
  background: color-mix(in srgb, var(--ok) 16%, transparent) !important;
  font-weight: 700;
}
.topicpick__lock { flex: none; align-self: center; white-space: nowrap; }

/* Admin: the subject-level release switch. */
.lockcard__row { display: flex; align-items: center; gap: 8.5px; flex-wrap: wrap; margin-top: 11.5px; }
.btn--locked { color: var(--text-mute); cursor: default; }

/* A locked row in a plain topic list stays visible and stops being a link. */
.topic-list li.is-locked { opacity: .68; }
.topic-list li.is-locked .dotmark {
  width: auto; height: auto; border-radius: 0; background: none; color: var(--text-mute);
  display: grid; place-items: center;
}
.topic-list__locked { display: inline-flex; align-items: center; gap: 7px; color: var(--text-dim); }

.topic-list--links a { color: var(--text); }
.topic-list--links a:hover { color: var(--accent); text-decoration: underline; }
.topic-list--links a[aria-current="page"] { color: var(--c); font-weight: 700; }

/* ---------- Admin topic manager ---------- */
.mgitem__q { font-size: 13.5px; font-weight: 600; margin-top: 9px; }
.mgitem__choices { list-style: none; margin: 9px 0 0; padding: 0; display: flex; flex-direction: column; gap: 4.5px; }
.mgitem__choices li { font-size: 12px; color: var(--text-dim); display: flex; gap: 7px; align-items: baseline; flex-wrap: wrap; }
.mgitem__choices li b { color: var(--text-mute); flex: none; }
.mgitem__choices li.is-key { color: var(--text); }
.mgitem__exp { font-size: 11.5px; color: var(--text-mute); margin-top: 9px; }
.mgitem__issues { margin: 6px 0 0; padding-left: 16px; font-size: 11.5px; }
.mgitem__result:empty { display: none; }
/* Red flag for an item that repeats a concept already covered in this topic. */
.mgitem--dup { border-color: color-mix(in srgb, var(--bad) 55%, transparent); }
.chip--flag {
  display: inline-flex; align-items: center; gap: 5px;
  color: var(--bad); border-color: color-mix(in srgb, var(--bad) 50%, transparent);
  background: color-mix(in srgb, var(--bad) 12%, transparent);
}
.chip--flag .ico { width: 12px; height: 12px; }

/* ---------- Generic cards / grids ---------- */
.grid { display: grid; gap: 13.5px; }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(193px, 1fr)); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(251px, 1fr)); }
.grid--tight { grid-template-columns: repeat(auto-fit, minmax(87px, 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(141px, 1fr)); }
.card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 18.5px;
}
.card--pad-lg { padding: 22px 23px; }
.card__title { font-size: 13.5px; font-weight: 700; }
.card__sub { color: var(--text-dim); font-size: 11.5px; margin-top: 5px; line-height: 1.6; }

.stat { text-align: center; }
.stat__num { font-size: 28px; font-weight: 800; letter-spacing: -1px; color: var(--accent); }
.stat__lbl { color: var(--text-dim); font-size: 11px; margin-top: 5px; }

.section-head { display: flex; align-items: center; justify-content: space-between; gap: 13.5px; flex-wrap: wrap; margin: 25px 0 13.5px; }
.section-head h2 { font-size: 16px; font-weight: 700; }
.section-head p { color: var(--text-dim); font-size: 11.5px; margin-top: 3.5px; }

.crumb { display: flex; align-items: center; gap: 6.5px; color: var(--text-dim); font-size: 11.5px; margin-bottom: 15px; }
.crumb a:hover { color: var(--accent); }
.crumb .ico { width: 12.5px; height: 12.5px; }

.bar { height: 6.5px; border-radius: 999px; background: var(--line); overflow: hidden; }
.bar i { display: block; height: 100%; border-radius: 999px; background: var(--c, var(--accent)); transition: width .6s var(--ease); }

.empty { text-align: center; padding: 50px 16.5px; }
.empty svg { width: 45px; height: 45px; color: var(--text-mute); fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.empty h3 { margin-top: 13.5px; font-size: 15px; }
.empty p { color: var(--text-dim); margin-top: 6.5px; font-size: 12px; }
.empty .btn { margin-top: 16.5px; }

/* ---------- Subject detail ---------- */
/* Light green header card. The fill and its ink are fixed rather than themed:
   this card is the same colour in all six themes, so its text colour has to be
   pinned to the fill or light mode would put pale text on pale green. */
.hero {
  --c: var(--accent);
  --hero-bg: #d8efa6;
  --hero-ink: #16240f;
  display: flex; align-items: center; gap: 18.5px; flex-wrap: wrap;
  background-color: var(--hero-bg);
  border-color: color-mix(in srgb, var(--hero-ink) 20%, transparent);
  color: var(--hero-ink);
}
.hero .subject__icon {
  width: 60px; height: 60px;
  border-color: var(--hero-ink); color: var(--hero-ink);
  background: color-mix(in srgb, var(--hero-ink) 8%, transparent);
}
.hero__text h2 { font-size: 21px; font-weight: 800; letter-spacing: 0.5px; text-transform: uppercase; color: var(--hero-ink); }
.hero__text p { color: color-mix(in srgb, var(--hero-ink) 82%, transparent); margin-top: 6.5px; max-width: 62ch; line-height: 1.6; }

/* Rename, sitting with the topic name it edits. It lives INSIDE the h2, so it
   has to opt out of the heading's uppercase, weight and letter-spacing or it
   would read as part of the title rather than as a control. The ink is pinned
   to the card's own like everything else here: .btn is themed, and a themed
   border on this fixed green would disappear in Light Mode. */
.hero__text h2 .hero__rename {
  margin-left: 10px; vertical-align: middle;
  text-transform: none; letter-spacing: 0; font-size: 11.5px; font-weight: 600;
  padding: 4px 9px;
  color: var(--hero-ink);
  border-color: color-mix(in srgb, var(--hero-ink) 32%, transparent);
  background: color-mix(in srgb, var(--hero-ink) 7%, transparent);
}
.hero__text h2 .hero__rename:hover {
  background: color-mix(in srgb, var(--hero-ink) 14%, transparent);
  border-color: color-mix(in srgb, var(--hero-ink) 50%, transparent);
}
.hero__text h2 .hero__rename .ico { width: 13.5px; height: 13.5px; }

.mode { --c: var(--accent); text-align: left; width: 100%; }
.mode.is-selected { border-color: var(--c); background: color-mix(in srgb, var(--c) 8%, var(--panel)); }
.mode__top { display: flex; align-items: center; gap: 10px; }
.mode__top .ico { color: var(--c); width: 18.5px; height: 18.5px; }
.mode h3 { font-size: 13.5px; font-weight: 700; }

/* The Bloom panel is the one card rendered in blue rather than the neutral
   panel colour. Everything is scoped to .card--bloom so no other card moves,
   and the tones are redefined per theme so it stays legible in Light Mode. */
.card--bloom {
  --bloom-bg:   #16243c;
  --bloom-tile: #1d2f4d;
  --bloom-line: #2b4269;
  background: var(--bloom-bg);
  border-color: var(--bloom-line);
}
[data-theme="light"] .card--bloom {
  --bloom-bg:   #e9f0fa;
  --bloom-tile: #ffffff;
  --bloom-line: #c1d4ee;
}

.bloom-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8.5px; margin-top: 13.5px; }
/* Transition background-COLOR, not the `background` shorthand: when a
   shorthand transition is fed from a var() that changes with the theme, the
   computed value can stick at the old colour and never repaint. */
.bloom-option { appearance: none; border: 1px solid var(--line); border-radius: var(--r-md); background-color: var(--panel-2); color: var(--text); padding: 11.5px 12.5px; text-align: left; cursor: pointer; transition: border-color .18s, background-color .18s, transform .18s; }
.card--bloom .bloom-option { background-color: var(--bloom-tile); border-color: var(--bloom-line); }
/* Declared outright rather than through the inherited variable, so the tile
   can never lag a theme change. */
[data-theme="light"] .card--bloom .bloom-option { background-color: #ffffff; border-color: #c1d4ee; }
.bloom-option:hover { border-color: var(--c); transform: translateY(-1px); }
.bloom-option.is-selected { border-color: var(--c); background-color: color-mix(in srgb, var(--c) 11%, var(--panel)); box-shadow: inset 0 0 0 1px var(--c); }
/* Selected tile keeps the subject accent, blended over the blue tile. */
.card--bloom .bloom-option.is-selected { background-color: color-mix(in srgb, var(--c) 13%, #1d2f4d); }
[data-theme="light"] .card--bloom .bloom-option.is-selected { background-color: color-mix(in srgb, var(--c) 13%, #ffffff); }
.bloom-option b { display: block; font-size: 11.5px; }
.bloom-option span { display: block; margin-top: 3.5px; color: var(--text-dim); font-size: 10px; line-height: 1.35; }
/* Honest availability. A level the bank cannot supply is shown as empty and
   disabled rather than quietly filled with lower-order questions. */
.bloom-option__n { margin-top: 5px !important; font-size: 9.5px !important; font-weight: 600; letter-spacing: .02em; color: var(--c) !important; opacity: .95; }
.bloom-option.is-empty { opacity: .5; cursor: not-allowed; }
.bloom-option.is-empty:hover { transform: none; border-color: var(--line); }
.bloom-option.is-empty .bloom-option__n { color: var(--text-dim) !important; }
.bloom-note { margin-top: 10px; color: var(--text-dim); font-size: 10px; line-height: 1.5; }

.pillbar { display: flex; gap: 8.5px; flex-wrap: wrap; }
.pill {
  padding: 7.5px 15px; border-radius: 999px; font-size: 11.5px; font-weight: 600;
  border: 1px solid var(--line); background: var(--panel-2); color: var(--text-dim);
  transition: all .18s var(--ease);
}
.pill:hover { color: var(--text); border-color: color-mix(in srgb, var(--c, var(--accent)) 55%, transparent); }
.pill.is-active { background: var(--c, var(--accent)); border-color: var(--c, var(--accent)); color: var(--accent-ink); }

.topic-list li {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 2px; border-bottom: 1px solid var(--line-soft);
  color: var(--text-dim); font-size: 12px;
}
.topic-list li:last-child { border-bottom: 0; }
.topic-list .dotmark { width: 6.5px; height: 6.5px; border-radius: 50%; background: var(--c, var(--accent)); flex: none; }
.topic-list .count { margin-left: auto; font-size: 10px; color: var(--text-mute); }

/* ---------- Quiz ---------- */
.quiz { --c: var(--accent); max-width: 787px; margin: 0 auto; }
.quiz__bar { display: flex; align-items: center; gap: 13.5px; flex-wrap: wrap; margin-bottom: 15px; }
.quiz__count { font-size: 11.5px; color: var(--text-dim); }
.quiz__count b { color: var(--text); }
.quiz__timer {
  margin-left: auto; display: inline-flex; align-items: center; gap: 6.5px;
  padding: 6.5px 11.5px; border-radius: 999px; font-variant-numeric: tabular-nums;
  border: 1px solid var(--line); background: var(--panel); font-size: 11.5px; font-weight: 600;
}
.quiz__timer.is-low { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 55%, transparent); }
.progress-track { height: 5px; border-radius: 999px; background: var(--line); overflow: hidden; margin-bottom: 18.5px; }
.progress-track i { display: block; height: 100%; background: var(--c); border-radius: 999px; transition: width .35s var(--ease); }

.qcard { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg); padding: 23px; }
.qcard__head { display: flex; align-items: flex-start; gap: 13.5px; }
.qcard__num {
  width: 33px; height: 33px; flex: none; display: grid; place-items: center; border-radius: 8.5px;
  background: color-mix(in srgb, var(--c) 14%, transparent); color: var(--c); font-weight: 800; font-size: 12.5px;
}
.qcard__text { font-size: 15px; line-height: 1.55; font-weight: 600; flex: 1; }
.qcard__bm { color: var(--text-mute); }
.qcard__bm.is-on { color: var(--warn); }
.qcard__bm.is-on svg { fill: currentColor; }
.qcard__topic { margin-top: 11.5px; margin-left: 47px; }

.options { display: flex; flex-direction: column; gap: 10px; margin-top: 20px; }
.option {
  display: flex; align-items: flex-start; gap: 11.5px; width: 100%; text-align: left;
  padding: 13.5px 15px; border-radius: var(--r-md);
  background: var(--panel-2); border: 1px solid var(--line);
  font-size: 13px; line-height: 1.5;
  transition: border-color .16s var(--ease), background .16s var(--ease), transform .12s var(--ease);
}
.option:hover { border-color: color-mix(in srgb, var(--c) 60%, transparent); background: var(--panel-hover); }
.option__key {
  width: 25px; height: 25px; flex: none; display: grid; place-items: center; border-radius: 6.5px;
  border: 1px solid var(--line); font-weight: 700; font-size: 11.5px; color: var(--text-dim);
}
.option.is-picked { border-color: var(--c); background: color-mix(in srgb, var(--c) 10%, transparent); }
.option.is-picked .option__key { background: var(--c); border-color: var(--c); color: var(--accent-ink); }
.option.is-right { border-color: var(--ok); background: color-mix(in srgb, var(--ok) 12%, transparent); }
.option.is-right .option__key { background: var(--ok); border-color: var(--ok); color: #04140a; }
.option.is-wrong { border-color: var(--bad); background: color-mix(in srgb, var(--bad) 12%, transparent); }
.option.is-wrong .option__key { background: var(--bad); border-color: var(--bad); color: #fff; }
.option.is-locked { pointer-events: none; }
.option__flag { margin-left: auto; font-size: 10px; font-weight: 700; letter-spacing: 0.5px; align-self: center; }

.explain {
  margin-top: 16.5px; padding: 13.5px 15px; border-radius: var(--r-md);
  border: 1px solid var(--line); background: var(--panel-2);
  border-left: 2.5px solid var(--c); font-size: 12px; line-height: 1.65; color: var(--text-dim);
}
.explain b { color: var(--text); }

.quiz__nav { display: flex; align-items: center; gap: 10px; margin-top: 18.5px; flex-wrap: wrap; }
.quiz__nav .spacer { flex: 1; }

.qmap { display: flex; flex-wrap: wrap; gap: 6.5px; margin-top: 18.5px; }
.qmap button {
  width: 32px; height: 32px; border-radius: 7.5px; font-size: 11px; font-weight: 700;
  border: 1px solid var(--line); background: var(--panel); color: var(--text-dim);
  transition: all .15s var(--ease);
}
.qmap button:hover { border-color: var(--c); color: var(--text); }
.qmap button.is-answered { background: color-mix(in srgb, var(--c) 18%, transparent); color: var(--text); border-color: color-mix(in srgb, var(--c) 45%, transparent); }
.qmap button.is-current { outline: 2px solid var(--c); outline-offset: 1px; color: var(--text); }

/* ---------- Result ---------- */
.score-ring { display: grid; place-items: center; gap: 3.5px; }
.score-ring svg { width: 141px; height: 141px; transform: rotate(-90deg); }
.score-ring .track { fill: none; stroke: var(--line); stroke-width: 12; }
.score-ring .fill { fill: none; stroke: var(--c, var(--accent)); stroke-width: 12; stroke-linecap: round; transition: stroke-dashoffset 1s var(--ease); }
.score-ring__val { position: absolute; text-align: center; }
.score-wrap { position: relative; display: grid; place-items: center; }
.score-wrap b { font-size: 32px; font-weight: 800; }
.score-wrap span { display: block; color: var(--text-dim); font-size: 11px; }

.review-item { border: 1px solid var(--line); border-radius: var(--r-md); padding: 15px 16.5px; background: var(--panel); }
.review-item + .review-item { margin-top: 10px; }
.review-item__q { font-weight: 600; font-size: 13px; line-height: 1.5; display: flex; gap: 10px; }
.review-item__tag { font-size: 9.5px; font-weight: 800; letter-spacing: 0.5px; padding: 2.5px 7.5px; border-radius: 999px; flex: none; height: fit-content; }
.tag--ok { color: var(--ok); background: color-mix(in srgb, var(--ok) 14%, transparent); }
.tag--bad { color: var(--bad); background: color-mix(in srgb, var(--bad) 14%, transparent); }
.tag--skip { color: var(--text-mute); background: var(--panel-2); }
.review-item__ans { margin-top: 10px; font-size: 11.5px; color: var(--text-dim); line-height: 1.7; }
.review-item__ans b { color: var(--text); font-weight: 600; }

/* ---------- Tables / lists ---------- */
.rowlist { display: flex; flex-direction: column; gap: 10px; }
.rowitem {
  display: flex; align-items: center; gap: 15px; width: 100%; text-align: left;
  padding: 13.5px 16.5px; background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-md);
  transition: border-color .18s var(--ease), background .18s var(--ease);
}
.rowitem:hover { border-color: color-mix(in srgb, var(--c, var(--accent)) 55%, transparent); background: var(--panel-2); }
.rowitem__main { flex: 1; min-width: 0; }
.rowitem__title { font-weight: 650; font-size: 12.5px; }
.rowitem__meta { color: var(--text-dim); font-size: 11px; margin-top: 4px; }
.rowitem__score { font-weight: 800; font-size: 15px; font-variant-numeric: tabular-nums; }

/* ---------- Modal ---------- */
.modal { position: fixed; inset: 0; z-index: 100; display: grid; place-items: center; padding: 16.5px; }
.modal[hidden] { display: none; }
.modal__scrim { position: absolute; inset: 0; background: rgba(0,0,0,.62); backdrop-filter: blur(2.5px); }
.modal__box {
  position: relative; width: min(502px, 100%); max-height: 86vh; display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-xl);
  box-shadow: var(--shadow); animation: pop .18s var(--ease);
}
.modal__head { display: flex; align-items: center; justify-content: space-between; gap: 13.5px; padding: 16.5px 18.5px; border-bottom: 1px solid var(--line); }
.modal__head h2 { font-size: 14px; font-weight: 700; }
.modal__body { padding: 18.5px; overflow-y: auto; font-size: 12px; line-height: 1.7; color: var(--text-dim); }
.modal__body h3 { color: var(--text); font-size: 12.5px; margin: 15px 0 5px; }
.modal__body h3:first-child { margin-top: 0; }
.modal__body a { color: var(--accent); }
.modal__foot { display: flex; justify-content: flex-end; gap: 8.5px; padding: 13.5px 18.5px; border-top: 1px solid var(--line); }
.modal__foot:empty { display: none; }
.faq { border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
.faq + .faq { margin-top: 8.5px; }
.faq summary { padding: 11px 13.5px; cursor: pointer; color: var(--text); font-weight: 600; font-size: 12px; list-style: none; display: flex; justify-content: space-between; gap: 10px; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; color: var(--accent); font-size: 15px; line-height: 1; }
.faq[open] summary::after { content: "−"; }
.faq p { padding: 0 13.5px 11.5px; }

/* ---------- Contributor toggle ---------- */
.rolebar {
  display: flex; align-items: center; gap: 8.5px; margin-top: auto;
  padding: 10px 11.5px; border-radius: var(--r-md); cursor: pointer;
  border: 1px solid var(--line); background: var(--panel-2);
  font-size: 11.5px; color: var(--text-dim);
  transition: border-color .18s var(--ease), color .18s var(--ease);
}
.rolebar:hover { border-color: color-mix(in srgb, var(--accent) 50%, transparent); color: var(--text); }
.rolebar input { position: absolute; opacity: 0; width: 0; height: 0; }
.rolebar__track {
  width: 30px; height: 16.5px; flex: none; border-radius: 999px;
  background: var(--line); position: relative; transition: background .2s var(--ease);
}
.rolebar__track i {
  position: absolute; top: 2.5px; left: 2.5px; width: 11.5px; height: 11.5px; border-radius: 50%;
  background: var(--text-mute); transition: transform .2s var(--ease), background .2s var(--ease);
}
.rolebar input:checked + .rolebar__track { background: color-mix(in srgb, var(--accent) 40%, transparent); }
.rolebar input:checked + .rolebar__track i { transform: translateX(13.5px); background: var(--accent); }
.rolebar input:focus-visible + .rolebar__track { outline: 2px solid var(--accent); outline-offset: 2px; }
body.is-contributor .rolebar { color: var(--text); border-color: color-mix(in srgb, var(--accent) 40%, transparent); }
body.is-admin .rolebar { cursor: default; }
body.is-admin .rolebar__track { background: color-mix(in srgb, var(--accent) 40%, transparent); }
body.is-admin .rolebar__track i { transform: translateX(13.5px); background: var(--accent); }

/* ---------- Account strip (admin portal) ---------- */
#accountBar { margin-top: 10px; }
.acct {
  display: flex; flex-direction: column; gap: 3.5px;
  padding: 11px 11.5px; border-radius: var(--r-md);
  border: 1px solid var(--line); background: var(--panel-2);
  font-size: 10.5px; color: var(--text-dim);
}
.acct b { color: var(--text); font-size: 11.5px; overflow-wrap: anywhere; }
.acct span { overflow-wrap: anywhere; }
.acct .btn { margin-top: 7.5px; justify-content: center; }
.acct__role { font-weight: 700; letter-spacing: 0.5px; }
.acct--ok { border-color: color-mix(in srgb, var(--ok) 45%, transparent); }
.acct--ok .acct__role { color: var(--ok); }
.acct--warn { border-color: color-mix(in srgb, var(--warn) 45%, transparent); }
.acct--warn .acct__role { color: var(--warn); }

/* ---------- Admin gate ---------- */
.gate__note { font-size: 10.5px; color: var(--text-mute); margin-top: 11.5px; }
.gate__actions { display: flex; gap: 8.5px; justify-content: center; flex-wrap: wrap; margin-top: 16.5px; }

/* ---------- Capacity meter ---------- */
.capline { display: flex; align-items: baseline; justify-content: space-between; gap: 13.5px; flex-wrap: wrap; }
.capline__num { font-size: 11.5px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.capline__num b { color: var(--text); font-size: 15px; }
.capline__meta { margin-top: 8.5px; font-size: 11px; color: var(--text-mute); }
.capline__hint { display: block; margin-top: 3.5px; }

/* ---------- Notices ---------- */
.notice {
  display: flex; align-items: center; gap: 11.5px; flex-wrap: wrap;
  padding: 11px 13.5px; border-radius: var(--r-md); font-size: 11.5px; line-height: 1.6;
  background: var(--panel-2); border: 1px solid var(--line); color: var(--text-dim);
}
.notice > div { flex: 1; min-width: 167px; }
.notice b { color: var(--text); }
.notice--warn { border-color: color-mix(in srgb, var(--warn) 45%, transparent); background: color-mix(in srgb, var(--warn) 8%, transparent); }
.notice--bad { border-color: color-mix(in srgb, var(--bad) 45%, transparent); background: color-mix(in srgb, var(--bad) 8%, transparent); color: var(--text); margin-top: 13.5px; }

/* ---------- Upload drop zone ---------- */
.drop {
  display: flex; flex-direction: column; align-items: center; gap: 6.5px; text-align: center;
  margin-top: 15px; padding: 28px 16.5px; cursor: pointer;
  border: 2px dashed var(--line); border-radius: var(--r-lg); background: var(--panel-2);
  transition: border-color .18s var(--ease), background .18s var(--ease);
}
.drop[hidden] { display: none; }
.drop:hover, .drop.is-over { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 7%, transparent); }
.drop b { color: var(--text); font-size: 12.5px; }
.drop span { font-size: 11px; color: var(--text-mute); }
.drop__ico { width: 28px; height: 28px; color: var(--accent); }

/* ---------- Upload progress ---------- */
.prog { margin-top: 16.5px; }
.prog__head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 8.5px; }
.prog__head span:first-child { color: var(--text); font-weight: 650; font-size: 12px; }
.prog__head span:last-child { font-variant-numeric: tabular-nums; color: var(--accent); font-weight: 700; }
.prog__detail { margin-top: 8.5px; font-size: 11px; color: var(--text-mute); min-height: 1.2em; }
.prog__stages { margin-top: 13.5px; display: flex; flex-direction: column; gap: 7.5px; }
.prog__stages li { display: flex; align-items: center; gap: 9px; font-size: 11.5px; color: var(--text-mute); }
.prog__stages .dotmark {
  width: 7.5px; height: 7.5px; border-radius: 50%; flex: none;
  background: var(--line); transition: background .2s var(--ease), box-shadow .2s var(--ease);
}
.prog__stages li.is-active { color: var(--text); }
.prog__stages li.is-active .dotmark { background: var(--accent); box-shadow: 0 0 0 3.5px color-mix(in srgb, var(--accent) 22%, transparent); }
.prog__stages li.is-done { color: var(--text-dim); }
.prog__stages li.is-done .dotmark { background: var(--ok); }

/* ---------- Review page ---------- */
.rev__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 15px; flex-wrap: wrap; }
.rev__actions { display: flex; gap: 8.5px; flex-wrap: wrap; margin-top: 16.5px; }

.revitem {
  --c: var(--accent);
  display: block; text-align: left; width: 100%;
  padding: 16.5px 18.5px; background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--r-md);
}
.revitem.is-rejected { border-color: color-mix(in srgb, var(--bad) 40%, transparent); background: color-mix(in srgb, var(--bad) 5%, transparent); }
.revitem__top { display: flex; align-items: center; gap: 8.5px; flex-wrap: wrap; }
.revitem__spacer { flex: 1; }
.revitem__q { margin-top: 11.5px; font-size: 13px; font-weight: 650; line-height: 1.55; }
.revitem__choices { margin-top: 10px; display: flex; flex-direction: column; gap: 6px; }
.revitem__choices li { font-size: 12px; color: var(--text-dim); line-height: 1.5; }
.revitem__choices li b { color: var(--text-mute); margin-right: 3.5px; }
.revitem__choices li.is-key { color: var(--text); }
.revitem__choices li.is-key b { color: var(--ok); }
.revitem__choices em { color: var(--ok); font-style: normal; font-size: 9.5px; font-weight: 800; letter-spacing: 0.5px; }
.revitem__src, .revitem__fix {
  margin-top: 11.5px; font-size: 11px; line-height: 1.6; color: var(--text-mute);
  padding-left: 10px; border-left: 2px solid var(--line);
}
.revitem__src b, .revitem__fix b { color: var(--text-dim); }
.revitem__issues { margin-top: 11.5px; display: flex; flex-direction: column; gap: 5px; }
.revitem__issues li { font-size: 10.5px; line-height: 1.5; color: var(--text-dim); }
.revitem__issues li b { font-family: ui-monospace, Consolas, monospace; font-size: 9.5px; margin-right: 5px; }
.revitem__issues .sev-error b { color: var(--bad); }
.revitem__issues .sev-warn b { color: var(--warn); }
.revitem__tools { display: flex; gap: 8.5px; margin-top: 15px; }
.tag--warn { color: var(--warn); background: color-mix(in srgb, var(--warn) 14%, transparent); }

.switch { display: inline-flex; align-items: center; gap: 6.5px; font-size: 11px; color: var(--text-dim); cursor: pointer; }
.switch input { width: 14px; height: 14px; accent-color: var(--accent); cursor: pointer; }
.switch:hover { color: var(--text); }

/* ---------- Inline editor ---------- */
.editor { margin-top: 15px; padding-top: 15px; border-top: 1px solid var(--line); display: flex; flex-direction: column; gap: 10px; }
.fld { display: flex; flex-direction: column; gap: 5px; font-size: 11px; color: var(--text-dim); }
.fld--row { flex-direction: row; align-items: center; gap: 8.5px; }
.fld__key { font-weight: 700; color: var(--text-mute); width: 13.5px; flex: none; }
.fld input[type="text"], .fld textarea {
  width: 100%; padding: 8.5px 10px; border-radius: 7.5px; font-size: 12px;
  background: var(--panel-2); border: 1px solid var(--line); color: var(--text); resize: vertical;
}
.fld input[type="text"]:focus, .fld textarea:focus { outline: none; border-color: var(--accent); }
.fld input[type="radio"] { width: 13.5px; height: 13.5px; accent-color: var(--ok); flex: none; cursor: pointer; }
.editor__actions { display: flex; gap: 8.5px; margin-top: 3.5px; }

/* ---------- Toast ---------- */
.toast {
  position: fixed; left: 50%; bottom: 23px; transform: translateX(-50%);
  z-index: 200; padding: 11px 18.5px; border-radius: 999px;
  background: var(--panel); border: 1px solid var(--line); box-shadow: var(--shadow);
  font-size: 11.5px; font-weight: 600; animation: toastIn .22s var(--ease);
}
@keyframes toastIn { from { opacity: 0; transform: translate(-50%, 10px); } }

/* ---------- Mobile ---------- */
.mobilebar { display: none; }
.scrim { display: none; }

@media (max-width: 900px) {
  .subject { flex-wrap: wrap; }
  .subject__arrow { display: none; }
  .subject__start { width: 100%; }
}
@media (max-width: 860px) {
  .bloom-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .mobilebar {
    display: flex; align-items: center; gap: 10px;
    position: sticky; top: 0; z-index: 60;
    padding: 8.5px 11.5px; background: var(--sidebar); border-bottom: 1px solid var(--line);
  }
  .mobilebar__title { font-weight: 700; font-size: 12.5px; }
  .sidebar {
    position: fixed; top: 0; left: 0; z-index: 80; height: 100vh;
    transform: translateX(-100%); transition: transform .25s var(--ease);
  }
  .sidebar.is-open { transform: none; }
  .sidebar__close.icon-btn { display: grid; }
  .scrim { display: block; position: fixed; inset: 0; z-index: 70; background: rgba(0,0,0,.55); }
  .scrim[hidden] { display: none; }
  .main { padding: 15px 13.5px 0; }
  .topbar__titles h1 { font-size: 18.5px; }
  .topbar__theme { justify-content: flex-start; }
  .subject { padding: 15px; gap: 13.5px; }
  .subject__icon { width: 42px; height: 42px; }
  .subject__icon svg { width: 20px; height: 20px; }
  .subject__name { font-size: 13.5px; }
  .qcard { padding: 16.5px; }
  .qcard__topic { margin-left: 0; }
  .footer { justify-content: center; text-align: center; }
}

@media (max-width: 460px) {
  .bloom-grid { grid-template-columns: 1fr; }
  .subjects--grid { grid-template-columns: 1fr; }
  .viewctl__text { display: none; }
  .topicpick__row { flex-direction: column; }
  .topicpick__manage, .topicpick__lock { align-self: stretch; text-align: center; }
}

/* ---------- Monitoring Board (admin) ---------- */
/* Live operations view. Colours come from theme variables throughout so the
   board reads correctly in all six themes, light mode included. */
.mon-toolbar {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px;
  padding: 11.5px 13.5px; margin-bottom: 13.5px;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg);
}
.mon-field { display: flex; flex-direction: column; gap: 4px; font-size: 10px; }
.mon-field > span { color: var(--text-mute); letter-spacing: .4px; text-transform: uppercase; font-weight: 700; }
.mon-field--grow { flex: 1 1 190px; }
.mon-field select, .mon-field input {
  background: var(--panel-2); color: var(--text);
  border: 1px solid var(--line); border-radius: 8px;
  padding: 7px 9px; font-size: 12px; font-family: inherit; width: 100%;
}
.mon-field select:focus, .mon-field input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.mon-note { margin-left: auto; color: var(--text-mute); font-size: 10.5px; }

/* The pulse is the one signal that the page is genuinely live. */
.mon-live { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 700;
            color: var(--ok); letter-spacing: .5px; text-transform: uppercase; }
.mon-live i { width: 8px; height: 8px; border-radius: 50%; background: var(--ok);
              box-shadow: 0 0 0 0 color-mix(in srgb, var(--ok) 70%, transparent);
              animation: monPulse 2s infinite; }
@keyframes monPulse {
  70% { box-shadow: 0 0 0 7px color-mix(in srgb, var(--ok) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ok) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) { .mon-live i { animation: none; } }

.mon-cards { display: grid; gap: 11.5px; grid-template-columns: repeat(auto-fit, minmax(178px, 1fr)); }
.mon-cards--3 { margin-top: 11.5px; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.mon-stat { text-align: left; }
.mon-stat .stat__num { font-size: 25px; }
.mon-stat__hint { color: var(--text-mute); font-size: 10px; margin-top: 5px; line-height: 1.4; }
.mon-stat--live { border-color: color-mix(in srgb, var(--ok) 45%, transparent); }
.mon-stat--live .stat__num { color: var(--ok); }

.mon-chart { overflow-x: auto; }
.mon-chart svg { width: 100%; min-width: 320px; height: 120px; color: var(--text-mute); display: block; }

.mon-rank { list-style: none; margin: 11.5px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.mon-rank li { display: grid; grid-template-columns: minmax(90px, 1.4fr) 2fr auto; align-items: center; gap: 9px; font-size: 11.5px; }
.mon-rank__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mon-rank__bar { background: var(--panel-2); border-radius: 999px; height: 7px; overflow: hidden; }
.mon-rank__bar i { display: block; height: 100%; border-radius: 999px; }
.mon-rank__n { font-weight: 700; font-variant-numeric: tabular-nums; }

.mon-feed { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.mon-feed li { display: flex; align-items: center; gap: 9px; padding: 8px 0;
               border-bottom: 1px solid var(--line); font-size: 11.5px; flex-wrap: wrap; }
.mon-feed li:last-child { border-bottom: 0; }
.mon-feed__dot { width: 7px; height: 7px; border-radius: 50%; background: var(--text-mute); flex: none; }
.mon-feed__dot.is-live { background: var(--ok); }
.mon-feed__id { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text-mute); }
.mon-feed__where { font-weight: 600; }
.mon-feed__meta { color: var(--text-mute); }
.mon-feed__ago { margin-left: auto; color: var(--text-mute); }

.mon-subj__head { display: flex; align-items: center; gap: 11.5px; }

.mon-tablewrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--panel); }
.mon-table { width: 100%; border-collapse: collapse; font-size: 11.5px; min-width: 780px; }
.mon-table th, .mon-table td { padding: 9px 11px; text-align: left; border-bottom: 1px solid var(--line); }
.mon-table thead th { position: sticky; top: 0; background: var(--panel-2); color: var(--text-mute);
                      font-size: 9.5px; letter-spacing: .5px; text-transform: uppercase; z-index: 1; }
.mon-table tbody tr:last-child td { border-bottom: 0; }
.mon-table tbody tr:hover { background: var(--panel-2); }
.mon-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.mon-table .is-zero { color: var(--text-mute); opacity: .5; }
.mon-empty { text-align: center; color: var(--text-mute); padding: 22px 11px; }

.mon-pill { display: inline-block; padding: 2.5px 8px; border-radius: 999px;
            font-size: 9.5px; font-weight: 700; letter-spacing: .4px; border: 1px solid transparent; }
.mon-pill--live { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 45%, transparent);
                  background: color-mix(in srgb, var(--ok) 12%, transparent); }
.mon-pill--ready { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 45%, transparent);
                   background: color-mix(in srgb, var(--warn) 12%, transparent); }
.mon-pill--locked, .mon-pill--empty {
  color: var(--text-mute); border-color: var(--line); background: var(--panel-2); }
.mon-chip--locked { color: var(--text-mute); border-color: var(--line); background: var(--panel-2); }

/* The live count sits beside the nav label, so it takes the "something is
   happening" colour rather than the pending-work colour Uploads uses. */
.nav__badge--live { background: var(--ok); color: var(--accent-ink); }

@media (max-width: 720px) {
  .mon-note { margin-left: 0; width: 100%; }
  .mon-feed__ago { margin-left: 0; }
  .mon-rank li { grid-template-columns: 1fr auto; }
  .mon-rank__bar { display: none; }
}

/* ---------- Sidebar promo: Monthly Quiz ----------

   The one control on the page that is meant to be noticed rather than found,
   so unlike the chat button's brief nudge this one keeps moving. Three layers,
   all slow and all small: the border and glow breathe over four seconds, a
   highlight sweeps the card every five, and the trophy rocks. Nothing here is
   fast, and nothing travels more than three pixels -- the aim is something
   alive in the corner of the eye, not something demanding to be looked at.

   AMBER, NOT THE ACCENT. --warn is defined by every one of the six themes, so
   the card follows the palette instead of importing a colour from outside it,
   and it stays clearly distinct from the green used for navigation and for
   every primary action. It is also, conveniently, the colour the trophy in the
   progress card already uses.

   Only box-shadow, transform and background-position animate, so the card
   cannot reflow the sidebar around it. */

.quizpromo {
  --qp: var(--warn);
  position: relative; overflow: hidden;
  display: flex; align-items: center; gap: 11px;
  padding: 12px 13px;
  border-radius: var(--r-lg);
  color: var(--text);
  background:
    linear-gradient(135deg,
      color-mix(in srgb, var(--qp) 15%, var(--panel-2)),
      color-mix(in srgb, var(--qp) 4%, var(--panel-2)));
  border: 1px solid color-mix(in srgb, var(--qp) 42%, transparent);
  animation: qpBreathe 4s var(--ease) infinite;
}
.quizpromo:hover {
  border-color: var(--qp);
  transform: translateY(-2px);
  transition: transform .18s var(--ease);
}
.quizpromo.is-active {
  border-color: var(--qp);
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--qp) 26%, var(--panel-2)),
    color-mix(in srgb, var(--qp) 8%, var(--panel-2)));
}

/* The sweep is a child rather than a background on the card itself, because the
   card already carries a gradient it must keep. overflow:hidden above is what
   confines it. */
.quizpromo__sheen {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg,
    transparent 38%,
    color-mix(in srgb, var(--qp) 26%, transparent) 50%,
    transparent 62%);
  transform: translateX(-140%);
  animation: qpSweep 5s var(--ease) infinite;
}

.quizpromo__icon {
  flex: none; width: 34px; height: 34px; display: grid; place-items: center;
  border-radius: 10px;
  color: var(--qp);
  background: color-mix(in srgb, var(--qp) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--qp) 34%, transparent);
  animation: qpRock 4s var(--ease) infinite;
}
.quizpromo__icon svg {
  width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.9;
  stroke-linecap: round; stroke-linejoin: round;
}

/* One line only. The subtitle was removed rather than shrunk: at 244px of
   sidebar there was no size at which it both fitted and stayed readable, and a
   line ending in an ellipsis looks like a bug. The title takes the space back
   and steps up from 12.5px, which also makes it the largest label in the
   sidebar -- appropriate, since this is the one item there being promoted. */
.quizpromo__text { min-width: 0; }
.quizpromo__text strong {
  display: flex; align-items: center; gap: 7px;
  font-size: 15px; font-weight: 700; letter-spacing: -.2px; line-height: 1.2;
  color: var(--text); white-space: nowrap;
}

/* A live indicator, so the card reads as an event rather than a banner. */
.quizpromo__dot {
  width: 6px; height: 6px; border-radius: 50%; flex: none;
  background: var(--qp);
  animation: qpPing 2s ease-out infinite;
}

.quizpromo__arrow {
  width: 15px; height: 15px; margin-left: auto; flex: none;
  fill: none; stroke: var(--qp); stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round;
  animation: qpNudge 4s var(--ease) infinite;
}

@keyframes qpBreathe {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--qp) 0%, transparent); }
  50%      { box-shadow: 0 0 16px -3px color-mix(in srgb, var(--qp) 55%, transparent); }
}
@keyframes qpSweep {
  0%        { transform: translateX(-140%); }
  45%, 100% { transform: translateX(140%); }
}
@keyframes qpRock {
  0%, 100% { transform: translateY(0) rotate(-3deg); }
  50%      { transform: translateY(-2px) rotate(3deg); }
}
@keyframes qpPing {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--qp) 60%, transparent); }
  70%  { box-shadow: 0 0 0 6px color-mix(in srgb, var(--qp) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--qp) 0%, transparent); }
}
@keyframes qpNudge {
  0%, 100% { transform: translateX(0); }
  50%      { transform: translateX(3px); }
}

/* Light Mode needs its own values, not the same ones on a pale background.
   --warn there is #d97706, and at the alphas above it landed at 1.59:1 against
   a white sidebar -- a border technically present and practically invisible,
   which is the entire affordance gone. The amber is deepened and the tints and
   border are strengthened so the card reads as a raised object on white the way
   it does on the dark themes. */
[data-theme="light"] .quizpromo {
  --qp: color-mix(in srgb, var(--warn) 86%, #000);
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--qp) 19%, #ffffff),
    color-mix(in srgb, var(--qp) 6%, #ffffff));
  border-color: color-mix(in srgb, var(--qp) 72%, transparent);
}
[data-theme="light"] .quizpromo__icon {
  background: color-mix(in srgb, var(--qp) 15%, #ffffff);
  border-color: color-mix(in srgb, var(--qp) 45%, transparent);
}
[data-theme="light"] .quizpromo.is-active {
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--qp) 30%, #ffffff),
    color-mix(in srgb, var(--qp) 12%, #ffffff));
}

/* Movement is the whole point of this card, so with it suppressed the card has
   to earn attention some other way: it keeps the full amber border and glow
   permanently rather than losing the treatment along with the animation. */
@media (prefers-reduced-motion: reduce) {
  .quizpromo, .quizpromo__sheen, .quizpromo__icon,
  .quizpromo__dot, .quizpromo__arrow { animation: none; }
  .quizpromo {
    border-color: var(--qp);
    box-shadow: 0 0 14px -4px color-mix(in srgb, var(--qp) 45%, transparent);
  }
  .quizpromo__sheen { opacity: 0; }
  .quizpromo:hover { transform: none; }
}

/* ---------- Monthly Quiz Challenge ----------
   The paper is scored and ranked, so the interface leans on size and contrast
   rather than decoration: a choice is a large target with an obvious selected
   state, and the irreversible action is the only primary button on its screen. */

.nav__badge--new { background: var(--info); color: #04121b; letter-spacing: .5px; }

.quiz-intro { display: flex; gap: 15px; align-items: flex-start; flex-wrap: wrap; }
.quiz-next { margin-left: auto; white-space: nowrap; color: var(--text-dim); font-size: 11px; }
.quiz-next b { color: var(--accent); }

.quizcards { display: grid; gap: 13.5px; grid-template-columns: repeat(auto-fit, minmax(258px, 1fr)); }
.quizcard { display: flex; flex-direction: column; gap: 13.5px; }
.quizcard.is-shut { opacity: .58; }
.quizcard__head { display: flex; gap: 12px; align-items: flex-start; }
.quizcard__icon {
  width: 42px; height: 42px; flex: none; display: grid; place-items: center;
  border-radius: 12px; color: var(--c, var(--accent));
  background: color-mix(in srgb, var(--c, var(--accent)) 13%, transparent);
  border: 1px solid color-mix(in srgb, var(--c, var(--accent)) 34%, transparent);
}
.quizcard__icon svg { width: 21px; height: 21px; fill: none; stroke: currentColor; stroke-width: 1.9;
                      stroke-linecap: round; stroke-linejoin: round; }
.quizcard__name { font-size: 13.5px; font-weight: 700; }
.quizcard__meta { font-size: 11px; color: var(--text-dim); margin-top: 4px; }
.quizcard__meta.is-done { color: var(--ok); font-weight: 600; }
.quizcard__go { margin-top: auto; }

/* ---- name gate ---- */
.quizgate__form { display: flex; gap: 9px; margin-top: 15px; flex-wrap: wrap; }
.quizgate__input {
  flex: 1 1 220px; padding: 11px 13px; border-radius: var(--r-md); font-size: 13px;
  background: var(--panel-2); border: 1px solid var(--line); color: var(--text);
}
.quizgate__input:focus { outline: none; border-color: var(--accent); }
.quizgate__err {
  margin-top: 11px; padding: 9px 12px; border-radius: 8px; font-size: 11.5px; color: var(--bad);
  background: color-mix(in srgb, var(--bad) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--bad) 35%, transparent);
}
.quizgate__note { margin-top: 11px; font-size: 10.5px; color: var(--text-mute); }
.quizgate__note code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

/* ---- brief ---- */
.quizbrief__who {
  display: inline-block; font-size: 10.5px; color: var(--text-dim);
  padding: 4px 10px; border-radius: 999px; background: var(--panel-2);
  border: 1px solid var(--line); margin-bottom: 11px;
}
.quizbrief__who b { color: var(--accent); }
.quizbrief__rules { margin-top: 15px; display: flex; flex-direction: column; gap: 9px; }
.quizbrief__rules li {
  position: relative; padding-left: 22px; font-size: 12.5px; color: var(--text-dim); line-height: 1.55;
}
.quizbrief__rules li::before {
  content: ""; position: absolute; left: 5px; top: 7px; width: 7px; height: 7px;
  border-radius: 50%; background: var(--accent); opacity: .7;
}
.quizbrief__rules b { color: var(--text); }
.quizbrief__go { display: flex; gap: 10px; margin-top: 20px; flex-wrap: wrap; }
.btn--lg { padding: 13px 22px; font-size: 13px; }

/* ---- runner ---- */
.quizrun__bar { margin-bottom: 15px; }
.quizrun__prog { height: 6px; border-radius: 999px; background: var(--line); overflow: hidden; }
.quizrun__prog i {
  display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--accent) 45%, #ffd166));
  transition: width .3s var(--ease);
}
.quizrun__meta {
  display: flex; gap: 13.5px; align-items: center; flex-wrap: wrap;
  margin-top: 8px; font-size: 11px; color: var(--text-dim);
}
.quizrun__meta b { color: var(--text); }
.quizrun__clock {
  margin-left: auto; font-variant-numeric: tabular-nums; font-weight: 700; color: var(--accent);
}
.quizrun__card { padding: 20px; }
.quizrun__topic {
  font-size: 10px; font-weight: 700; letter-spacing: .6px; text-transform: uppercase;
  color: var(--text-mute); margin-bottom: 9px;
}
.quizrun__stem { font-size: 16px; font-weight: 700; line-height: 1.5; }
.quizrun__choices { display: flex; flex-direction: column; gap: 9px; margin-top: 18px; }

.quizopt {
  display: flex; align-items: flex-start; gap: 12px; width: 100%; text-align: left;
  padding: 13px 15px; border-radius: var(--r-md);
  background: var(--panel-2); border: 1px solid var(--line); color: var(--text);
  font-size: 13px; line-height: 1.5;
  transition: border-color .16s var(--ease), background .16s var(--ease);
}
.quizopt:hover { border-color: color-mix(in srgb, var(--accent) 50%, transparent); background: var(--panel-hover); }
.quizopt__key {
  flex: none; width: 25px; height: 25px; display: grid; place-items: center;
  border-radius: 7px; font-size: 11.5px; font-weight: 700;
  background: var(--panel); border: 1px solid var(--line); color: var(--text-dim);
}
.quizopt.is-picked {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.quizopt.is-picked .quizopt__key { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.quizopt__text { padding-top: 3px; }

.quizrun__nav { display: flex; align-items: center; gap: 11.5px; margin-top: 15px; flex-wrap: wrap; }
.quizrun__dots { display: flex; gap: 5px; flex-wrap: wrap; flex: 1; justify-content: center; }
.quizdot {
  width: 26px; height: 26px; border-radius: 7px; font-size: 10.5px; font-weight: 700;
  background: var(--panel); border: 1px solid var(--line); color: var(--text-mute);
  transition: border-color .15s var(--ease), color .15s var(--ease);
}
.quizdot:hover { border-color: var(--accent); color: var(--text); }
.quizdot.is-done { color: var(--text); border-color: color-mix(in srgb, var(--accent) 40%, transparent); }
.quizdot.is-here { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }

/* ---- result ---- */
.quizdone__flash {
  display: inline-block; font-size: 10px; font-weight: 800; letter-spacing: .6px;
  text-transform: uppercase; padding: 4px 11px; border-radius: 999px; margin-bottom: 11px;
  color: var(--ok); background: color-mix(in srgb, var(--ok) 13%, transparent);
  border: 1px solid color-mix(in srgb, var(--ok) 40%, transparent);
}
.quizdone__grid {
  display: grid; gap: 11.5px; margin-top: 16px;
  grid-template-columns: repeat(auto-fit, minmax(122px, 1fr));
}
.quizdone__grid > div {
  padding: 13px 15px; border-radius: var(--r-md);
  background: var(--panel-2); border: 1px solid var(--line);
}
.quizdone__k {
  display: block; font-size: 9.5px; font-weight: 700; letter-spacing: .5px;
  text-transform: uppercase; color: var(--text-mute); margin-bottom: 6px;
}
.quizdone__grid b { font-size: 19px; font-weight: 800; color: var(--accent); display: block;
                    overflow-wrap: anywhere; line-height: 1.2; }
.quizdone__grid small { font-size: 10.5px; color: var(--text-mute); }
.quizdone__note { margin-top: 15px; font-size: 11.5px; color: var(--text-dim); }

/* ---- leaderboard ---- */
/* Subject switcher on the hub. One table at a time: three stacked tables of
   twenty rows is a wall, not a summary. Each tab carries its subject's own
   accent so the selected board is identifiable at a glance. */
.quiztabs { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 13.5px; }
.quiztab {
  padding: 8px 14px; border-radius: 999px; font-size: 11.5px; font-weight: 600;
  color: var(--text-dim); background: var(--panel); border: 1px solid var(--line);
  transition: color .16s var(--ease), border-color .16s var(--ease), background .16s var(--ease);
}
.quiztab:hover { color: var(--text); border-color: color-mix(in srgb, var(--c, var(--accent)) 55%, transparent); }
.quiztab.is-on {
  color: var(--text);
  background: color-mix(in srgb, var(--c, var(--accent)) 14%, transparent);
  border-color: color-mix(in srgb, var(--c, var(--accent)) 60%, transparent);
}

.quizboard__list { display: flex; flex-direction: column; }
.quizrow {
  display: grid; grid-template-columns: 34px 1fr auto auto; gap: 11.5px; align-items: center;
  padding: 9px 4px; border-bottom: 1px solid var(--line); font-size: 12.5px;
}
.quizrow:last-child { border-bottom: 0; }
.quizrow__rank {
  display: grid; place-items: center; width: 26px; height: 26px; border-radius: 8px;
  font-size: 11px; font-weight: 800; color: var(--text-mute);
  background: var(--panel-2); border: 1px solid var(--line);
}
.quizrow__rank.is-top { color: var(--accent-ink); background: var(--accent); border-color: var(--accent); }
.quizrow__name { overflow-wrap: anywhere; font-weight: 600; }
.quizrow__name em { font-style: normal; font-size: 9px; font-weight: 800; letter-spacing: .5px;
                    text-transform: uppercase; color: var(--accent); margin-left: 5px; }
.quizrow__score { font-weight: 800; font-variant-numeric: tabular-nums; }
.quizrow__score small { color: var(--text-mute); font-weight: 600; }
.quizrow__time { color: var(--text-dim); font-variant-numeric: tabular-nums; min-width: 46px; text-align: right; }
.quizrow.is-me {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border-radius: 8px; padding-left: 8px; padding-right: 8px;
}

@media (max-width: 560px) {
  .quizrun__stem { font-size: 14.5px; }
  .quizrun__card { padding: 16px; }
  .quizopt { padding: 11px 12px; font-size: 12.5px; }
  .quizrun__dots { order: 3; width: 100%; justify-content: flex-start; }
  .quiz-next { margin-left: 0; }
}

/* ---------- Anonymous Chat ----------
   A right-hand drawer on desktop and a full-height sheet on a phone. Every
   colour comes from a theme variable, so the room reads correctly in all six
   themes including Light Mode -- the same rule the Monitoring Board follows.

   The one exception is the per-speaker hue, which is derived from an
   anonymous id in js/chat.js and handed in as --hue. Its lightness is set
   here rather than there, because a value legible on a near-black panel is
   invisible on white. */

.btn--chat { position: relative; }
/* Unread count on the topbar button. Sits over the corner rather than in the
   flow, so opening and closing the room never reflows the button row. */
.btn__badge {
  position: absolute; top: -6px; right: -6px;
  min-width: 17px; padding: 0 4.5px;
  font-style: normal; font-size: 9px; font-weight: 700; line-height: 17px;
  text-align: center; border-radius: 999px;
  background: var(--bad); color: #fff;
  box-shadow: 0 0 0 2px var(--bg);
}

/* ---------- the chat button's attention cue ----------

   THE CUE IS INTERMITTENT, AND IT ENDS.

   A continuously animating control in a persistent toolbar stops being a
   signal within about thirty seconds: the eye filters it out, and anyone it
   does not filter out is being irritated rather than informed. So the whole
   9-second cycle is quiet except for roughly the first second -- a single
   ripple, a slow sheen across the face, and a 1px lift -- and then nothing.

   It also stops for good once the room has been opened. The cue exists to
   answer "what is that?", and that question is only asked once; js/chat.js
   records the first open in localStorage and marks the button `is-seen`,
   which removes the animation on this and every later visit. After that the
   only thing that moves is the unread badge, which is a real event.

   Nothing here animates layout. Ripple is box-shadow, sheen is
   background-position, lift is transform -- all compositor-friendly, so the
   cue costs no reflow and cannot shift the toolbar around it. */

@keyframes chatRipple {
  0%   { box-shadow: 0 0 0 0   color-mix(in srgb, var(--accent) 30%, transparent); }
  9%   { box-shadow: 0 0 0 7px color-mix(in srgb, var(--accent) 0%,  transparent); }
  100% { box-shadow: 0 0 0 0   color-mix(in srgb, var(--accent) 0%,  transparent); }
}
@keyframes chatSheen {
  0%   { background-position: 190% 0; }
  13%  { background-position: -90% 0; }
  100% { background-position: -90% 0; }
}
@keyframes chatLift {
  0%, 11%, 100% { transform: none; }
  4%            { transform: translateY(-1px); }
}

.btn--chat:not(.is-seen) {
  animation: chatRipple 9s var(--ease) 2.5s infinite,
             chatLift   9s var(--ease) 2.5s infinite;
}
/* The sheen rides a pseudo-element and moves its BACKGROUND rather than
   itself, so it never leaves the button's box. Translating a child would have
   meant `overflow: hidden` on the button, which would clip the unread badge
   sitting deliberately outside the corner. */
.btn--chat:not(.is-seen)::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(105deg,
    transparent 42%,
    color-mix(in srgb, var(--accent) 18%, transparent) 50%,
    transparent 58%);
  background-size: 260% 100%;
  background-position: 190% 0;
  animation: chatSheen 9s var(--ease) 2.5s infinite;
}
/* Anyone hovering, focusing or already in the room has found it. Stop. */
.btn--chat:hover, .btn--chat:focus-visible,
.btn--chat[aria-expanded="true"] { animation: none; }
.btn--chat:hover::before, .btn--chat:focus-visible::before,
.btn--chat[aria-expanded="true"]::before { animation: none; opacity: 0; }

/* An arriving message is an event, not an advertisement, so the badge gets a
   single pop rather than a loop. It re-runs because js/chat.js reinserts the
   element's animation on each change. */
@keyframes chatBadgePop {
  0%   { transform: scale(.4); opacity: 0; }
  60%  { transform: scale(1.15); opacity: 1; }
  100% { transform: none; opacity: 1; }
}
.btn__badge { animation: chatBadgePop .34s var(--ease); }

@media (prefers-reduced-motion: reduce) {
  .btn--chat:not(.is-seen),
  .btn--chat:not(.is-seen)::before,
  .btn__badge { animation: none; }
  /* Without motion the cue becomes a static tint — still a signal, no movement. */
  .btn--chat:not(.is-seen) {
    border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  }
}

.chat { position: fixed; inset: 0; z-index: 120; }
.chat__scrim { position: absolute; inset: 0; background: rgba(0,0,0,.5); backdrop-filter: blur(2px); }
.chat__box {
  position: absolute; top: 0; right: 0; height: 100%;
  width: min(420px, 100%);
  display: flex; flex-direction: column;
  background: var(--sidebar); border-left: 1px solid var(--line);
  box-shadow: var(--shadow);
  animation: chatIn .22s var(--ease);
}
@keyframes chatIn { from { transform: translateX(24px); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .chat__box { animation: none; } }

.chat__head {
  display: flex; align-items: center; gap: 11.5px; flex: none;
  padding: 13.5px 15px; border-bottom: 1px solid var(--line);
}
.chat__mark {
  width: 34px; height: 34px; flex: none; display: grid; place-items: center;
  border-radius: 50%; color: var(--accent);
  background: radial-gradient(circle at 50% 40%, var(--accent-soft), transparent 72%);
}
.chat__ident { flex: 1; min-width: 0; }
.chat__ident h2 { font-size: 13.5px; font-weight: 700; }
.chat__status { font-size: 10.5px; color: var(--text-dim); margin-top: 2.5px; }
.chat__status--ok { color: var(--ok); }
.chat__status--bad { color: var(--bad); }
/* --ok is a mid green, which carries a dark panel easily and only reaches
   3.3:1 on Light Mode's white. This line is 10.5px, so it needs 4.5:1 --
   darkened here rather than in the theme block, which other components share. */
[data-theme="light"] .chat__status--ok { color: color-mix(in srgb, var(--ok) 70%, #000); }

/* ---- name gate ---- */
.chat__gate { flex: 1; overflow-y: auto; padding: 20px 18.5px; }
.chat__gate h3 { font-size: 13px; font-weight: 700; }
.chat__gate > p { font-size: 11.5px; line-height: 1.65; color: var(--text-dim); margin-top: 8px; }
.chat__nameform { margin-top: 15px; display: flex; flex-direction: column; gap: 9px; }
.chat__nameinput {
  width: 100%; padding: 10px 12px; border-radius: var(--r-md); font-size: 12.5px;
  background: var(--panel-2); border: 1px solid var(--line); color: var(--text);
}
.chat__nameinput:focus { outline: none; border-color: var(--accent); }
.chat__namebtns { display: flex; gap: 8.5px; }
.chat__namebtns .btn { flex: 1; }
.chat__fineprint {
  margin-top: 18px; padding-top: 13.5px; border-top: 1px solid var(--line);
  font-size: 10.5px; line-height: 1.6; color: var(--text-mute);
}
.chat__err {
  font-size: 10.5px; line-height: 1.55; color: var(--bad);
  padding: 8px 10px; border-radius: 8px; margin-top: 10px;
  background: color-mix(in srgb, var(--bad) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--bad) 35%, transparent);
}

/* ---- message log ---- */
.chat__log {
  flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  padding: 15px 15px 6px; display: flex; flex-direction: column; gap: 3px;
}
.chat__empty { color: var(--text-mute); font-size: 11.5px; text-align: center; padding: 32px 15px; line-height: 1.6; }

.chat-msg { position: relative; padding: 4px 0; }
.chat-msg.is-grouped { padding-top: 0; }
.chat-msg__head { display: flex; align-items: baseline; gap: 6.5px; flex-wrap: wrap; margin-top: 9px; }
.chat-msg:first-child .chat-msg__head { margin-top: 0; }
.chat-msg__who {
  font-size: 11.5px; font-weight: 700;
  color: hsl(var(--hue) 62% 66%);
  overflow-wrap: anywhere;
}
/* 28% rather than the 34% this started at. The hue is whatever an id hashes
   to, so the value has to hold for the WORST of the 360 — which is yellow,
   around hue 60, and only clears 4.5:1 on white once it is this dark. */
[data-theme="light"] .chat-msg__who { color: hsl(var(--hue) 62% 28%); }
.chat-msg__tag {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 9px; color: var(--text-mute); letter-spacing: .3px;
}
.chat-msg__you {
  font-size: 8.5px; font-weight: 700; letter-spacing: .5px; text-transform: uppercase;
  padding: 1px 5.5px; border-radius: 999px;
  color: var(--accent); background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
}
.chat-msg__time { font-size: 9.5px; color: var(--text-mute); margin-left: auto; }
.chat-msg__body {
  margin-top: 4px; padding: 8px 11px; border-radius: var(--r-md);
  background: var(--panel); border: 1px solid var(--line);
  font-size: 12px; line-height: 1.6; color: var(--text);
  /* User text is never trusted to lay itself out: an unbroken 400-character
     run would otherwise push the drawer sideways for everyone reading it. */
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.chat-msg.is-mine .chat-msg__body {
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 32%, transparent);
}
.chat-msg__del {
  position: absolute; top: 4px; right: 0;
  font-size: 9px; font-weight: 700; letter-spacing: .3px;
  color: var(--text-mute); padding: 2px 6px; border-radius: 999px;
  opacity: 0; transition: opacity .15s var(--ease), color .15s var(--ease);
}
.chat-msg:hover .chat-msg__del,
.chat-msg__del:focus-visible { opacity: 1; }
.chat-msg__del:hover { color: var(--bad); }
.chat-msg__del[data-armed="1"] { opacity: 1; color: var(--bad); }
/* Touch has no hover, so the control has to be permanently visible there or
   it does not exist at all. */
@media (hover: none) { .chat-msg__del { opacity: .75; } }

/* ---- composer ---- */
.chat__foot { flex: none; padding: 10px 15px 13.5px; border-top: 1px solid var(--line); }
.chat__foot .chat__err { margin: 0 0 9px; }
.chat__composer { display: flex; align-items: flex-end; gap: 8.5px; }
.chat__input {
  flex: 1; min-width: 0; resize: none; max-height: 132px;
  padding: 9.5px 11.5px; border-radius: var(--r-md); font-size: 12px; line-height: 1.5;
  background: var(--panel-2); border: 1px solid var(--line); color: var(--text);
}
.chat__input:focus { outline: none; border-color: var(--accent); }
.chat__send { flex: none; padding: 9.5px 12.5px; }
.chat__meta {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  margin-top: 9px; font-size: 10.5px; color: var(--text-mute);
}
.chat__meta b { color: var(--text-dim); }
.chat__link { font-size: 10.5px; font-weight: 600; color: var(--accent); padding: 0; }
.chat__link:hover { text-decoration: underline; }

@media (max-width: 620px) {
  .chat__box { width: 100%; border-left: 0; }
  /* The label is the first thing to go: three buttons and a heading do not fit
     across a phone, and the icon plus its aria-label still say what it is. */
  .btn--chat .btn__text { display: none; }
  .topbar__actions { flex-wrap: wrap; }
}

/* The drawer scrolls its own log; the page behind it must not move with it. */
body.chat-open { overflow: hidden; }
