/* =============================================================================
   Lysia — layout + components. Reads tokens.css.
   ========================================================================== */

* { box-sizing: border-box; }
/* overflow-x:hidden stops the off-canvas #assistant-panel (fixed, parked right of
   the viewport) from leaking horizontal scroll — which otherwise skews the
   centered Plan shell + clips the topbar's right cluster. */
html, body { margin: 0; height: 100%; max-width: 100%; overflow-x: hidden; }
body {
  /* transparent so the fixed ambient backdrop (#bg-grove) shows through the
     frosted sidebar + breadcrumb gaps. */
  background: transparent;
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--lead-normal);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

::selection { background: color-mix(in oklch, var(--accent) 35%, transparent); }
h1, h2, h3, p { margin: 0; }
button { font: inherit; cursor: pointer; }
:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-radius: 3px; }
/* Screen-reader-only utility: visually hidden, still in the accessibility tree. */
.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; }
.tnum { font-variant-numeric: tabular-nums; }
.mono { font-family: var(--font-mono); }

/* ---------- App shell (Ink & Wash: frosted left sidebar + main) ---------- */
.app { min-height: 100%; display: flex; align-items: stretch; background: transparent; }

/* The sidebar — a frosted translucent panel with an azure active rail. Sticky
   full-height so it stays put while the main column scrolls. backdrop-filter
   degrades to a near-solid surface where unsupported (see @supports below). */
.sidebar {
  width: 236px; flex: none; align-self: stretch;
  position: sticky; top: 0; height: 100vh;
  display: flex; flex-direction: column; gap: 4px;
  padding: 20px 16px;
  background: color-mix(in oklch, var(--surface) 58%, transparent);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-right: 1px solid var(--border);
  box-shadow: 8px 0 30px color-mix(in oklch, var(--ink) 4%, transparent);
  z-index: var(--z-sticky);
}
@supports not ((backdrop-filter: blur(14px)) or (-webkit-backdrop-filter: blur(14px))) {
  .sidebar { background: color-mix(in oklch, var(--surface) 94%, transparent); }
}

/* brand row + hairline divider */
.sb-brand {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 8px 16px; margin-bottom: 8px;
  border-bottom: 1px solid var(--border);
}
.sb-nav { display: flex; flex-direction: column; gap: 2px; flex: none; overflow: visible; }

/* nav items reuse .tab[data-surface] for routing but render as left-rail rows. */
.nav-item {
  display: flex; align-items: center; gap: 11px; width: 100%;
  text-align: left; padding: 9px 12px;
  border: 0; border-radius: var(--r-sm); background: transparent;
  color: var(--ink-muted); font-family: var(--font-body);
  font-size: 13.5px; font-weight: 500; white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.nav-item:hover { background: var(--hover-bg); color: var(--ink); }
.nav-item[aria-selected="true"] {
  background: var(--surface); color: var(--ink); font-weight: 600;
  box-shadow: inset 3px 0 0 var(--accent), var(--shadow-1);
}
/* the active-tab underline (::after) from the old top tabs must not show here. */
.nav-item[aria-selected="true"]::after { content: none; }
.nav-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.nav-dot-empty { background: transparent; }

/* sidebar footer: the "Your courses" group grows to fill and scrolls when the
   list is long; the account row + logout stay pinned at the bottom and always
   visible. (Was margin-top:auto with no scroll → a long course list pushed the
   footer controls off-screen.) */
.sb-foot { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 12px; }
.sb-courses-group {
  display: flex; flex-direction: column; gap: 4px; flex: 1; min-height: 0;
  border-top: 1px solid var(--border); padding-top: 14px;
}
.sb-courses-label { flex: none; padding: 2px 8px 4px; letter-spacing: 0.12em; }
.sb-courses { display: flex; flex-direction: column; gap: 2px; overflow-y: auto; min-height: 0; }
.sb-course {
  display: flex; flex-direction: column; gap: 6px; width: 100%;
  text-align: left; padding: 9px 12px;
  border: 0; border-radius: var(--r-sm); background: transparent; cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.sb-course:hover { background: var(--hover-bg); }
.sb-course[data-active="true"] { background: var(--surface); box-shadow: inset 3px 0 0 var(--accent), var(--shadow-1); }
.sb-course-head { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; }
.sb-course-code { font-weight: 600; font-size: 12.5px; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sb-course-pct { letter-spacing: 0.04em; flex: none; }
.sb-course-bar { height: 4px; border-radius: 2px; background: var(--border); overflow: hidden; }
.sb-course-bar i { display: block; height: 100%; width: 100%; transform-origin: left; transform: scaleX(0); background: var(--accent); transition: transform var(--dur-normal) var(--ease-out); }

.sb-account {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  flex: none; padding: 14px 6px 2px; border-top: 1px solid var(--border);
}
.sb-email { text-transform: none; letter-spacing: 0.03em; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sb-mode { width: 30px; height: 30px; border-radius: 50%; font-size: 13px; flex: none; }
.sb-authctl { display: flex; flex: none; gap: 6px; padding: 0 4px; }

/* Narrow viewports: the rail stops being full-height/sticky so the main column
   gets the width; it scrolls inline above the content. (A full mobile drawer is
   out of scope for this reskin — the rail simply stacks.) */
@media (max-width: 720px) {
  .app { flex-direction: column; }
  .sidebar {
    width: auto; height: auto; position: static;
    flex-direction: row; flex-wrap: wrap; align-items: center; gap: 8px;
    border-right: 0; border-bottom: 1px solid var(--border); box-shadow: none;
  }
  .sb-nav { flex-direction: row; flex-wrap: wrap; flex: 1; }
  .nav-item[aria-selected="true"] { box-shadow: inset 0 -3px 0 var(--accent), var(--shadow-1); }
  .sb-foot { margin-top: 0; flex-direction: row; flex-wrap: wrap; align-items: center; gap: 10px; width: 100%; }
  .sb-courses-group { border-top: 0; padding-top: 0; flex: 1; min-width: 200px; }
  .sb-courses { flex-direction: row; flex-wrap: wrap; }
  .sb-course { width: auto; min-width: 120px; }
  .sb-account { border-top: 0; padding: 0; }
}

/* ---------- Main column + sticky breadcrumb strip ---------- */
.main-col { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.topstrip {
  position: sticky; top: 0; z-index: var(--z-sticky);
  display: flex; align-items: center; gap: 14px;
  padding: 14px 30px; border-bottom: 1px solid var(--border);
  background: color-mix(in oklch, var(--bg) 80%, transparent);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
.topstrip .crumb { letter-spacing: 0.1em; color: var(--ink-subtle); }
.topstrip-spacer { flex: 1; }

/* Collapsible sidebar: the #sb-toggle hamburger flips this class on .app. The
   rail is removed from flow so the main column reclaims its full width. */
.app.sb-collapsed .sidebar { display: none; }
.sb-toggle svg { display: block; }
.days-to-final { color: var(--missed); }
.days-to-final[hidden] { display: none; }

/* legacy brand-tag (wordmark suffix) is unused in the sidebar; keep the rule
   harmless for any residual markup. */
.brand {
  /* center, not baseline: the SVG mark opts out of baseline alignment
     (align-self: center below), so baseline mode hung the wordmark off the
     mark's bottom edge and floated "Lysia" above the nav tabs' midline */
  display: flex; align-items: center; gap: 9px;
  color: inherit; text-decoration: none; cursor: pointer; border-radius: var(--r1);
  transition: opacity var(--dur-fast, 150ms) var(--ease-out, ease);
}
.brand:hover { opacity: 0.78; }
.brand:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.brand-mark {
  width: 34px; height: 28px; flex: none; display: block; align-self: center;
  color: var(--ink); overflow: visible;
}
.brand-name {
  font-family: var(--font-head); font-weight: 600;
  font-size: 20px; line-height: 1; letter-spacing: var(--track-tight);
}
/* mono uppercase product tag beside the wordmark (mockup .lysah-tag) */
.brand-tag {
  font-family: var(--font-data); font-size: 9px; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--ink-subtle); white-space: nowrap;
  margin-left: 2px;
}
@media (max-width: 1180px) { .brand-tag { display: none; } }

/* The old top-tab chrome (.header-site-link, hamburger, mobile drawer, nav-wrap
   clip, tab-cluster separators, the "More ▾" overflow menu, .header-tools) was
   removed when the top tab-row became the left sidebar (Ink & Wash). The
   sidebar's .nav-item rules above own the nav look now; .tab keeps only the
   base reset the .nav-item buttons (class="tab nav-item") still inherit. The
   matching JS controllers (mobileNav, navOverflow) early-return since their
   elements no longer exist. */
.tab {
  appearance: none; border: 0; background: transparent;
  color: var(--ink-muted); font-family: var(--font-body);
  font-weight: 500; font-size: 13.5px; white-space: nowrap;
  position: relative;
}

.icon-btn {
  appearance: none; border: 1px solid var(--border); background: var(--surface);
  color: var(--ink-muted); width: calc(var(--control-h) - 6px); height: calc(var(--control-h) - 6px);
  border-radius: var(--r1); display: grid; place-items: center; font-size: 14px;
}
.icon-btn:hover { color: var(--ink); border-color: var(--border-strong); }
/* [hidden] override: display:grid above otherwise keeps a hidden icon-btn
   (e.g. #mode-toggle on a mode-locked paper theme) visible. */
.icon-btn[hidden] { display: none; }

/* Inline link-style button for empty-state CTAs that deep-link to another tab */
.cta-link {
  appearance: none; border: 0; background: transparent; padding: 0;
  color: var(--accent); font: inherit; cursor: pointer;
  text-decoration: underline; text-underline-offset: 2px;
}
.cta-link:hover { color: var(--accent-hover, var(--accent)); opacity: 0.82; }
.cta-link:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-radius: 2px; }

/* ---------- Buttons ---------- */
.btn {
  appearance: none; border: 1px solid var(--border); background: var(--surface);
  color: var(--ink); height: var(--control-h);
  padding: 0 var(--sp-4); border-radius: var(--r-sm);
  font-size: var(--text-sm); font-weight: var(--w-medium);
  display: inline-flex; align-items: center; gap: var(--sp-2); white-space: nowrap;
}
.btn:hover { background: var(--hover-bg); border-color: var(--border-strong); }
.btn-accent {
  border-color: color-mix(in oklch, var(--accent) 55%, transparent);
  background: var(--accent-quiet); color: var(--accent);
}
[data-mode="light"] .btn-accent { color: var(--accent-hover); }
.btn-accent:hover { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.btn-ghost { border-color: transparent; background: transparent; color: var(--ink-muted); }
.btn-ghost:hover { color: var(--ink); background: var(--hover-bg); }
/* mockup .btn-quiet: a borderless quiet control for secondary chrome actions */
.btn-quiet { border-color: transparent; background: transparent; color: var(--ink-muted); }
.btn-quiet:hover { color: var(--ink); background: var(--hover-bg); border-color: transparent; }
.btn-sm { height: calc(var(--control-h) - 6px); padding: 0 var(--sp-3); font-size: var(--text-xs); }
/* The single headline action on the Exam Generation surface (#readiness-generate-btn).
   Larger and weightier than a normal accent button so it reads as THE primary
   call to action now that the inline Generator panel is gone. */
.btn-generate-hero {
  height: calc(var(--control-h) + 8px);
  padding: 0 var(--sp-5);
  font-size: var(--text-md);
  font-weight: var(--w-bold);
  border-radius: var(--r-md);
  letter-spacing: var(--track-tight);
}
/* Pressed feedback: an inset rule, no layout shift. */
.btn:active { box-shadow: inset 0 -2.5px 0 var(--border-strong); }
.btn-accent:active { box-shadow: inset 0 -2.5px 0 color-mix(in oklch, var(--accent) 60%, var(--accent-ink)); }
/* Designed focus ring: keep the global 2px :focus-visible baseline (style.css:22)
   and layer an accent halo so it reads on these controls. */
.btn:focus-visible {
  outline: 2px solid var(--focus-ring); outline-offset: 2px;
  box-shadow: 0 0 0 4px color-mix(in oklch, var(--accent) 18%, transparent);
}
/* Keep the pressed inset when a focused button is activated by keyboard —
   layer both shadows so :focus-visible doesn't clobber the :active rule. */
.btn:focus-visible:active {
  box-shadow: inset 0 -2.5px 0 var(--border-strong),
              0 0 0 4px color-mix(in oklch, var(--accent) 18%, transparent);
}
.btn-accent:focus-visible:active {
  box-shadow: inset 0 -2.5px 0 color-mix(in oklch, var(--accent) 60%, var(--accent-ink)),
              0 0 0 4px color-mix(in oklch, var(--accent) 18%, transparent);
}
/* Disabled: dimmed, not-allowed, no hover lift. Mirrors .seg button:disabled. */
.btn:disabled, .btn[aria-disabled="true"] {
  opacity: 0.45; cursor: not-allowed; box-shadow: none;
}
.btn:disabled:hover, .btn[aria-disabled="true"]:hover {
  background: var(--surface); border-color: var(--border);
}
/* Neutralize the accent variant when disabled so it reads inert, not a quieter
   purple — drop back to the plain surface/border vocabulary. */
.btn-accent:disabled, .btn-accent[aria-disabled="true"] {
  background: var(--surface); border-color: var(--border); color: var(--ink-muted);
}
.btn-accent:disabled:hover, .btn-accent[aria-disabled="true"]:hover {
  background: var(--surface); border-color: var(--border);
}

/* ---------- Inputs ---------- */
input[type="text"], input[type="number"], input[type="date"],
input[type="password"], select, textarea {
  appearance: none; font: inherit; font-size: var(--text-sm);
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 0 var(--sp-3); height: var(--control-h);
}
textarea { padding: var(--sp-2) var(--sp-3); height: auto; line-height: 1.45; resize: vertical; }
input::placeholder, textarea::placeholder { color: var(--ink-subtle); }
input:focus, select:focus, textarea:focus { outline: 2px solid var(--focus-ring); outline-offset: 1px; border-color: transparent; }
select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23999' stroke-width='1.4' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--sp-3) center;
  padding-right: var(--sp-6);
}

/* ---------- Main / surfaces ---------- */
.main { flex: 1; padding: 32px 30px 70px; }
.surface { display: none; }
.surface.active { display: block; position: relative; animation: fade-in var(--dur-normal) var(--ease-out); }

@keyframes fade-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .surface.active { animation: none; } }
/* .main now owns the outer 32px gutter (Ink & Wash shell); .wrap only centers +
   caps width, so surfaces don't double-pad inside the main column. */
.wrap { max-width: 980px; margin: 0 auto; padding: 0; }

/* Instrument-panel surface header: a mono-uppercase eyebrow (data-eyebrow) sits
   above the serif H1, with a muted one-line subtitle inline. Matches the
   reference's .spine-label eyebrow + page-heading title pairing. */
.surface-head {
  display: grid; grid-template-columns: 1fr auto; align-items: baseline;
  gap: var(--sp-1) var(--sp-4); margin-bottom: var(--sp-5);
}
.surface-head[data-eyebrow]::before {
  content: attr(data-eyebrow); grid-column: 1 / -1; order: -1;
  font-family: var(--font-data); font-size: 10px;
  text-transform: uppercase; letter-spacing: 0.18em;
  color: var(--accent); font-weight: 600;
  display: inline-flex; align-items: center; gap: 10px;
}
.surface-head h1 {
  /* H1 (audit): marketing serif on page titles for brand cohesion. The token
     falls back to the user's chosen display face on the mono/grotesk axes so a
     deliberate typeface choice still wins. */
  font-family: var(--font-page-heading); font-weight: var(--w-bold);
  font-size: var(--text-2xl); letter-spacing: var(--track-tight); text-wrap: balance;
  line-height: var(--lead-tight);
}
.surface-head .sub { color: var(--ink-muted); font-size: var(--text-sm); text-align: right; }
.surface-head .muted { grid-column: 1 / -1; color: var(--ink-muted); font-size: var(--text-sm); max-width: 64ch; }
@media (max-width: 560px) {
  .surface-head { grid-template-columns: 1fr; }
  .surface-head .sub { text-align: left; }
}

/* ===========================================================================
   PLAN — FinalRedesign pv2: "the day's ledger"
   Left rail: readiness receipt → Horizon (exams/deadlines) → open gaps →
   backlog → Plan with AI. Main: hairline ledger rows (time · entry · defense
   kicker · status) with a now line, slip annotations, and a mono foot strip.
   Tokens only; course color binds through the .is-cN → --block-edge contract.
   START PLAN-REDESIGN
   ======================================================================== */

/* still used by the Plan toolbar date + retention chip */
.date-label { font-family: var(--font-display); font-weight: var(--w-bold); font-size: var(--text-md); letter-spacing: var(--track); }
.date-label-lg { font-size: var(--text-lg, 1.125rem); color: var(--ink, inherit); }
.streak-chip { display:inline-flex; align-items:center; gap:.4ch; font-size: var(--text-xs); font-weight: var(--w-bold); color: var(--ink); border:1px solid var(--border); border-radius:999px; padding:.15em .85em; letter-spacing: var(--track); }
.streak-chip[hidden] { display:none; }
/* H9 (audit): green "key saved" affirmation on the Settings API-key hint. */
.hint.ok { color: var(--done); }

/* Demo mode banner — shown whenever demo content is visible */
.demo-banner {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-4); margin: var(--sp-3) auto 0;
  max-width: none;
  border: 1px solid color-mix(in oklch, var(--accent) 50%, transparent);
  background: color-mix(in oklch, var(--accent) 10%, var(--bg));
  border-radius: var(--r-md); font-size: var(--text-sm);
}
.demo-banner[hidden] { display: none; }
.demo-banner-label {
  font-family: var(--font-mono); font-size: 10px; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--accent); background: color-mix(in oklch, var(--accent) 15%, transparent);
  padding: 2px 6px; border-radius: var(--r-sm); flex-shrink: 0;
}
.demo-banner-msg { flex: 1; color: var(--ink-muted); }
.demo-course-badge {
  font-family: var(--font-mono); font-size: 9px; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--accent); background: color-mix(in oklch, var(--accent) 15%, transparent);
  padding: 1px 5px; border-radius: var(--r-sm); margin-left: auto; flex-shrink: 0;
}
/* Demo artifact tabs inside #gen-out */
.demo-artifact-tabs {
  display: flex; gap: var(--sp-1); border-bottom: 1px solid var(--border);
  margin-bottom: var(--sp-3);
}
.demo-artifact-tab {
  appearance: none; border: 0; border-bottom: 2px solid transparent;
  background: transparent; color: var(--ink-muted);
  font-family: var(--font-display); font-size: var(--text-sm);
  padding: var(--sp-1) var(--sp-3); cursor: pointer; border-radius: var(--r-sm) var(--r-sm) 0 0;
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.demo-artifact-tab:hover { color: var(--ink); }
.demo-artifact-tab.is-active {
  color: var(--accent); border-bottom-color: var(--accent); font-weight: var(--w-medium);
}
.demo-artifact-content {
  white-space: pre-wrap; word-break: break-word; font-family: var(--font-mono);
  font-size: var(--text-xs); line-height: var(--lead-relaxed);
  color: var(--ink); margin: 0; padding: 0;
}
/* "Try a demo course" CTA button inside the empty state */
.demo-try-btn {
  margin-top: var(--sp-3); display: block; width: 100%;
}
/* The pv2 shell: horizon rail (left) · the day's ledger (right). Class names
   the JS binds to (#rail #exam-track #radar-chains #backlog, .block, .b-body,
   .b-actions, .tag, .rationale, …) are preserved; the VALUES are the mockup's
   pv2 vocabulary, token-bound. */
.plan-shell.pv2 {
  width: 100%; max-width: none; margin-inline: auto; min-width: 0;
  display: grid; grid-template-columns: 320px minmax(0, 1fr);
  align-items: stretch;
}

/* ---- left rail (mockup .pv2-left) ---- */
.pv2-left {
  display: flex; flex-direction: column; gap: 26px;
  padding: 34px 30px;
  border-right: 1px solid var(--border);
  background: color-mix(in oklch, var(--surface) 55%, var(--bg));
  min-width: 0;
}
.pv2-left .dv { display: block; margin-bottom: var(--sp-2); }
.pv2-empty { font-size: var(--text-xs); color: var(--ink-muted); line-height: 1.5; }
.pv2-ai-btn { margin-top: auto; justify-content: center; }

/* readiness receipt (mockup .pv2-ready) — plan.js reveals it only on scoped data */
.pv2-ready[hidden] { display: none; }
.pv2-ready .n {
  font-family: var(--font-head); font-weight: 600;
  font-size: clamp(64px, 6vw, 96px); line-height: 0.85; letter-spacing: -0.04em;
  color: var(--ink);
}
.pv2-ready .n i { font-style: normal; font-size: 40px; color: var(--ink-subtle); }
.pv2-ready-sub { margin-top: var(--sp-2); font-size: var(--text-xs); color: var(--ink-muted); line-height: 1.5; max-width: 30ch; }

/* horizon rows (mockup .pv2-exrow) */
.pv2-exams { display: flex; flex-direction: column; }
.pv2-exrow {
  display: grid; grid-template-columns: 1fr auto; gap: 4px 12px;
  padding: 12px 0; border-top: 1px solid var(--border); min-width: 0;
}
.pv2-exrow .nm { font-weight: 600; font-size: 14px; }
.pv2-exrow .dt2,
.pv2-gap-name .dt2,
.pv2-more {
  font-family: var(--font-data); font-size: 9.5px; color: var(--ink-subtle);
  letter-spacing: 0.06em; text-transform: uppercase;
}
.pv2-exrow .td { font-family: var(--font-head); font-size: 26px; font-weight: 600; text-align: right; align-self: center; }
.pv2-more { padding: 10px 0 0; }

/* open gaps (mockup .pv2-gap) — the radar's verdicts, one hairline row each */
.pv2-gaps { display: flex; flex-direction: column; }
.pv2-gapcount { margin-top: calc(-1 * var(--sp-2)); }
.pv2-gap {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: 8px 0; border-top: 1px solid var(--border); font-size: 13px;
  cursor: default;
}
.pv2-gap:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-radius: var(--r1); }
.pv2-gap .pv2-gap-name { min-width: 0; }
.pv2-gap .sc { font-family: var(--font-data); font-size: 11px; color: var(--missed); white-space: nowrap; }
[data-mode="light"] .pv2-gap .sc { color: oklch(46% 0.18 25); }
.pv2-gap .sc.is-ok { color: var(--done); }
[data-mode="light"] .pv2-gap .sc.is-ok { color: oklch(42% 0.13 158); }

/* ---- connective tissue: data-topic linking (gaps ⇄ ledger rows) ---- */
.topic-engaged .pv2-gap:not(.is-linked),
.topic-engaged #rail .block:not(.is-linked) { opacity: 0.42; }
.is-linked.pv2-gap { border-top-color: color-mix(in oklch, var(--accent) 45%, var(--border)); background: color-mix(in oklch, var(--accent) 7%, transparent); }
.is-linked.block { background: color-mix(in oklch, var(--accent) 7%, transparent); }

/* ---- main column: the day's ledger (mockup .pv2-main) ---- */
.pv2-main { padding: 34px 42px; display: flex; flex-direction: column; min-width: 0; }
.pv2-mainhead {
  display: flex; align-items: flex-end; justify-content: space-between; gap: var(--sp-4);
  border-bottom: 1.5px solid var(--ink); padding-bottom: 16px; margin-bottom: 4px;
  flex-wrap: wrap;
}
.pv2-mainhead h1 {
  font-family: var(--font-head); font-weight: 600;
  font-size: clamp(28px, 3.2vw, 38px); letter-spacing: var(--track-tight);
  line-height: var(--lead-tight);
}
.pv2-mainhead .dv { margin-bottom: 6px; }
.pv2-sum { text-align: right; }

/* toolbar (real controls the mockup omits): daynav · re-plan · accept · io */
.pv2-controls {
  display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap;
  padding: 10px 0; border-bottom: 1px solid var(--border);
}
.pv2-controls .sep { width: 1px; align-self: stretch; background: var(--border); margin: 0 var(--sp-1); }
.daynav { display: inline-flex; gap: var(--sp-1); }
.iconbtn {
  appearance: none; border: 1px solid var(--border); background: var(--surface);
  color: var(--ink-muted); width: calc(var(--control-h) - 6px); height: calc(var(--control-h) - 6px);
  border-radius: var(--r-sm); display: grid; place-items: center; cursor: pointer; line-height: 1;
}
.iconbtn:hover { color: var(--ink); border-color: var(--border-strong); }
.iconbtn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

.rail-empty { color: var(--ink-muted); font-size: var(--text-sm); padding: var(--sp-6) var(--sp-4); border-bottom: 1px solid var(--border); text-align: center; }

/* a ledger row (mockup .pv2-row): time · entry · defense kicker · status */
.pv2-row {
  display: grid; grid-template-columns: 92px minmax(0, 1fr) 230px 70px;
  gap: 4px 18px; align-items: baseline;
  padding: 17px 4px; border-bottom: 1px solid var(--border);
  transition: background-color 120ms var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
.pv2-row:hover, .pv2-row:focus-within { background: var(--hover-bg); }
.pv2-row .tm { font-family: var(--font-data); font-size: 11px; color: var(--ink-muted); white-space: nowrap; }
.pv2-row .tm i { font-style: normal; color: var(--ink-subtle); }
.b-body { min-width: 0; }
.pv2-row .ts { font-family: var(--font-head); font-size: 19px; font-weight: 500; letter-spacing: -0.01em; line-height: 1.25; }
/* per-course ink chip — the .is-cN → --block-edge contract (mockup --bc) */
.pv2-row .ts::before {
  content: ""; display: inline-block; width: 8px; height: 8px; border-radius: 3px;
  background: var(--block-edge, var(--border-strong)); margin-right: 10px; vertical-align: 1px;
}
.pv2-row .kd, .pv2-row .st {
  font-family: var(--font-data); font-size: 8.5px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-subtle); line-height: 1.7;
}
.pv2-row .st { font-size: 9px; text-align: right; white-space: nowrap; }

/* row states — real statuses the mockup hints at (is-done / is-now) plus the
   ones it omits (missed, proposed, fixed) */
.pv2-row.is-done .ts { color: var(--ink-subtle); text-decoration: line-through; text-decoration-thickness: 1px; }
.pv2-row.is-done .st, .pv2-row .st.is-held { color: var(--done); }
[data-mode="light"] .pv2-row.is-done .st, [data-mode="light"] .pv2-row .st.is-held { color: oklch(42% 0.13 158); }
.pv2-row.is-missed .ts { color: var(--ink-subtle); }
.pv2-row.is-missed .st { color: var(--missed); }
[data-mode="light"] .pv2-row.is-missed .st { color: oklch(46% 0.18 25); }
.pv2-row.is-proposed { border-bottom-style: dashed; }
.pv2-row.is-proposed .ts { font-style: italic; color: var(--ink-muted); }
.pv2-row.is-proposed .st { color: var(--accent); }
[data-mode="light"] .pv2-row.is-proposed .st { color: var(--accent-hover); }
.pv2-row.is-fixed .ts { color: var(--ink-muted); font-weight: 500; }
.pv2-row.is-now {
  background: color-mix(in oklch, var(--accent) 7%, transparent);
  border-bottom-color: color-mix(in oklch, var(--accent) 35%, var(--border));
}
.pv2-row.is-now .st { color: var(--accent); }
[data-mode="light"] .pv2-row.is-now .st { color: var(--accent-hover); }
@media (prefers-reduced-motion: no-preference) {
  .pv2-row.is-now .st { animation: pv2-pulse 2.2s ease infinite; }
  @keyframes pv2-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }
}

/* defense receipts inside the kicker — semantic ink, same mono voice */
.tag { white-space: nowrap; }
.tag.t-exam { color: var(--exam); }
[data-mode="light"] .tag.t-exam { color: oklch(46% 0.13 70); }
.tag.t-heavy { color: var(--accent); }
[data-mode="light"] .tag.t-heavy { color: var(--accent-hover); }
.tag.t-gap { color: var(--missed); }
[data-mode="light"] .tag.t-gap { color: oklch(46% 0.18 25); }
.tag.t-sr { color: var(--done); }
[data-mode="light"] .tag.t-sr { color: oklch(42% 0.13 158); }

.rationale { margin-top: 3px; font-family: var(--font-data); font-size: 9.5px; letter-spacing: 0.02em; color: var(--ink-subtle); line-height: 1.5; }
.rationale .em { color: var(--ink-muted); }

/* the mono foot strip (mockup .pv2-foot) */
.pv2-foot { margin-top: auto; display: flex; justify-content: space-between; gap: var(--sp-3); flex-wrap: wrap; padding-top: 18px; }
.pv2-foot span {
  font-family: var(--font-data); font-size: 9px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-subtle);
}

/* inline per-row actions strip (upload · reschedule · done · missed) */
.b-actions { margin-top: 6px; display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.upload-btn {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  font-size: var(--text-xs); font-weight: var(--w-medium); color: var(--ink-subtle);
  padding: 4px var(--sp-2); border-radius: var(--r-sm);
  border: 1px dashed var(--border-strong); background: transparent; opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}
.block:hover .upload-btn, .block:focus-within .upload-btn, .upload-btn:focus-visible, .upload-btn.has-files, .upload-btn.is-busy { opacity: 1; }
.upload-btn:hover { color: var(--accent); border-color: color-mix(in oklch, var(--accent) 50%, transparent); background: var(--accent-quiet); }
[data-mode="light"] .upload-btn:hover { color: var(--accent-hover); }
.upload-btn svg { width: 12px; height: 12px; flex: none; }
.upload-btn input { display: none; }
.upload-btn.has-files { color: var(--done); border-style: solid; border-color: color-mix(in oklch, var(--done) 45%, transparent); background: var(--done-bg); }
[data-mode="light"] .upload-btn.has-files { color: oklch(42% 0.13 158); }
@media (hover: none) { .upload-btn { opacity: 1; } }
.miss-btn {
  appearance: none; border: 1px solid var(--border); background: transparent; color: var(--ink-subtle);
  font-size: var(--text-xs); padding: 4px var(--sp-2); border-radius: var(--r-sm); cursor: pointer; opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.block:hover .miss-btn, .block:focus-within .miss-btn, .miss-btn:focus-visible { opacity: 1; }
.miss-btn:hover { color: var(--missed); border-color: color-mix(in oklch, var(--missed) 45%, transparent); }
@media (hover: none) { .miss-btn { opacity: 1; } }

/* mark-done trigger — shares the quiet, reveal-on-hover treatment in the done color */
.done-btn {
  appearance: none; border: 1px solid var(--border); background: transparent; color: var(--ink-subtle);
  font-size: var(--text-xs); padding: 4px var(--sp-2); border-radius: var(--r-sm); cursor: pointer; opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.block:hover .done-btn, .block:focus-within .done-btn, .done-btn:focus-visible { opacity: 1; }
.done-btn:hover { color: var(--done); border-color: color-mix(in oklch, var(--done) 45%, transparent); }
@media (hover: none) { .done-btn { opacity: 1; } }

/* reschedule trigger — shares the quiet, reveal-on-hover treatment */
.edit-btn {
  appearance: none; border: 1px solid var(--border); background: transparent; color: var(--ink-subtle);
  font-size: var(--text-xs); padding: 4px var(--sp-2); border-radius: var(--r-sm); cursor: pointer; opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.block:hover .edit-btn, .block:focus-within .edit-btn, .edit-btn:focus-visible { opacity: 1; }
.edit-btn:hover { color: var(--accent); border-color: color-mix(in oklch, var(--accent) 50%, transparent); }
[data-mode="light"] .edit-btn:hover { color: var(--accent-hover); }
@media (hover: none) { .edit-btn { opacity: 1; } }

/* inline time editor (reschedule/resize) + create form */
.block-editor {
  margin-top: var(--sp-3); padding: var(--sp-3);
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  background: var(--surface-raised); display: grid; gap: var(--sp-2);
}
.be-row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--sp-2); }
.be-field { display: grid; gap: 2px; }
.be-field-grow { flex: 1 1 12ch; min-width: 0; }
.be-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-subtle); font-weight: var(--w-medium); }
.be-input {
  font: inherit; font-size: var(--text-sm); color: var(--ink);
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 4px var(--sp-2); width: 100%; min-width: 0; font-variant-numeric: tabular-nums;
}
.be-input:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; border-color: var(--accent); }
.be-del { color: var(--missed); border-color: color-mix(in oklch, var(--missed) 40%, transparent); }
.be-del:hover { background: var(--missed-bg); }
.be-status { font-size: var(--text-xs); color: var(--missed); min-height: 1em; }
[data-mode="light"] .be-status { color: oklch(46% 0.18 25); }

/* foot-of-rail "add a block" affordance — a quiet ledger row */
.rail-add-btn {
  appearance: none; width: 100%; display: inline-flex; align-items: center; gap: 8px;
  border: 0; border-bottom: 1px dashed var(--border-strong); background: transparent;
  color: var(--ink-subtle); font-family: var(--font-data); font-size: 10px;
  letter-spacing: 0.1em; text-transform: uppercase;
  padding: 12px 4px; cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}
.rail-add-btn svg { width: 13px; height: 13px; flex: none; }
.rail-add-btn:hover { color: var(--accent); background: var(--hover-bg); }
.rail-add-btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; color: var(--accent); }
[data-mode="light"] .rail-add-btn:hover, [data-mode="light"] .rail-add-btn:focus-visible { color: var(--accent-hover); }
.rail-add .block-editor { margin: var(--sp-3) 0; }

/* now line in the rail */
.now { display: flex; align-items: center; gap: var(--sp-3); margin: var(--sp-1) 0; color: var(--accent); font-family: var(--font-mono); font-size: var(--text-xs); letter-spacing: 0; }
[data-mode="light"] .now { color: var(--accent-hover); }
.now .line { flex: 1; height: 1px; background: currentColor; opacity: 0.45; }
.now .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: none; }
@media (prefers-reduced-motion: no-preference) {
  .now .dot { animation: now-pulse 2.4s var(--ease-out) infinite; }
  @keyframes now-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }
}

/* slip diagnosis callout — reads as a ledger annotation row */
.slip {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  padding: 14px 4px;
  border-bottom: 1px solid var(--border);
  font-size: 13px; color: var(--ink-muted); line-height: 1.5;
}
.slip svg { width: 16px; height: 16px; flex: none; margin-top: 2px; color: var(--ink-subtle); }
.slip b { color: var(--ink); }

/* "Add card" panel — manual card entry on the Review surface */
.rc-add {
  margin-top: var(--sp-5);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface);
  overflow: hidden;
}
.rc-add > summary {
  list-style: none;
  cursor: pointer;
  padding: var(--sp-3) var(--sp-4);
  font-weight: 600;
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  user-select: none;
}
.rc-add > summary::-webkit-details-marker { display: none; }
.rc-add > summary::before {
  content: "+";
  font-size: 1.15em;
  line-height: 1;
  color: var(--accent);
  transition: transform var(--dur-fast, 150ms) ease;
}
.rc-add[open] > summary { border-bottom: 1px solid var(--border); }
.rc-add[open] > summary::before { transform: rotate(45deg); }
.rc-add-body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-4);
}
.rc-add-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-3);
}
.rc-field { display: flex; flex-direction: column; gap: var(--sp-1); }
.rc-field-label {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--ink-muted);
  letter-spacing: 0.02em;
}
.rc-add textarea,
.rc-add select,
.rc-add input {
  width: 100%;
  box-sizing: border-box;
  font: inherit;
  color: var(--ink);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: var(--sp-2) var(--sp-3);
  transition: border-color 150ms ease, box-shadow 150ms ease;
}
.rc-add textarea { resize: vertical; min-height: 2.6em; line-height: 1.4; }
.rc-add textarea:focus,
.rc-add select:focus,
.rc-add input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--accent) 22%, transparent);
}
.rc-add-actions { display: flex; justify-content: flex-end; }
@media (max-width: 540px) {
  .rc-add-row { grid-template-columns: 1fr; }
}
/* ---- shared panel card (Courses/Settings use these) ---- */
.panel { background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--r2); padding: var(--sp-4); box-shadow: var(--shadow-2); }
.panel-title { font-family: var(--font-head); font-weight: 600; font-size: var(--text-md); letter-spacing: var(--track-tight); margin-bottom: var(--sp-1); overflow-wrap: anywhere; }
.panel-note { color: var(--ink-muted); font-size: var(--text-xs); margin-bottom: var(--sp-3); font-family: var(--font-data); letter-spacing: 0.02em; }

/* ---- left-rail backlog (click to drop onto today) ---- */
.pv2-backlog { display: flex; flex-direction: column; align-items: flex-start; }
.pv2-backlog .btn { margin-top: var(--sp-3); }
.backlog-list { display: flex; flex-direction: column; align-self: stretch; }
.backlog-empty { font-size: var(--text-xs); color: var(--ink-subtle); padding: 8px 0; border-top: 1px solid var(--border); }
.backlog-item {
  display: grid; gap: 2px; padding: 9px 0 9px 14px; cursor: pointer; position: relative;
  border-top: 1px solid var(--border);
  transition: background-color var(--dur-fast) var(--ease-out);
}
/* course ink chip, same vertical-bar voice as the ledger rows */
.backlog-item::before {
  content: ""; position: absolute; left: 0; top: 11px; width: 7px; height: 7px;
  border-radius: 3px; background: var(--block-edge, var(--border-strong));
}
.backlog-item:hover { background: var(--hover-bg); }
.backlog-item:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-radius: var(--r1); }
.backlog-item .bi-task { font-size: 13px; font-weight: 500; }
.backlog-item .bi-meta { font-family: var(--font-data); font-size: 9px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-subtle); }

/* END PLAN-REDESIGN */

/* ---------- Review (FinalRedesign rv2) ----------
   Class names stay the app's own (.review-wrap, .review-card, .rc-*, .grade …)
   so review.js keeps binding; the VALUES are the mockup's rv2 vocabulary,
   token-bound. The grade row + tally live OUTSIDE the card, per the mockup. */
.review-wrap {
  max-width: none; margin: 0 auto; padding: 34px 24px;
  display: flex; flex-direction: column; min-height: calc(100vh - 54px);
}
/* rv1-meta: dv count · sage track · the two segs */
.review-progress { display: flex; align-items: center; gap: 14px; margin-bottom: 16px; }
.review-progress .dv { color: var(--ink-muted); white-space: nowrap; }
.review-progress .seg button {
  font-family: var(--font-data); font-size: 10px; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase; padding: 3px 8px;
}
.progress-track { flex: 1; min-width: 48px; height: 4px; background: var(--border); border-radius: 2px; overflow: hidden; }
.progress-fill { height: 100%; background: var(--done); border-radius: 2px; }
@media (max-width: 600px) {
  .review-progress { flex-wrap: wrap; row-gap: 8px; }
  .progress-track { flex-basis: 100%; order: 1; }
  .review-card { padding: 24px 20px; }
}
/* rv2-card: sage-tinted hairline, raised, the bloom anchored top-right */
.review-card {
  background: var(--surface);
  border: 1px solid color-mix(in oklch, var(--done) 40%, var(--border));
  border-radius: var(--r3); box-shadow: var(--shadow-2);
  padding: 36px 44px; flex: 1; min-height: 320px;
  display: flex; flex-direction: column; position: relative; overflow: hidden;
}
.review-card .rc-course { color: inherit; }
/* rv2-q: the serif question */
.review-card .rc-front {
  font-family: var(--font-head); font-size: 22px; font-weight: 600;
  line-height: 1.25; letter-spacing: var(--track-tight); margin-top: 14px; text-wrap: balance;
  overflow-wrap: anywhere;
}
/* rv2-a: hairline-topped answer, serif emphasis */
.review-card .rc-back {
  margin-top: 18px; padding-top: 18px; border-top: 1px solid var(--border);
  font-size: 15px; line-height: 1.65; color: var(--ink);
  overflow-wrap: anywhere;
}
.review-card .rc-back em { font-family: var(--font-head); }
.review-card .rc-back.hidden { display: none; }
/* rv2-bloom: the lysia sprig; the crown blossoms when the answer is revealed */
.review-card .rv2-bloom {
  position: absolute; right: 22px; top: 16px; width: 58px; height: 70px;
  color: var(--ink-muted); pointer-events: none;
}
.rv2-bloom .lys-crown { opacity: 0; transform: scale(0.3); transform-origin: 50% 80%; }
.review-card .rc-back:not(.hidden) ~ .rv2-bloom .lys-crown { opacity: 1; transform: scale(1); }
@media (prefers-reduced-motion: no-preference) {
  .review-card .rc-back:not(.hidden) ~ .rv2-bloom .lys-crown {
    animation: rv2-bloom-in 900ms cubic-bezier(.2, .8, .2, 1) both;
  }
}
@keyframes rv2-bloom-in {
  from { transform: scale(0.3); opacity: 0; }
  to   { transform: scale(1);   opacity: 1; }
}
.review-foot { margin-top: auto; padding-top: 20px; }
.review-foot .btn { width: 100%; justify-content: center; height: var(--control-h); }
/* rv2-grades: serif grade buttons under the card */
.grade-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-top: 14px; }
.grade {
  appearance: none; border: 1px solid var(--border); background: var(--surface-raised);
  color: var(--ink); border-radius: var(--r1); padding: 11px 8px;
  display: flex; flex-direction: column; align-items: center; gap: 2px; text-align: center;
  transition: transform var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.grade:hover { border-color: var(--border-strong); background: var(--hover-bg); transform: translateY(-2px); box-shadow: var(--shadow-1); }
.grade:active { transform: translateY(0); box-shadow: inset 0 -2.5px 0 var(--border-strong); }
.grade .g-key { font-family: var(--font-head); font-size: 16px; font-weight: 600; }
.grade .g-sub { font-family: var(--font-data); font-size: var(--text-xs); color: var(--ink-muted); letter-spacing: 0; }
.grade.again:hover { border-color: color-mix(in oklch, var(--missed) 55%, transparent); background: var(--missed-bg); color: var(--missed); }
.grade.again:active { box-shadow: inset 0 -2.5px 0 var(--missed); }
.grade.easy:hover  { border-color: color-mix(in oklch, var(--done) 55%, transparent); background: var(--done-bg); color: var(--done); }
.grade.easy:active { box-shadow: inset 0 -2.5px 0 var(--done); }
/* Designed focus ring on the raised grade surface (the global :focus-visible at
   style.css:22 is too quiet here). */
.grade:focus-visible {
  outline: 2px solid var(--focus-ring); outline-offset: 2px;
  box-shadow: 0 0 0 4px color-mix(in oklch, var(--accent) 18%, transparent);
}
/* Preserve the pressed inset for keyboard activation (layer both shadows). */
.grade:focus-visible:active {
  box-shadow: inset 0 -2.5px 0 var(--border-strong),
              0 0 0 4px color-mix(in oklch, var(--accent) 18%, transparent);
}
.grade.again:focus-visible:active {
  box-shadow: inset 0 -2.5px 0 var(--missed),
              0 0 0 4px color-mix(in oklch, var(--accent) 18%, transparent);
}
.grade.easy:focus-visible:active {
  box-shadow: inset 0 -2.5px 0 var(--done),
              0 0 0 4px color-mix(in oklch, var(--accent) 18%, transparent);
}
/* Disabled grade: dimmed, not-allowed, no hover lift. */
.grade:disabled, .grade[aria-disabled="true"] {
  opacity: 0.45; cursor: not-allowed; transform: none; box-shadow: none;
}
.grade:disabled:hover, .grade[aria-disabled="true"]:hover {
  transform: none; box-shadow: none; background: var(--surface-raised);
  border-color: var(--border); color: var(--ink);
}
@media (prefers-reduced-motion: reduce) {
  .grade:hover, .grade:active { transform: none; }
}
/* rv2-tally: dashed this-session strip; review.js appends one <i> per grade */
.rv2-tally {
  display: flex; align-items: center; gap: 7px; margin-top: 16px; padding: 10px 14px;
  border: 1px dashed var(--border-strong); border-radius: var(--r2);
}
.rv2-tally[hidden] { display: none; }
.rv2-tally .lbl {
  font-family: var(--font-data); font-size: 8.5px; letter-spacing: 0.14em;
  color: var(--ink-subtle); text-transform: uppercase; margin-right: 4px; white-space: nowrap;
}
.rv2-tally .tally-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; flex: 1; }
.rv2-tally i { width: 16px; height: 4px; border-radius: 2px; display: block; background: var(--done); }
.rv2-tally i.t-hard  { background: var(--exam); }
.rv2-tally i.t-again { background: var(--missed); }
.sr-mode { display: flex; align-items: center; gap: var(--sp-3); margin-top: var(--sp-4); justify-content: center; color: var(--ink-muted); font-size: var(--text-sm); }

/* segmented control */
.seg { display: inline-flex; background: var(--surface-raised); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 2px; gap: 2px; }
.seg button { appearance: none; border: 0; background: transparent; color: var(--ink-muted); font-size: var(--text-sm); font-weight: var(--w-medium); padding: var(--sp-1) var(--sp-3); border-radius: calc(var(--r-sm) - 2px); }
.seg button[aria-pressed="true"] { background: var(--surface); color: var(--ink); box-shadow: var(--shadow-1); }
.seg button:focus-visible {
  outline: 2px solid var(--focus-ring); outline-offset: 2px;
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--accent) 16%, transparent);
}

/* ---------- Exam queue (FinalRedesign eq1) ----------
   Class names stay the app's own (.pri-card, .pm-*, .gap-alert …) so exam.js
   keeps binding; the VALUES are the mockup's eq1 vocabulary, token-bound.
   The mockup's --bc per-row course ink binds through the existing is-cN →
   --block-edge contract (themes.css). */
.exam-filter { display: flex; gap: var(--sp-2); margin-bottom: var(--sp-4); }
/* gap-alert carries the eq1-verdict treatment: gold-bordered callout, big serif
   count (exam.js fills .ga-glyph with the gap count), muted body with ink bolds */
.gap-alert {
  display: flex; align-items: baseline; gap: 14px;
  border: 1px solid color-mix(in oklch, var(--exam) 40%, var(--border));
  background: var(--exam-bg); border-radius: var(--r2);
  padding: 14px 18px; margin-bottom: var(--sp-5);
}
.gap-alert .ga-glyph {
  font-family: var(--font-head); font-size: 34px; font-weight: 600; line-height: 1;
  color: color-mix(in oklch, var(--exam) 60%, var(--ink));
  font-variant-numeric: tabular-nums; flex: none;
}
.gap-alert .ga-body { font-size: 13.5px; color: var(--ink-muted); line-height: 1.5; }
.gap-alert b { color: var(--ink); font-weight: var(--w-bold); }
.pri-list { display: flex; flex-direction: column; }
/* eq1-row: rank · course+title · mono why-line · due chip */
.pri-card {
  display: grid; grid-template-columns: 44px minmax(0, 1fr) minmax(140px, 200px) auto;
  gap: 16px; align-items: center; padding: 14px 16px 14px 10px;
  border: 1px solid var(--border); border-left: 3px solid var(--block-edge, var(--border-strong));
  border-radius: var(--r2); background: var(--surface); margin-bottom: 7px;
  box-shadow: var(--shadow-1);
}
@media (prefers-reduced-motion: no-preference) {
  .pri-card { transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out); }
  .pri-card:hover { transform: translateX(3px); box-shadow: var(--shadow-2); }
}
.pri-rank { font-family: var(--font-head); font-size: 22px; font-weight: 600; color: var(--ink-subtle); text-align: center; }
.pri-main { min-width: 0; }
.pri-main .pm-course {
  font-family: var(--font-data); font-size: 9px; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--block-edge, var(--ink-muted));
  display: block; margin-bottom: 2px;
}
.pri-main .pm-title { font-size: 15px; font-weight: 500; }
/* eq1-why: the reweighting receipt in the mono data voice; <b> = risk emphasis */
.pri-why {
  font-family: var(--font-data); font-size: 9px; letter-spacing: 0.05em;
  text-transform: uppercase; color: var(--ink-subtle); line-height: 1.6;
  font-variant-numeric: tabular-nums;
}
.pri-why b { color: var(--missed); font-weight: 600; }
/* due chip states (mockup chip is-exam / is-risk), derived from contract tokens */
.chip.is-exam {
  color: color-mix(in oklch, var(--exam) 75%, var(--ink));
  border-color: color-mix(in oklch, var(--exam) 45%, transparent);
  background: var(--exam-bg); white-space: nowrap;
}
.chip.is-risk {
  color: var(--missed);
  border-color: color-mix(in oklch, var(--missed) 45%, transparent);
  background: var(--missed-bg); white-space: nowrap;
}
/* eq1-foot: mono strip under the queue — count + the honest weighting formula */
.pri-foot { display: flex; justify-content: space-between; gap: var(--sp-3); padding-top: 14px; }
.pri-foot[hidden] { display: none; }
.pri-foot span {
  font-family: var(--font-data); font-size: 9px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-subtle);
}
@media (max-width: 640px) {
  /* stack the why-line under the title; chip pins to the first row's end */
  .pri-card { grid-template-columns: 32px minmax(0, 1fr) auto; gap: var(--sp-2) var(--sp-3); }
  .pri-card .pri-why { grid-column: 2 / -1; grid-row: 2; }
  .pri-card .chip { grid-column: 3; grid-row: 1; justify-self: end; }
}

/* ---------- Courses & materials (FinalRedesign cr1) ----------
   Three-column shell: semester rail · selected course · readiness receipt.
   Class names stay the app's own (.course-item, .ci-*, .pcard, .rating …) so
   courses.js / readiness.js / demo.js / tests keep binding; the VALUES are the
   mockup's cr1 vocabulary, token-bound. */
/* Selected-course detail: a single column below the all-courses card list
   (the retired 3-column courses-shell + its duplicate rail are gone). */
.courses-detail {
  max-width: 980px; margin: var(--sp-5) auto 0;
  padding: 0;
}
.courses-detail[hidden] { display: none; }
.courses-main { min-width: 0; }
/* data voice — tiny mono caption (the mockups' .dv) */
.dv {
  font-family: var(--font-data); font-size: 10px; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-subtle);
}
.course-list { display: flex; flex-direction: column; }
.course-list[hidden] { display: none; }
/* mockup .cr1-course: hairline card row; the selected row tints accent-quiet */
.course-item {
  display: flex; align-items: center; gap: 12px; text-align: left; width: 100%;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r2);
  padding: 13px 15px; margin-bottom: 8px; cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.course-item:hover { border-color: var(--border-strong); }
.course-item[aria-selected="true"] {
  border-color: color-mix(in oklch, var(--accent) 50%, transparent);
  background: var(--accent-quiet); box-shadow: var(--shadow-1);
}
.course-dot { width: 11px; height: 11px; border-radius: 4px; background: var(--block-edge, var(--accent)); flex: none; }
.course-item .ci-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.course-item .ci-name { font-weight: 600; font-size: 13.5px; }
.course-item .ci-exam {
  font-family: var(--font-data); font-size: 8.5px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--ink-subtle); margin-top: 2px;
}
/* readiness mini-bar (mockup .cr1-mini); readiness.js scales the <i> */
.ci-bar { height: 3px; border-radius: 2px; background: var(--border); margin-top: 6px; position: relative; overflow: hidden; }
.ci-bar i {
  position: absolute; inset: 0; transform-origin: left; transform: scaleX(0);
  border-radius: 2px; background: var(--block-edge, var(--accent));
  transition: transform var(--dur-normal) var(--ease-out);
}
/* middle-column serif title — the selected course (mockup .sh) */
.course-title {
  font-family: var(--font-head); font-weight: 600;
  font-size: var(--text-2xl); letter-spacing: var(--track-tight);
  line-height: var(--lead-tight); margin: 0 0 var(--sp-4); text-wrap: balance;
  overflow-wrap: anywhere;
}
.course-title em { font-style: italic; color: var(--ink-muted); }
.panel + .panel { margin-top: var(--sp-4); }
.panel-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); margin-bottom: var(--sp-1); }

/* Generated exams: the selected course's practice-exam history (right column,
   under the Materials panel). Rows mirror .course-item; the chips carry the
   artifact's labels (difficulty / focus / counts / rated). */
.exams-panel { margin-top: var(--sp-4); }
.exam-list { display: flex; flex-direction: column; gap: var(--sp-2); margin-top: var(--sp-2); }
.exam-item {
  display: flex; flex-direction: column; gap: var(--sp-2); text-align: left; width: 100%;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-md);
  padding: var(--sp-3); box-shadow: var(--shadow-1);
  transition: transform var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.exam-item:hover { border-color: var(--border-strong); transform: translateX(2px); box-shadow: var(--shadow-2); }
.exam-item .ex-head { display: flex; align-items: center; gap: var(--sp-2); }
.exam-item .ex-name { font-weight: var(--w-medium); font-size: var(--text-sm); flex: 1; }
.exam-item .ex-date { font-size: var(--text-xs); color: var(--ink-muted); }
.exam-item .ex-chips { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.exam-chip {
  font-size: var(--text-xs); color: var(--ink-muted);
  border: 1px solid var(--border); border-radius: 999px;
  padding: 1px var(--sp-2); background: var(--surface-raised);
}
.exam-chip.is-easy { color: var(--success-ink, var(--done)); border-color: color-mix(in oklch, var(--success-ink, var(--done)) 40%, transparent); }
.exam-chip.is-medium { color: var(--accent); border-color: color-mix(in oklch, var(--accent) 40%, transparent); }
.exam-chip.is-hard,
.exam-chip.is-mixed { color: var(--missed); border-color: color-mix(in oklch, var(--missed) 40%, transparent); }
.exam-chip.is-verified { color: var(--success-ink, var(--done)); }
/* last_session carries 1–7 self-ratings — re-opening lets the student revise. */
.exam-chip.is-rated { color: var(--accent); border-color: color-mix(in oklch, var(--accent) 40%, transparent); background: var(--accent-quiet); }

/* materials folder tree */
.diff-banner {
  display: flex; gap: var(--sp-3); align-items: flex-start;
  background: var(--accent-quiet); border: 1px solid color-mix(in oklch, var(--accent) 45%, transparent);
  border-radius: var(--r-md); padding: var(--sp-3); margin: var(--sp-3) 0;
}
/* The author display:flex above outranks the UA [hidden] default, so the banner
   must opt back into display:none when hidden (courses.js sets hidden=false on a
   real rescan diff). Mirrors .auth-gate[hidden] / .modal-backdrop[hidden]. */
.diff-banner[hidden] { display: none; }
.diff-banner .db-glyph { color: var(--accent); font-size: 15px; line-height: 1.3; flex: none; }
.diff-banner .db-body { font-size: var(--text-sm); color: var(--ink); }
.tree { margin-top: var(--sp-3); }
.folder { border-radius: var(--r-sm); }
.folder > summary {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2); cursor: pointer; list-style: none; border-radius: var(--r-sm);
  font-size: var(--text-sm);
}
.folder > summary::-webkit-details-marker { display: none; }
.folder > summary:hover { background: var(--hover-bg); }
.fld-tw {
  width: 8px; height: 8px; flex: none; margin: 0 2px;
  border-right: 1.6px solid var(--ink-subtle); border-bottom: 1.6px solid var(--ink-subtle);
  transform: rotate(-45deg); transition: transform var(--dur-fast) var(--ease-out);
}
.folder[open] > summary > .fld-tw { transform: rotate(45deg); }
.fld-name { flex: 1; font-weight: var(--w-medium); }
.folder.sub > summary .fld-name { font-weight: var(--w-normal); color: var(--ink-muted); }
.fld-meta { display: flex; align-items: center; gap: var(--sp-2); color: var(--ink-subtle); font-size: var(--text-xs); font-variant-numeric: tabular-nums; }
.fld-body { padding-left: var(--sp-5); }
.tag-updated { color: var(--accent); background: var(--accent-quiet); border-radius: 999px; padding: 0 var(--sp-2); font-weight: var(--w-medium); font-size: var(--text-xs); }
.mat-kind.sol { color: var(--done); background: var(--done-bg); }
.mat-row.pending { opacity: 0.5; }
.materials-list { display: flex; flex-direction: column; gap: 0; margin-top: var(--sp-3); }
/* mockup .cr1-mat rows: hairline-ruled list, mono kind chip, mono size */
.mat-row { display: flex; align-items: center; gap: 10px; padding: 8px 2px; border-top: 1px solid var(--border); font-size: 13px; }
.mat-row:first-child { border-top: 0; }
.mat-kind {
  font-family: var(--font-data); font-size: 9px; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--accent); background: var(--accent-quiet);
  padding: 1px 6px; border-radius: 3px; flex: none;
}
.mat-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mat-size { color: var(--ink-subtle); font-family: var(--font-data); font-size: 9px; font-variant-numeric: tabular-nums; }
.upload-row { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; margin-top: var(--sp-3); }
/* Adjust (B1): manual per-topic study-weight rows — one topic name + a small
   numeric weight input each, in a compact bordered surface vocabulary. */
.adjust-list { display: flex; flex-direction: column; gap: var(--sp-2); margin-bottom: var(--sp-3); }
.adjust-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  background: var(--surface-raised); border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: var(--sp-2) var(--sp-3); min-width: 0;
}
.adjust-row .adjust-name { flex: 1; min-width: 0; font-family: var(--font-mono); font-size: var(--text-xs); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.adjust-row .adjust-weight { flex: none; width: 5rem; }
.adjust-actions { display: flex; align-items: center; gap: var(--sp-3); }

.gen-out { margin-top: var(--sp-4); background: var(--surface-raised); border: 1px solid var(--border); border-radius: var(--r-md); padding: var(--sp-4); font-family: var(--font-mono); font-size: var(--text-sm); color: var(--ink-muted); white-space: pre-wrap; min-height: 96px; }
/* N5: instructional placeholder renders in the UI font, not code mono. */
.gen-out.is-placeholder { font-family: var(--font-sans); }

/* ---------- Courses: setup steps (mockup .cr1-steps) ---------- */
/* courses.js stamps data-step on the <ol> plus is-done / is-current per <li>
   (and swaps a completed step's number for ✓). */
.step-indicator { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 0 0 var(--sp-3); padding: 0; list-style: none; }
.step-indicator .step { display: inline-flex; align-items: center; gap: 7px; font-size: 11.5px; color: var(--ink-subtle); }
.step-indicator .step-num {
  display: grid; place-items: center; width: 17px; height: 17px; border-radius: 50%;
  border: 1px solid var(--border-strong);
  font-family: var(--font-data); font-size: 9px; font-variant-numeric: tabular-nums;
}
.step-indicator .step-arrow { color: var(--ink-subtle); opacity: 0.6; font-size: 11.5px; }
.step-indicator .step.is-done { color: var(--ink-muted); }
.step-indicator .step.is-done .step-num { background: var(--done); border-color: var(--done); color: var(--surface); }
.step-indicator .step.is-current { color: var(--ink); }
.step-indicator .step.is-current .step-num { border-color: var(--accent); background: var(--accent-quiet); color: var(--accent); }

/* H2: custom file drop-zone wrapping the hidden native input. */
.dropzone { position: relative; flex: 1; min-width: 180px; display: inline-flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3); border: 1px dashed var(--border-strong); border-radius: var(--r-sm); color: var(--ink-muted); font-size: var(--text-xs); cursor: pointer; background: var(--surface-raised); }
.dropzone:hover, .dropzone:focus-visible { border-color: var(--accent); color: var(--ink); outline: none; }
.dropzone.is-dragover { border-color: var(--accent); background: var(--accent-quiet); color: var(--accent); }
.dropzone.has-file { border-style: solid; color: var(--ink); }
.dz-input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
.dz-glyph { flex: none; font-size: var(--text-sm); }
.dz-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dz-link { color: var(--accent); text-decoration: underline; }

/* H3: gated-generators explanatory note. */
.gen-locked-note { color: var(--ink-subtle); }
/* [hidden] overrides — these elements set their own display, so the UA
   [hidden]{display:none} would otherwise be ignored when gated. */
.gen-out[hidden] { display: none; }
.btn[hidden] { display: none; }

/* N10: inline "uses API credits" confirm before Generate fires. */
.gen-confirm { margin-top: var(--sp-3); display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3); border: 1px solid color-mix(in oklch, var(--accent) 40%, transparent); background: var(--accent-quiet); border-radius: var(--r-sm); font-size: var(--text-xs); color: var(--ink); }
.gen-confirm .gc-msg { flex: 1; min-width: 160px; }

/* ---------- Settings ---------- */
.settings-grid { display: grid; grid-template-columns: 1fr; gap: var(--sp-6); max-width: 760px; }
.section-label { font-size: var(--text-sm); font-weight: var(--w-bold); letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-subtle); margin-bottom: var(--sp-3); font-family: var(--font-sans); }
/* identity picker — two selectable cards (garden default | riso print).
   bootstrap.js toggles aria-pressed and persists the choice per-account. */
.identity-seg { display: flex; flex-direction: column; gap: var(--sp-3); max-width: 460px; }
.identity-card {
  appearance: none; font: inherit; text-align: left; cursor: pointer; width: 100%;
  display: flex; align-items: center; gap: var(--sp-4);
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r2);
  padding: var(--sp-4); color: var(--ink);
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.identity-card:hover { border-color: var(--border-strong); }
.identity-card[aria-pressed="true"] {
  border: 1.5px solid var(--accent); box-shadow: var(--shadow-2);
}
.identity-card:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.identity-swatch { display: flex; flex-direction: column; width: 72px; height: 54px; border: 1px solid var(--border-strong); border-radius: var(--r1); overflow: hidden; flex: none; }
.identity-swatch .is-paper { flex: 1.4; background: var(--bg); }
.identity-swatch .is-inks { flex: 1; display: flex; }
.identity-swatch .is-inks i { flex: 1; }
.identity-meta { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.identity-name { font-family: var(--font-head); font-weight: 600; font-size: var(--text-md); }
.identity-sub { font-size: var(--text-sm); color: var(--ink-muted); line-height: 1.5; }
.identity-sub i { font-family: var(--font-head); font-style: italic; color: var(--ink); }
.field { display: flex; flex-direction: column; gap: var(--sp-2); margin-bottom: var(--sp-3); max-width: 420px; }
.field label { font-size: var(--text-sm); font-weight: var(--w-medium); color: var(--ink-muted); }
.field .hint { font-size: var(--text-xs); color: var(--ink-subtle); }
.status-line { font-size: var(--text-sm); color: var(--ink-muted); }
.status-line.ok { color: var(--success-ink); }
.status-line.err { color: var(--error-ink); }

/* ---------- Import & auto-sort (instrument-panel cards) ---------- */
/* The Import surface ships bare .card/.row/.list/.table — give them the same
   surface treatment as .panel: surface fill, hairline border, shadow-1, mono
   numeric labels, a serif step heading. Scoped to #surface-import so the generic
   class names don't leak into other surfaces. */
#surface-import .card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: var(--sp-5); box-shadow: var(--shadow-1);
  margin-bottom: var(--sp-4);
}
#surface-import .card > h2 {
  font-family: var(--font-page-heading); font-weight: var(--w-bold);
  font-size: var(--text-md); letter-spacing: var(--track-tight);
  margin-bottom: var(--sp-2); display: flex; align-items: baseline; gap: var(--sp-2);
}
#surface-import .card .muted { color: var(--ink-muted); font-size: var(--text-sm); margin-bottom: var(--sp-3); }
#surface-import .card code {
  font-family: var(--font-mono); font-size: var(--text-xs);
  background: var(--surface-raised); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 1px var(--sp-2); color: var(--ink-muted);
}
#surface-import input[type="file"] {
  width: 100%; height: auto; padding: var(--sp-3); cursor: pointer;
  border: 1px dashed var(--border-strong); background: var(--surface-raised);
  color: var(--ink-muted); font-size: var(--text-sm); margin-bottom: var(--sp-3);
}
#surface-import input[type="file"]:hover { border-color: var(--accent); color: var(--ink); }
#surface-import .row { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; }
#surface-import .status { font-size: var(--text-sm); color: var(--ink-muted); margin-top: var(--sp-3); min-height: 1.2em; }
#surface-import .list { list-style: none; padding: 0; margin: var(--sp-3) 0 0; display: flex; flex-direction: column; }
#surface-import .list li {
  display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-2) 0;
  border-top: 1px solid var(--border); font-family: var(--font-mono);
  font-size: var(--text-xs); color: var(--ink-muted);
}
#surface-import .list li:first-child { border-top: 0; }
.table-scroll { overflow-x: auto; margin-top: var(--sp-3); border: 1px solid var(--border); border-radius: var(--r-md); }
#ingest-review-table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
#ingest-review-table th {
  text-align: left; font-family: var(--font-mono); font-size: 10px;
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--ink-subtle);
  font-weight: var(--w-medium); padding: var(--sp-2) var(--sp-3);
  background: var(--surface-raised); border-bottom: 1px solid var(--border); white-space: nowrap;
}
#ingest-review-table td { padding: var(--sp-2) var(--sp-3); border-top: 1px solid var(--border); vertical-align: middle; }
#ingest-review-table tr:hover td { background: var(--hover-bg); }
#ingest-review-table select, #ingest-review-table input { font-size: var(--text-xs); height: calc(var(--control-h) - 6px); max-width: 100%; }

/* ---------- Empty-state (H7/H8: Review + Exam Queue + Courses) ----------
   Branded, editorial empty states. Everything lays out and is fully visible by
   DEFAULT (opacity:1) — no JS gate, no IntersectionObserver reveal. The bold
   `_redesign_samples/states-bold.html` shipped a defect where panels sat at
   opacity:0 until an observer revealed them, leaving 3 of 4 blank on load; this
   block deliberately avoids that. Any entrance motion is progressive
   enhancement only (see .es-enter below, reduced-motion-gated). */
.empty-state {
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-3);
  text-align: center; padding: var(--sp-6) var(--sp-4);
}
.empty-state .es-eyebrow {
  font-family: var(--font-data); font-size: 9px; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-subtle);
}
.empty-state .es-bloom { width: 48px; height: 58px; color: var(--done); }
.empty-state .es-title {
  font-family: var(--font-head); font-weight: 600;
  font-size: clamp(22px, 3vw, 30px); line-height: 1.08;
  letter-spacing: var(--track-tight); color: var(--ink);
}
.empty-state .es-title em { font-style: italic; }
.empty-state .es-body { font-size: var(--text-sm); color: var(--ink-muted); max-width: 42ch; line-height: 1.6; }
.empty-state .es-sub {
  font-family: var(--font-data); font-size: 9px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-subtle); margin-top: var(--sp-1);
}
/* Editorial split: an oversized ghost word echo behind a left-aligned serif
   headline, a numbered next-step ladder on the right. Reuses the .empty-state
   hooks but lays them out as a magazine spread. */
.empty-editorial {
  display: grid; grid-template-columns: 1.06fr 0.94fr;
  gap: clamp(24px, 4vw, 56px); align-items: stretch;
}
.empty-editorial .ee-left {
  position: relative; display: flex; flex-direction: column; justify-content: flex-end;
  min-height: 260px; padding: var(--sp-4) 0;
}
.empty-editorial .ee-ghost {
  font-family: var(--font-head); font-weight: 600;
  font-size: clamp(60px, 12vw, 150px); line-height: 0.82; letter-spacing: -0.04em;
  color: var(--accent); opacity: 0.12;
  position: absolute; left: -0.04em; top: -0.06em; transform: translate(3px, -3px);
  pointer-events: none; user-select: none; z-index: 0;
}
.empty-editorial .ee-left .empty-state {
  position: relative; z-index: 1; align-items: flex-start; text-align: left;
  padding: 0; gap: var(--sp-3); max-width: 34ch; margin-top: auto;
}
.empty-editorial .ee-right {
  border-left: 1px solid var(--border); padding-left: clamp(20px, 3vw, 40px);
  display: flex; flex-direction: column; justify-content: center;
}
.empty-editorial .ee-rh {
  font-family: var(--font-data); font-size: 9px; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-subtle);
  margin: 0 0 var(--sp-4);
}
.empty-editorial .ladder { list-style: none; margin: 0; padding: 0; counter-reset: step; }
.empty-editorial .ladder li {
  counter-increment: step;
  display: grid; grid-template-columns: 30px 1fr; gap: 14px;
  align-items: start; padding: 14px 0; border-top: 1px solid var(--border);
}
.empty-editorial .ladder li:last-child { border-bottom: 1px solid var(--border); }
.empty-editorial .ladder li::before {
  content: counter(step, decimal-leading-zero);
  font-family: var(--font-data); font-size: 11px; font-weight: 600;
  letter-spacing: 0.06em; color: var(--accent);
  font-variant-numeric: tabular-nums; padding-top: 2px;
}
[data-mode="light"] .empty-editorial .ladder li::before { color: var(--accent-hover); }
.empty-editorial .lad-step { font-size: var(--text-sm); color: var(--ink); line-height: 1.5; }
.empty-editorial .lad-note {
  display: block; margin-top: 4px; font-size: var(--text-xs);
  color: var(--ink-muted); line-height: 1.5;
}
@media (max-width: 760px) {
  .empty-editorial { grid-template-columns: 1fr; }
  .empty-editorial .ee-right {
    border-left: 0; padding-left: 0;
    border-top: 1px solid var(--border); padding-top: var(--sp-5);
  }
  .empty-editorial .ee-ghost { font-size: clamp(52px, 20vw, 104px); }
}
/* Progressive-enhancement entrance: JS adds .is-ready to the host AFTER layout,
   never before. Content is visible without it; this only adds a one-shot ease. */
@media (prefers-reduced-motion: no-preference) {
  .es-enter { opacity: 1; }
  .es-enter.is-ready { animation: es-rise 360ms var(--ease-out) both; }
}
@keyframes es-rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

/* ---------- Skeleton loaders (shaped like the real cards) ----------
   Pulse animates OPACITY only (GPU-friendly) and holds still under reduced
   motion. The skeleton fills sit at opacity:1 by default; the pulse only dips
   them, so a no-JS / no-animation render still shows the full shape. */
.skel-stack { display: flex; flex-direction: column; gap: 7px; }
.skel-fill { background: color-mix(in oklch, var(--ink) 8%, var(--surface)); border-radius: var(--r1); opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  .skel-pulse { animation: skel-pulse 1.4s var(--ease-out) infinite; }
}
@keyframes skel-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }

/* Shared Skeleton helper (skeleton.js) — generic card/list/stat placeholders,
   built from the same .skel-fill/.skel-pulse primitives as the review skeleton. */
.skel-block { display: flex; flex-direction: column; gap: var(--sp-3, 12px); }
.skel-card { padding: var(--sp-3, 12px); border: 1px solid var(--border); border-radius: var(--r1); }
.sk-line { display: block; height: 12px; }
/* pri-card-shaped row — traces .pri-card's 44px rank / main / why / chip grid */
.skel-pri {
  display: grid; grid-template-columns: 44px minmax(0, 1fr) minmax(120px, 180px);
  gap: 16px; align-items: center; padding: 14px 16px 14px 10px;
  border: 1px solid var(--border); border-left: 3px solid var(--border-strong);
  border-radius: var(--r2); background: var(--surface); box-shadow: var(--shadow-1);
}
.skel-pri .sk-rank { width: 22px; height: 22px; margin: 0 auto; border-radius: 999px; }
.skel-pri .sk-main { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.skel-pri .sk-course { width: 64px; height: 8px; }
.skel-pri .sk-title  { width: 72%; height: 13px; }
.skel-pri .sk-why    { width: 40%; height: 9px; }
.skel-pri .sk-chip   { width: 92px; height: 22px; border-radius: var(--r-sm); justify-self: end; }
/* review-card-shaped panel — traces .review-card's serif front / hairline back */
.skel-review {
  position: relative; overflow: hidden;
  border: 1px solid color-mix(in oklch, var(--done) 40%, var(--border));
  border-radius: var(--r3); box-shadow: var(--shadow-2); background: var(--surface);
  padding: 36px 44px; min-height: 320px;
  display: flex; flex-direction: column; gap: 14px;
}
.skel-review .sk-tag    { width: 88px; height: 9px; }
.skel-review .sk-front  { width: 86%; height: 22px; margin-top: 4px; }
.skel-review .sk-front2 { width: 64%; height: 22px; }
.skel-review .sk-rule   { width: 100%; height: 1px; background: var(--border); margin: 6px 0; }
.skel-review .sk-back   { width: 94%; height: 13px; }
.skel-review .sk-back2  { width: 80%; height: 13px; }
.skel-review .sk-back3  { width: 60%; height: 13px; }
.skel-caption {
  font-family: var(--font-data); font-size: 9px; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-subtle);
  line-height: 1.7; margin-top: var(--sp-2);
}
.skel-caption b { color: var(--ink-muted); }

/* ---------- Inline form error (H9: replaces window.alert) ----------
   role="alert" / aria-live region, sits under the field. Direct copy, a mono
   error-code label, the --missed left rule. */
.form-error {
  display: flex; flex-direction: column; gap: 2px;
  color: var(--missed); background: var(--missed-bg);
  border-left: 3px solid var(--missed); border-radius: 0 var(--r-sm) var(--r-sm) 0;
  padding: var(--sp-2) var(--sp-3); font-size: var(--text-sm); margin-top: var(--sp-2);
}
.form-error[hidden] { display: none; }
.form-error .fe-code {
  font-family: var(--font-data); font-size: 9px; letter-spacing: 0.1em;
  text-transform: uppercase; color: color-mix(in oklch, var(--missed) 80%, var(--ink-muted));
}

/* ---------- Toast ----------
   Reconciled to the App_Shell handoff (PR3c): a bottom-center transient pill with
   a leading accent dot. The `.toast-region` is the fixed, centered stack so
   multiple toasts queue without overlapping; each `.toast` is a static pill
   inside it. The JS contract is unchanged — toast.js still appends `.toast`
   (with `.toast-msg` / optional `.toast-action`) and removes it after the
   caller's duration, so no `.show` toggle is needed. */
.toast-region {
  position: fixed; left: 50%; bottom: var(--sp-5); transform: translateX(-50%);
  z-index: var(--z-toast); display: flex; flex-direction: column; align-items: center;
  gap: var(--sp-2); pointer-events: none;
}
.toast {
  pointer-events: auto; display: flex; align-items: center; gap: 11px;
  background: var(--ink); color: var(--bg);
  border-radius: 13px; box-shadow: var(--shadow-3);
  padding: 13px 20px; font-size: var(--text-sm); font-weight: var(--w-medium);
  animation: toast-in var(--dur-normal) var(--ease-out);
}
.toast::before {
  content: ""; flex: none; width: 9px; height: 9px; border-radius: 99px;
  background: var(--accent);
}
.toast .toast-action {
  color: var(--accent); font-weight: var(--w-bold); background: none; border: 0;
  cursor: pointer; padding: 0;
}
@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .toast { animation: none; }
}

/* ---------- Assistant (chat) ---------- */
.chat-wrap { max-width: 820px; margin: 0 auto; height: calc(100vh - 64px); display: flex; flex-direction: column; padding: var(--sp-5) var(--sp-6) var(--sp-4); }
.chat-head { margin-bottom: var(--sp-4); }
.chat-title { display: flex; align-items: center; gap: var(--sp-2); font-family: var(--font-display); font-weight: var(--w-bold); font-size: var(--text-lg); letter-spacing: var(--track-tight); }
.ch-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
@media (prefers-reduced-motion: no-preference) { .ch-dot { animation: pulse 2.4s ease-in-out infinite; } }
.chat-sub { color: var(--ink-muted); font-size: var(--text-sm); margin-top: 2px; }
.chat-thread { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: var(--sp-4); padding: var(--sp-2) var(--sp-2) var(--sp-4) 0; scrollbar-width: thin; }
.msg { display: flex; flex-direction: column; gap: 4px; max-width: 86%; }
.msg.ai { align-self: flex-start; }
.msg.user { align-self: flex-end; align-items: flex-end; }
.msg-role { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--ink-subtle); letter-spacing: 0.02em; }
.msg-body { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-md); padding: var(--sp-3) var(--sp-4); font-size: var(--text-base); }
.msg.user .msg-body { background: var(--accent-quiet); border-color: color-mix(in oklch, var(--accent) 35%, transparent); }
.msg-body p { margin: 0; }
.msg-body p + p { margin-top: var(--sp-2); }
.msg-body .muted-line { color: var(--ink-muted); font-size: var(--text-sm); }
.attachments { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-3); }
.att { display: inline-flex; align-items: center; gap: var(--sp-2); background: var(--surface-raised); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 3px var(--sp-2); font-family: var(--font-mono); font-size: var(--text-xs); white-space: nowrap; }
.att-k { color: var(--accent); background: var(--accent-quiet); border-radius: 3px; padding: 0 4px; text-transform: uppercase; }

/* proposal card */
.proposal { margin-top: var(--sp-3); background: var(--surface-raised); border: 1px solid var(--border-strong); border-radius: var(--r-md); padding: var(--sp-4); }
.prop-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); }
.prop-title { font-family: var(--font-display); font-weight: var(--w-bold); font-size: var(--text-md); }
.prop-window { color: var(--ink-muted); font-size: var(--text-sm); }
.prop-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-2); margin: var(--sp-4) 0; }
.ps { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm); padding: var(--sp-3) var(--sp-2); text-align: center; }
.ps-n { font-family: var(--font-display); font-weight: var(--w-bold); font-size: var(--text-xl); }
.ps-l { color: var(--ink-muted); font-size: var(--text-xs); margin-top: 2px; }
.prop-rationale { margin: 0 0 var(--sp-4); padding-left: 1.1em; display: flex; flex-direction: column; gap: var(--sp-2); }
.prop-rationale li { font-size: var(--text-sm); color: var(--ink); }
.prop-rationale li::marker { color: var(--accent); }
.prop-week { display: flex; flex-direction: column; gap: var(--sp-2); margin-bottom: var(--sp-4); }
.pw-row { display: grid; grid-template-columns: 82px 1fr auto; align-items: center; gap: var(--sp-3); font-size: var(--text-sm); }
.pw-day { color: var(--ink-muted); }
.pw-bar { height: 6px; background: var(--border); border-radius: 999px; overflow: hidden; }
.pw-bar i { display: block; height: 100%; background: var(--accent); border-radius: 999px; }
.pw-meta { color: var(--ink-muted); font-size: var(--text-xs); }
.prop-actions { display: flex; gap: var(--sp-2); }

/* composer (terminal-ish) */
.composer { display: flex; align-items: center; gap: var(--sp-3); background: var(--surface-raised); border: 1px solid var(--border); border-radius: var(--r-md); padding: var(--sp-2) var(--sp-3); margin-top: var(--sp-2); }
.composer:focus-within { border-color: color-mix(in oklch, var(--accent) 55%, transparent); }
.att-btn { appearance: none; border: 1px solid var(--border); background: var(--surface); color: var(--ink-muted); width: 30px; height: 30px; border-radius: var(--r-sm); font-size: 17px; line-height: 1; flex: none; }
.att-btn:hover { color: var(--ink); border-color: var(--border-strong); }
.prompt-caret { color: var(--accent); font-family: var(--font-mono); font-weight: var(--w-bold); }
.composer input { flex: 1; border: 0; background: transparent; height: auto; padding: 0; font-family: var(--font-mono); font-size: var(--text-sm); }
.composer input:focus { outline: none; }
.btn-send { flex: none; }

/* ---------- Assistant side panel ---------- */
.assistant-panel {
  position: fixed; top: 0; right: 0; bottom: 0; width: 420px; max-width: 92vw;
  z-index: var(--z-modal); background: var(--bg); border-left: 1px solid var(--border);
  box-shadow: var(--shadow-2); display: flex; flex-direction: column;
  padding: var(--sp-4) var(--sp-5); transform: translateX(101%);
  transition: transform var(--dur-normal) var(--ease-out);
}
.assistant-panel.open { transform: translateX(0); }
.ap-head { display: flex; align-items: center; justify-content: space-between; }
.ap-sub { color: var(--ink-muted); font-size: var(--text-xs); margin: 2px 0 var(--sp-3); }
.assistant-panel .chat-thread { padding: 0 var(--sp-1) var(--sp-3) 0; gap: var(--sp-3); }
.assistant-panel .msg { max-width: 100%; }
.assistant-panel .composer { margin-top: auto; }


/* AI-generated solution tags */
.mat-kind.ai { color: var(--accent); background: var(--accent-quiet); }
[data-mode="light"] .mat-kind.ai { color: var(--accent-hover); }

/* Per-material auto-generate-solution button — sits between mat-name and the delete ✕ */
.mat-solve-btn { margin-left: auto; padding: 0 6px; }
.ai-tag { font-size: 10px; color: var(--accent); border: 1px solid color-mix(in oklch, var(--accent) 40%, transparent); border-radius: 999px; padding: 0 6px; margin-left: 6px; font-weight: var(--w-medium); }
.mat-size.redo { color: var(--accent); font-weight: var(--w-medium); }
[data-mode="light"] .mat-size.redo, [data-mode="light"] .ai-tag { color: var(--accent-hover); }

/* ---------- Modal: mistake analysis ---------- */
.modal-backdrop { position: fixed; inset: 0; z-index: var(--z-modal); background: var(--overlay); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); display: grid; place-items: center; padding: var(--sp-5); animation: fade-in var(--dur-fast) var(--ease-out); }
.modal-backdrop[hidden] { display: none; }
.modal { position: relative; width: 100%; max-width: 560px; max-height: 86vh; overflow-y: auto; background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--r-lg); box-shadow: var(--shadow-2); padding: var(--sp-6); }
.modal-x { position: absolute; top: var(--sp-4); right: var(--sp-4); appearance: none; border: 0; background: transparent; color: var(--ink-subtle); font-size: 15px; width: 28px; height: 28px; border-radius: var(--r-sm); }
.modal-x:hover { background: var(--hover-bg); color: var(--ink); }
.diag-head { display: flex; align-items: center; gap: var(--sp-4); margin-bottom: var(--sp-4); }
.diag-score { display: flex; align-items: baseline; gap: 2px; width: 96px; height: 96px; border-radius: 50%; border: 3px solid var(--exam); justify-content: center; flex: none; background: var(--exam-bg); }
.ds-num { font-family: var(--font-display); font-weight: var(--w-bold); font-size: 2.2rem; }
.ds-unit { color: var(--ink-muted); font-size: var(--text-md); }
.diag-title { font-family: var(--font-display); font-weight: var(--w-bold); font-size: var(--text-lg); letter-spacing: var(--track-tight); }
.diag-sub { color: var(--ink-muted); font-size: var(--text-sm); margin-top: 2px; }
.diag-verdict { font-size: var(--text-md); line-height: var(--lead-normal); color: var(--ink); margin: 0 0 var(--sp-5); text-wrap: pretty; }
.loss-list { display: flex; flex-direction: column; }
.loss-row { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-4); padding: var(--sp-3) 0; border-top: 1px solid var(--border); }
.loss-row:first-child { border-top: 0; }
.loss-topic { font-weight: var(--w-medium); font-size: var(--text-sm); }
.loss-detail { color: var(--ink-muted); font-size: var(--text-xs); margin-top: 2px; }
.loss-pts { color: var(--missed); font-family: var(--font-display); font-weight: var(--w-bold); font-variant-numeric: tabular-nums; flex: none; }
.focus-chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-2); }
/* generic .chip (courses.js focus chips render class="chip") */
.chip {
  font-size: var(--text-xs); font-weight: var(--w-medium);
  padding: 3px var(--sp-2); border-radius: var(--r-sm);
  border: 1px solid var(--border); background: var(--surface-raised); color: var(--ink-muted);
}
.focus-chip { background: var(--accent-quiet); color: var(--accent); border: 1px solid color-mix(in oklch, var(--accent) 35%, transparent); border-radius: 999px; padding: 3px var(--sp-3); font-size: var(--text-sm); font-weight: var(--w-medium); }
[data-mode="light"] .focus-chip { color: var(--accent-hover); }
.diag-actions { display: flex; gap: var(--sp-2); margin-top: var(--sp-5); flex-wrap: wrap; }

/* (The old ≤768px responsive-header rules — hamburger + mobile drawer — were
   removed with the top tab-row; the left sidebar handles narrow widths via the
   ≤720px rule near the shell.) */

/* graded rows */
.mat-kind.grade { color: var(--exam); background: var(--exam-bg); }
.mat-row.graded { appearance: none; width: 100%; background: transparent; border: 0; border-top: 1px solid var(--border); cursor: pointer; color: inherit; }
.mat-row.graded:hover { background: var(--hover-bg); }
.mat-size.link { color: var(--accent); font-weight: var(--w-medium); }
[data-mode="light"] .mat-size.link { color: var(--accent-hover); }

/* ---- Plan responsive: hold at 320 / 768 / 1024 / 1440 with no overflow ---- */
@media (max-width: 1080px) {
  /* collapse to a single column; the ledger leads, the horizon rail follows */
  .plan-shell.pv2 { grid-template-columns: 1fr; }
  .pv2-main { order: 1; padding: 24px clamp(var(--sp-4), 4vw, 32px); }
  .pv2-left { order: 2; border-right: 0; border-top: 1px solid var(--border); }
  .pv2-ai-btn { margin-top: 0; align-self: flex-start; }
}
@media (max-width: 760px) {
  /* ledger rows fold: time+status on the first line, entry + kicker beneath */
  .pv2-row { grid-template-columns: minmax(0, 1fr) auto; gap: 2px 12px; padding: 13px 4px; }
  .pv2-row .tm { grid-column: 1; grid-row: 1; }
  .pv2-row .st { grid-column: 2; grid-row: 1; }
  .pv2-row .b-body { grid-column: 1 / -1; grid-row: 2; }
  .pv2-row .kd { grid-column: 1 / -1; grid-row: 3; }
  .pv2-mainhead h1 { font-size: 26px; }
  .pv2-sum { text-align: left; }
}
@media (max-width: 480px) {
  .pv2-left { padding: 24px var(--sp-4); }
  .pv2-main { padding: 20px var(--sp-4); }
  .pv2-ready .n { font-size: 56px; }
  .pv2-ready .n i { font-size: 26px; }
  .pv2-foot { flex-direction: column; gap: var(--sp-1); }
}

/* ---------- Auth gate (FinalRedesign split sign-in) ----------
   Left: mono eyebrow → serif headline → compact card (brand mark, Google, OR
   hairline, email + password, solid CTA, footer links, fine print). Right: a
   decorative grove panel — tinted gradient, three status trees (risk →
   in-progress → verified), drifting petals, brand caption. One markup, both
   modes free via [data-mode]; riso side theme keeps working (all token-bound). */
.auth-gate {
  position: fixed; inset: 0; z-index: var(--z-modal, 1000);
  display: grid; place-items: center;
  background: var(--bg); overflow: auto;
}
/* The author display:grid above outranks the UA [hidden] default, so the gate
   must opt back into display:none when JS sets `hidden` (e.g. after login).
   Mirrors .modal-backdrop[hidden] above. */
.auth-gate[hidden] { display: none; }
/* width/min-height 100% so the split layout fills the gate even though the
   gate grid is place-items:center (centering survives for reset.html, which
   puts a bare .auth-card straight inside .auth-gate). */
.auth-layout { display: grid; grid-template-columns: 1fr 470px; width: 100%; min-height: 100%; }
.auth-main { display: grid; place-items: center; padding: var(--sp-6) var(--sp-5); }
.auth-pane { width: min(360px, 100%); }
.auth-eyebrow {
  font-family: var(--font-data); font-size: 0.625rem; font-weight: var(--w-bold);
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-subtle);
  margin: 0 0 var(--sp-4);
}
.auth-headline {
  font-family: var(--font-head); font-size: 2.375rem; font-weight: var(--w-bold);
  letter-spacing: var(--track-tight); line-height: 1.08; color: var(--ink);
  margin: 0 0 var(--sp-5);
}
.auth-card {
  display: flex; flex-direction: column; gap: var(--sp-3);
  width: min(360px, 92vw); padding: var(--sp-6) var(--sp-6) var(--sp-5);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r3); box-shadow: var(--shadow-3);
}
.auth-brand { display: flex; color: var(--ink); }
.auth-brand-mark { width: 44px; height: 37px; }
.auth-card h1 {
  font-family: var(--font-head); font-size: var(--text-xl); font-weight: var(--w-bold);
  letter-spacing: var(--track); margin: var(--sp-1) 0;
}
.auth-card label { display: flex; flex-direction: column; gap: var(--sp-1); font-size: var(--text-sm); color: var(--ink-muted); }
/* Same [hidden] override: the label display:flex above would otherwise keep a
   hidden field (e.g. the invite-token row) visible. */
.auth-card label[hidden] { display: none; }
/* Field labels speak the mono data voice: tiny, letterspaced, uppercase. */
.auth-card label > span {
  font-family: var(--font-data); font-size: 0.625rem; font-weight: var(--w-bold);
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-subtle);
}
.auth-card input {
  width: 100%; height: 38px; padding: 0 var(--sp-3);
  font-size: var(--text-sm); color: var(--ink);
  background: var(--surface-raised); border: 1px solid var(--border);
  border-radius: var(--r1);
}
.auth-card .btn {
  width: 100%; height: 38px;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  font-size: var(--text-sm); font-weight: var(--w-medium);
  border-radius: var(--r1);
}
.auth-divider {
  display: flex; align-items: center; gap: var(--sp-2);
  margin-top: var(--sp-2);
  color: var(--ink-subtle); font-family: var(--font-data);
  font-size: 0.625rem; letter-spacing: 0.2em; text-transform: uppercase;
}
.auth-divider::before, .auth-divider::after {
  content: ""; flex: 1; height: 1px; background: var(--border);
}
.auth-foot { display: flex; justify-content: space-between; align-items: center; gap: var(--sp-3); }
.auth-link {
  background: none; border: 0; padding: 0; cursor: pointer;
  font-size: var(--text-xs); color: var(--accent);
  text-decoration: underline; text-underline-offset: 2px;
}
.auth-link:hover { color: var(--accent-hover); }
.auth-fineprint {
  font-size: var(--text-xs); color: var(--ink-subtle);
  line-height: var(--lead-normal); margin: 0;
}
/* The gate's primary CTA is a solid fill (not the app's quiet accent wash) —
   the one unmistakable action on the card, per the professional sign-in spec. */
.auth-card .btn-accent {
  background: var(--accent); color: var(--accent-ink); border-color: var(--accent);
  box-shadow: none;
}
.auth-card .btn-accent:hover,
.auth-card .btn-accent:active {
  background: var(--accent-hover); color: var(--accent-ink); border-color: var(--accent-hover);
  transform: none; box-shadow: none;
}
/* "Continue with Google" — a real, bordered, full-width choice with the mark. */
.btn-google {
  border: 1px solid var(--border-strong); background: var(--surface-raised);
  color: var(--ink); text-decoration: none;
}
.btn-google:hover { background: var(--hover-bg); border-color: var(--ink-subtle); color: var(--ink); }
.google-mark { flex: none; display: block; }
/* ---- right panel: the grove (decorative, aria-hidden) ---- */
.auth-side {
  position: relative; border-left: 1px solid var(--border); overflow: hidden;
  background: linear-gradient(180deg,
    color-mix(in oklch, var(--course-2) 5%, var(--bg)),
    color-mix(in oklch, var(--done) 6%, var(--bg)));
}
.auth-grove {
  /* full-bleed vintage-botanical cover. object-position favors the right-hand
     grove (the left of the source is quiet ledger paper), so the trees stay in
     frame as the portrait panel crops the square image. */
  position: absolute; inset: 0;
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: 64% center;
}
/* Bottom scrim: a soft paper-toned gradient so the brand caption reads over the
   watercolor without washing the whole image. Sits above the image + petals,
   below the caption (DOM order: scrim → caption). */
.auth-side-scrim {
  position: absolute; left: 0; right: 0; bottom: 0; height: 42%;
  pointer-events: none;
  background: linear-gradient(180deg,
    transparent,
    color-mix(in oklch, var(--bg) 72%, transparent) 62%,
    color-mix(in oklch, var(--bg) 88%, transparent));
}
.auth-side-cap {
  position: absolute; left: 0; right: 0; bottom: 34px; z-index: 1;
  display: flex; flex-direction: column; align-items: center; gap: 7px;
}
.auth-cap-kicker {
  font-family: var(--font-data); font-size: 0.6875rem; font-weight: var(--w-bold);
  letter-spacing: 0.32em; text-indent: 0.32em; text-transform: uppercase;
  color: var(--accent);
}
.auth-cap-tag { font-family: var(--font-head); font-style: italic; font-size: var(--text-base); color: var(--ink-muted); }
.auth-cap-rule { width: 34px; height: 1px; background: var(--ink); opacity: 0.25; }
.auth-cap-micro {
  font-family: var(--font-data); font-size: 0.625rem;
  letter-spacing: 0.3em; text-indent: 0.3em; text-transform: uppercase;
  color: var(--ink-subtle);
}
/* sage petals drifting down the panel — compositor-only (transform/opacity) */
.auth-petals { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.lys-petal {
  position: absolute; top: 0; display: block;
  width: 5px; height: 6px; border-radius: 60% 40% 55% 45%;
  background: color-mix(in oklch, var(--done) 60%, transparent);
  opacity: 0;
  animation: lys-petal 11s linear infinite;
}
.lys-petal:nth-child(1) { left: 13%; width: 3.5px; height: 4.5px; --drift: 14px; animation-duration: 9s; }
.lys-petal:nth-child(2) { left: 92%; width: 4.5px; height: 5.75px; --drift: 21px; animation-duration: 11.3s; animation-delay: 1.7s; }
.lys-petal:nth-child(3) { left: 76%; width: 5.5px; height: 7px;    --drift: 28px; animation-duration: 13.6s; animation-delay: 3.4s; }
.lys-petal:nth-child(4) { left: 60%; width: 4px;   height: 4.75px; --drift: 35px; animation-duration: 15.9s; animation-delay: 5.1s; }
.lys-petal:nth-child(5) { left: 43%; width: 5px;   height: 6px;    --drift: 16px; animation-duration: 11.2s; animation-delay: 6.8s; }
@keyframes lys-petal {
  0%   { transform: translate(0, -3vh) rotate(0deg); opacity: 0; }
  8%   { opacity: 0.7; }
  90%  { opacity: 0.55; }
  100% { transform: translate(var(--drift), 104vh) rotate(160deg); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .lys-petal { animation: none; opacity: 0; }
}
/* narrow: drop the decorative panel, keep the sign-in column centered */
@media (max-width: 960px) {
  .auth-layout { grid-template-columns: 1fr; }
  .auth-side { display: none; }
}
@media (max-width: 480px) {
  .auth-main { padding: var(--sp-5) var(--sp-4); }
  .auth-headline { font-size: var(--text-2xl); }
}
.auth-control { display: inline-flex; align-items: center; gap: var(--sp-2); }
/* N7: the account name is a label, not a control — render it dimmer + smaller
   than nav text and non-interactive so it never reads as clickable. */
#auth-who { font-size: var(--text-xs); color: var(--ink-subtle); cursor: default; user-select: none; }

/* daily slip prompt — first-class "did you finish?" nudge for an ended block. */
.slip-prompt { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-3); margin: var(--sp-2) 0; padding: var(--sp-2) var(--sp-3); background: var(--surface-raised); border: 1px solid var(--border-strong); border-radius: var(--r-sm); }
.slip-prompt-q { flex: 1; font-size: var(--text-sm); color: var(--ink); }
.slip-prompt-controls { display: inline-flex; align-items: center; gap: var(--sp-2); }
.slip-btn {
  appearance: none; border: 1px solid var(--border); background: transparent; color: var(--ink-subtle);
  font-size: var(--text-xs); padding: 4px var(--sp-2); border-radius: var(--r-sm); cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}
.slip-btn:hover { color: var(--accent); border-color: color-mix(in oklch, var(--accent) 50%, transparent); }
.slip-yes { color: var(--done); border-color: color-mix(in oklch, var(--done) 45%, transparent); }
.slip-yes:hover { background: var(--done-bg); border-color: color-mix(in oklch, var(--done) 60%, transparent); }
.slip-dismiss { color: var(--ink-muted); border-color: transparent; padding: 4px 8px; }
.slip-dismiss:hover { color: var(--ink); border-color: var(--border); }

/* mode segment disabled while a paper theme locks light mode */
.seg button:disabled { opacity: 0.4; cursor: not-allowed; }
#mode-lock-note { font-style: italic; }

/* =============================================================================
   Boot splash shell — the bloom loader (loader.js) mounts into this; its own
   chrome (canvas, caption, fade via .bl-out) lives in loader.css.
   ========================================================================== */
.boot-loader { position: fixed; inset: 0; z-index: var(--z-loader); display: flex; align-items: center; justify-content: center; background: var(--bg); transition: opacity 0.4s var(--ease-out); }
/* [hidden] override: display:flex above otherwise keeps the splash in the layout
   (transparent, on top) after hidden=true, blocking all input. */
.boot-loader[hidden] { display: none; }

/* =============================================================================
   Riso identity polish — the print devices from Redesign/lysia-app.html.
   Applied globally (there is one theme now); modes only swap the palette.
   ========================================================================== */

/* ---- misregistration headings: a crimson offset ghost behind the H1 -------
   Any element carrying [data-text] gets a crimson "off-register" duplicate
   printed 2px up/left, exactly like a riso second-pass that didn't line up.
   Headings opt in by setting data-text="<their text>"; JS-rendered titles can
   add it too. Falls back gracefully (no ghost) when the attribute is absent. */
.gh, [data-text].gh { position: relative; isolation: isolate; }
.gh::before {
  content: attr(data-text);
  position: absolute; left: -2px; top: -2px; z-index: -1;
  color: var(--accent); opacity: 0.34; pointer-events: none;
  -webkit-user-select: none; user-select: none;
}
@media (prefers-reduced-motion: reduce) { .gh::before { opacity: 0.28; } }

/* surface-head + dayhead H1s carry the ghost automatically via JS-free markup:
   we duplicate their text through data-text on the element (set in index.html /
   by the per-surface modules). The serif title sits on top, crimson ghost behind. */

/* ---- hard offset shadows: the riso "print didn't quite register" depth ----
   Replaces soft blurred shadows on the key surfaces with a sharp crimson
   registration offset. The tokens (--shadow-1/2) already drive most of this;
   these targeted rules give the hero surfaces the full 3px offset. */
.panel, .modal, .digest-card,
.pri-card, .exam-item {
  box-shadow: var(--shadow-1);
}
.modal { box-shadow: var(--shadow-2); }

/* primary button: the mockup's signature — ink fill, crimson hard offset that
   collapses on press (translate into the shadow). */
.btn-accent {
  box-shadow: 2px 2px 0 var(--accent-soft);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.btn-accent:hover { transform: translate(-1px, -1px); box-shadow: 3px 3px 0 var(--accent); }
.btn-accent:active { transform: translate(2px, 2px); box-shadow: 0 0 0 var(--accent); }

/* ---- data voice: mono eyebrows, statuslines, chips read uppercase + tracked -
   The surface-head eyebrow already does this; reinforce the riso letterspacing. */
.surface-head[data-eyebrow]::before { letter-spacing: 0.18em; }

/* the header tools' ◐ theme toggle reads as the mockup's bordered glyph */
.themebtn { font-size: 14px; line-height: 1; }

/* --- Practice Mode (PRD practice-mode; PDF-first rework 2026-06-10) -------
   Full-screen viewer over a generated practice set: the compiled PDF in an
   iframe, with a problems/solutions toggle and a marks review screen. */
.practice-overlay {
  position: fixed;
  inset: 0;
  /* Modal tier (1300): MUST clear the sticky app header (--z-sticky, 1100).
     This was hardcoded 1000 — the header rendered on top of the viewer's bar
     and intercepted every click on Exit / the doc tabs (the "can't exit the
     PDF in any manner" bug). Toasts (1400) still show above. */
  z-index: var(--z-modal);
  display: flex;
  flex-direction: column;
  background: var(--bg);
  overflow: hidden;
}
.practice-bar {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1.5px solid var(--border-strong);
  background: var(--surface-raised);
  box-shadow: var(--shadow-1);
}
.practice-title { font-size: var(--text-lg); }
.practice-spacer { flex: 1 1 auto; }
/* Problems | Solutions doc switcher: one bordered group, the active doc
   filled with the accent — reads as "two documents, this one is open". */
.practice-seg {
  display: inline-flex;
  border: 1.5px solid var(--border-strong);
  border-radius: 8px;
  overflow: hidden;
}
.practice-seg-tab {
  border: 0;
  border-radius: 0;
  background: transparent;
}
.practice-seg-tab + .practice-seg-tab { border-left: 1.5px solid var(--border-strong); }
.practice-seg-tab.is-active {
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
}
.practice-seg-tab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
/* First-open how-to strip between the bar and the PDF. */
.practice-hint {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-4);
  font-size: var(--text-sm);
  color: var(--ink-muted);
  background: var(--surface-raised);
  border-bottom: 1.5px solid var(--border-strong);
}
.practice-hint-text { flex: 1 1 auto; }
.practice-hint-dismiss { flex: 0 0 auto; }
.practice-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--sp-4);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}
.practice-close:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* PDF viewer body: the compiled practice PDF fills the surface; the status
   line carries the "Compiling…" / error states while the iframe is hidden. */
.practice-body-pdf { padding: 0; gap: 0; overflow: hidden; }
.practice-pdf-status {
  padding: var(--sp-4);
  font-size: var(--text-sm);
  color: var(--ink-muted);
}
.practice-pdf-status[hidden] { display: none; }
.practice-pdf-frame {
  flex: 1 1 auto;
  width: 100%;
  border: 0;
  background: var(--surface);
}
/* Labeled exit: outlined so it reads as a real control, not chrome. */
.practice-close {
  border: 1.5px solid var(--border-strong);
  white-space: nowrap;
}
/* review screen */
.practice-review-title { font-size: var(--text-xl); margin-bottom: var(--sp-3); }
.practice-review-note {
  font-size: var(--text-sm);
  color: var(--ink-muted);
  margin: 0 0 var(--sp-4);
  max-width: 60ch;
}
.practice-review-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-3);
  border-bottom: 1px solid var(--border);
}
/* Per-problem self-rating: a 5-point Easy…Impossible segmented pill (MCQ uses a
   2-segment Correct/Incorrect variant). Token-driven so it themes light + dark;
   the danger end (Impossible/Incorrect), when selected, carries the terracotta
   accent — every other selected segment gets a neutral ink fill. Mirrors the
   landing demo + App_Shell handoff rating control. */
.practice-rating {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  flex: 0 1 340px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  overflow: hidden;
}
.practice-rating .prl-seg {
  appearance: none;
  border: 0;
  border-left: 1px solid var(--border);
  background: transparent;
  color: var(--ink-subtle);
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 7px 4px;
  white-space: nowrap;
  cursor: pointer;
  transition: background 120ms, color 120ms;
}
.practice-rating .prl-seg:first-child { border-left: 0; }
.practice-rating .prl-seg:hover {
  color: var(--ink);
  background: color-mix(in oklch, var(--ink) 5%, transparent);
}
.practice-rating .prl-seg:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.practice-rating .prl-seg.is-selected {
  color: var(--ink);
  font-weight: 600;
  background: color-mix(in oklch, var(--ink) 8%, transparent);
}
.practice-rating .prl-seg.is-selected.is-danger {
  color: var(--accent);
  background: var(--accent-soft);
}
.practice-review-comment {
  display: flex; flex-direction: column; gap: var(--sp-2);
  margin-top: var(--sp-4); max-width: 60ch;
  font-size: var(--text-sm); color: var(--ink-muted);
}
.practice-review-comment textarea { width: 100%; }
.practice-review-actions {
  display: flex;
  gap: var(--sp-3);
  margin-top: var(--sp-4);
}

/* =============================================================================
   Course readiness devices — transplanted from Redesign/lysia-app.html and
   wired to real data by readiness.js. The mockup's signature composition:
   sechead (serif + mono note), topic-map mastery rows with crimson .weak
   left-borders, and the readiness column (grown tree + legend + index + total).
   ========================================================================== */

.readiness-block { margin: 0 0 var(--sp-5); }
.readiness-block[hidden] { display: none; }

/* sechead: a serif H2 baselined against a mono note + an optional right link —
   the mockup's section header idiom, replacing the flat .panel-title look. */
.sechead { display: flex; align-items: baseline; gap: 13px; margin-bottom: var(--sp-3); flex-wrap: wrap; }
.sechead h2 { font-family: var(--font-head); font-weight: 600; font-size: 20px; letter-spacing: var(--track-tight); }
.sechead .note { font-family: var(--font-data); font-size: 10px; color: var(--ink-subtle); letter-spacing: 0.06em; text-transform: uppercase; }
.sechead .note.right { margin-left: auto; }
.sechead a.note { color: var(--accent); }

/* card: sharp r2, line-strong border + the hard crimson offset — NOT a soft
   uniform card. This is the mockup's surface vocabulary. */
.card { background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--r2); }

/* ---- Readiness view (Ink & Wash): all courses at once ---------------------
   The retired 3-column receipt (#ready-col + 3D grove) is replaced by a list of
   one card per course. readiness.js renders #readiness-list. */
.readiness-view { max-width: 980px; margin: 0 auto var(--sp-6); display: flex; flex-direction: column; gap: 18px; }
.readiness-head { display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; gap: 16px; }
.readiness-head-actions { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.readiness-h1 {
  font-family: var(--font-head); font-weight: 600; font-size: 34px;
  letter-spacing: -0.02em; margin-top: 6px;
}
.readiness-list { display: flex; flex-direction: column; gap: 18px; }
.readiness-list:empty { display: none; }
.rd-card {
  background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--r-md);
  box-shadow: var(--shadow-2); padding: 20px 24px;
  display: grid; grid-template-columns: 1fr; gap: 24px; align-items: center;
  overflow: hidden;
  transition: box-shadow var(--dur-fast) var(--ease-out);
}
.rd-card[data-active="true"] { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent), var(--shadow-2); }
/* body column — the existing text/bars/topics stack */
.rd-card-body { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
/* Quiet, destructive Delete control on each course card — the only visible
   delete affordance (the mirror in #course-list is hidden). Stays understated
   until hover so it never competes with the readiness % / generate action. */
.rd-card-del {
  align-self: flex-start; appearance: none; cursor: pointer;
  font-size: var(--text-xs); font-weight: var(--w-medium); letter-spacing: var(--track);
  padding: 4px 10px; border-radius: var(--r-sm);
  border: 1px solid color-mix(in oklch, var(--missed) 35%, var(--border));
  background: color-mix(in oklch, var(--missed) 8%, transparent);
  color: var(--missed); opacity: 0.8;
  transition: opacity var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.rd-card-del:hover { opacity: 1; background: color-mix(in oklch, var(--missed) 16%, transparent); }
/* card header — course code (selection button) */
.rd-card-id {
  text-align: left; background: transparent; border: 0; padding: 0; cursor: pointer; align-self: flex-start;
}
.rd-code { font-family: var(--font-head); font-weight: 600; font-size: 24px; color: var(--ink); }
.rd-card-id:hover .rd-code { color: var(--accent); }
/* big readiness % + status label (mockup: 56px serif number · mono label) */
.rd-pct-row { display: flex; align-items: flex-end; gap: 8px; }
.rd-bigpct { font-family: var(--font-head); font-weight: 600; font-size: 56px; line-height: 0.85; letter-spacing: -0.03em; color: var(--ink-muted); }
.rd-pct-sign { font-family: var(--font-head); font-size: 24px; color: var(--ink-subtle); padding-bottom: 6px; }
.rd-status { font-family: var(--font-data); font-size: 9px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-subtle); padding-bottom: 11px; margin-left: 6px; }
.rd-card-bar { display: block; max-width: 380px; }
/* shared readiness bars (course + sidebar) */
.rd-bar { width: 100%; border-radius: 4px; background: var(--border); overflow: hidden; display: block; }
.rd-bar-i { display: block; width: 100%; height: 100%; transform-origin: left; transform: scaleX(0); background: var(--accent); border-radius: 4px; transition: transform var(--dur-normal) var(--ease-out); }
@media (prefers-reduced-motion: reduce) { .rd-bar-i, .sb-course-bar i { transition: none; } }
/* topic pills */
.rd-chips { display: flex; flex-wrap: wrap; gap: 7px; }
.rd-chips.is-empty { display: none; }
.rd-chip {
  font-family: var(--font-data); font-size: 9.5px; letter-spacing: 0.04em; text-transform: uppercase;
  border: 1px solid var(--border); border-radius: 999px; padding: 4px 9px; color: var(--ink-subtle);
}
@media (max-width: 860px) { .readiness-view { max-width: none; } }

/* ---- rail readiness % badge (.ci-pct) on each course row ------------------ */
.ci-pct {
  margin-left: auto; font-family: var(--font-data); font-size: 12px; font-weight: 600;
  color: var(--ink-muted); font-variant-numeric: tabular-nums; min-width: 2ch; text-align: right;
  align-self: flex-start;
}
.ci-pct.is-low { color: var(--missed); }
.ci-pct.is-hi { color: var(--done); }
/* keep the edit/delete glyphs after the pct without shoving it to the far edge */
.course-item .ci-pct + span { margin-left: var(--sp-2); }

/* ---- Selected-course detail: single column, full-width below ~860px ---- */
@media (max-width: 860px) {
  .courses-detail { max-width: none; }
}

/* ---- practice-final idiom: 1–7 rating cells (mockup .cr1-cells) ----------- */
/* Shared by the practice-final cards (.pcard, readiness.js) and the
   Generated-exams rows (.exam-item, courses.js). */
.exam-item { box-shadow: var(--shadow-1); }
.rating { display: flex; align-items: center; gap: 4px; margin-top: var(--sp-2); flex-wrap: wrap; }
.rating .rl { font-family: var(--font-data); font-size: 8.5px; letter-spacing: 0.1em; color: var(--ink-subtle); width: 100%; }
.rating .cell {
  width: 26px; height: 26px; border-radius: var(--r1); border: 1px solid var(--border-strong);
  font-family: var(--font-data); font-size: 10px; display: grid; place-items: center;
  color: var(--ink-subtle);
}
.rating .cell.on { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); font-weight: 600; }
.rating .verdict {
  font-family: var(--font-data); font-size: 8.5px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-subtle); margin-left: 8px;
}
/* unrated shell: greyed cells so the device reads before self-assessment */
.rating.is-unrated .cell { border-style: dashed; opacity: 0.7; }

/* practice-final cards (mockup .cr1-pex): full-width stack in the middle col */
.pex { display: grid; gap: 12px; }
.pcard {
  border: 1px solid var(--border); border-radius: var(--r2); background: var(--surface);
  padding: 16px 18px; display: flex; flex-direction: column; gap: 9px;
  box-shadow: var(--shadow-1); cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.pcard:hover { transform: translateY(-1px); border-color: var(--border-strong); box-shadow: var(--shadow-2); }
.pcard:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.pcard .ptop { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.pcard h4 { font-family: var(--font-head); font-size: 17px; font-weight: 600; }
.pcard .when { font-family: var(--font-data); font-size: 9px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-subtle); }
.pcard .pmeta {
  font-family: var(--font-data); font-size: 9px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-subtle); margin-top: 3px;
}
.pex .gen {
  border: 1.5px dashed var(--border-strong); border-radius: var(--r2);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 9px;
  padding: 20px 14px; text-align: center;
}
.pex .gen p { font-size: 11.5px; color: var(--ink-muted); max-width: 360px; }

/* ===================== Owner analytics surface ===================== */
/* The Owner nav item is gated by .owner-gated; owner.js removes it only for the
   owner account. */
.tab.owner-gated { display: none !important; }
.owner-toolbar {
  display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
  margin: var(--sp-4) 0 var(--sp-5);
}
.owner-toolbar .dv { color: var(--ink-muted); }

/* Org context switcher rows (WireUpInstitution.txt D4). Reuses owner-surface
   tokens — no new design token (the role-aware nav gating reuses nav/surface
   tokens too). Each row is a label/sublabel on the left, switch action on the right. */
.org-context-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); padding: var(--sp-3) 0; border-bottom: 1px solid var(--border);
}
.org-context-row .hint { color: var(--ink-muted); }
.owner-stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--sp-3); margin-bottom: var(--sp-6);
}
.owner-tile {
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface-raised); padding: var(--sp-4);
  display: flex; flex-direction: column; gap: 2px;
}
.owner-tile-val { font-size: var(--text-2xl); font-family: var(--font-data, var(--font-mono)); line-height: 1.1; }
.owner-tile-label { text-transform: uppercase; letter-spacing: 0.04em; color: var(--ink-muted); }
.owner-tile-sub { color: var(--ink-subtle); font-size: var(--text-xs); }
.owner-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4);
  margin-bottom: var(--sp-5);
}
@media (max-width: 768px) { .owner-grid { grid-template-columns: 1fr; } }
.owner-card {
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface); padding: var(--sp-4);
}
.owner-card > h2.dv { display: block; margin-bottom: var(--sp-3); color: var(--ink-muted); }
.owner-bars { display: flex; flex-direction: column; gap: 6px; }
.owner-bar-row { display: grid; grid-template-columns: 84px 1fr 44px; align-items: center; gap: var(--sp-2); }
.owner-bar-label { color: var(--ink-muted); font-size: var(--text-xs); }
.owner-bar-track { height: 8px; background: var(--surface-raised); border-radius: 4px; overflow: hidden; }
.owner-bar-fill { display: block; height: 100%; background: var(--accent); border-radius: 4px; }
.owner-bar-val { text-align: right; font-size: var(--text-xs); color: var(--ink); }
.owner-table-wrap { overflow-x: auto; }
.owner-table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
.owner-table th, .owner-table td { text-align: left; padding: 6px 10px; border-bottom: 1px solid var(--border); white-space: nowrap; }
.owner-table th { color: var(--ink-muted); font-weight: 600; text-transform: uppercase; font-size: var(--text-xs); letter-spacing: 0.03em; }
.owner-table tbody tr:hover { background: var(--surface-raised); }
.owner-table td { font-variant-numeric: tabular-nums; }

/* ── Owner prompt registry (every LLM prompt + static-token cost) ─────── */
#owner-prompts-summary { margin-bottom: var(--sp-3); }
#owner-prompts { display: flex; flex-direction: column; gap: 6px; }
.owner-prompt {
  border: 1px solid var(--border); border-radius: var(--r-sm, var(--r-md));
  background: var(--surface-raised); padding: 6px 10px;
}
.owner-prompt > summary {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: var(--sp-3); cursor: pointer; list-style: none;
}
.owner-prompt > summary::-webkit-details-marker { display: none; }
.owner-prompt-title { font-weight: 600; color: var(--ink); }
.owner-prompt-meta {
  font-size: var(--text-xs); color: var(--ink-muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.owner-prompt-purpose { margin: var(--sp-2) 0; color: var(--ink-muted); font-size: var(--text-sm); }
.owner-prompt-template {
  margin: 0; padding: var(--sp-3); border-radius: var(--r-sm, var(--r-md));
  background: var(--surface); border: 1px solid var(--border);
  font-family: var(--font-mono); font-size: var(--text-xs); line-height: 1.5;
  white-space: pre-wrap; word-break: break-word; max-height: 18rem; overflow: auto;
}

/* ── Today digest (unified check-in + digest) ─────────────────────────── */
.today-review {
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface); padding: var(--sp-4);
  margin-bottom: var(--sp-5);
  display: flex; flex-direction: column; gap: var(--sp-2); align-items: flex-start;
}
.today-review-head { margin: 0; font-size: var(--text-lg); font-weight: 600; color: var(--ink); }
.today-review-breakdown { margin: 0; color: var(--ink-muted); }
.today-review .btn { margin-top: var(--sp-2); }
.today-section-head { font-size: var(--text-sm); text-transform: uppercase; letter-spacing: 0.03em; color: var(--ink-muted); margin: 0 0 var(--sp-3); }
.today-courses { display: flex; flex-direction: column; gap: var(--sp-3); }
.today-course {
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface); padding: var(--sp-4);
  display: flex; flex-direction: column; gap: var(--sp-2); align-items: flex-start;
}
.today-course-title { font-weight: 600; color: var(--ink); }
.today-course-exam {
  font-size: var(--text-xs); color: var(--ink-muted);
  border: 1px solid var(--border); border-radius: 999px; padding: 1px 8px; margin-left: 4px;
}
.today-prep { display: flex; align-items: center; gap: var(--sp-3); width: 100%; }
.today-prep-track { flex: 1; height: 8px; background: var(--surface-raised); border-radius: 4px; overflow: hidden; }
.today-prep-fill { display: block; height: 100%; background: var(--accent); border-radius: 4px; transition: width var(--dur-2, 240ms) ease; }
.today-prep-label { font-size: var(--text-xs); color: var(--ink-muted); white-space: nowrap; }
.today-course-rec { margin: 0; color: var(--ink-muted); }
