:root {
  /* Plum colour system — CLAUDE.md "Color system — resolved Aug 8, 2026".
     Generated in OKLCH, anchored on primary/900 #561045. Pushed to app/
     Aug 13, 2026, retiring the dead indigo (#4F46E5 / #4A4A8C family) that
     survived here after Framer and Figma had already moved.
     Three-tier background system + single hairline stroke carry hierarchy,
     no box-shadow on in-flow surfaces. */

  /* Primary ramp — 900 is the anchor. Rungs 600+ clear AA on white. */
  --primary-50:  #FCF3F9;
  --primary-100: #FCE5F3;
  --primary-200: #F8CEE9;
  --primary-300: #F1B2DC;
  --primary-400: #E291C8;
  --primary-500: #CE6FB1;
  --primary-600: #B55199;
  --primary-700: #94377B;
  --primary-800: #71215C;
  --primary-900: #561045;
  --primary-950: #3A072E;

  /* Neutral — warm, same 340° hue at ~0.013 chroma. Not cool Slate. */
  --neutral-50:  #F8F5F7;
  --neutral-100: #F0EBEE;
  --neutral-200: #E0DADE;
  --neutral-300: #CEC6CB;
  --neutral-400: #B5ADB2;
  --neutral-500: #9B9298;
  --neutral-600: #81797E;
  --neutral-700: #655E63;
  --neutral-800: #4B4448;
  --neutral-900: #363134;
  --neutral-950: #231F22;

  /* Expressive — sunrise arc. Logomark and illustration ONLY.
     Never UI chrome, never adjacent to a status tag. */
  --expressive-1: #561045;
  --expressive-2: #9A1A5D;
  --expressive-3: #E56452;
  --expressive-4: #FAAB3F;

  --bg: var(--neutral-50);       /* shell tier — neutral, never a primary tint */
  --surface: #FFFFFF;            /* base tier — page/content */
  --surface-tint: var(--primary-50); /* card / banner tint — small areas only */
  --border: var(--neutral-200);  /* stroke, global */
  --text-primary: var(--neutral-950);
  /* --text-secondary/--text-muted recalibrated to a neutral-cool gray family
     off the new ink — not specified by Kim directly, derived to match the
     old scale's contrast relationships. Flagged in CLAUDE.md as unconfirmed,
     revisit if it reads off against real content. */
  --text-secondary: #585258;  /* warm neutral, Aug 10 2026 reconciliation — 7.60:1 — see docs/TOKEN-DRIFT-AUDIT.md */
  /* --text-muted darkened Aug 7 2026 from #8A8D94 (3.32:1 on white — failed WCAG
     AA, which needs 4.5:1 for normal text). #6B6F78 measures 5.04:1, same
     neutral-cool family, still reads as quiet grey. Changed before the Framer
     style port so the failing value was not copied into a second system. */
  --text-muted: #777077;      /* warm neutral, Aug 10 2026 reconciliation — 4.81:1 on white */

  --ink: var(--neutral-950);   /* default/dark button fill, same as text-primary */
  --ink-hover: var(--neutral-900);
  --ink-contrast: #FFFFFF;
  --cta: var(--primary-900);   /* #561045 — primary button, per CLAUDE.md's ramp
                                   usage table. Used sparingly; not a general
                                   accent. Supersedes indigo #4F46E5 (Aug 3–13,
                                   2026), which itself superseded #166EE1. */
  --cta-hover: var(--primary-950);
  --cta-contrast: #FFFFFF;     /* 11.9:1 on --cta, clears AAA */

  --tag-overdue-bg: #FFE4E6;
  --tag-overdue-text: #9F1239;
  --tag-due-bg: #FEF3C7;
  --tag-due-text: #92400E;
  --tag-done-bg: #D1FAE5;
  --tag-done-text: #065F46;
  /* tag/neutral and tag/plus moved off cool slate + orphaned indigo onto the
     brand family, Aug 13 2026 — CLAUDE.md "Semantic normalised into one family". */
  --tag-neutral-bg: var(--neutral-100);
  --tag-neutral-text: var(--neutral-800);
  --tag-plus-bg: var(--primary-100);
  --tag-plus-text: var(--primary-800);

  /* Accent aliases now point at the primary ramp. Kept as names so every
     existing rule referencing --accent-* picks up Plum automatically. */
  --accent-100: var(--primary-100);
  --accent-200: var(--primary-200);
  --accent-300: var(--primary-300);
  --accent-400: var(--primary-400);
  --accent-500: var(--primary-500);
  --accent-600: var(--primary-600);
  --accent-700: var(--primary-700);
  --accent-800: var(--primary-800);
  --accent-900: var(--primary-900);

  --accent: var(--primary-900);      /* solid fills — buttons, primary actions */
  --accent-hover: var(--primary-950);
  --accent-bg: var(--primary-100);   /* pale tag / active-nav background */
  --accent-text: var(--primary-800); /* text on --accent-bg — 8.50:1, AAA */
  --accent-contrast: #FFFFFF;        /* text on solid --accent fill — 11.9:1, AAA */

  /* Beta access gate primary action — SCREENS.md §1.4's settled ruling, Aug 15
     2026. PUNCHLIST.md A3 (launch-safety UI foundation) has now landed: every
     primary action, including onboard-primary, is repointed onto --cta, so
     --gate-primary is kept only as a named alias for this screen's markup,
     not a separately-tuned value. Light: primary/900, white label, 13.50:1. */
  --gate-primary: var(--cta);
  --gate-primary-hover: var(--cta-hover);
  --gate-on-primary: var(--cta-contrast);

  --font-display: 'Literata', serif;   /* headlines, card titles, quotes */
  --font-body: 'Plus Jakarta Sans', -apple-system, sans-serif; /* body, UI, eyebrows */

  /* Elevation scale — Untitled UI convention (tinted with ink, not pure black;
     resting state gets a whisper of depth, hover/active states step up). */
  --shadow-xs: 0 1px 2px rgba(31, 29, 26, 0.04);
  --shadow-sm: 0 2px 6px rgba(31, 29, 26, 0.06);
  --shadow-md: 0 8px 20px rgba(31, 29, 26, 0.08);

  /* Content-width scale — docs/DESIGN-SYSTEM.md "Notion's task-based content
     widths." Three tiers, not one universal max-width: narrow single-column
     forms/cards, medium single-focus task views, wide table/data views. Values
     are the ceilings already coherent on the approved surfaces (beta gate,
     syllabus parser, Daily Brief) — this formalizes them as shared tokens
     rather than repeating a bare max-width per component; it is not a
     redesign of every screen's width. All three land on the 8px grid. */
  --width-narrow: 640px;  /* beta gate, onboarding cards, Daily Brief card */
  --width-task: 840px;    /* syllabus upload card (was a non-grid 820px) */
  --width-data: 1040px;   /* syllabus results / review, table-heavy content */

  /* Nav — resizable range, wide-desktop tier only (>=1281px). Responsive-shell
     architecture: replaces the old fixed 240px .shell column with a variable
     JS clamps to [--nav-min, --nav-max] and persists. Outside the wide-desktop
     tier, the tablet+laptop and mobile media queries override
     grid-template-columns directly, so this variable has no effect there. */
  --nav-w: 240px;
  --nav-min: 220px;
  --nav-max: 320px;
  --nav-compact-w: 64px;

  color-scheme: light;
}

[data-mode="dark"] {
  --bg: #17161A;
  --surface: #201F24;
  /* Dark surface tint — PUNCHLIST.md A3, Aug 16 2026. Was #2A2A4D, an indigo
     tint left over from before the Aug 13 Plum move; light mode had already
     moved its tint onto --primary-50 but dark mode's --accent-* family never
     migrated. Now the same primary ramp, dark end: primary/950. */
  --surface-tint: var(--primary-950);
  --border: #34323A;
  --text-primary: #F2F1EC;
  --text-secondary: #B4B1A9;
  /* Independent review of PR #28, Aug 17 2026: #837F76 was 4.10:1 against the
     real --surface (#201F24), which normal text sits on constantly (any
     muted label inside a card) — failing WCAG AA's 4.5:1 floor despite
     clearing it against --bg. Lightened along the same hue/saturation to
     #8E8A82: 4.761:1 vs --surface, 5.238:1 vs --bg — both AA with real
     margin, not a bare pass. Computed via the standard relative-luminance
     formula (WCAG 2.x), not estimated. */
  --text-muted: #8E8A82;

  /* --accent-* — light/dark token unification, PUNCHLIST.md A3. Previously an
     unrelated indigo family (#8484C4 etc.) sat next to a Plum light mode; this
     is the same primary ramp used everywhere else, dark end. */
  --accent-100: var(--primary-950);
  --accent-800: var(--primary-200);
  --accent: var(--primary-500);      /* lightened for contrast against dark surfaces */
  --accent-hover: var(--primary-600);
  --accent-bg: var(--accent-100);
  --accent-text: var(--accent-800); /* light primary/200 on dark primary/950 tint — high contrast */
  /* Dark ink (neutral/950) on the primary/500 fill clears 4.78:1+; matches
     --cta-contrast below rather than a separate indigo-derived ink. */
  --accent-contrast: var(--neutral-950);

  /* Primary action fill, dark mode — PUNCHLIST.md A3 unifies every primary
     action (gate, onboarding, upgrade CTA) onto --cta, so this single
     override now drives all of them. primary/500 fill, dark-ink label.
     Verified against the real dark surface #201F24: fill 5.09:1 (UI element,
     needs 3:1), label 5.06:1 (normal text, needs 4.5:1). Chosen over
     primary/600 + white (3.59:1 / 4.56:1 — passed but sat close to both
     floors) for margin, per Kim's Aug 15 ruling in SCREENS.md §1.4. Light
     mode's --cta (primary/900 fill, white label, 13.5:1) stays untouched —
     that same dark plum fill would read as near-black-on-near-black here. */
  --cta: var(--primary-500);
  --cta-hover: var(--primary-600);
  --cta-contrast: var(--neutral-950); /* #231F22 */

  color-scheme: dark;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--text-primary);
  line-height: 1.5;
  transition: background-color 150ms ease, color 150ms ease;
}

.shell {
  display: grid;
  grid-template-columns: var(--nav-w) minmax(0, 1fr);
  min-height: 100vh;
}

/* Beta gate — responsive shell, PUNCHLIST.md A3. Nothing in the sidebar is
   reachable pre-gate, and on mobile the full nav stack pushed the gate form
   off-screen below the fold. Full-width single column instead, at every
   breakpoint — this rule has to win over both the laptop and mobile shell
   media queries below, so it sets grid-template-columns directly rather than
   only hiding the sidebar. */
.shell.gate-active { grid-template-columns: 1fr; }
.shell.gate-active .sidebar { display: none; }
/* Independent review of PR #30: .shell.gate-active and the wide-desktop
   .shell.nav-collapsed rule (responsive-shell-arch, further down) share the
   same two-class specificity, and nav-collapsed's later source position let
   it win a 64px column back if a returning user had previously collapsed the
   nav and gate-active became true again — cosmetic (the sidebar itself stays
   hidden either way) but the gate screen should never depend on source order
   for its full-width layout. Three-class selector outranks both regardless
   of where either rule sits in the file. */
.shell.gate-active.nav-collapsed { grid-template-columns: 1fr; }

/* ---- sidebar ---- */
.sidebar {
  position: relative;
  background: var(--surface);
  border-right: 0.5px solid var(--border);
  padding: 24px 16px;
  display: flex;
  flex-direction: column;
}

/* Nav collapse button — wide-desktop tier only (>=1281px); hidden at the
   compact icon-rail and mobile-drawer tiers below, since collapse is an
   affordance layered on top of the already-resizable wide tier, not a
   separate control those tiers need. */
.nav-collapse-btn {
  margin-left: auto;
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  border-radius: 6px;
  cursor: pointer;
  color: var(--text-secondary);
  flex-shrink: 0;
}
.nav-collapse-btn:hover { background: var(--bg); }
.nav-collapse-btn:focus-visible { outline: 2px solid var(--cta); outline-offset: 2px; }
.nav-collapse-btn svg { transition: transform 150ms ease; }
.shell.nav-collapsed .nav-collapse-btn svg { transform: rotate(180deg); }

/* Nav resize handle — wide-desktop tier only. display:none by default (also
   inert: not draggable, not keyboard-adjustable at other tiers, per app.js's
   wideDesktopMQ guards on every listener) and only shown at >=1281px below. */
.nav-resize-handle {
  display: none;
  position: absolute;
  top: 0;
  right: -4px;
  bottom: 0;
  width: 8px;
  cursor: col-resize;
  z-index: 10;
}
.nav-resize-handle::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 3px;
  width: 2px;
  background: transparent;
}
.nav-resize-handle:hover::after,
.nav-resize-handle:focus-visible::after,
.nav-resize-handle.dragging::after { background: var(--cta); }
.nav-resize-handle:focus-visible { outline: 2px solid var(--cta); outline-offset: 2px; }

/* Single wide-desktop-tier media query — nav resize handle, collapse button,
   and the collapsed-nav icon-only treatment all live here together. */
@media (min-width: 1281px) {
  .nav-resize-handle { display: block; }

  /* Nav collapse — icon-only rail, same visual pattern as the tablet+laptop
     compact tier below, but toggled by the user (and persisted) rather than
     forced by viewport width. */
  .shell.nav-collapsed { grid-template-columns: var(--nav-compact-w) minmax(0, 1fr); }

  /* Collapsed nav, wide-desktop tier: icon-only, labels visually hidden but
     still present for assistive tech (same pattern as the tablet+laptop
     compact tier further down, applied here only when the user has
     explicitly collapsed the wide-desktop nav). */
  .shell.nav-collapsed .sidebar { padding-inline: 8px; }
  .shell.nav-collapsed .brand-label,
  .shell.nav-collapsed .profile-meta,
  .shell.nav-collapsed .nav-group-label,
  .shell.nav-collapsed .nav-label,
  .shell.nav-collapsed .nav-badge {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
  .shell.nav-collapsed .nav-item { justify-content: center; gap: 0; }
  .shell.nav-collapsed .profile-card { justify-content: center; gap: 0; width: 48px; }
  .shell.nav-collapsed .profile-card > svg { display: none; }
  .shell.nav-collapsed .brand-row { justify-content: center; gap: 0; }
  .shell.nav-collapsed .nav-collapse-btn { margin-left: 0; }
}

.brand-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 8px 24px;
  font-weight: 500;
  font-size: 15px;
}

.brand-mark {
  /* No box — a filled gradient mark doesn't need a container the way the old
     line-drawn glyph did, and per the stroke rule (docs/DESIGN-SYSTEM.md,
     "Surfaces, strokes, and elevation") a logomark isn't something a person
     acts on. Real mark is 64:33, not square. */
  width: 21px;
  height: 11px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.profile-card {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px;
  border-radius: 8px;
  background: var(--surface);
  border: 0.5px solid var(--border);
  margin-bottom: 24px;
  cursor: pointer;
  transition: border-color 150ms ease;
}
.profile-card:hover { border-color: var(--text-muted); }
.profile-card:focus-visible { outline: 2px solid var(--cta); outline-offset: 2px; }

.avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--ink);
  color: var(--ink-contrast);
  font-size: 12px;
  font-weight: 500;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.profile-meta { flex: 1; min-width: 0; }
.profile-name { font-size: 13px; font-weight: 500; line-height: 1.3; }
.profile-plan { font-size: 12px; color: var(--text-secondary); line-height: 1.3; }
.profile-card svg { color: var(--text-secondary); flex-shrink: 0; }

.nav-group-label {
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
  padding: 0 8px;
  margin: 20px 0 8px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.nav-add {
  width: 16px;
  height: 16px;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  opacity: 0.6;
  cursor: default;
  position: relative;
}
.nav-add:hover .nav-add-tip { opacity: 1; transform: translateY(0); }
.nav-add-tip {
  position: absolute;
  top: 20px;
  right: 0;
  background: var(--ink);
  color: var(--ink-contrast);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 500;
  padding: 4px 8px;
  border-radius: 6px;
  white-space: nowrap;
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 150ms ease, transform 150ms ease;
  pointer-events: none;
  z-index: 5;
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px;
  border-radius: 8px;
  font-size: 14px;
  color: var(--text-secondary);
  cursor: pointer;
  margin-bottom: 2px;
  border: none;
  background: none;
  width: 100%;
  text-align: left;
  font-family: inherit;
  transition: background-color 120ms ease, color 120ms ease, transform 120ms ease;
}
.nav-item svg { flex-shrink: 0; color: var(--text-secondary); transition: color 120ms ease; }
.nav-item:hover:not(.disabled) { background: var(--bg); color: var(--text-primary); }
.nav-item:active:not(.disabled) { transform: scale(0.98); }
/* Independent review of PR #28, Aug 17 2026: nav-item had no focus-visible at
   all — a keyboard user tabbing through the sidebar (or the mobile drawer,
   same markup) got zero visible indicator of which item was focused. */
.nav-item:focus-visible { outline: 2px solid var(--cta); outline-offset: -2px; }

.nav-item.active { background: var(--bg); color: var(--text-primary); font-weight: 500; }
.nav-item.active svg { color: var(--text-primary); }

.nav-item.disabled { color: var(--text-muted); opacity: 0.55; cursor: default; }
.nav-item.disabled svg { color: var(--text-muted); }

.nav-badge {
  font-size: 11px;
  color: var(--text-muted);
  margin-left: auto;
  display: flex;
  align-items: center;
}
/* tag/plus — indigo-100 fill with an indigo-700 label. Deliberately NOT
   indigo-50, which sits within three values of the card tint and would
   disappear on tinted surfaces. Tier palette, never the deadline palette. */
.nav-badge.plus {
  font-size: 10.5px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: 999px;
  background: var(--tag-plus-bg);
  color: var(--tag-plus-text);
  border: 0.5px solid transparent;
}
.nav-badge.soon {
  font-size: 10.5px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: 999px;
  background: var(--surface);
  border: 0.5px solid var(--border);
  color: var(--text-secondary);
}

.sidebar-footer {
  margin-top: auto;
  padding-top: 12px;
  border-top: 0.5px solid var(--border);
}

/* ---- main / topbar ---- */
.shell-main { display: flex; flex-direction: column; min-width: 0; }

.shell-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 32px;
  border-bottom: 0.5px solid var(--border);
  background: var(--surface);
  flex-shrink: 0;
}

.topbar-left { display: flex; align-items: center; gap: 8px; }

/* Mobile menu toggle — hidden above the 760px shell breakpoint, where the
   sidebar is always visible as a static column. PUNCHLIST.md A3. */
.menu-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border: none;
  background: none;
  color: var(--text-secondary);
  border-radius: 8px;
  cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease;
}
.menu-toggle:hover { background: var(--bg); color: var(--text-primary); }
.menu-toggle:focus-visible { outline: 2px solid var(--cta); outline-offset: 2px; }

.nav-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.32);
  z-index: 15;
}
.topbar-back {
  display: none;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-secondary);
  cursor: pointer;
  background: none;
  border: none;
  font-family: inherit;
  padding: 4px 8px 4px 4px;
  border-radius: 6px;
  transition: background-color 120ms ease;
}
.topbar-back:hover { background: var(--bg); }
.topbar-back:focus-visible { outline: 2px solid var(--cta); outline-offset: 2px; }
.topbar-back.visible { display: flex; }

.topbar-title { font-size: 14px; font-weight: 500; }

.mode-toggle {
  border: 0.5px solid var(--border);
  background: var(--surface);
  color: var(--text-secondary);
  font-family: var(--font-body);
  font-size: 13px;
  padding: 6px 14px;
  border-radius: 999px;
  cursor: pointer;
  transition: border-color 150ms ease;
}
.mode-toggle:hover { border-color: var(--text-muted); }
.mode-toggle:active { transform: scale(0.97); }
.mode-toggle:focus-visible { outline: 2px solid var(--cta); outline-offset: 2px; }

/* ---- content ---- */
/* 24px vertical / 32px horizontal — blueprint.json layout.autoLayout.responsive
   .mainContent.desktop, reconciled here (code previously ran a uniform 32px).
   Also the sized CONTAINER for the workspace grid below: container-type makes
   .shell-content the inline-size reference every @container query underneath
   reacts to. A container query can never style the element it's declared on,
   so the grid it drives lives one level down, on .workspace-grid — this is
   what makes the Preview rail react to available workspace width rather than
   viewport width or route identity. */
.shell-content {
  container-type: inline-size;
  container-name: workspace;
  padding: 24px 32px;
  overflow-y: auto;
  flex: 1;
}

@keyframes content-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}
.shell-content.content-in { animation: content-in 180ms cubic-bezier(0.16, 1, 0.3, 1); }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    scroll-behavior: auto !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .shell-content.content-in { animation: none; }
  .syllabus-spinner svg { animation: none !important; }
  .schedule-stream-row { animation: none !important; }
}

.eyebrow {
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin: 0 0 8px;
}
.welcome-line { font-family: var(--font-display); font-weight: 500; font-size: 30px; line-height: 1.15; letter-spacing: -0.015em; margin: 0 0 8px; }
.welcome-sub { font-size: 15px; color: var(--text-secondary); margin: 0 0 24px; max-width: 480px; line-height: 1.6; }

/* ================================================================
   WORKSPACE — nav (sidebar, above) -> workspace(.shell-content, the
   inline-size container above) -> task column + optional Preview rail.
   Replaces the old route-specific .page-with-rail / viewport-breakpoint
   architecture with one shared, container-query-driven mechanism. Rail
   state reacts only to the workspace's own measured width (ResizeObserver +
   @container query below), never to viewport width or route identity — see
   app.js's syncRailUI()/railMode().
   ================================================================ */
.workspace-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 24px;
  align-items: start;
}
.task-column { min-width: 0; }

/* Centers WITHIN the workspace/task-column track, not the viewport — the
   existing --width-narrow/--width-task/--width-data ceilings, now paired
   with margin-inline:auto so content sits centered in whatever width the
   track actually has, rather than pinned to its left edge. One shared
   mechanism, no route-specific centering classes. */
.brief-card,
.syllabus-card,
.syllabus-page { margin-inline: auto; }

/* Rail — closed by default (pre-JS / inline tier), avoids a flash of open
   content before app.js's syncRailUI() runs. */
.content-rail { display: none; }
.workspace-grid.has-rail[data-rail-open="true"] .content-rail { display: block; }
.content-rail {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 18px;
  position: relative;
}
.rail-section + .rail-section { margin-top: 18px; padding-top: 18px; border-top: 1px solid var(--border); }
.rail-eyebrow {
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin: 0 0 8px;
}
.rail-body { font-size: 13px; color: var(--text-secondary); line-height: 1.6; margin: 0; }
.rail-link {
  display: inline-flex;
  background: transparent;
  border: none;
  color: var(--cta);
  cursor: pointer;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  padding: 0;
  text-align: left;
}
.rail-link:hover { text-decoration: underline; }
.rail-link:focus-visible { outline: 2px solid var(--cta); outline-offset: 2px; border-radius: 2px; }
.rail-close-btn {
  display: none;
  position: absolute;
  top: 12px;
  right: 12px;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  border: none;
  background: var(--bg);
  color: var(--text-secondary);
  cursor: pointer;
  align-items: center;
  justify-content: center;
}
.rail-close-btn:focus-visible { outline: 2px solid var(--cta); outline-offset: 2px; }

/* Preview toggle — shown whenever a rail exists but isn't side-by-side
   (inline-collapsed tier or mobile sheet tier). Collapsed by default; see
   the "below that (but rail present)" requirement — the rail never opens
   stacked by default outside the side-by-side tier. */
.rail-toggle {
  display: none;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-primary);
  border-radius: 8px;
  padding: 9px 14px;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  width: max-content;
}
.rail-toggle:hover { border-color: var(--text-muted); }
.rail-toggle:focus-visible { outline: 2px solid var(--cta); outline-offset: 2px; }
.rail-toggle svg { transition: transform 150ms ease; }
.workspace-grid.has-rail[data-rail-open="true"] .rail-toggle svg { transform: rotate(180deg); }
.workspace-grid.has-rail .rail-toggle { display: inline-flex; }

/* ---- Rail state: side-by-side. Workspace-driven, not viewport-driven. ----
   844px = 560px comfortable task-column minimum + 24px gap + 260px rail
   minimum. One shared constant across every route, not derived per-route or
   per-viewport. */
@container workspace (min-width: 844px) {
  .workspace-grid.has-rail { grid-template-columns: minmax(0, 1fr) minmax(260px, 320px); }
  .workspace-grid.has-rail .content-rail { display: block; position: static; transform: none; }
  .workspace-grid.has-rail .rail-toggle { display: none; }
}

/* ---- Rail state: mobile bottom sheet. Reuses the same #content-rail node —
   only its position/transform/visibility change, not its content. The 844px
   container minimum can never be reached inside a <=760px viewport (the
   workspace itself is narrower than that once sidebar-as-drawer and page
   padding are accounted for), so this never collides with the rule above.
   The actual mobile-tier rules live in the single canonical
   @media (max-width: 760px) block further down, alongside the rest of the
   mobile shell overrides — kept as one block rather than scattered ones, so
   structural checks (and future readers) only ever have one mobile-tier
   query to reason about. */
.rail-sheet-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.32);
  z-index: 42;
}

/* ---- Plus preview screens ----
   Applied to an inner wrapper inside the task column (not to #content /
   .shell-content itself, which is the container-query sizing element and
   must never carry a route-specific max-width of its own). */
.plus-preview-page {
  display: flex;
  flex-direction: column;
  gap: 24px;
  max-width: 1120px;
  margin-inline: auto;
}

.plus-preview-head {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.plus-preview-title-row {
  display: flex;
  align-items: center;
  gap: 12px;
}
.plus-preview-title-row h1 {
  font-family: var(--font-display);
  font-size: 28px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.2;
  margin: 0;
}
.plus-preview-head > p {
  color: var(--text-secondary);
  font-size: 14px;
  line-height: 1.6;
  margin: 0;
}
.plus-tag {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  background: var(--tag-plus-bg);
  color: var(--tag-plus-text);
  border-radius: 999px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.03em;
  line-height: 1;
  padding: 4px 8px;
}
.plus-preview-example {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.preview-example-label,
.reading-card-label {
  color: var(--text-secondary);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  line-height: 1.4;
  margin: 0;
  text-transform: uppercase;
}
.preview-search {
  width: 100%;
  min-height: 40px;
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--text-secondary);
  background: var(--surface);
  border: 1.5px solid var(--cta);
  border-radius: 8px;
  padding: 8px 12px;
  font-size: 13px;
}
.preview-search svg { flex-shrink: 0; }
.plus-section-title {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 500;
  line-height: 1.4;
  margin: 0;
}
.notes-table-wrap {
  width: 100%;
  overflow-x: auto;
}
.notes-preview-table {
  width: 100%;
  min-width: 760px;
  border-collapse: collapse;
  table-layout: fixed;
}
.notes-preview-table th,
.notes-preview-table td {
  border-bottom: 0.5px solid var(--border);
  text-align: left;
}
.notes-preview-table th {
  color: var(--text-secondary);
  font-size: 12px;
  font-weight: 500;
  padding: 12px 0;
}
.notes-preview-table td {
  color: var(--text-secondary);
  font-size: 13px;
  padding: 12px 0;
}
.notes-preview-table th:nth-child(1) { width: 52%; }
.notes-preview-table th:nth-child(2) { width: 16%; }
.notes-preview-table th:nth-child(3) { width: 18%; }
.notes-preview-table th:nth-child(4) { width: 14%; }
.notes-preview-table th:not(:first-child),
.notes-preview-table td:not(:first-child) { padding-left: 16px; }
.notes-title-cell {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--text-primary);
}
.notes-status-tag {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  color: var(--tag-neutral-text);
  background: var(--tag-neutral-bg);
  border-radius: 999px;
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  padding: 4px 8px;
}
.reading-preview-grid {
  display: grid;
  /* Intrinsic auto-fit sizing — collapses to a single column on its own once
     the task column narrows below ~280px per card (e.g. side-by-side rail
     tier), instead of a viewport-width media query fighting that. */
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 16px;
}
.reading-preview-card {
  min-width: 0;
  background: var(--surface);
  border: 0.5px solid var(--border);
  border-radius: 8px;
  padding: 16px;
  box-shadow: none;
}
.reading-preview-card ul,
.reading-preview-card ol {
  display: grid;
  gap: 12px;
  color: var(--text-secondary);
  font-size: 13px;
  line-height: 1.6;
  margin: 12px 0 0;
  padding-left: 20px;
}
.reading-preview-card li { padding-left: 4px; }
.plus-upgrade-gate {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  background: var(--surface-tint);
  border-radius: 8px;
  padding: 16px;
}
.plus-upgrade-copy {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.plus-upgrade-copy h2 {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 500;
  line-height: 1.4;
  margin: 0;
}
.plus-upgrade-copy p {
  color: var(--text-secondary);
  font-size: 13px;
  line-height: 1.6;
  margin: 0;
  max-width: 72ch;
}
.plus-upgrade-button {
  flex-shrink: 0;
  color: var(--cta-contrast);
  background: var(--cta);
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  padding: 8px 16px;
  transition: background-color 150ms ease, transform 100ms ease;
}
.plus-upgrade-button:hover { background: var(--cta-hover); }
.plus-upgrade-button:active { transform: scale(0.98); }
.plus-upgrade-button:focus-visible { outline: 2px solid var(--cta); outline-offset: 2px; }

.tool-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 16px;
  max-width: 900px;
}

.tool-card {
  position: relative;
  background: var(--surface);
  border: 1.5px solid var(--text-primary);
  border-radius: 12px;
  padding: 16px;
  cursor: pointer;
  box-shadow: none;
  transition: background-color 140ms ease, transform 140ms ease;
  text-align: left;
  font-family: inherit;
}
.tool-card:hover:not(.soon) { background: var(--bg); }
.tool-card:active:not(.soon) { transform: scale(0.99); }
.tool-card.soon { opacity: 0.6; cursor: default; box-shadow: none; }

.tool-card-head { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 16px; }

.tool-icon {
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-primary);
  flex-shrink: 0;
}

.tool-title { font-family: var(--font-display); font-weight: 500; font-size: 17px; margin-bottom: 4px; }
.tool-desc { font-size: 12.5px; color: var(--text-secondary); line-height: 1.5; }

.tool-pill {
  display: inline-block;
  font-size: 10.5px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 4px 8px;
  border-radius: 999px;
  background: var(--surface);
  border: 0.5px solid var(--border);
  color: var(--text-secondary);
  flex-shrink: 0;
}
.tool-card.soon .tool-pill { background: var(--border); color: var(--text-secondary); }

/* ---- Daily Brief ---- */
.brief-empty {
  max-width: 420px;
  margin: 48px auto 0;
  text-align: center;
}
.brief-empty-mark {
  width: 48px;
  height: 48px;
  border-radius: 12px;
  border: 1px solid var(--border);
  color: var(--text-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 16px;
}
.brief-empty-title { font-family: var(--font-display); font-weight: 500; font-size: 20px; margin: 0 0 8px; letter-spacing: -0.01em; }
.brief-empty-body { font-size: 14px; color: var(--text-secondary); line-height: 1.6; margin: 0 0 24px; }

.brief-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 16px;
  max-width: var(--width-narrow);
  box-shadow: none;
}
.day-row { display: flex; gap: 16px; padding: 12px 0; border-bottom: 0.5px solid var(--border); align-items: flex-start; }
.day-row:last-child { border-bottom: none; }
/* Day label carries more weight than the items under it — a scannable date
   column needs to read before the row's own contents, not blend into
   text-secondary the way the rest of the row's supporting text does. */
.day-label { width: 84px; flex-shrink: 0; font-size: 12px; font-weight: 600; color: var(--text-primary); padding-top: 2px; }
.day-items { flex: 1; display: flex; flex-direction: column; gap: 8px; }
.item-row { display: flex; align-items: center; gap: 8px; font-size: 14px; }
.item-class { font-size: 11.5px; color: var(--text-secondary); }
.dot { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
.dot.high { background: #C0524E; }
.dot.med { background: #C08A3E; }
.dot.low { background: var(--text-muted); }

/* ---- Settings ----
   STROKE RULE — settled and current. First applied here Aug 13 2026; this
   comment previously read "proposed, pending Kim's approval" and attributed
   the rule to CLAUDE.md — both stale as of the Aug 17 2026 PR #28 review.
   The rule now lives in docs/DESIGN-SYSTEM.md's "Surfaces, strokes, and
   elevation" section (CLAUDE.md itself only routes to that file, it no
   longer carries the reasoning inline), and as of that same review round it
   governs five more surfaces beyond Settings — .onboard-card, .onboard-mark,
   .brief-empty-mark, .brief-card, .syllabus-card — not just this one.

     A group of rows is expressed by a surface and hairline dividers.
     It is never expressed by an outer box.

   Stroke is reserved for things a person acts on directly — inputs, buttons,
   selects — plus overlays, which already get elevation. In-flow grouping
   surfaces (settings, FAQ, cards, tables) get background + dividers and no
   border. This is the Apple Settings / Stripe dashboard pattern.

   Why the old treatment was wrong on two counts: a 1.5px near-black border is
   heavier than any content inside it, so the container outranked the content;
   and box-shadow on an in-flow surface contradicts docs/DESIGN-SYSTEM.md's
   rule that only overlay surfaces get elevation.

   The one sanctioned exception: a surface that must be distinguished from an
   identical sibling directly beside it (side-by-side compare, selected state).
   Nothing in the app does that today. */
.settings-section {
  max-width: 560px;
  background: var(--surface);
  border-radius: 12px;
  overflow: hidden;
  padding: 4px 16px;
}
.settings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 0;
  border-bottom: 1px solid var(--border);
}
.settings-row:last-child { border-bottom: none; }
.settings-title { font-size: 14px; font-weight: 500; margin: 0 0 2px; }
.settings-desc { font-size: 12.5px; color: var(--text-secondary); margin: 0; line-height: 1.5; }
.settings-input {
  font: inherit;
  font-size: 13.5px;
  color: var(--text-primary);
  border: 0.5px solid var(--border);
  border-radius: 8px;
  padding: 8px 12px;
  background: var(--surface);
  flex-shrink: 0;
  width: 200px;
}
.settings-input:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.settings-toggle-btn {
  border: 0.5px solid var(--border);
  background: var(--surface);
  color: var(--text-secondary);
  font-family: var(--font-body);
  font-size: 13px;
  padding: 6px 14px;
  border-radius: 999px;
  cursor: pointer;
  flex-shrink: 0;
}
.settings-toggle-btn:hover { border-color: var(--text-muted); }
.settings-saved {
  font-size: 12.5px;
  color: var(--text-secondary);
  margin-left: 12px;
  opacity: 0;
  transition: opacity 150ms ease;
}
.settings-saved.visible { opacity: 1; }

/* ---- Help & Support ---- */
.faq-item { padding: 16px 0; border-bottom: 1px solid var(--border); }
.faq-item:last-child { border-bottom: none; }
.faq-item summary {
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  color: var(--text-primary);
  list-style: none;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::before { content: "+"; display: inline-block; width: 14px; color: var(--text-secondary); }
.faq-item[open] summary::before { content: "\2212"; }
.faq-item p { font-size: 13px; color: var(--text-secondary); line-height: 1.6; margin: 8px 0 0 22px; }

.suggest-card {
  max-width: 560px;
  border: 0.5px solid var(--border);
  border-radius: 12px;
  padding: 16px 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--surface);
}
.suggest-card p { font-size: 13px; margin: 0; color: var(--text-secondary); }
.suggest-card a { font-size: 12.5px; color: var(--accent); text-decoration: none; font-weight: 500; white-space: nowrap; }

/* ---- syllabus parser ---- */
.syllabus-page { max-width: var(--width-data); }
.syllabus-card { max-width: var(--width-task); background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 24px; margin-top: 24px; box-shadow: none; }
.syllabus-label { display: block; font-size: 13px; font-weight: 600; margin: 0 0 8px; }
.syllabus-label:not(:first-child) { margin-top: 24px; }
.syllabus-control { width: 100%; box-sizing: border-box; font: inherit; font-size: 14px; color: var(--text-primary); background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 8px 16px; }
select.syllabus-control { max-width: 320px; }
.syllabus-file { padding: 12px; }
.syllabus-text { resize: vertical; line-height: 1.55; min-height: 180px; }
.syllabus-control:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.syllabus-hint { color: var(--text-secondary); font-size: 12.5px; line-height: 1.55; margin: 8px 0 0; }
.syllabus-divider { display: flex; align-items: center; gap: 16px; color: var(--text-muted); font-size: 11px; text-transform: uppercase; letter-spacing: .07em; margin: 24px 0 0; }
.syllabus-divider::before, .syllabus-divider::after { content: ""; height: 1px; background: var(--border); flex: 1; }
.syllabus-actions, .syllabus-state-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 24px; }
.syllabus-submit { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-width: 168px; }
.syllabus-spinner { display: none; width: 16px; height: 16px; flex: 0 0 16px; }
.syllabus-spinner svg { display: block; width: 100%; height: 100%; animation: syllabus-spinner 720ms linear infinite; }
.syllabus-submit.is-loading .syllabus-spinner { display: inline-flex; }
.syllabus-submit.is-loading { cursor: wait; }
@keyframes syllabus-spinner { to { transform: rotate(360deg); } }
.syllabus-result { overflow: hidden; }
.syllabus-result[data-state="success"] { max-width: none; overflow: visible; }
.syllabus-result[data-state="streaming"] { max-width: none; overflow: visible; }
.syllabus-state-title { font-family: var(--font-display); font-size: 20px; margin: 0 0 8px; }
.syllabus-state-message { color: var(--text-secondary); font-size: 14px; line-height: 1.65; margin: 0; white-space: pre-wrap; }
.syllabus-result[data-state="term-conflict"] { border-color: #b57a2d; }
.syllabus-result[data-state="term-conflict-summary"] { border-color: #b57a2d; }
.syllabus-result[data-state="term-conflict-retry-failed"] { border-color: #b57a2d; }
.syllabus-result[data-state="unavailable"], .syllabus-result[data-state="failure"] { border-color: #a94d49; }
.conflict-summary-facts { margin: 16px 0 0; padding-left: 20px; color: var(--text-secondary); font-size: 14px; line-height: 1.65; }
.conflict-summary-facts li + li { margin-top: 6px; }
.conflict-summary-hint { margin: 12px 0 0; font-size: 13px; color: var(--text-secondary); min-height: 1em; }

.schedule-result-header,
.section-heading,
.review-row-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}
.schedule-save-status {
  color: var(--text-secondary);
  font-size: 12px;
  white-space: nowrap;
}
.schedule-result-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px; }
.schedule-result-actions .onboard-ghost { white-space: nowrap; }
.schedule-save-status[data-saved="false"] { color: var(--tag-overdue-text); }
.calendar-export {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  margin-top: 24px;
  padding: 16px;
  background: var(--tag-neutral-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.calendar-export .onboard-primary { flex: 0 0 auto; white-space: nowrap; }
.calendar-export p {
  color: var(--text-secondary);
  font-size: 12.5px;
  line-height: 1.55;
  margin: 0;
  max-width: 64ch;
}
.calendar-export .calendar-export-status { margin-top: 4px; }
.calendar-export-status[data-state="success"] { color: var(--tag-done-text); }
.calendar-export-status[data-state="error"] { color: var(--tag-overdue-text); }
.schedule-stream-header { margin-bottom: 8px; }
.schedule-stream-badge::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: currentColor;
  margin-right: 8px;
}
@keyframes schedule-row-in {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: translateY(0); }
}
.schedule-stream-row { animation: schedule-row-in 160ms ease-out both; }
.schedule-stream-row td {
  color: var(--text-secondary);
  font-size: 12px;
  line-height: 1.5;
  overflow-wrap: anywhere;
}
.flag-summary {
  margin-top: 24px;
  padding: 16px;
  background: var(--tag-neutral-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.flag-summary p { margin: 0; }
.flag-summary #flag-summary-copy {
  color: var(--tag-neutral-text);
  font-size: 14px;
  line-height: 1.6;
  margin-top: 8px;
  max-width: 72ch;
}
.review-section,
.full-schedule,
.schedule-diagnostics { margin-top: 32px; }
.section-kicker {
  color: var(--text-secondary);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  margin: 0 0 8px;
}
.section-heading h2 {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 500;
  letter-spacing: -.01em;
  line-height: 1.3;
  margin: 0;
}
.section-intro {
  color: var(--text-secondary);
  font-size: 13px;
  line-height: 1.6;
  margin: 8px 0 16px;
  max-width: 72ch;
}
.neutral-tag {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  color: var(--tag-neutral-text);
  background: var(--tag-neutral-bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  min-height: 24px;
  padding: 0 8px;
}
.review-list { display: grid; gap: 16px; }
.review-row {
  border: 1px solid var(--border);
  border-left: 4px solid var(--text-primary);
  border-radius: 8px;
  padding: 16px;
  background: var(--surface);
}
.review-row.is-reviewed { border-left-color: var(--tag-done-text); }
.review-row-kicker {
  color: var(--text-secondary);
  font-size: 11px;
  font-weight: 600;
  margin: 0 0 8px;
}
.review-row h3 {
  font-size: 15px;
  font-weight: 600;
  line-height: 1.4;
  margin: 0;
}
.review-check {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--text-secondary);
  font-size: 12px;
  white-space: nowrap;
  cursor: pointer;
}
.review-check input { accent-color: var(--tag-done-text); }
.review-fields {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
  margin-top: 16px;
}
.review-field { min-width: 0; }
.review-field-label {
  display: block;
  color: var(--text-secondary);
  font-size: 11px;
  font-weight: 600;
  margin-bottom: 8px;
}
.field-assignment { grid-column: span 2; }
.field-notes { grid-column: 1 / -1; }
.review-empty {
  padding: 24px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
}
.review-empty p { font-size: 14px; font-weight: 600; margin: 0 0 8px; }
.review-empty span { color: var(--text-secondary); font-size: 13px; }
.schedule-input {
  width: 100%;
  min-width: 0;
  color: var(--text-primary);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  font: inherit;
  font-size: 12px;
  line-height: 1.5;
  padding: 8px;
}
textarea.schedule-input { resize: vertical; }
.schedule-input:focus-visible,
.review-check input:focus-visible,
.schedule-row-actions button:focus-visible,
.schedule-table-wrap:focus-visible,
.schedule-diagnostics summary:focus-visible {
  outline: 2px solid var(--cta);
  outline-offset: 2px;
}
.date-shape {
  display: block;
  color: var(--text-muted);
  font-size: 10px;
  margin-top: 8px;
}
.schedule-table-wrap {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
}
.schedule-table {
  width: 100%;
  min-width: 78rem;
  border-collapse: collapse;
  table-layout: fixed;
}
.schedule-table th {
  color: var(--text-secondary);
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .03em;
  padding: 8px;
  text-align: left;
}
.schedule-table td {
  border-bottom: 1px solid var(--border);
  padding: 8px;
  vertical-align: top;
}
.schedule-table tr:last-child td { border-bottom: none; }
.schedule-table th:nth-child(1) { width: 5%; }
.schedule-table th:nth-child(2) { width: 12%; }
.schedule-table th:nth-child(3) { width: 20%; }
.schedule-table th:nth-child(4) { width: 11%; }
.schedule-table th:nth-child(5) { width: 12%; }
.schedule-table th:nth-child(6) { width: 8%; }
.schedule-table th:nth-child(7) { width: 22%; }
.schedule-table th:nth-child(8) { width: 10%; }
.schedule-row[data-confidence="Medium"] .schedule-cell-notes {
  background: var(--tag-neutral-bg);
}
.schedule-row-actions {
  display: flex;
  align-items: center;
  min-height: 24px;
  opacity: 0;
  transition: opacity 140ms ease;
}
.schedule-row:hover .schedule-row-actions,
.schedule-row:focus-within .schedule-row-actions,
.schedule-row-actions:focus-within,
.schedule-row:has(.schedule-row-actions button:focus-visible) .schedule-row-actions { opacity: 1; }
.schedule-row-actions button {
  color: var(--text-secondary);
  background: transparent;
  border: 0;
  border-radius: 4px;
  cursor: pointer;
  font: inherit;
  font-size: 10px;
  min-height: 24px;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.schedule-row-actions button:hover { color: var(--text-primary); }
.schedule-diagnostics details {
  border-top: 1px solid var(--border);
  padding-top: 16px;
}
.schedule-diagnostics summary {
  color: var(--text-secondary);
  cursor: pointer;
  font-size: 12px;
  font-weight: 600;
  width: fit-content;
}
.diagnostic-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px;
  padding-top: 16px;
}
.diagnostic-grid h3 { font-size: 12px; margin: 0 0 8px; }
.diagnostic-grid p {
  color: var(--text-secondary);
  font-size: 12px;
  line-height: 1.6;
  margin: 0;
  white-space: pre-wrap;
}

/* ---- onboarding ---- */
.onboard-wrap { max-width: var(--width-narrow); margin: 32px auto; }
.onboard-wrap-wide { max-width: var(--width-data); }

.onboard-progress { display: flex; gap: 8px; margin-bottom: 24px; }
.onboard-dot { height: 4px; flex: 1; border-radius: 999px; background: var(--border); }
.onboard-dot.done, .onboard-dot.current { background: var(--accent); }

/* Independent review of PR #28, Aug 17 2026: this card carried a 1.5px
   near-black border plus a shadow, contradicting docs/DESIGN-SYSTEM.md's own
   "overlay surfaces get elevation, in-flow surfaces never do" rule and the
   stroke rule ("stroke belongs to controls, not in-flow grouping surfaces")
   — .onboard-card is in-flow (onboarding steps 1-5, sharing markup with the
   beta gate and Syllabus Parser), not an overlay. Now the same subtle
   hairline + no shadow already used on .gate-card. */
.onboard-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 32px;
  text-align: center;
  box-shadow: none;
}

.onboard-mark {
  width: 48px;
  height: 48px;
  border-radius: 12px;
  border: 1px solid var(--border);
  color: var(--text-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 16px;
}

.onboard-title { font-family: var(--font-display); font-weight: 500; font-size: 22px; margin: 0 0 8px; letter-spacing: -0.01em; }
.onboard-body { font-size: 14px; color: var(--text-secondary); margin: 0 auto 24px; max-width: 46ch; line-height: 1.6; }

.onboard-tool-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-bottom: 24px; text-align: left; }
.onboard-tool { border: 1px solid var(--border); border-radius: 8px; padding: 12px; }
.onboard-tool-icon { width: 20px; height: 20px; color: var(--text-primary); margin-bottom: 8px; }
.onboard-tool.soon .onboard-tool-icon { color: var(--text-muted); }
.onboard-tool-label { font-size: 12px; font-weight: 500; }
.onboard-tool-sub { font-size: 11px; color: var(--text-secondary); margin-top: 2px; }

.onboard-field { text-align: left; margin-bottom: 24px; }
.onboard-field label { display: block; font-size: 13px; font-weight: 500; margin-bottom: 8px; }
.onboard-field select {
  width: 100%;
  font: inherit;
  font-size: 14px;
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 12px;
  background: var(--surface);
}
.onboard-field select:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }

.onboard-actions { display: flex; align-items: center; justify-content: center; gap: 8px; }
.onboard-back-row { margin-top: 16px; }
.onboard-review-result { margin-top: 0; }
.onboard-review-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: 32px;
  padding-top: 24px;
  border-top: 1px solid var(--border);
}
.onboard-skip {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 16px;
  text-align: center;
}
.onboard-skip p {
  color: var(--text-secondary);
  font-size: 12px;
  line-height: 1.5;
  margin: 0;
}

/* Primary action — PUNCHLIST.md A3, Aug 16 2026. Was --ink (near-black in
   both themes), which made the app's actual primary buttons visually
   indistinguishable from neutral chrome like the avatar fill or tooltip
   background that legitimately still use --ink. Every real primary action
   (syllabus submit, calendar download, review complete, onboarding
   next/continue, settings save, daily-brief empty-state CTA) now shares one
   semantic Plum treatment with the beta gate's button, themed per mode via
   --cta / --cta-contrast. */
button.onboard-primary {
  font: inherit;
  font-weight: 500;
  font-size: 14px;
  border: none;
  border-radius: 8px;
  padding: 8px 24px;
  cursor: pointer;
  background: var(--cta);
  color: var(--cta-contrast);
  transition: background-color 150ms ease, transform 100ms ease;
}
button.onboard-primary:hover { background: var(--cta-hover); }
button.onboard-primary:active { transform: scale(0.98); }
button.onboard-primary:focus-visible { outline: 2px solid var(--cta); outline-offset: 2px; }
button.onboard-primary:disabled { opacity: 0.7; cursor: not-allowed; }

button.onboard-ghost {
  font: inherit;
  font-weight: 500;
  font-size: 14px;
  border: none;
  background: none;
  color: var(--text-secondary);
  cursor: pointer;
  padding: 8px 12px;
  border-radius: 8px;
  transition: color 150ms ease, background-color 150ms ease;
}
button.onboard-ghost:hover { color: var(--text-primary); background: var(--bg); }
button.onboard-ghost:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Beta access gate — SCREENS.md §1.4. Own class name for this screen's
   markup, but no longer a divergent treatment: PUNCHLIST.md A3 (launch-safety
   UI foundation) landed the same subtle 1px border + no shadow on
   .onboard-card, and the same --cta fill (via the --gate-primary alias
   above) on .onboard-primary. This comment previously said .onboard-card/
   .onboard-primary "still carry" a heavy stroke, shadow, and --ink fill —
   stale as of that pass; corrected here rather than left describing CSS
   this file no longer contains. */
.gate-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 32px;
  text-align: center;
}

.onboard-field input[type="text"],
.onboard-field input[type="email"],
.onboard-field input[type="password"] {
  width: 100%;
  font: inherit;
  font-size: 14px;
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 12px;
  background: var(--surface);
}
.onboard-field input[type="text"]:focus,
.onboard-field input[type="email"]:focus,
.onboard-field input[type="password"]:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }

.gate-field-error {
  color: var(--tag-overdue-text);
  font-size: 12px;
  margin: 6px 0 0;
  text-align: left;
}
.gate-error-banner {
  color: var(--tag-overdue-text);
  background: var(--tag-overdue-bg);
  border-radius: 8px;
  padding: 10px 12px;
  font-size: 13px;
  margin: 0 0 16px;
  text-align: left;
}

button.gate-primary {
  font: inherit;
  font-weight: 500;
  font-size: 14px;
  border: none;
  border-radius: 8px;
  padding: 8px 24px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--gate-primary);
  color: var(--gate-on-primary);
  transition: background-color 150ms ease, transform 100ms ease;
}
button.gate-primary:hover { background: var(--gate-primary-hover); }
button.gate-primary:active { transform: scale(0.98); }
button.gate-primary:focus-visible { outline: 2px solid var(--gate-primary); outline-offset: 2px; }
button.gate-primary:disabled { opacity: 0.7; cursor: not-allowed; }

.gate-spinner { display: none; width: 16px; height: 16px; flex: 0 0 16px; }
.gate-spinner svg { display: block; width: 100%; height: 100%; animation: syllabus-spinner 720ms linear infinite; }
button.gate-primary.loading .gate-spinner { display: inline-flex; }

/* Tablet + laptop collapsed rail — docs/DESIGN-SYSTEM.md "Google responsive
   rigor". blueprint.json's layout.autoLayout.responsive.sidebar only declares
   three tiers (desktop fixed-width, tablet collapsible/64px, mobile drawer) —
   no separate laptop entry. This band now covers the real tablet range
   (761–1199px, previously unhandled and silently inheriting the full
   240px-labeled desktop sidebar) as well as the 1200–1280px laptop range
   already built (Aug 3 2026 laptop-breakpoint brief), applying blueprint's
   single "tablet" treatment to both, since that's what the spec defines. */
/* Preview rail collapse/side-by-side is now handled entirely by the
   container query on .workspace-grid (see "WORKSPACE" section above) —
   driven by the workspace's own measured width via ResizeObserver, not by
   any viewport media query here. */

@media (min-width: 761px) and (max-width: 1280px) {
  .shell,
  .shell.nav-collapsed { grid-template-columns: 64px minmax(0, 1fr); }
  .nav-resize-handle, .nav-collapse-btn { display: none; }

  .sidebar { padding: 24px 8px; }
  .brand-row {
    justify-content: center;
    gap: 0;
    padding: 4px 0 24px;
  }
  .profile-card {
    justify-content: center;
    gap: 0;
    width: 48px;
  }
  .profile-card > svg { display: none; }
  .nav-group-label { display: none; }
  .nav-item {
    justify-content: center;
    gap: 0;
  }

  /* Keep icon-only controls named for assistive technology. The responsive
     spec does not define a hover-tooltip treatment, so none is introduced. */
  .brand-label,
  .profile-meta,
  .nav-label,
  .nav-badge {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }

  .shell-topbar { padding: 16px 24px; }
  .shell-content { padding: 24px; }
  .schedule-table { min-width: 100%; }
}

@media (max-width: 760px) {
  .shell,
  .shell.nav-collapsed { grid-template-columns: 1fr; }
  /* Collapse is a wide-desktop-only affordance; without this it would still
     be visible and focusable (though a no-op) inside the mobile drawer. */
  .nav-collapse-btn, .nav-resize-handle { display: none; }

  /* Off-canvas drawer, not a stacked column — PUNCHLIST.md A3. The sidebar
     used to be a normal grid row above the page content, which meant every
     mobile screen (including the launch-critical syllabus upload path)
     started below a full nav list. Closed by default; .nav-open (toggled by
     #menu-toggle in app.js) slides it in as an overlay instead. */
  .menu-toggle { display: inline-flex; }
  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: 260px;
    max-width: 82vw;
    z-index: 20;
    border-right: none;
    box-shadow: var(--shadow-md);
    transform: translateX(-100%);
    transition: transform 200ms ease;
  }
  @media (prefers-reduced-motion: reduce) {
    .sidebar { transition: none; }
  }
  .shell.nav-open .sidebar { transform: translateX(0); }
  .shell.nav-open .nav-backdrop { display: block; }
  .shell.gate-active .menu-toggle { display: none; }

  /* 16px — blueprint.json layout.autoLayout.responsive.mainContent.mobile. */
  .shell-content { padding: 16px; }

  /* Touch targets — docs/DESIGN-SYSTEM.md "Google responsive rigor". Scoped to
     the mobile drawer only: desktop keeps its denser 8px nav-item padding,
     where the input is a mouse, not a finger. 44px is the WCAG 2.5.5/Material
     touch-target floor; the drawer's nav rows and the hamburger toggle were
     both under it (~32px). */
  .menu-toggle { width: 44px; height: 44px; }
  .sidebar .nav-item { min-height: 44px; padding: 12px 8px; }
  .sidebar .profile-card { min-height: 44px; }

  .onboard-tool-grid { grid-template-columns: 1fr; }
  .syllabus-card { padding: 16px; }
  .schedule-result-header,
  .section-heading,
  .review-row-header { flex-direction: column; }
  .calendar-export { flex-direction: column; }
  .review-fields { grid-template-columns: 1fr; }
  .field-assignment,
  .field-notes { grid-column: auto; }
  .diagnostic-grid { grid-template-columns: 1fr; }
  /* .reading-preview-grid intentionally has no override here — its
     auto-fit(minmax(240px,1fr)) intrinsic sizing already collapses to one
     column once the task column is this narrow. */
  .plus-upgrade-gate { align-items: flex-start; flex-direction: column; }
  .plus-upgrade-button { align-self: flex-end; }

  /* Preview rail, mobile-sheet tier. Reuses the same #content-rail node —
     only its position/transform/visibility change here, not its content.
     The 844px container minimum (see the @container workspace query above)
     can never be reached inside this viewport, so this never collides with
     the side-by-side rule. */
  .rail-toggle { position: fixed; right: 16px; bottom: 16px; z-index: 35; box-shadow: var(--shadow-md); border-radius: 999px; }
  .workspace-grid.has-rail .content-rail {
    display: block;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 45;
    max-height: 78vh;
    overflow-y: auto;
    border-radius: 16px 16px 0 0;
    box-shadow: var(--shadow-md);
    padding: 20px;
    transform: translateY(100%);
    transition: transform 200ms ease;
  }
  .workspace-grid.has-rail[data-rail-open="true"] .content-rail { transform: translateY(0); }
  .workspace-grid.has-rail[data-rail-open="false"] .content-rail { pointer-events: none; }
  .rail-close-btn { display: flex; }
  body.rail-sheet-open .rail-sheet-backdrop { display: block; }
  @media (prefers-reduced-motion: reduce) {
    .workspace-grid.has-rail .content-rail { transition: none; }
  }
}

/* Replace-course confirmation (app/lib/replace-course.mjs). Native <dialog>
   so focus trapping, Escape, and the backdrop come from the platform. */
.replace-dialog {
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--surface);
  color: var(--text-primary);
  padding: 24px;
  max-width: 460px;
  width: calc(100vw - 48px);
}
.replace-dialog::backdrop { background: rgba(0, 0, 0, 0.5); }
.replace-dialog-title { font-family: var(--font-display); font-size: 20px; margin: 0 0 8px; }
.replace-dialog-body { color: var(--text-secondary); font-size: 14px; line-height: 1.65; margin: 0 0 20px; }
.replace-dialog-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
.replace-pending-notice {
  margin: 0 0 16px;
  padding: 10px 14px;
  border-left: 3px solid var(--cta);
  background: var(--surface-tint);
  color: var(--text-secondary);
  font-size: 14px;
  line-height: 1.5;
  border-radius: 0 8px 8px 0;
}
