/* ============================================================
   CSA Compass — Design System
   Simple & flat: white + blue (light), black + blue (dark).
   No gradients, no glows, no glass. Clarity first.
   ============================================================ */

:root {
  --bg: #212121;
  --bg-soft: #282828;
  --bg-elevated: #2e2e2e;
  --surface: #282828;
  --surface-strong: #353535;
  --border: #3d3d3d;
  --border-strong: #4d4d4d;
  --text: #f5f5f5;
  --text-dim: #b0b0b0;
  --text-faint: #888888;
  --accent: #3b82f6;
  --accent-strong: #2563eb;
  --accent-soft: rgba(59, 130, 246, 0.14);
  --success: #22c55e;
  --success-strong: #16a34a;
  --success-soft: rgba(34, 197, 94, 0.14);
  --dot: rgba(255, 255, 255, 0.06);
  --danger: #f16161;
  --danger-strong: #dc2626;
  --danger-soft: rgba(239, 68, 68, 0.14);
  --warning: #eab308;
  --code-bg: #262626;
  --code-text: #e5e5e5;
  --radius: 10px;
  --radius-sm: 8px;
  --shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
  --font: "Poppins", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --sb-w: 250px;
  --sb-w-min: 72px;
  --mono: "JetBrains Mono", "SF Mono", "Fira Code", Menlo, Consolas, monospace;
}

[data-theme="light"] {
  --bg: #f6f6f8;
  --bg-soft: #eeeef0;
  --bg-elevated: #ffffff;
  --surface: #ffffff;
  --surface-strong: #ececee;
  --border: #dddde1;
  --border-strong: #c7c7cc;
  --text: #0f172a;
  --text-dim: #475569;
  --text-faint: #5b6b7f;
  --accent: #2563eb;
  --accent-strong: #1d4ed8;
  --accent-soft: rgba(37, 99, 235, 0.08);
  --code-bg: #f1f5f9;
  --code-text: #0f172a;
  --shadow: 0 1px 3px rgba(15, 23, 42, 0.08);
  --dot: rgba(15, 23, 42, 0.07);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  line-height: 1.6;
  min-height: 100vh;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  transition: background 0.2s ease, color 0.2s ease;
}

/* ---- theme flip ----------------------------------------------------------
   Two dozen rules animate a theme-dependent property so hovers feel smooth,
   each with its own duration (0.15s / 0.2s / 0.3s), while plenty of surfaces
   (navbar, sidebar, most cards) have no transition at all. On a palette flip
   that mixture is what read as lag: some things snapped, others drifted, and
   they finished at different moments.

   setTheme() hangs .theme-x on <html> for the length of the flip, which puts
   EVERY element on one identical curve — same duration, same easing, no delay.
   Surfaces that never transitioned now join in, which is what makes it land as
   a single motion. Outside the flip these rules are gone, so ordinary hover
   transitions keep their own timings.

   Only SURFACES are listed below — colour/fill/stroke are deliberately absent.
   Two reasons:

   1. Crossfading text against the surface under it walks both to the same
      mid-grey: halfway between #0f172a->#f5f5f5 and #f6f6f8->#212121 everything
      sits near rgb(140), measuring 1.08:1. The text is briefly invisible, worst
      on the largest bold text.
   2. Transitioning `color` per element cannot be kept in step. A transition is
      only scheduled where the computed colour actually changes, and `a { color:
      inherit }` (the brand lockup) gets none at all — so it snapped at 0ms while
      every other heading stepped at 110ms. That mismatch is the stagger.

   Instead setTheme() pins the foreground CSS variables to their current values,
   flips the theme so surfaces begin dissolving, then releases the variables at
   the midpoint. Custom properties are not transitioned, so every piece of text
   on the page changes in a single style recalculation — same instant, whatever
   its nesting or specificity. At that midpoint the outgoing and incoming text
   measure 3.10:1 and 6.90:1 against the half-faded background, so nothing ever
   washes out. Keep 220ms in sync with THEME_MS in app.js. */
html.theme-x,
html.theme-x *,
html.theme-x *::before,
html.theme-x *::after {
  transition-property: background-color, border-color, box-shadow,
                       outline-color !important;
  transition-duration: 220ms !important;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1) !important;
  transition-delay: 0s !important;
}

/* Boot applies the stored theme with no animation at all — there is nothing to
   crossfade from on first paint. Also the reduced-motion path. */
html.theme-instant,
html.theme-instant *,
html.theme-instant *::before,
html.theme-instant *::after {
  transition: none !important;
}

@media (prefers-reduced-motion: reduce) {
  html.theme-x,
  html.theme-x *,
  html.theme-x *::before,
  html.theme-x *::after {
    transition-duration: 0s !important;
  }
}

/* Decorative background removed — keep the page clean */
.bg-orbs { display: none; }

::selection { background: var(--accent-soft); }

a { color: inherit; text-decoration: none; }
img { max-width: 100%; }
button { font-family: inherit; cursor: pointer; }

.container { width: min(1120px, 92%); margin: 0 auto; }

/* ============ Card (formerly glass) ============ */
.glass {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: border-color 0.2s ease;
}
.glass.hoverable:hover {
  background: var(--surface-strong);
}

/* ============ Buttons ============ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 11px 22px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  font-size: 0.95rem;
  font-weight: 600;
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.btn:active { transform: scale(0.98); }
.btn-primary {
  background: var(--accent);
  color: #fff;
}
.btn-primary:hover { background: var(--accent-strong); }
/* Test is the "check my work" action, so it takes the same green the site uses
   for a correct answer, keeping Run as the neutral primary. */
.btn-success {
  background: var(--success);
  color: #fff;
}
.btn-success:hover { background: var(--success-strong); }
.btn-success:disabled { background: var(--success); }
.btn-ghost {
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border-strong);
}
.btn-ghost:hover { border-color: var(--accent); color: var(--accent); }
.btn-danger {
  background: transparent;
  color: var(--danger);
  border: 1px solid var(--danger);
}
.btn-danger:hover { background: var(--danger-soft); }
.btn-discord {
  background: #5865f2;
  color: #fff;
}
.btn-discord:hover { background: #4752c4; }
.btn-google {
  background: #fff;
  color: #1f1f1f;
  border: 1px solid #dadce0;
}
.btn-google:hover { background: #f8f9fa; }
.btn-google .ico { width: 18px; height: 18px; }
.btn-danger-solid {
  background: var(--danger);
  color: #fff;
}
.btn-danger-solid:hover { background: var(--danger-strong); }
.btn-sm { padding: 8px 16px; font-size: 0.85rem; }
/* A square icon-only button that sits exactly level with the text buttons
   beside it. Used for the Quick Reference book.

   The ::before is a zero-width space, and it is doing real work: with no text
   inside, the button's height comes from the 17px icon instead of a text line
   box, so it rendered 8px shorter than the Run button next to it. The
   zero-width space restores a proper line box using the button's own font
   metrics — so it matches at any size, .btn and .btn-sm alike, without
   hardcoding a height that would drift if the type scale ever changed.

   gap:0 matters as much as the ::before: .btn sets gap:8px between its flex
   items, and the zero-width space counts as one — so the icon was being
   centred as "spacer + 8px + icon" and sat 4px right of true centre. */
.btn-icon { padding-left: 0; padding-right: 0; width: 44px; gap: 0; }
.btn-icon::before { content: "\200b"; }
.btn-icon.btn-sm { width: 36px; }
.btn-icon .ico { width: 17px; height: 17px; }
.btn-block { width: 100%; }
.btn[disabled] { opacity: 0.5; cursor: not-allowed; transform: none !important; }

/* ============ Navbar ============ */
.navbar {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}
.nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 0;
}
.brand { display: flex; align-items: center; gap: 12px; font-weight: 800; font-size: 1.15rem; }
.brand .logo-img {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  object-fit: cover;
}
.brand span em { font-style: normal; color: var(--accent); }
.nav-links { display: flex; align-items: center; gap: 22px; }
.nav-links a:not(.btn) { color: var(--text-dim); font-weight: 500; font-size: 0.93rem; transition: color 0.2s; }
.nav-links a:not(.btn):hover { color: var(--text); }

.icon-btn {
  width: 38px; height: 38px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm);
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text);
  font-size: 1rem;
  transition: background 0.2s, border-color 0.2s;
}
.icon-btn:hover { background: var(--surface-strong); }

/* Hamburger */
.hamburger {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  padding: 8px;
}
.hamburger span {
  width: 24px; height: 2px;
  background: var(--text);
  border-radius: 2px;
  transition: transform 0.3s ease, opacity 0.3s ease;
}
.hamburger.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.hamburger.open span:nth-child(2) { opacity: 0; }
.hamburger.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

.mobile-menu {
  display: none;
  flex-direction: column;
  gap: 6px;
  padding: 12px 4%;
  border-bottom: 1px solid var(--border);
  background: var(--bg);
}
.mobile-menu a {
  padding: 12px 14px;
  border-radius: var(--radius-sm);
  color: var(--text-dim);
  font-weight: 500;
}
.mobile-menu a:hover { background: var(--surface-strong); color: var(--text); }
.mobile-menu.open { display: flex; }

/* ============ Hero (landing) ============ */
.hero {
  padding: 96px 0 72px;
  text-align: center;
}
.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 16px;
  border-radius: 999px;
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  color: var(--accent);
  font-size: 0.83rem;
  font-weight: 600;
  margin-bottom: 26px;
}
.hero h1 {
  font-size: clamp(2.4rem, 6vw, 4rem);
  font-weight: 800;
  line-height: 1.12;
  letter-spacing: -0.02em;
  margin-bottom: 20px;
}
.hero h1 .grad { color: var(--accent); }
.hero p.sub {
  max-width: 640px;
  margin: 0 auto 36px;
  color: var(--text-dim);
  font-size: 1.13rem;
}
.hero-cta { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }
.hero-logo {
  width: 88px; height: 88px;
  border-radius: 50%;
  object-fit: cover;
  margin: 0 auto 28px;
  display: block;
}

.stats-row {
  display: flex;
  justify-content: center;
  gap: 14px;
  flex-wrap: wrap;
  margin-top: 52px;
}
.stat-chip {
  padding: 16px 28px;
  text-align: center;
  min-width: 150px;
}
.stat-chip .num {
  font-size: 1.7rem;
  font-weight: 800;
  color: var(--accent);
}
.stat-chip .lbl { color: var(--text-dim); font-size: 0.85rem; }

/* ============ Sections ============ */
.section { padding: 72px 0; }
.section-head { text-align: center; margin-bottom: 44px; }
.section-head h2 { font-size: clamp(1.7rem, 4vw, 2.4rem); font-weight: 800; letter-spacing: -0.015em; margin-bottom: 10px; }
.section-head p { color: var(--text-dim); max-width: 560px; margin: 0 auto; }

.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }

.feature-card { padding: 28px; }
.feature-card .icon {
  width: 48px; height: 48px;
  border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center;
  font-size: 1.4rem;
  background: var(--accent-soft);
  margin-bottom: 18px;
}
.feature-card h3 { font-size: 1.1rem; margin-bottom: 8px; }
.feature-card p { color: var(--text-dim); font-size: 0.93rem; }

/* Curriculum preview on landing */
.phase-row {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 18px 24px;
  margin-bottom: 12px;
}
.phase-row .phase-num {
  min-width: 44px; height: 44px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700;
  background: var(--accent);
  color: #fff;
  font-size: 0.95rem;
}
.phase-row .info { flex: 1; }
.phase-row .info h4 { font-size: 1.02rem; }
.phase-row .info p { color: var(--text-dim); font-size: 0.87rem; }
.phase-row .count { color: var(--text-faint); font-size: 0.83rem; white-space: nowrap; }

/* Discord section */
.discord-card {
  padding: 52px 36px;
  text-align: center;
}
.discord-card h2 { font-size: 1.8rem; margin-bottom: 12px; }
.discord-card p { color: var(--text-dim); margin-bottom: 26px; max-width: 480px; margin-inline: auto; }

/* Footer */
.footer {
  border-top: 1px solid var(--border);
  padding: 36px 0;
  margin-top: 40px;
}
.footer-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  flex-wrap: wrap;
}
.footer p { color: var(--text-faint); font-size: 0.88rem; }
.footer-links { display: flex; gap: 18px; align-items: center; }
.footer-links a { color: var(--text-dim); font-size: 0.88rem; transition: color 0.2s; }
.footer-links a:hover { color: var(--text); }

/* ============ Auth (login) ============ */
.auth-wrap {
  min-height: calc(100vh - 76px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 4%;
}
.auth-card {
  width: min(420px, 100%);
  padding: 42px 36px;
  text-align: center;
}
.auth-card .hero-logo { width: 64px; height: 64px; margin-bottom: 20px; }
.auth-card h1 { font-size: 1.6rem; margin-bottom: 6px; }
.auth-card > p { color: var(--text-dim); font-size: 0.93rem; margin-bottom: 28px; }
.field { text-align: left; margin-bottom: 18px; }
.field label { display: block; font-size: 0.85rem; font-weight: 600; color: var(--text-dim); margin-bottom: 7px; }
.field input {
  width: 100%;
  padding: 12px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--bg);
  color: var(--text);
  font-size: 0.95rem;
  font-family: inherit;
  transition: border-color 0.2s, box-shadow 0.2s;
}
.field input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.auth-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 18px 0;
  color: var(--text-faint);
  font-size: 0.8rem;
}
.auth-divider::before,
.auth-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}
.auth-error {
  display: none;
  background: var(--danger-soft);
  border: 1px solid var(--danger);
  color: var(--danger);
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  font-size: 0.85rem;
  margin-bottom: 16px;
}
.auth-error.show { display: block; }
.auth-note { margin-top: 20px; color: var(--text-faint); font-size: 0.8rem; }

/* ============ Profile page ============ */
.profile-card {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 24px;
  margin-bottom: 22px;
}
.profile-avatar {
  width: 72px;
  height: 72px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.6rem;
  font-weight: 700;
  overflow: hidden;
}
.profile-avatar img { width: 100%; height: 100%; object-fit: cover; }
.profile-info { min-width: 0; }
.profile-name { font-size: 1.25rem; font-weight: 700; }
.profile-email { color: var(--text-dim); font-size: 0.92rem; margin-top: 2px; word-break: break-all; }
.profile-badge {
  display: inline-block;
  margin-top: 8px;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--surface-strong);
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-size: 0.75rem;
  font-weight: 600;
}

/* ============ Dashboard ============ */
.dash-header { padding: 44px 0 10px; }
.dash-header h1 { font-size: clamp(1.6rem, 4vw, 2.2rem); font-weight: 800; }
.dash-header p { color: var(--text-dim); margin-top: 4px; }

.progress-overview {
  padding: 26px 28px;
  margin: 26px 0 34px;
}
.progress-overview .row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 12px;
  flex-wrap: wrap;
  gap: 8px;
}
.progress-overview h2 { font-size: 1.05rem; font-weight: 700; }
.progress-overview .pct { font-weight: 800; font-size: 1.3rem; color: var(--accent); }

.progress-bar {
  height: 8px;
  border-radius: 999px;
  background: var(--surface-strong);
  border: 1px solid var(--border);
  overflow: hidden;
}
.progress-bar .fill {
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
  transition: width 0.5s ease;
}
.progress-bar.thin { height: 6px; }
.progress-meta { color: var(--text-faint); font-size: 0.83rem; margin-top: 10px; }

/* Unit accordion */
.unit-card { margin-bottom: 16px; overflow: hidden; }
.unit-head {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 20px 24px;
  cursor: pointer;
  user-select: none;
  transition: background 0.2s;
}
.unit-head:hover { background: var(--surface-strong); }
.unit-head .phase-num {
  min-width: 44px; height: 44px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700;
  font-size: 0.95rem;
  background: var(--accent);
  color: #fff;
}
.unit-card.unit-done .phase-num { background: var(--success); }
.unit-head .info { flex: 1; min-width: 0; }
.unit-head .info h3 { font-size: 1.05rem; }
.unit-head .info .progress-bar { margin-top: 9px; max-width: 320px; }
.unit-head .meta { color: var(--text-faint); font-size: 0.82rem; white-space: nowrap; }
.unit-head .chev { color: var(--text-faint); transition: transform 0.3s ease; font-size: 0.8rem; }
.unit-card.expanded .chev { transform: rotate(180deg); }

.unit-lessons {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.4s ease;
}
.unit-card.expanded .unit-lessons { max-height: 1800px; }
.unit-lessons-inner { padding: 4px 24px 20px; display: flex; flex-direction: column; gap: 8px; }

.lesson-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 13px 16px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  transition: background 0.2s, border-color 0.2s;
}
.lesson-row.unlocked { cursor: pointer; }
.lesson-row.unlocked:hover { background: var(--surface-strong); border-color: var(--border); }
.lesson-row .status {
  min-width: 30px; height: 30px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 0.8rem;
  border: 1.5px solid var(--border-strong);
  color: var(--text-faint);
  font-weight: 700;
}
.lesson-row.done .status {
  background: var(--success);
  border-color: var(--success);
  color: #fff;
}
.lesson-row.current .status {
  border-color: var(--accent);
  color: var(--accent);
}
.lesson-row .l-title { flex: 1; font-size: 0.95rem; font-weight: 500; min-width: 0; }
.lesson-row .l-tag { font-size: 0.75rem; color: var(--text-faint); }
.lesson-row.done .l-title { color: var(--text-dim); }

/* unit-exam checkpoint at the bottom of a phase's lessons */
.unit-exam-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 13px 16px;
  margin-top: 2px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  cursor: pointer;
  text-decoration: none;
  transition: background 0.2s, border-color 0.2s;
}
.unit-exam-row:hover { background: var(--surface-strong); }
.unit-exam-row .status {
  min-width: 30px; height: 30px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  border: 1.5px solid var(--accent);
  color: var(--accent);
}
.unit-exam-row .l-title { flex: 1; font-size: 0.95rem; font-weight: 600; color: var(--text); }
.unit-exam-row .l-tag { font-size: 0.75rem; color: var(--accent); font-weight: 600; }

/* a project sits in the same rail as the unit exam, one step earlier.
   A finished one fills in solid, the same way a finished lesson does, so the
   two read as the same kind of "done" down the column. */
.unit-project-row.done .status {
  background: var(--success);
  border-color: var(--success);
  color: #fff;
}
.unit-project-row.done .l-tag { color: var(--success); }

/* ============ Lesson page ============ */
.lesson-wrap { padding: 36px 0 80px; }
.lesson-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 26px;
  flex-wrap: wrap;
}
.lesson-topbar .back-link {
  color: var(--text-dim);
  font-size: 0.9rem;
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition: color 0.2s;
}
.lesson-topbar .back-link:hover { color: var(--text); }
.lesson-unit-tag {
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--accent);
  background: var(--accent-soft);
  padding: 5px 14px;
  border-radius: 999px;
}

.lesson-header h1 { font-size: clamp(1.5rem, 4vw, 2.1rem); font-weight: 800; margin-bottom: 8px; }
.lesson-header .objective { color: var(--text-dim); font-size: 1rem; margin-bottom: 28px; }

.lesson-section { margin-bottom: 30px; padding: 30px; }
.lesson-section .sec-label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--accent);
  margin-bottom: 18px;
}

/* Video */
.video-frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface-strong);
  border: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 12px;
}
.video-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.video-frame .play-circle {
  width: 70px; height: 70px;
  border-radius: 50%;
  background: var(--accent);
  display: flex; align-items: center; justify-content: center;
  font-size: 1.5rem;
  color: #fff;
}
.video-frame .vf-note { color: var(--text-dim); font-size: 0.9rem; }
.bonus-video {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--accent);
}
.bonus-video:hover { text-decoration: underline; }
.bonus-video .ico { width: 15px; height: 15px; }

/* Reading typography */
.reading h3 { font-size: 1.2rem; margin: 26px 0 10px; font-weight: 700; }
.reading h3:first-child { margin-top: 0; }
.reading p { color: var(--text-dim); margin-bottom: 14px; font-size: 0.97rem; }
.reading strong { color: var(--text); }
.reading ul, .reading ol { margin: 0 0 16px 22px; color: var(--text-dim); font-size: 0.95rem; }
.reading li { margin-bottom: 6px; }
/* Keep >=, <=, !=, ->, == as literal two-character shapes — disable the
   programming ligatures JetBrains Mono / Cascadia Code turn on by default
   (this matches VS Code's own default, editor.fontLigatures: false).
   textarea is in the list because the code editors on the project and
   playground pages are textareas, not <pre> — without it they were the one
   place on the site still rendering >= as a single glyph. */
code, pre, kbd, samp, textarea {
  font-variant-ligatures: none;
  font-feature-settings: "liga" 0, "clig" 0, "calt" 0, "dlig" 0;
}

.reading code {
  font-family: var(--mono);
  font-size: 0.85em;
  background: var(--code-bg);
  border: 1px solid var(--border);
  padding: 2px 7px;
  border-radius: 6px;
  color: var(--accent);
}
.reading pre {
  background: var(--code-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 18px 20px;
  overflow-x: auto;
  margin: 6px 0 18px;
}
.reading pre code {
  background: none;
  border: none;
  padding: 0;
  color: var(--code-text);
  font-size: 0.86rem;
  line-height: 1.65;
}
.callout {
  border-left: 3px solid var(--accent);
  background: var(--accent-soft);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: 14px 18px;
  margin: 6px 0 18px;
  font-size: 0.92rem;
  color: var(--text-dim);
}
.callout strong { color: var(--text); }

/* Practice */
.q-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 22px;
  margin-bottom: 18px;
  background: var(--surface);
  transition: border-color 0.3s;
}
.q-card.correct { border-color: var(--success); }
.q-card.incorrect { border-color: var(--danger); }
.q-top { display: flex; gap: 12px; align-items: baseline; margin-bottom: 8px; }
.q-num {
  font-size: 0.75rem;
  font-weight: 800;
  color: var(--accent);
  background: var(--accent-soft);
  padding: 3px 11px;
  border-radius: 999px;
  white-space: nowrap;
}
.q-type { font-size: 0.72rem; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.06em; font-weight: 700; }
.q-text { font-size: 0.98rem; font-weight: 500; margin-bottom: 14px; }
.q-text pre {
  background: var(--code-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
  overflow-x: auto;
  margin: 12px 0 4px;
  font-weight: 400;
}
.q-text pre code { font-family: var(--mono); font-size: 0.84rem; color: var(--code-text); line-height: 1.6; background: none; border: none; padding: 0; }
.q-text code { font-family: var(--mono); font-size: 0.85em; background: var(--code-bg); border: 1px solid var(--border); padding: 1px 6px; border-radius: 5px; color: var(--accent); }
/* Section I of a full-length exam: the accent on inline code goes too, so the
   multiple-choice half reads as the same printed paper as the free-response
   half. app.js also skips the syntax highlighter inside .exam-paper. */
.exam-paper .q-text code { color: var(--text); }

/* ============================================================
   VS Code (Dark+) code blocks
   Every <pre><code> gets a .code-panel class from app.js and is
   painted to match VS Code's Dark+ editor. Always dark, regardless
   of the page's light/dark theme. Token colors come from the
   .tok-* spans the highlighter injects.
   ============================================================ */
/* The window chrome lives on this wrapper, NOT on the scrolling <pre>. When the
   title bar and dots were pseudo-elements of the <pre>, they scrolled away with
   the code on a long line: swipe sideways and the bar slid off, leaving bare
   panel past the first screenful. Anchoring them to a wrapper that does not
   scroll keeps the bar pinned while the code moves underneath it. */
.code-panel-wrap {
  position: relative;
  background: #1e1e1e;
  border: 1px solid #333333;
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  margin: 10px 0 20px;
  padding-top: 34px;              /* the strip the bar occupies */
  overflow: hidden;               /* clip the scroller to the rounded corners */
}
/* title bar strip */
.code-panel-wrap::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 34px;
  background: #252526;
  border-bottom: 1px solid #333333;
  z-index: 1;
}
/* three window-control dots (red / yellow / green) */
.code-panel-wrap::after {
  content: "";
  position: absolute;
  top: 12px; left: 16px;
  width: 11px; height: 11px;
  border-radius: 50%;
  background: #ff5f56;
  box-shadow: 18px 0 0 #ffbd2e, 36px 0 0 #27c93f;
  z-index: 2;
}
/* the scroller itself carries no chrome any more */
pre.code-panel {
  position: relative;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  margin: 0;
  padding: 14px 20px 20px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
pre.code-panel > code {
  display: block;
  background: none;
  border: none;
  padding: 0;
  color: #d4d4d4;
  font-family: "Cascadia Code", "Cascadia Mono", "Consolas", "SF Mono", Menlo, "JetBrains Mono", monospace;
  font-size: 14px;
  line-height: 1.6;
  white-space: pre;
  tab-size: 4;
  -moz-tab-size: 4;
}
/* Dark+ syntax token colors */
pre.code-panel .tok-keyword { color: #569CD6; }
pre.code-panel .tok-string  { color: #CE9178; }
pre.code-panel .tok-comment { color: #6A9955; }
pre.code-panel .tok-number  { color: #B5CEA8; }
pre.code-panel .tok-method  { color: #DCDCAA; }
pre.code-panel .tok-class   { color: #4EC9B0; }
/* dark scrollbar to match the panel */
pre.code-panel::-webkit-scrollbar { height: 10px; }
pre.code-panel::-webkit-scrollbar-track { background: transparent; }
pre.code-panel::-webkit-scrollbar-thumb { background: #424242; border-radius: 5px; border: 2px solid #1e1e1e; }
pre.code-panel::-webkit-scrollbar-thumb:hover { background: #4f4f4f; }

/* ---- Light mode: VS Code "Light+" palette (only when the site is light) ---- */
[data-theme="light"] .code-panel-wrap {
  background: #ffffff;
  border-color: #e3e3e3;
  box-shadow: 0 6px 20px rgba(15, 23, 42, 0.10);
}
[data-theme="light"] .code-panel-wrap::before {
  background: #f3f3f3;
  border-bottom-color: #e3e3e3;
}
/* window dots keep the universal red / yellow / green */
[data-theme="light"] pre.code-panel > code { color: #1f1f1f; }
[data-theme="light"] pre.code-panel .tok-keyword { color: #0000FF; }
[data-theme="light"] pre.code-panel .tok-string  { color: #A31515; }
[data-theme="light"] pre.code-panel .tok-comment { color: #008000; }
[data-theme="light"] pre.code-panel .tok-number  { color: #098658; }
[data-theme="light"] pre.code-panel .tok-method  { color: #795E26; }
[data-theme="light"] pre.code-panel .tok-class   { color: #267F99; }
[data-theme="light"] pre.code-panel::-webkit-scrollbar-thumb { background: #c1c1c1; border-color: #ffffff; }
[data-theme="light"] pre.code-panel::-webkit-scrollbar-thumb:hover { background: #a8a8a8; }

.q-options { display: flex; flex-direction: column; gap: 9px; }
.q-option {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  cursor: pointer;
  transition: border-color 0.2s, background 0.2s;
  font-size: 0.92rem;
  text-align: left;
  color: var(--text);
  width: 100%;
}
.q-option:hover:not([disabled]) { background: var(--surface-strong); }
.q-option .opt-key {
  min-width: 26px; height: 26px;
  border-radius: 50%;
  border: 1.5px solid var(--border-strong);
  display: flex; align-items: center; justify-content: center;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--text-dim);
  flex-shrink: 0;
}
.q-option.selected { border-color: var(--accent); background: var(--accent-soft); }
.q-option.right { border-color: var(--success); background: var(--success-soft); }
.q-option.right .opt-key { border-color: var(--success); color: var(--success); }
.q-option.wrong { border-color: var(--danger); background: var(--danger-soft); }
.q-option.wrong .opt-key { border-color: var(--danger); color: var(--danger); }
.q-option code { font-family: var(--mono); font-size: 0.85em; white-space: pre-wrap; }
/* Code answer choices are shown multi-line (like the AP exam) and stay
   monochrome — the syntax highlighter only touches <pre><code>, not the
   inline <code> used here, so options never get color. */
.q-option:has(code) { align-items: flex-start; }
.q-option:has(code) .opt-key { margin-top: 1px; }
.q-option:has(code) > span:last-child { display: block; line-height: 1.55; }
.q-option[disabled] { cursor: default; }

.q-input {
  width: 100%;
  padding: 12px 16px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--bg);
  color: var(--text);
  font-family: var(--mono);
  font-size: 0.9rem;
  transition: border-color 0.2s, box-shadow 0.2s;
}
.q-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
textarea.q-input { min-height: 120px; resize: vertical; line-height: 1.6; }

.q-actions { display: flex; gap: 10px; margin-top: 14px; flex-wrap: wrap; }

.feedback {
  margin-top: 14px;
  padding: 14px 18px;
  border-radius: var(--radius-sm);
  font-size: 0.9rem;
  display: none;
}
.feedback.show { display: block; }
.feedback.good { background: var(--success-soft); border: 1px solid var(--success); color: var(--success); }
.feedback.bad { background: var(--danger-soft); border: 1px solid var(--danger); color: var(--danger); }
.feedback .expl { color: var(--text-dim); margin-top: 6px; display: block; }
.feedback .expl code { font-family: var(--mono); font-size: 0.85em; }

/* Lesson progress + completion */
.lesson-progress-sticky {
  position: sticky;
  top: 76px;
  z-index: 50;
  padding: 12px 20px;
  margin-bottom: 24px;
  display: flex;
  align-items: center;
  gap: 16px;
  background: var(--bg);
}
.lesson-progress-sticky .progress-bar { flex: 1; }
.lesson-progress-sticky .lbl { font-size: 0.82rem; color: var(--text-dim); white-space: nowrap; font-weight: 600; }

.completion-panel {
  text-align: center;
  padding: 48px 32px;
  display: none;
}
.completion-panel.show { display: block; }
.completion-panel .big-check {
  width: 80px; height: 80px;
  margin: 0 auto 20px;
  border-radius: 50%;
  background: var(--success);
  display: flex; align-items: center; justify-content: center;
  font-size: 2.2rem;
  color: #fff;
}
.completion-panel h2 { font-size: 1.6rem; margin-bottom: 8px; }
.completion-panel p { color: var(--text-dim); margin-bottom: 26px; }
.completion-panel .cta-row { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }

/* ============ Modal (settings) ============ */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  z-index: 200;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.modal-overlay.open { display: flex; }
.modal {
  width: min(440px, 100%);
  padding: 30px;
  background: var(--bg-elevated);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  position: relative;
}
.modal h2 { font-size: 1.3rem; margin-bottom: 4px; }
.modal .modal-sub { color: var(--text-dim); font-size: 0.88rem; margin-bottom: 24px; }
.modal-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 15px 0;
  border-top: 1px solid var(--border);
}
.modal-row .lbl { font-weight: 600; font-size: 0.93rem; }
.modal-row .desc { color: var(--text-faint); font-size: 0.8rem; }
.modal-close {
  position: absolute;
  top: 18px; right: 18px;
}
.confirm-modal .modal-sub { margin-bottom: 24px; }
.confirm-actions {
  display: flex;
  gap: 12px;
  justify-content: flex-end;
  flex-wrap: wrap;
}
.confirm-actions .btn { flex: 0 1 auto; }

/* Toggle switch */
.switch { position: relative; width: 48px; height: 26px; flex-shrink: 0; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch .slider {
  position: absolute;
  inset: 0;
  border-radius: 999px;
  background: var(--surface-strong);
  border: 1px solid var(--border-strong);
  transition: background 0.2s, border-color 0.2s;
  cursor: pointer;
}
.switch .slider::before {
  content: "";
  position: absolute;
  width: 18px; height: 18px;
  left: 3px; top: 3px;
  border-radius: 50%;
  background: var(--text-dim);
  transition: transform 0.2s ease, background 0.2s;
}
.switch input:checked + .slider { background: var(--accent); border-color: var(--accent); }
.switch input:checked + .slider::before { transform: translateX(22px); background: #fff; }

/* Toast */
.toast {
  position: fixed;
  bottom: 28px;
  left: 50%;
  transform: translateX(-50%) translateY(80px);
  background: var(--bg-elevated);
  border: 1px solid var(--border-strong);
  color: var(--text);
  padding: 12px 24px;
  border-radius: var(--radius-sm);
  font-size: 0.9rem;
  font-weight: 500;
  box-shadow: var(--shadow);
  z-index: 300;
  opacity: 0;
  transition: transform 0.3s ease, opacity 0.3s;
  pointer-events: none;
  max-width: 92%;
  text-align: center;
}
.toast.show { transform: translateX(-50%) translateY(0); opacity: 1; }

/* Reveal on scroll — content is always visible; no animation */
.reveal { opacity: 1; transform: none; }
.reveal.visible { opacity: 1; transform: none; }

/* ============ Inline SVG icons ============ */
.ico { width: 1.1em; height: 1.1em; flex-shrink: 0; display: inline-block; vertical-align: -0.18em; }
.icon-btn .ico { width: 17px; height: 17px; vertical-align: 0; }
.feature-card .icon .ico { width: 22px; height: 22px; color: var(--accent); }
.hero-badge .ico { width: 15px; height: 15px; margin-right: 7px; }
.sec-label .ico { width: 15px; height: 15px; }
.unit-head .info h3 { display: flex; align-items: center; gap: 8px; }
.unit-head .info h3 .ico { width: 17px; height: 17px; color: var(--accent); }
.phase-num .ico { width: 18px; height: 18px; }
.lesson-row .status .ico { width: 13px; height: 13px; }
.chev .ico { width: 15px; height: 15px; }
.big-check .ico { width: 38px; height: 38px; }
.play-circle .ico { width: 26px; height: 26px; }
.lesson-unit-tag { display: inline-flex; align-items: center; gap: 6px; }
.lesson-unit-tag .ico { width: 13px; height: 13px; }
.feedback .ico { width: 14px; height: 14px; }
.lesson-progress-sticky .lbl { display: inline-flex; align-items: center; gap: 6px; }
.lesson-progress-sticky .lbl .ico { width: 14px; height: 14px; color: var(--accent); }
.btn .ico { width: 15px; height: 15px; }

/* ============ AI tutor ============ */

/* Every way into the tutor looks the same, because they are the same feature:
   the lesson-wide button, the per-question pill, the one on a reviewed exam
   question, and the floating button on the coding pages. They had drifted into
   three different hover behaviours (grey, accent-outline, and none at all).
   One shared treatment: the sparkle sits in the accent colour at rest so the
   control reads as "AI" at a glance, and hovering commits the whole control to
   the accent. Size and placement still differ per context — only the colour
   language is shared. */
.ask-ai-lesson,
.tutor-open,
.ct-fab {
  --ai-ico: var(--accent);
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.ask-ai-lesson:hover,
.tutor-open:hover,
.ct-fab:hover {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}
.ask-ai-lesson .ico,
.tutor-open .ico,
.ct-fab .ico { color: var(--ai-ico); }
/* On hover the icon joins the text rather than staying a separate blue. */
.ask-ai-lesson:hover,
.tutor-open:hover,
.ct-fab:hover { --ai-ico: currentColor; }

.ask-ai-lesson { margin-bottom: 26px; display: inline-flex; gap: 8px; }
.tutor-open {
  display: inline-flex; align-items: center; gap: 6px;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-size: 0.78rem; font-weight: 600;
  padding: 5px 12px; border-radius: 999px;
  cursor: pointer;
}
/* Pushed to the right-hand end of the question header, in practice and in
   exam review alike — the review button is appended to the same row. */
.q-top .tutor-open { margin-left: auto; }
.tutor-open .ico { width: 13px; height: 13px; }

.tutor-drawer {
  position: fixed; top: 0; right: 0; height: 100dvh;
  width: min(420px, 100vw);
  background: var(--bg);
  border-left: 1px solid var(--border);
  z-index: 250;
  display: flex; flex-direction: column;
  transform: translateX(100%);
  transition: transform 0.25s ease;
}
.tutor-drawer.open { transform: translateX(0); }
.tutor-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 14px 18px;
  border-bottom: 1px solid var(--border);
}
.tutor-title { display: inline-flex; align-items: center; gap: 8px; font-weight: 700; }
.tutor-title .ico { width: 16px; height: 16px; color: var(--accent); }
.tutor-ctx { padding: 9px 18px; font-size: 0.8rem; font-weight: 600; color: var(--accent); background: var(--accent-soft); }
.tutor-msgs { flex: 1; overflow-y: auto; padding: 18px; display: flex; flex-direction: column; gap: 10px; }
.tutor-empty { text-align: center; color: var(--text-dim); font-size: 0.9rem; padding: 28px 12px; }
.tutor-empty .ico { width: 26px; height: 26px; color: var(--accent); display: block; margin: 0 auto 10px; }
.t-msg { max-width: 88%; padding: 10px 14px; border-radius: var(--radius-sm); font-size: 0.9rem; line-height: 1.55; }
.t-msg.student { align-self: flex-end; background: var(--accent); color: #fff; }
.t-msg.tutor { align-self: flex-start; background: var(--surface-strong); border: 1px solid var(--border); }
.t-msg.err { border-color: var(--danger); color: var(--danger); background: var(--danger-soft); }
.t-msg code { font-family: var(--mono); font-size: 0.85em; background: var(--code-bg); border: 1px solid var(--border); border-radius: 5px; padding: 1px 5px; }
.t-msg.student code { background: rgba(255, 255, 255, 0.18); border-color: transparent; color: #fff; }
/* A fenced block from the tutor, in either panel — the lesson drawer and the
   coding-page panel render the same replies through the same formatter, so
   they share one rule. <pre> keeps the indentation the reply was written with,
   and both panels are narrow, so long lines scroll inside the block instead of
   stretching the message bubble. */
.t-msg pre.t-code,
.ct-msg pre.t-code {
  font-family: var(--mono); font-size: 0.8rem; line-height: 1.6;
  background: var(--code-bg); border: 1px solid var(--border);
  border-radius: var(--radius-sm); color: var(--code-text);
  padding: 10px 12px; margin: 9px 0; overflow-x: auto;
  white-space: pre; tab-size: 4;
}
.t-msg pre.t-code:first-child,
.ct-msg pre.t-code:first-child { margin-top: 0; }
.t-msg pre.t-code:last-child,
.ct-msg pre.t-code:last-child { margin-bottom: 0; }
.t-msg pre.t-code code,
.ct-msg pre.t-code code { background: none; border: 0; padding: 0; font-size: inherit; color: inherit; }
.t-msg.typing { display: flex; gap: 5px; padding: 14px; }
.t-msg.typing span { width: 7px; height: 7px; border-radius: 50%; background: var(--text-faint); animation: blink 1.2s infinite; }
.t-msg.typing span:nth-child(2) { animation-delay: 0.2s; }
.t-msg.typing span:nth-child(3) { animation-delay: 0.4s; }
@keyframes blink { 0%, 80%, 100% { opacity: 0.25; } 40% { opacity: 1; } }
.tutor-suggests { display: flex; flex-wrap: wrap; gap: 8px; padding: 0 18px 12px; }
.tutor-suggest {
  border: 1px solid var(--border-strong);
  background: transparent;
  color: var(--text-dim);
  border-radius: 999px;
  padding: 7px 14px;
  font-size: 0.8rem; font-weight: 500;
  transition: background 0.2s, border-color 0.2s, color 0.2s;
}
.tutor-suggest:hover { background: var(--surface-strong); color: var(--text); }
.tutor-inputrow {
  display: flex; gap: 10px; align-items: flex-end;
  padding: 12px 18px 16px;
  border-top: 1px solid var(--border);
}
.tutor-inputrow textarea {
  flex: 1; resize: none;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--bg);
  color: var(--text);
  font-family: inherit;
  font-size: 0.9rem;
  line-height: 1.5;
}
.tutor-inputrow textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }

/* iOS Safari force-zooms the whole page whenever a focused field is under 16px,
   and both tutor panels focus their input the moment they open — so tapping
   "Ask AI" lurched the page in. 16px is the threshold, not a preference; below
   it the zoom is not suppressible without disabling pinch-zoom for everyone,
   which is not a trade worth making. Scoped to coarse pointers so the desktop
   sizing is untouched. The matching rule for the coding pages' panel lives
   beside .ct-foot further down — a media query adds no specificity, so it has
   to come after the declaration it overrides, not before it. */
@media (pointer: coarse) {
  .tutor-inputrow textarea { font-size: 16px; }
}

/* ============ Responsive ============ */
@media (max-width: 900px) {
  .grid-3 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 720px) {
  .nav-links { display: none; }
  .hamburger { display: flex; }
  .grid-3, .grid-2 { grid-template-columns: 1fr; }
  .hero { padding: 64px 0 48px; }
  .section { padding: 52px 0; }
  .lesson-section { padding: 22px 18px; }
  .unit-head { padding: 16px 16px; gap: 12px; }
  .unit-lessons-inner { padding: 4px 12px 16px; }
  .unit-head .meta { display: none; }
  .auth-card { padding: 32px 22px; }
  .lesson-progress-sticky { top: 70px; }
  .phase-row { padding: 14px 16px; }
  .lesson-row .l-tag { display: none; }
}

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

/* ============================================================
   Left sidebar (app pages: dashboard, lesson, tests)
   Expandable/retractable; state persisted in localStorage.
   ============================================================ */
body.has-sb { padding-left: var(--sb-w); transition: padding-left 0.22s ease; }
html.sb-collapsed body.has-sb { padding-left: var(--sb-w-min); }

.sidebar {
  position: fixed;
  top: 0; left: 0; bottom: 0;
  width: var(--sb-w);
  background: var(--bg);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  z-index: 150;
  transition: width 0.22s ease, transform 0.22s ease;
  overflow: hidden;
}
html.sb-collapsed .sidebar { width: var(--sb-w-min); }

.sb-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 13px 12px;
  border-bottom: 1px solid var(--border);
  min-height: 65px;
}
.sb-head .brand { min-width: 0; overflow: hidden; }
.sb-head .brand .logo-img { width: 34px; height: 34px; flex-shrink: 0; }
.sb-head .brand span { white-space: nowrap; font-size: 1.02rem; }
.sb-toggle { margin-left: auto; flex-shrink: 0; border: none; }
html.sb-collapsed .sb-head { justify-content: center; padding: 13px 8px; }
html.sb-collapsed .sb-head .brand { display: none; }
html.sb-collapsed .sb-toggle { margin-left: 0; }

/* overscroll-behavior stops a swipe that reaches the end of the nav from
   chaining on to the page underneath the open drawer. */
.sb-nav { flex: 1; padding: 12px 10px; display: flex; flex-direction: column; gap: 4px; overflow-y: auto; overscroll-behavior: contain; }
.sb-foot { padding: 12px 10px; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 4px; }

.sb-link {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  border: none;
  background: transparent;
  color: var(--text-dim);
  font-weight: 500;
  font-size: 0.92rem;
  font-family: inherit;
  width: 100%;
  text-align: left;
  white-space: nowrap;
  transition: background 0.15s, color 0.15s;
}
.sb-link:hover { background: var(--surface-strong); color: var(--text); }
.sb-link.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.sb-link .ico { width: 18px; height: 18px; flex-shrink: 0; }
.sb-link [data-theme-icon] { display: inline-flex; }
.sb-link [data-theme-icon] .ico { width: 18px; height: 18px; }
html.sb-collapsed .sb-link { justify-content: center; padding: 10px 0; }
html.sb-collapsed .sb-txt { display: none; }

/* Discord + Log out get their own solid-color buttons, not plain nav rows */
.sb-link.sb-btn { margin-top: 6px; font-weight: 700; }
.sb-btn-discord { background: #5865f2; color: #fff; }
.sb-btn-discord:hover { background: #4752c4; color: #fff; }
.sb-btn-discord .ico-discord { width: 17px; height: 17px; }
.sb-btn-danger { background: var(--danger); color: #fff; }
.sb-btn-danger:hover { background: var(--danger-strong); color: #fff; }

/* Mobile topbar + off-canvas drawer */
.mobile-topbar {
  display: none;
  position: sticky;
  top: 0;
  z-index: 120;
  align-items: center;
  gap: 12px;
  padding: 10px 4%;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}
.mobile-topbar .brand .logo-img { width: 32px; height: 32px; }
.sb-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  z-index: 149;
}

/* Lesson sticky bar sits under no top navbar on sidebar pages */
body.has-sb .lesson-progress-sticky { top: 12px; }

@media (max-width: 860px) {
  body.has-sb { padding-left: 0 !important; }
  .sidebar { transform: translateX(-101%); width: var(--sb-w) !important; box-shadow: 4px 0 24px rgba(0,0,0,0.25); }
  html.sb-open .sidebar { transform: translateX(0); }
  html.sb-open .sb-backdrop { display: block; }
  /* Freeze the page while the drawer is open. Without this the document keeps
     scrolling under the backdrop: the sticky topbar and the sticky progress bar
     slide out of place and leave gaps where they used to sit, so chrome appears
     and disappears at the top and bottom instead of a clean darkened page.
     body is pinned rather than given overflow:hidden because iOS Safari ignores
     overflow:hidden on the document for touch scrolling. openSidebar() in
     app.js writes the offset to body.style.top and restores it on close. */
  html.sb-open, html.sb-open body { overflow: hidden; }
  html.sb-open body {
    position: fixed;
    left: 0;
    right: 0;
    width: 100%;
  }
  html.sb-collapsed .sb-head .brand { display: flex; }   /* drawer always full-width */
  html.sb-collapsed .sb-txt { display: inline; }
  html.sb-collapsed .sb-link { justify-content: flex-start; padding: 10px 12px; }
  .sb-toggle { display: none; }
  .mobile-topbar { display: flex; }
  body.has-sb .lesson-progress-sticky { top: 62px; }
}

/* ============================================================
   Practice tests
   ============================================================ */
.tests-wrap { padding-bottom: 80px; }
/* Two clearly separated groups: a ruled header, a one-line explanation,
   then the list. */
.tests-section-head {
  margin: 40px 0 18px; padding-bottom: 12px;
  border-bottom: 1px solid var(--border);
}
.tests-section-head:first-child { margin-top: 8px; }
.tests-section-head h2 {
  font-size: 1.05rem; font-weight: 800; letter-spacing: -0.01em;
  display: flex; align-items: center; gap: 8px;
}
.tests-section-head p {
  margin-top: 5px; font-size: 0.86rem; line-height: 1.55; color: var(--text-dim);
  max-width: 68ch;
}
.tests-section-head p { color: var(--text-dim); font-size: 0.88rem; margin-top: 4px; }

.test-sticky {
  position: sticky;
  top: 12px;
  z-index: 50;
  padding: 12px 20px;
  margin-bottom: 24px;
  display: flex;
  align-items: center;
  gap: 16px;
  background: var(--bg);
}
.test-sticky .progress-bar { flex: 1; }
.test-sticky .lbl { font-size: 0.82rem; color: var(--text-dim); white-space: nowrap; font-weight: 600; }
.test-sticky .timer {
  font-family: var(--mono);
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--text);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.test-sticky .timer .ico { width: 14px; height: 14px; color: var(--accent); }
.test-sticky .timer.low { color: var(--danger); }
.test-sticky .timer.low .ico { color: var(--danger); }

.result-panel {
  text-align: center;
  padding: 40px 28px;
  margin-bottom: 26px;
}
.result-panel .r-score {
  font-size: 3rem;
  font-weight: 800;
  color: var(--accent);
  line-height: 1.1;
}
.result-panel .r-score.good { color: var(--success); }
.result-panel .r-score.bad { color: var(--danger); }
.result-panel .r-sub { color: var(--text-dim); margin: 6px 0 4px; font-weight: 600; }
.result-panel .r-best { color: var(--text-faint); font-size: 0.85rem; margin-bottom: 22px; }
.result-panel .cta-row { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }

@media (max-width: 720px) {
  .test-sticky { flex-wrap: wrap; gap: 10px; }
  .test-sticky .progress-bar { flex-basis: 100%; order: 3; }
}

/* Dashboard promo row for tests */
.tests-promo {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 20px 24px;
  margin-bottom: 34px;
}
.tests-promo .tp-ico {
  width: 44px; height: 44px;
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.tests-promo .tp-ico .ico { width: 20px; height: 20px; color: var(--accent); }
.tests-promo .info { flex: 1; min-width: 0; }
.tests-promo .info h2 { font-size: 1rem; font-weight: 700; }
.tests-promo .info p { color: var(--text-dim); font-size: 0.86rem; }
.tests-promo .btn { flex-shrink: 0; }
@media (max-width: 720px) {
  .tests-promo { flex-wrap: wrap; }
  /* The blurb wraps to three or four lines on a phone and pushes the card
     past the fold. The heading and the button already say what this is. */
  .tests-promo .info p { display: none; }
}
@media (max-width: 560px) {
  /* Sharing the row with the button left the heading about 120px, so
     "MCQ Practice Exams" broke across three lines. Drop the button onto its
     own row and the heading gets the full width of the card. */
  .tests-promo { row-gap: 14px; }
  .tests-promo .info h2 { white-space: nowrap; }
  .tests-promo .btn { flex: 1 0 100%; }
}

/* ============================================================
   Tutor graceful fallback — hidden when no backend is reachable
   ============================================================ */
body.no-tutor .tutor-open,
body.no-tutor .ask-ai-lesson,
body.no-tutor .tutor-drawer { display: none !important; }

/* ============================================================
   Design pop: dot-grid backdrop, heading accents, card lift
   ============================================================ */
body {
  background-image: none;
}
.navbar, .sidebar, .mobile-topbar { background-image: none; background-color: var(--bg); }

.dash-header h1::after,
.lesson-header h1::after {
  content: "";
  display: block;
  width: 58px;
  height: 4px;
  border-radius: 2px;
  margin-top: 12px;
  background: var(--accent);
}
.lesson-header h1::after { margin-top: 10px; }

.glass.hoverable:hover { transform: translateY(-2px); box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18); }
.glass.hoverable { transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease; }

.btn-lg { padding: 14px 32px; font-size: 1.02rem; }

/* ============================================================
   MCQ exam flow
   ============================================================ */

/* pre-exam chooser */
.exam-intro {
  padding: 44px 32px;
  text-align: center;
  margin-bottom: 30px;
}
.exam-intro .ei-icon {
  width: 56px; height: 56px;
  margin: 0 auto 16px;
  border-radius: 50%;
  background: var(--success-soft);
  display: flex; align-items: center; justify-content: center;
}
.exam-intro .ei-icon .ico { width: 26px; height: 26px; color: var(--success); }
.exam-intro h2 { font-size: 1.45rem; margin-bottom: 18px; }
.ei-facts {
  list-style: none;
  margin: 0 auto 26px;
  max-width: 460px;
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.ei-facts li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  color: var(--text-dim);
  font-size: 0.93rem;
}
.ei-facts .ico { width: 16px; height: 16px; color: var(--accent); flex-shrink: 0; margin-top: 3px; }
.ei-modes { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }
.ei-note { margin-top: 16px; color: var(--text-faint); font-size: 0.82rem; }

/* sticky exam bar + question palette */
.test-sticky .ts-row { display: flex; align-items: center; gap: 16px; }
.q-palette {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}
.pal-chip {
  width: 30px; height: 26px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-faint);
  font-size: 0.72rem;
  font-weight: 600;
  font-family: var(--mono);
  transition: border-color 0.15s, background 0.15s, color 0.15s;
}
.pal-chip:hover { background: var(--surface-strong); color: var(--text); }
.pal-chip.answered { background: var(--accent); border-color: var(--accent); color: #fff; }
.pal-chip.chip-right { background: var(--success-soft); border-color: var(--success); color: var(--success); }
.pal-chip.chip-wrong { background: var(--danger-soft); border-color: var(--danger); color: var(--danger); }
.pal-chip.current { outline: 2px solid var(--accent); outline-offset: 1px; font-weight: 800; }

/* bottom finish panel */
.finish-panel {
  text-align: center;
  padding: 34px 20px 12px;
  border-top: 1px solid var(--border);
  margin-top: 10px;
}
.finish-panel p { color: var(--text-dim); font-size: 0.92rem; margin-bottom: 16px; }

/* result panel tweaks */
.result-panel .r-hint {
  margin-top: 18px;
  color: var(--text-faint);
  font-size: 0.85rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
}
.result-panel .r-hint .ico { width: 14px; height: 14px; color: var(--accent); }

/* Ask-AI button inside review cards */
.review-ask { margin-left: auto; }

/* ============================================================
   One-question-at-a-time exam navigation (Bluebook style)
   ============================================================ */
.q-hidden { display: none !important; }
.exam-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 18px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border);
}
.exam-nav .nav-counter { font-weight: 700; font-size: 0.95rem; }
.exam-nav .btn[disabled] { opacity: 0.35; }
#testQHost .q-card { margin-bottom: 0; border: none; padding: 8px 0 0; background: transparent; }
#testQHost .q-card .q-text { font-size: 1.05rem; }
#testQHost .q-option { padding: 14px 16px; }

/* ============================================================
   Java Quick Reference — floating, draggable, resizable window
   ============================================================ */
.qr-window {
  position: fixed;
  top: 84px;
  right: 26px;
  width: min(480px, 92vw);
  height: 440px;
  min-width: 300px;
  min-height: 220px;
  max-width: 96vw;
  max-height: 88vh;
  background: var(--bg-elevated);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: 0 14px 44px rgba(0, 0, 0, 0.35);
  z-index: 260;
  display: none;
  flex-direction: column;
  resize: both;            /* stretch & warp from the corner */
  overflow: hidden;
}
.qr-window.open { display: flex; }
.qr-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  cursor: move;
  user-select: none;
  background: var(--surface-strong);
  flex-shrink: 0;
}
.qr-head .qr-name { display: inline-flex; align-items: center; gap: 8px; font-weight: 700; font-size: 0.9rem; }
.qr-head .qr-name .ico { width: 15px; height: 15px; color: var(--accent); }
.qr-head .icon-btn { width: 28px; height: 28px; border: none; }
.qr-body {
  flex: 1;
  overflow: auto;
  padding: 14px 16px;
  font-family: var(--mono);
  font-size: 0.78rem;
  line-height: 1.6;
  color: var(--text-dim);
}
.qr-body h4 {
  font-family: var(--font);
  color: var(--accent);
  font-size: 0.85rem;
  margin: 16px 0 6px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--border);
}
.qr-body h4.qr-title { color: var(--text); font-size: 0.95rem; margin-top: 0; }
.qr-body .qr-line { white-space: pre-wrap; word-break: break-word; }

/* The same reference content as reference.html, laid out for a small
   floating window: no masthead, no chips, no table columns. During an exam
   this is a lookup, so each entry is a signature with its meaning directly
   underneath and nothing else competing for the width. */
.qr-class {
  display: flex; align-items: baseline; gap: 8px;
  font-family: var(--font); font-weight: 700; font-size: 0.86rem;
  color: var(--accent);
  margin: 18px 0 8px; padding-bottom: 5px;
  border-bottom: 1px solid var(--border);
  position: sticky; top: -14px;
  background: var(--surface);
}
.qr-class:first-child { margin-top: 0; }
.qr-class .qr-count { margin-left: auto; font-weight: 600; color: var(--text-faint); font-size: 0.74rem; }
.qr-entry { padding: 7px 0; border-bottom: 1px solid var(--border); }
.qr-entry:last-child { border-bottom: none; }
.qr-sig {
  font-family: var(--mono); font-size: 0.76rem; line-height: 1.55;
  color: var(--text); font-weight: 600; overflow-wrap: anywhere;
}
.qr-sig .ret { color: var(--text-faint); font-weight: 400; }
.qr-sig .sname { color: var(--accent); }
.qr-desc {
  font-family: var(--font); font-size: 0.78rem; line-height: 1.5;
  color: var(--text-dim); margin-top: 3px;
}
.qr-desc code {
  font-family: var(--mono); font-size: 0.9em;
  background: var(--code-bg); border: 1px solid var(--border);
  border-radius: 4px; padding: 0 4px; color: var(--text);
}
.qr-desc .qr-note { display: block; margin-top: 2px; color: var(--text-faint); font-size: 0.74rem; }
.qr-foot {
  font-family: var(--font); font-size: 0.7rem; line-height: 1.5;
  color: var(--text-faint); margin-top: 16px; padding-top: 10px;
  border-top: 1px solid var(--border);
}

/* ============================================================
   Progress / statistics page
   ============================================================ */
.stats-wrap { padding-bottom: 80px; }
.ov-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
  margin: 26px 0 34px;
}
.ov-card { padding: 20px 22px; }
.ov-card .ov-label { font-size: 0.78rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-faint); margin-bottom: 8px; }
.ov-card .ov-num { font-size: 1.7rem; font-weight: 800; margin-bottom: 8px; }
.ov-card .ov-sub { font-size: 0.8rem; color: var(--text-faint); }

.topic-table-head,
.topic-row {
  display: grid;
  grid-template-columns: minmax(180px, 1.4fr) 1fr 70px 130px;
  gap: 16px;
  align-items: center;
  padding: 11px 4px;
}
.topic-table-head {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-faint);
  border-bottom: 1px solid var(--border);
}
.topic-row { border-bottom: 1px solid var(--border); }
.topic-row:last-child { border-bottom: none; }
.topic-row .tr-name { font-weight: 600; font-size: 0.92rem; }
.topic-row .tr-name .tr-n { display: block; font-weight: 400; font-size: 0.75rem; color: var(--text-faint); }
.topic-row .tr-acc { font-family: var(--mono); font-weight: 600; font-size: 0.9rem; text-align: right; }
.topic-row .fill.strong { background: var(--success); }
.topic-row .fill.needs { background: var(--warning); }
.topic-row .fill.weak { background: var(--danger); }
.topic-row .fill.nodata { background: var(--border-strong); }

/* Topic status: a severity rail and a label, left-aligned so the column reads
   as a column. The old rounded chips were centred and outlined, so each one
   sat as its own floating object and the status column had no edge to scan
   down — the point of a table is that everything lines up. Colour still
   carries the state, but the label carries it too, so it does not depend on
   distinguishing red from amber. */
.topic-row .tr-status,
.workon-card .tr-status {
  display: flex; align-items: center; gap: 10px;
  font-size: 0.78rem; font-weight: 700;
  letter-spacing: 0.03em; text-transform: uppercase;
  white-space: nowrap;
}
.topic-row .tr-rail,
.workon-card .tr-rail {
  width: 3px; height: 16px; border-radius: 2px; flex: none;
  background: var(--border-strong);
}
.topic-row .tr-status.strong,
.workon-card .tr-status.strong { color: var(--success); }
.topic-row .tr-status.strong .tr-rail,
.workon-card .tr-status.strong .tr-rail { background: var(--success); }
.topic-row .tr-status.needs,
.workon-card .tr-status.needs { color: var(--warning); }
.topic-row .tr-status.needs .tr-rail,
.workon-card .tr-status.needs .tr-rail { background: var(--warning); }
.topic-row .tr-status.weak,
.workon-card .tr-status.weak { color: var(--danger); }
.topic-row .tr-status.weak .tr-rail,
.workon-card .tr-status.weak .tr-rail { background: var(--danger); }
.topic-row .tr-status.nodata,
.workon-card .tr-status.nodata { color: var(--text-faint); font-weight: 600; }
.wo-pct { font-family: var(--mono); font-variant-numeric: tabular-nums; margin-right: 2px; }

/* .chip removed — the Progress page now uses the .tr-status severity rail. */

.workon-section { margin-top: 44px; }
.workon-card { padding: 22px 24px; margin-bottom: 16px; }
.workon-card .wo-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.workon-card h3 { font-size: 1.02rem; }
/* No pills here. A weak topic card was showing five sub-skill chips and three
   lesson chips — eight rounded outlines per card, several cards deep, which
   turned the page into a field of lozenges and made the one thing that matters
   (the score) compete with decoration. Both lists are now plain text: the
   sub-skills read as a sentence, and the lessons read as links, which is what
   they are. */
.wo-includes {
  margin-bottom: 12px;
  font-size: 0.85rem; line-height: 1.6; color: var(--text-dim);
}
.wo-includes span:not(:last-child)::after { content: " · "; color: var(--text-faint); }

.wo-lessons { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 14px; }
.wo-review { font-size: 0.8rem; font-weight: 700; color: var(--text-faint); }
.wo-lesson {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.85rem; font-weight: 600; color: var(--accent);
  text-decoration: none;
}
.wo-lesson:hover { text-decoration: underline; text-underline-offset: 2px; }
.wo-lesson .ico { width: 13px; height: 13px; }
.workon-empty { text-align: center; padding: 44px 24px; color: var(--text-dim); }
.workon-empty .ico { width: 30px; height: 30px; color: var(--accent); display: block; margin: 0 auto 12px; }
.workon-empty p { max-width: 460px; margin: 0 auto 18px; }

@media (max-width: 720px) {
  .topic-table-head { display: none; }
  .topic-row { grid-template-columns: 1fr 70px 100px; }
  .topic-row .progress-bar { display: none; }
  .exam-nav .btn { padding: 8px 12px; }
}

@media (max-width: 720px) {
  .test-sticky .ts-row { flex-wrap: wrap; gap: 10px; }
  .test-sticky .ts-row .progress-bar { flex-basis: 100%; order: 3; }
  .ei-modes .btn-lg { width: 100%; }
}

/* ============================================================
   Legal pages, legal footer & cookie notice
   ============================================================ */
.legal { max-width: 780px; padding-top: 56px; padding-bottom: 40px; }
.legal h1 { font-size: clamp(1.8rem, 4vw, 2.4rem); font-weight: 800; letter-spacing: -0.015em; margin-bottom: 4px; }
.legal-updated { color: var(--text-faint); font-size: 0.85rem; margin-bottom: 28px; }
.legal h2 { font-size: 1.25rem; font-weight: 700; margin: 34px 0 10px; }
.legal h3 { font-size: 1rem; font-weight: 600; margin: 20px 0 6px; }
.legal p { color: var(--text-dim); font-size: 0.95rem; margin-bottom: 12px; }
.legal ul { margin: 0 0 12px 22px; }
.legal li { color: var(--text-dim); font-size: 0.95rem; margin-bottom: 8px; }
.legal strong { color: var(--text); font-weight: 600; }
.legal a { color: var(--accent); }
.legal a:hover { text-decoration: underline; }

/* Checkbox row (signup age confirmation) */
.check-row {
  display: flex; align-items: flex-start; gap: 10px;
  text-align: left; margin-bottom: 18px; cursor: pointer;
}
.check-row input[type="checkbox"] {
  width: 17px; height: 17px; margin-top: 3px; flex-shrink: 0;
  accent-color: var(--accent); cursor: pointer;
}
.check-row span { font-size: 0.88rem; color: var(--text-dim); }

/* Slim legal footer injected on pages without a full <footer> */
.app-footer {
  padding: 26px 16px 22px;
  text-align: center;
  font-size: 0.8rem;
  color: var(--text-faint);
}
.app-footer a { color: var(--text-dim); transition: color 0.2s; }
.app-footer a:hover { color: var(--text); }
.app-footer .dot { margin: 0 8px; }

/* Cookie notice banner (first visit). Deliberately in normal document flow
   (not position:fixed) so it displaces the page instead of floating on top
   of it — a fixed overlay could otherwise cover content underneath it, e.g.
   the "you agree to our Terms / Privacy Policy" line on signup. */
/* The same floating card the landing page uses, so the notice looks like one
   thing across the whole site rather than a top bar in the app and a card on
   the marketing page. Themed rather than hardcoded white — the landing page is
   a fixed light design, but this shows on pages that follow the user's theme.

   It used to be an in-flow bar specifically so it could not cover the "you
   agree to our Terms" line at the bottom of the signup form. That still
   matters, so body.cookie-open below reserves the height back — the notice
   floats, and the page can still scroll clear of it. */
.cookie-banner {
  position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%);
  z-index: 500;
  display: flex; align-items: center; gap: 14px;
  max-width: 640px; width: calc(100% - 32px);
  padding: 14px 18px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 12px 40px rgba(20, 40, 80, 0.2);
}
.cookie-banner p { font-size: 0.88rem; line-height: 1.5; color: var(--text-dim); flex: 1; margin: 0; }
.cookie-banner a { color: var(--accent); font-weight: 600; }
.cookie-banner a:hover { text-decoration: underline; }
.cookie-banner .btn { padding: 9px 16px; font-size: 0.85rem; flex-shrink: 0; margin-left: auto; }
/* Room underneath so a fixed notice never sits on top of the last thing on
   the page. Removed with the notice itself. */
body.cookie-open { padding-bottom: 108px; }
@media (max-width: 560px) {
  .cookie-banner { flex-direction: column; align-items: stretch; text-align: center; gap: 10px; }
  .cookie-banner .btn { margin-left: 0; }
  body.cookie-open { padding-bottom: 150px; }
}

/* WCAG AA contrast fixes: --accent (4.38:1) and --danger (4.28:1) both
   pass fine as button fills / borders (>=3:1 non-text threshold) but fall
   just short of the 4.5:1 normal-text threshold when read as body copy on
   dark backgrounds. Scoped overrides here so buttons/icons/chips elsewhere
   keep the original brand colors untouched. */
[data-theme="dark"] .legal a,
[data-theme="dark"] .cookie-banner a {
  color: #62a0ff;
}
[data-theme="dark"] .feedback.bad,
[data-theme="dark"] .t-msg.err,
[data-theme="dark"] .q-option.wrong .opt-key,
[data-theme="dark"] .test-sticky .timer.low,
[data-theme="dark"] .result-panel .r-score.bad {
  color: #f16161;
}

/* Visually hidden but available to assistive tech — used for structural
   headings that would be visually redundant (e.g. a "Course units" label
   above a list that's already visually obvious from layout/context). */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Skip-to-content link: hidden until keyboard-focused, then pinned top-left */
.skip-link {
  position: absolute;
  left: 8px; top: -60px;
  z-index: 3000;
  background: var(--accent);
  color: #fff;
  padding: 10px 18px;
  border-radius: var(--radius-sm);
  font-weight: 600;
  font-size: 0.9rem;
  transition: top 0.15s ease;
}
.skip-link:focus {
  top: 8px;
}

/* Visible keyboard-focus ring — only for :focus-visible so mouse/touch
   clicks are unaffected; inputs already have their own focus treatment. */
a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible,
.icon-btn:focus-visible,
.btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Shown under every lesson video: the embed can fail for reasons the page
   cannot see (blockers, filtered hosts), so the direct link is always there. */
.video-fallback {
  display: inline-flex; align-items: center; gap: 7px;
  margin-top: 10px; font-size: 0.82rem; font-weight: 600;
  color: var(--text-faint); text-decoration: none;
}
.video-fallback:hover { color: var(--accent); }
.video-fallback .ico { width: 14px; height: 14px; }

/* Appears next to a player that has buffered for 20s without playing a frame —
   the state YouTube shows its own "An error occurred" over. The player is left
   in place in case it recovers, so this is a note, not a replacement. */
.video-stalled {
  display: flex; align-items: flex-start; gap: 8px;
  margin-top: 10px; padding: 10px 13px;
  font-size: 0.84rem; line-height: 1.5;
  background: var(--warn-soft, var(--surface-strong));
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--text-dim);
}
.video-stalled .ico { width: 15px; height: 15px; flex: none; margin-top: 2px; color: var(--accent); }
.video-stalled a { color: var(--accent); font-weight: 600; }

/* Replaces the player when YouTube reports it cannot play the video here.
   Fills the same 16:9 box so the page does not jump. */
.video-dead {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; text-align: center; padding: 26px 22px;
  aspect-ratio: 16 / 9;
  background: var(--surface-strong);
  border: 1px solid var(--border); border-radius: var(--radius);
  color: var(--text-dim);
}
.video-dead .ico { width: 30px; height: 30px; color: var(--text-faint); }
.video-dead p { margin: 0; font-size: 0.92rem; line-height: 1.5; max-width: 46ch; }
.video-dead strong { color: var(--text); font-weight: 700; }
.video-dead .vd-sub { font-size: 0.84rem; color: var(--text-faint); }
.video-dead .vd-code { font-size: 0.72rem; color: var(--text-faint); font-family: var(--mono); }

/* ============ AI tutor on the coding pages (code-tutor.js) ============ */
.ct-fab {
  position: fixed; right: 22px; bottom: 22px; z-index: 60;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 11px 17px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--border); background: var(--surface);
  color: var(--text); font-family: inherit; font-size: 0.85rem; font-weight: 700;
  box-shadow: var(--shadow);
}
/* Hover is shared with the other tutor buttons — see the "AI tutor" block. */
.ct-fab .ico { width: 15px; height: 15px; }

.ct-panel {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(420px, 100vw);
  z-index: 70; display: flex; flex-direction: column;
  background: var(--surface); border-left: 1px solid var(--border);
  transform: translateX(102%); transition: transform 0.24s ease;
}
.ct-panel.open { transform: translateX(0); }
.ct-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 15px 18px; border-bottom: 1px solid var(--border); font-size: 0.95rem;
}
.ct-x { border: 0; background: none; color: var(--text-dim); font-size: 1.5rem; line-height: 1; cursor: pointer; }
.ct-x:hover { color: var(--text); }
.ct-body { flex: 1; overflow-y: auto; padding: 16px 18px; display: flex; flex-direction: column; gap: 12px; }
.ct-empty { color: var(--text-faint); font-size: 0.88rem; line-height: 1.7; }
.ct-msg { font-size: 0.9rem; line-height: 1.65; border-radius: var(--radius-sm); padding: 10px 13px; max-width: 92%; }
.ct-you { align-self: flex-end; background: var(--accent-soft); color: var(--text); }
.ct-ai { align-self: flex-start; background: var(--surface-strong); color: var(--text-dim); border: 1px solid var(--border); }
.ct-err { color: var(--danger); }
.ct-msg code { font-family: var(--mono); font-size: 0.85em; background: var(--code-bg); border: 1px solid var(--border); padding: 1px 5px; border-radius: 5px; color: var(--accent); }
/* Fenced blocks are styled once, up with the lesson drawer's — see .t-code. */
.ct-typing span {
  display: inline-block; width: 6px; height: 6px; margin-right: 4px; border-radius: 50%;
  background: var(--text-faint); animation: ctBlink 1.2s infinite;
}
.ct-typing span:nth-child(2) { animation-delay: 0.18s; }
.ct-typing span:nth-child(3) { animation-delay: 0.36s; }
@keyframes ctBlink { 0%, 60%, 100% { opacity: 0.25; } 30% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .ct-panel { transition: none; }
  .ct-typing span { animation: none; }
}
.ct-foot { display: flex; gap: 8px; padding: 13px 16px; border-top: 1px solid var(--border); }
.ct-foot input {
  flex: 1; font-family: inherit; font-size: 0.88rem; color: var(--text);
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 9px 12px; outline: none;
}
.ct-foot input:focus { border-color: var(--accent); }
/* Same anti-zoom rule as the lesson drawer's — see the note up by
   .tutor-inputrow. It sits here because it must follow the declaration above. */
@media (pointer: coarse) {
  .ct-foot input { font-size: 16px; }
}
@media (max-width: 620px) { .ct-fab span { display: none; } .ct-fab { padding: 13px; } }

/* ============================================================
   Full-length exam — Section I / break / Section II
   ============================================================
   A deliberately plainer surface than the rest of the app. A real AP exam
   is a quiet room and a sheet of paper: one column, one question, no
   gradients, no cards floating on cards, no colour until it means
   something. The unit exams keep the friendlier app look; this does not.
   ============================================================ */
.exam-shell { max-width: 860px; margin: 0 auto; }

/* Section banner: which section, how long, how far in. */
.exam-banner {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 14px 18px; margin-bottom: 20px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.exam-banner .sec {
  font-size: 0.7rem; font-weight: 800; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--accent);
}
.exam-banner .sec-name { font-size: 0.92rem; font-weight: 700; color: var(--text); }
.exam-banner .sec-meta { font-size: 0.82rem; color: var(--text-faint); }
.exam-banner .spacer { margin-left: auto; }
.exam-banner .clock {
  font-family: var(--mono); font-size: 1.05rem; font-weight: 700;
  color: var(--text); font-variant-numeric: tabular-nums;
}
.exam-banner .clock.warn { color: var(--warning); }
.exam-banner .clock.crit { color: var(--danger); }

/* ---- the 10-minute break ---- */
.exam-break[hidden] { display: none; }
.exam-break {
  max-width: 620px; margin: 40px auto; padding: 40px 32px;
  text-align: center;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius);
}
.exam-break .brk-eyebrow {
  font-size: 0.7rem; font-weight: 800; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--text-faint);
}
.exam-break h2 { font-size: 1.5rem; font-weight: 800; margin: 10px 0 6px; }
.exam-break p { color: var(--text-dim); font-size: 0.95rem; line-height: 1.6; max-width: 46ch; margin: 0 auto; }
.exam-break .brk-clock {
  font-family: var(--mono); font-size: 3rem; font-weight: 700;
  color: var(--text); margin: 22px 0 4px; font-variant-numeric: tabular-nums;
}
.exam-break .brk-sub { font-size: 0.82rem; color: var(--text-faint); }
.exam-break .brk-actions { margin-top: 26px; display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.exam-break .brk-score { margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--border);
  font-size: 0.86rem; color: var(--text-dim); }
.exam-break .brk-score b { color: var(--text); }

/* ---- Section II ---- */
.frq-section[hidden] { display: none; }
.frq-q { margin-bottom: 26px; }
.frq-head {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding-bottom: 12px; margin-bottom: 18px; border-bottom: 2px solid var(--text);
}
.frq-head .n { font-size: 1.3rem; font-weight: 800; color: var(--text); }
.frq-head .topic { font-size: 0.9rem; color: var(--text-dim); }
.frq-head .pts { margin-left: auto; font-size: 0.8rem; font-weight: 700; color: var(--text-faint); }

/* The prompt reads like exam paper: serif-free, generous, uncoloured. */
.frq-prompt { font-size: 0.95rem; line-height: 1.72; color: var(--text); }
.frq-prompt p { margin: 0 0 14px; }
.frq-prompt code {
  font-family: var(--mono); font-size: 0.88em;
  background: var(--surface-strong); border: 1px solid var(--border);
  border-radius: 4px; padding: 1px 5px;
}
.frq-prompt pre {
  background: var(--surface-strong); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 14px 16px; margin: 0 0 16px;
  overflow-x: auto;
}
.frq-prompt pre code {
  background: none; border: 0; padding: 0;
  font-size: 0.82rem; line-height: 1.65; white-space: pre; font-variant-ligatures: none;
  color: var(--text);
}

/* ---- figures inside a prompt ----
   Two kinds: the example tables (method call → return value → explanation)
   and the array / ArrayList diagrams. Both are real <table>s, ruled and
   uncoloured, so they read the way they do on the printed exam. Each sits
   in a wrapper that scrolls sideways on its own, so a wide table never
   pushes the page out. */
.frq-figure {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  margin: 0 0 18px;
}
.frq-prompt table {
  border-collapse: collapse;
  font-size: 0.86rem; line-height: 1.5;
  color: var(--text);
}
.frq-prompt th,
.frq-prompt td {
  border: 1px solid var(--border-strong);
  padding: 8px 11px; text-align: left; vertical-align: top;
}
.frq-prompt thead th {
  background: var(--surface-strong); font-weight: 700;
  vertical-align: bottom;
}
/* Code in a cell is monospace but wears no chip — the borders are the grid.
   It never wraps either: a return value broken across two lines reads as two
   different strings. The column widens instead, and the figure scrolls. */
.frq-prompt td code,
.frq-prompt th code {
  background: none; border: 0; padding: 0; font-size: 0.86em;
  white-space: nowrap;
}
/* A statement that wrapped onto a second line on the exam keeps its break
   and its indentation. */
.frq-prompt td code.blk { display: block; white-space: pre; }
.frq-prompt .num { text-align: center; white-space: nowrap; }
.frq-prompt tr.total td { font-weight: 700; }
.frq-prompt tr.total .tot { text-align: right; }

/* Array / ArrayList diagrams: square cells, indices in the header band. */
.frq-prompt table.frq-array td,
.frq-prompt table.frq-array th {
  text-align: center; padding: 7px 14px; min-width: 40px;
}
.frq-prompt table.frq-array thead th,
.frq-prompt table.frq-array tbody th {
  background: var(--surface-strong); color: var(--text-dim);
  font-weight: 700; white-space: nowrap;
}
.frq-prompt table.frq-array tbody th { text-align: right; }
.frq-prompt .fig-label {
  font-family: var(--mono); font-size: 0.82rem; font-weight: 700;
  color: var(--text-dim); margin: 0 0 6px;
}
.frq-answer-label {
  font-size: 0.72rem; font-weight: 800; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-faint); margin: 22px 0 8px;
}
/* Plain, uncoloured, no autocomplete — the exam is handwritten. */
.frq-editor {
  display: block; width: 100%; min-height: 300px; resize: vertical;
  font-family: var(--mono); font-size: 13.5px; line-height: 1.6;
  padding: 14px 16px; tab-size: 4; -moz-tab-size: 4;
  white-space: pre; overflow: auto;
  color: var(--text); background: var(--bg);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  outline: none;
}
.frq-editor:focus { border-color: var(--accent); }
.frq-nav { display: flex; align-items: center; gap: 10px; margin-top: 18px; flex-wrap: wrap; }
.frq-nav .spacer { margin-left: auto; }
.frq-pager { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 20px; }
.frq-pager button {
  width: 38px; height: 38px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--surface);
  font-weight: 700; font-size: 0.85rem; color: var(--text-dim); cursor: pointer;
}
.frq-pager button.on { border-color: var(--accent); color: var(--accent); }
.frq-pager button.written { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }

/* ---- grading progress ---- */
.frq-grading { max-width: 560px; margin: 50px auto; text-align: center; }
.frq-grading h2 { font-size: 1.3rem; font-weight: 800; margin-bottom: 8px; }
.frq-grading p { color: var(--text-dim); font-size: 0.9rem; }
.frq-steps { margin-top: 22px; text-align: left; display: grid; gap: 8px; }
.frq-step {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 14px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font-size: 0.88rem; color: var(--text-dim);
}
.frq-step .ico { width: 15px; height: 15px; flex: none; color: var(--text-faint); }
.frq-step.doing { border-color: var(--accent); color: var(--text); }
.frq-step.doing .ico { color: var(--accent); }
.frq-step.done .ico { color: var(--success); }

/* ---- composite result ---- */
.ap-result { max-width: 720px; margin: 0 auto; }
.ap-score {
  display: flex; align-items: center; gap: 26px; flex-wrap: wrap;
  padding: 26px 28px; margin-bottom: 18px;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
}
.ap-score .big {
  font-size: 4.2rem; font-weight: 800; line-height: 1; color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.ap-score .lbl { font-size: 0.72rem; font-weight: 800; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--text-faint); }
.ap-score .composite { font-size: 1.15rem; font-weight: 700; color: var(--text); margin-top: 4px; }
.ap-score .tonext { font-size: 0.84rem; color: var(--text-dim); margin-top: 6px; }
.ap-split { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 18px; }
.ap-split .half {
  padding: 16px 18px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--surface);
}
.ap-split .half .h {
  font-size: 0.7rem; font-weight: 800; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-faint); margin-bottom: 8px;
}
.ap-split .half .v { font-family: var(--mono); font-size: 1.3rem; font-weight: 700; color: var(--text); }
.ap-split .half .raw { font-size: 0.82rem; color: var(--text-dim); margin-top: 3px; }
.ap-note {
  padding: 13px 16px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-size: 0.8rem; line-height: 1.6; color: var(--text-faint); margin-bottom: 12px;
}
.ap-note b { color: var(--text-dim); }

/* per-question rubric breakdown */
.rub-q { border: 1px solid var(--border); border-radius: var(--radius-sm); margin-bottom: 12px; overflow: hidden; }
.rub-q > summary {
  display: flex; align-items: center; gap: 12px; cursor: pointer;
  padding: 13px 16px; font-size: 0.9rem; font-weight: 700; list-style: none;
}
.rub-q > summary::-webkit-details-marker { display: none; }
.rub-q .sc { margin-left: auto; font-family: var(--mono); font-weight: 700; }
.rub-q .sc.full { color: var(--success); }
.rub-q .sc.part { color: var(--warning); }
.rub-q .sc.none { color: var(--danger); }
.rub-row {
  display: grid; grid-template-columns: 26px 1fr; gap: 10px;
  padding: 10px 16px; border-top: 1px solid var(--border); font-size: 0.85rem;
}
.rub-row .mk { font-family: var(--mono); font-weight: 700; }
.rub-row.earned .mk { color: var(--success); }
.rub-row.lost .mk { color: var(--danger); }
.rub-row .why { color: var(--text-dim); margin-top: 3px; font-size: 0.82rem; }
.rub-evidence {
  padding: 10px 16px 14px; border-top: 1px solid var(--border);
  font-size: 0.8rem; color: var(--text-faint);
}

/* The three actions after an exam. They were falling back to a generic
   .cta-row: different heights, no consistent gap, and "Retake"/"All exams"
   touching. Given their own row so they line up on one baseline, share one
   height, and sit apart. */
.ap-result .cta-row {
  display: flex; align-items: stretch; gap: 10px; flex-wrap: wrap;
  margin-top: 22px; padding-top: 20px; border-top: 1px solid var(--border);
}
.ap-result .cta-row .btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; padding: 0 20px; margin: 0; font-size: 0.9rem; white-space: nowrap;
}
.ap-result .cta-row .btn-ghost { background: var(--surface); }

@media (max-width: 640px) {
  .ap-split { grid-template-columns: 1fr; }
  .ap-score .big { font-size: 3.2rem; }
  .ap-result .cta-row .btn { flex: 1 1 100%; }
}

/* ============================================================
   Practice exam list
   ============================================================
   Rows, not a grid of boxes. Equal-sized cards flattened the difference
   between a 12-question unit check and a three-hour full exam, and buried
   the three facts that actually decide which one you open — how long it
   is, how long it takes, how you did last time. In fixed columns those
   line up down the page and the list reads at a glance.
   ============================================================ */
.tests-list {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
}
.test-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 190px 130px auto;
  align-items: center;
  gap: 20px;
  padding: 16px 20px;
  border-top: 1px solid var(--border);
  text-decoration: none;
  color: inherit;
  transition: background 0.15s ease;
}
.test-row:first-child { border-top: 0; }
.test-row:hover { background: var(--surface-strong); }

.tr-kind {
  display: block;
  font-size: 0.66rem; font-weight: 800; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--text-faint); margin-bottom: 3px;
}
.test-row:not(.unit) .tr-kind { color: var(--accent); }
.tr-main h3 { font-size: 1rem; font-weight: 700; color: var(--text); margin: 0 0 3px; }
.tr-main p {
  margin: 0; font-size: 0.84rem; line-height: 1.5; color: var(--text-dim);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

.tr-facts { display: grid; gap: 4px; font-size: 0.8rem; color: var(--text-faint); }
.tr-facts span { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.tr-facts .ico { width: 13px; height: 13px; flex: none; }

.tr-best { display: grid; gap: 2px; }
.tr-score {
  font-family: var(--mono); font-size: 0.95rem; font-weight: 700;
  color: var(--success); font-variant-numeric: tabular-nums;
}
.tr-tries { font-size: 0.74rem; color: var(--text-faint); }
.tr-none { font-size: 0.8rem; color: var(--text-faint); }

.tr-go .btn { min-width: 84px; justify-content: center; }
.test-row.soon { opacity: 0.6; }
.test-row.soon .tr-main h3 { color: var(--text-dim); }

@media (max-width: 900px) {
  .test-row { grid-template-columns: minmax(0, 1fr) auto; gap: 12px 18px; }
  .tr-facts { grid-column: 1; grid-row: 2; grid-auto-flow: column; justify-content: start; gap: 16px; }
  .tr-best { grid-column: 2; grid-row: 2; justify-items: end; }
  .tr-go { grid-column: 2; grid-row: 1; }
}
@media (max-width: 560px) {
  .test-row { padding: 14px 16px; }
  .tr-main p { display: none; }
}
