/* ===========================================================================
   AI Learning Companion

   Hand-written CSS, no framework: no build step means editing a file and
   clicking Restart is the whole deployment loop, which is the point of this
   stack.

   The visual language is deliberately quiet. This is an app students sit inside
   for an hour at a time — contrast is spent on content, not on chrome.
   =========================================================================== */

/* ---------------------------------------------------------------------------
   Tokens

   Components reference roles (surface, line, muted) rather than raw colours, so
   the dark palette is the only place the mapping flips.
--------------------------------------------------------------------------- */
:root {
  /* Cool neutrals with a trace of blue. Pure grey reads cheap next to white. */
  --background: #fbfcfd;
  --surface: #ffffff;
  --surface-muted: #f4f6f8;
  --surface-hover: #eceff3;
  --surface-sunken: #f7f9fa;

  --line: #e4e8ed;
  --line-strong: #cdd4dc;

  --fg: #16191f;
  --fg-muted: #596170;
  --fg-subtle: #858e9c;

  /* A deep, slightly desaturated indigo. Saturated purple looks like a
     template; this reads closer to something considered. */
  --brand: #4f46b8;
  --brand-hover: #423a9d;
  --brand-fg: #ffffff;
  --brand-muted: #eeecfa;
  --brand-line: #cfc9f0;

  --success: #12735a;
  --success-muted: #e6f4ef;
  --warning: #8f5f0f;
  --warning-muted: #fbf1e1;
  --danger: #b3352f;
  --danger-muted: #fceceb;
  --info: #1f6296;
  --info-muted: #e9f1f8;

  /* Selection gets its own tokens: the default browser highlight over a dark
     input is close to unreadable. */
  --selection-bg: #cdc7f5;
  --selection-fg: #14121f;

  --shadow-sm: 0 1px 2px rgba(16, 20, 28, 0.05);
  --shadow-card: 0 1px 2px rgba(16, 20, 28, 0.04), 0 1px 3px rgba(16, 20, 28, 0.05);
  --shadow-lifted: 0 2px 4px rgba(16, 20, 28, 0.04), 0 8px 20px rgba(16, 20, 28, 0.07);
  --shadow-overlay: 0 4px 12px rgba(16, 20, 28, 0.1), 0 16px 44px rgba(16, 20, 28, 0.14);

  --radius-sm: 6px;
  --radius: 9px;
  --radius-lg: 12px;
  --radius-xl: 16px;

  color-scheme: light;
}

html[data-theme="dark"] {
  --background: #101318;
  --surface: #171b22;
  --surface-muted: #1d222b;
  --surface-hover: #262c37;
  --surface-sunken: #13171d;

  --line: #2a303b;
  --line-strong: #3b4350;

  /* Not pure white: #fff on a dark ground haloes and tires the eye. */
  --fg: #e9edf3;
  --fg-muted: #aab3c1;
  --fg-subtle: #8892a1;

  /* Lifted for dark: the light-mode indigo is illegible on a dark surface. */
  --brand: #9b90f2;
  --brand-hover: #ada4f5;
  --brand-fg: #15121f;
  --brand-muted: #232043;
  --brand-line: #3c3663;

  --success: #4cbf95;
  --success-muted: #12281f;
  --warning: #d69a4a;
  --warning-muted: #2b2110;
  --danger: #e8807a;
  --danger-muted: #2e1817;
  --info: #6aa9dd;
  --info-muted: #12222f;

  /* White on the brand purple: unambiguous against every dark surface,
     including inside inputs and textareas. */
  --selection-bg: #5b4fc4;
  --selection-fg: #ffffff;

  /* Light-mode shadows are invisible on a dark ground, so cards lose their
     edges entirely. */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.4), 0 1px 3px rgba(0, 0, 0, 0.3);
  --shadow-lifted: 0 2px 4px rgba(0, 0, 0, 0.35), 0 8px 20px rgba(0, 0, 0, 0.4);
  --shadow-overlay: 0 4px 12px rgba(0, 0, 0, 0.5), 0 16px 44px rgba(0, 0, 0, 0.45);

  color-scheme: dark;
}

/* ---------------------------------------------------------------------------
   Base
--------------------------------------------------------------------------- */
*,
*::before,
*::after { box-sizing: border-box; }

/*
  The `hidden` attribute must win over component display rules.

  The browser default is only `[hidden] { display: none }`, which any class rule
  setting `display` outranks on specificity — so `.drawer { display: flex }`
  silently defeated `hidden` and left the mobile navigation permanently open.
  This is the one place !important is the correct tool: `hidden` is a statement
  about whether an element exists for the user, not a style preference.
*/
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--background);
  color: var(--fg);
  font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI",
    Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* A tight, restrained type scale. Oversized headings are the fastest way to
   make an application look like a landing page. */
h1, h2, h3, h4 {
  margin: 0;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.014em;
  color: var(--fg);
}
h1 { font-size: 1.375rem; }
h2 { font-size: 1.0625rem; }
h3 { font-size: 0.9375rem; }
h4 { font-size: 0.875rem; }

p { margin: 0 0 0.75rem; }
p:last-child { margin-bottom: 0; }

a { color: var(--brand); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 2px; }

/* One consistent, always-visible focus ring. The keyboard path through this app
   is a requirement, not an afterthought. */
:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* Selection must be stated for form controls separately: they do not inherit
   the document rule in every browser, which is where highlighted text was
   unreadable in dark mode. */
::selection { background: var(--selection-bg); color: var(--selection-fg); }
::-moz-selection { background: var(--selection-bg); color: var(--selection-fg); }
input::selection,
textarea::selection,
.input::selection,
.textarea::selection { background: var(--selection-bg); color: var(--selection-fg); }
input::-moz-selection,
textarea::-moz-selection { background: var(--selection-bg); color: var(--selection-fg); }

code, pre, .mono {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.9em;
}

/* Icons sit on the text baseline rather than floating above it. */
.icon { display: block; flex-shrink: 0; }

/* ---------------------------------------------------------------------------
   Utilities
--------------------------------------------------------------------------- */
.muted { color: var(--fg-muted); }
.subtle { color: var(--fg-subtle); }
.small { font-size: 0.8125rem; }
.tiny { font-size: 0.75rem; }
.strong { font-weight: 600; }
.medium { font-weight: 500; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.center { text-align: center; }
.tabular { font-variant-numeric: tabular-nums; }
.nowrap { white-space: nowrap; }

/* An uppercase micro-label. Used for course codes and column headers, where a
   small-caps treatment reads as structure rather than shouting. */
.eyebrow {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-subtle);
}

.row { display: flex; align-items: center; gap: 0.75rem; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.row-top { align-items: flex-start; }
.wrap { flex-wrap: wrap; }
.grow { flex: 1; min-width: 0; }
.shrink-0 { flex-shrink: 0; }
.gap-sm { gap: 0.5rem; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute; left: 1rem; top: -3.5rem; z-index: 200;
  padding: 0.5rem 1rem; border-radius: var(--radius);
  background: var(--brand); color: var(--brand-fg); font-weight: 500;
  transition: top 0.15s;
}
.skip-link:focus { top: 1rem; }

/* ---------------------------------------------------------------------------
   Application shell
--------------------------------------------------------------------------- */
.shell { display: flex; min-height: 100vh; min-height: 100dvh; }

.sidebar {
  display: none;
  width: 244px;
  flex-shrink: 0;
  flex-direction: column;
  border-right: 1px solid var(--line);
  background: var(--surface);
}
@media (min-width: 1024px) { .sidebar { display: flex; } }

.sidebar-brand {
  display: flex; align-items: center; gap: 0.625rem;
  height: 60px; padding: 0 1rem;
  border-bottom: 1px solid var(--line);
}
.brand-mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; flex-shrink: 0;
  border-radius: 8px;
  background: var(--brand); color: var(--brand-fg);
}
.brand-name { font-size: 0.875rem; font-weight: 600; letter-spacing: -0.01em; color: var(--fg); }

.sidebar-nav { flex: 1; overflow-y: auto; padding: 0.75rem 0.625rem; }

.nav-section + .nav-section { margin-top: 1.25rem; }
.nav-section-label {
  padding: 0 0.625rem 0.375rem;
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--fg-subtle);
}

.nav-link {
  display: flex; align-items: center; gap: 0.625rem;
  padding: 0.4375rem 0.625rem; margin-bottom: 1px;
  border-radius: var(--radius);
  color: var(--fg-muted); font-size: 0.875rem; font-weight: 500;
  transition: background 0.12s, color 0.12s;
}
.nav-link:hover { background: var(--surface-hover); color: var(--fg); text-decoration: none; }
.nav-link .icon { color: var(--fg-subtle); transition: color 0.12s; }
.nav-link:hover .icon { color: var(--fg-muted); }
.nav-link.is-active { background: var(--brand-muted); color: var(--brand); font-weight: 600; }
.nav-link.is-active .icon { color: var(--brand); }

.sidebar-footer { border-top: 1px solid var(--line); padding: 0.625rem; }

.main-column { display: flex; flex-direction: column; flex: 1; min-width: 0; }

.topbar {
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; gap: 0.5rem;
  height: 60px; padding: 0 0.875rem;
  border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--background) 86%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
}
@media (min-width: 640px) { .topbar { padding: 0 1.5rem; } }
/* The desktop sidebar already names the product; repeating it in the bar is
   noise, so the title and menu button only appear where the sidebar is not. */
@media (min-width: 1024px) { .topbar-title, .topbar-menu { display: none; } }

.content { flex: 1; min-width: 0; padding: 1.75rem 1rem 6rem; }
@media (min-width: 640px) { .content { padding: 2rem 1.5rem 6rem; } }
@media (min-width: 1024px) { .content { padding: 2rem 2rem 3rem; } }

.container { max-width: 1120px; margin: 0 auto; }
.container-narrow { max-width: 720px; margin: 0 auto; }

/* Page header — consistent rhythm at the top of every screen. */
.page-head { margin-bottom: 1.75rem; }
.page-head h1 + p { margin-top: 0.375rem; color: var(--fg-muted); font-size: 0.875rem; }
.page-head-row {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
}

.breadcrumb {
  display: flex; align-items: center; gap: 0.375rem;
  margin-bottom: 0.75rem; font-size: 0.8125rem; color: var(--fg-muted);
}
.breadcrumb a { color: var(--fg-muted); }
.breadcrumb a:hover { color: var(--fg); }
.breadcrumb .icon { color: var(--fg-subtle); }

/* ---------------------------------------------------------------------------
   Mobile navigation
--------------------------------------------------------------------------- */
.tabbar {
  position: fixed; inset-inline: 0; bottom: 0; z-index: 40;
  display: grid;
  border-top: 1px solid var(--line);
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
}
@media (min-width: 1024px) { .tabbar { display: none; } }

.tabbar a, .tabbar button {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 0.5rem 0.125rem calc(0.4375rem + env(safe-area-inset-bottom));
  border: 0; background: none; cursor: pointer;
  color: var(--fg-subtle); font-size: 0.6875rem; font-weight: 500;
  font-family: inherit; line-height: 1.2; min-height: 54px;
  overflow: hidden; text-align: center;
}
/* Very narrow phones: drop to the icon only rather than wrapping the label. */
@media (max-width: 360px) {
  .tabbar span:last-child { font-size: 0.625rem; }
}
.tabbar a.is-active { color: var(--brand); }
.tabbar a:hover { text-decoration: none; }
.tabbar span:last-child { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.drawer-backdrop {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(8, 10, 14, 0.5); backdrop-filter: blur(2px);
}
.drawer {
  position: fixed; inset-block: 0; left: 0; z-index: 51;
  display: flex; flex-direction: column;
  width: 268px; max-width: 84vw;
  background: var(--surface); box-shadow: var(--shadow-overlay);
}
@media (min-width: 1024px) { .drawer, .drawer-backdrop { display: none; } }

/* ---------------------------------------------------------------------------
   Buttons
--------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.4375rem;
  height: 36px; padding: 0 0.875rem;
  border: 1px solid transparent; border-radius: var(--radius);
  font: inherit; font-size: 0.875rem; font-weight: 500;
  cursor: pointer; white-space: nowrap;
  transition: background 0.12s, border-color 0.12s, color 0.12s, opacity 0.12s;
}
.btn:hover { text-decoration: none; }
.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; }
.btn .icon { margin-inline: -1px; }

.btn-primary { background: var(--brand); color: var(--brand-fg); box-shadow: var(--shadow-sm); }
.btn-primary:hover:not(:disabled) { background: var(--brand-hover); }

.btn-secondary { background: var(--surface); border-color: var(--line-strong); color: var(--fg); }
.btn-secondary:hover:not(:disabled) { background: var(--surface-hover); }

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

.btn-danger { background: var(--danger); color: #fff; }
.btn-danger:hover:not(:disabled) { filter: brightness(0.94); }

.btn-sm { height: 30px; padding: 0 0.6875rem; font-size: 0.8125rem; }
.btn-lg { height: 42px; padding: 0 1.25rem; font-size: 0.9375rem; }
.btn-block { width: 100%; }

.icon-button {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0; flex-shrink: 0;
  border: 0; border-radius: var(--radius); background: none; cursor: pointer;
  color: var(--fg-muted); font-family: inherit;
  transition: background 0.12s, color 0.12s;
}
.icon-button:hover { background: var(--surface-hover); color: var(--fg); }

/* ---------------------------------------------------------------------------
   Theme toggle

   Shows the theme you would switch TO, not the one you are in — a sun while
   dark means "go light", which is what people reach for.
--------------------------------------------------------------------------- */
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0; flex-shrink: 0;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); color: var(--fg-muted);
  cursor: pointer; font-family: inherit;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.theme-toggle:hover { background: var(--surface-hover); color: var(--fg); border-color: var(--line-strong); }
.theme-toggle-floating { position: fixed; top: 1rem; right: 1rem; z-index: 60; }

.theme-toggle .icon-when-dark { display: none; }
html[data-theme="dark"] .theme-toggle .icon-when-dark { display: block; }
html[data-theme="dark"] .theme-toggle .icon-when-light { display: none; }

/* ---------------------------------------------------------------------------
   Cards
--------------------------------------------------------------------------- */
.card {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.card-pad { padding: 1.125rem; }
@media (min-width: 640px) { .card-pad { padding: 1.375rem; } }

.card-interactive { transition: border-color 0.15s, box-shadow 0.15s; }
.card-interactive:hover { border-color: var(--line-strong); box-shadow: var(--shadow-lifted); }

.card-header {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem;
  padding: 1rem 1.125rem; border-bottom: 1px solid var(--line);
}
.card-body { padding: 1.125rem; }
.card-footer {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.75rem 1.125rem; border-top: 1px solid var(--line);
}
.card-accent { border-color: var(--brand-line); background: var(--brand-muted); }

.section { margin-bottom: 2rem; }
.section-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 1rem; margin-bottom: 0.875rem; flex-wrap: wrap;
}
.section-title { font-size: 0.875rem; font-weight: 600; letter-spacing: -0.006em; }
.section-head p { margin: 0.125rem 0 0; font-size: 0.8125rem; color: var(--fg-muted); }

.grid { display: grid; gap: 0.875rem; }
.grid-cards { grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); }
.grid-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 900px) { .grid-stats { grid-template-columns: repeat(5, minmax(0, 1fr)); } }
.grid-2 { grid-template-columns: 1fr; gap: 1.25rem; }
@media (min-width: 900px) { .grid-2 { grid-template-columns: 1fr 1fr; } }

/* ---------------------------------------------------------------------------
   Course cards
--------------------------------------------------------------------------- */
.course-card { position: relative; display: flex; flex-direction: column; overflow: hidden; min-height: 168px; }
.course-card .accent-bar { position: absolute; inset-inline: 0; top: 0; height: 3px; background: var(--brand); }
.course-card-body { display: flex; flex-direction: column; flex: 1; padding: 1.125rem; }
.course-card h3 { margin: 0.3125rem 0 0; font-size: 0.9375rem; line-height: 1.35; }
.course-card h3 a { color: var(--fg); }
.course-card h3 a::after { content: ""; position: absolute; inset: 0; }
.course-card h3 a:hover { text-decoration: none; }
.course-card:hover h3 a { color: var(--brand); }

.course-meta {
  display: flex; flex-wrap: wrap; gap: 0.875rem;
  margin: 0.75rem 0 0; font-size: 0.8125rem; color: var(--fg-muted);
}
.course-meta span { display: inline-flex; align-items: center; gap: 0.3125rem; }

.course-card-foot {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.6875rem 1.125rem; border-top: 1px solid var(--line);
  background: var(--surface-sunken);
}

.week-card { display: flex; gap: 0.75rem; align-items: flex-start; padding: 0.875rem 1rem; }
.week-number {
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; flex-shrink: 0;
  border-radius: 8px; background: var(--brand-muted); color: var(--brand);
  font-size: 0.8125rem; font-weight: 600;
}
.week-number.is-done { background: var(--success); color: #fff; }

/* ---------------------------------------------------------------------------
   Stat tiles
--------------------------------------------------------------------------- */
.stat { padding: 1rem 1.125rem; }
.stat-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; margin-bottom: 0.75rem;
  border-radius: 8px; background: var(--brand-muted); color: var(--brand);
}
.stat-value { font-size: 1.5rem; font-weight: 600; letter-spacing: -0.02em; line-height: 1.1; }
.stat-label { margin-top: 0.1875rem; font-size: 0.8125rem; color: var(--fg-muted); }
.stat-hint { margin-top: 0.25rem; font-size: 0.6875rem; color: var(--fg-subtle); }

/* ---------------------------------------------------------------------------
   Forms
--------------------------------------------------------------------------- */
.field { margin-bottom: 1rem; }
.field:last-child { margin-bottom: 0; }
.label { display: block; margin-bottom: 0.3125rem; font-size: 0.8125rem; font-weight: 500; color: var(--fg); }
.required-mark { color: var(--danger); margin-left: 1px; }
.hint { margin: 0.3125rem 0 0; font-size: 0.75rem; color: var(--fg-muted); line-height: 1.45; }
.field-error { margin: 0.3125rem 0 0; font-size: 0.75rem; color: var(--danger); }

.input, .textarea, .select {
  width: 100%; padding: 0.5rem 0.6875rem;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--surface); color: var(--fg);
  font: inherit; font-size: 0.875rem;
  transition: border-color 0.12s, box-shadow 0.12s;
}
.input::placeholder, .textarea::placeholder { color: var(--fg-subtle); }
.input:hover, .textarea:hover, .select:hover { border-color: var(--fg-subtle); }
.input:focus, .textarea:focus, .select:focus {
  outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 18%, transparent);
}
.input[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--danger); }
.textarea { min-height: 84px; resize: vertical; line-height: 1.55; }
.select { cursor: pointer; padding-right: 2rem; }
.input[type="file"] { padding: 0.4375rem 0.6875rem; cursor: pointer; }

.input-group { display: flex; gap: 0.5rem; }
.input-group .input { flex: 1; min-width: 0; }

.password-wrap { position: relative; }
.password-wrap .input { padding-right: 2.5rem; }
.password-toggle {
  position: absolute; right: 0; top: 0; bottom: 0; width: 2.5rem;
  display: flex; align-items: center; justify-content: center;
  border: 0; background: none; cursor: pointer; color: var(--fg-subtle); font-family: inherit;
}
.password-toggle:hover { color: var(--fg); }

.search-field { position: relative; }
.search-field > .icon {
  position: absolute; left: 0.625rem; top: 50%; transform: translateY(-50%);
  color: var(--fg-subtle); pointer-events: none;
}
.search-field .input { padding-left: 2.125rem; }

/* Real checkbox behind a styled track: keeps native keyboard support and
   correct announcement, which a div with role="switch" only imitates. */
.switch { position: relative; display: inline-flex; align-items: center; cursor: pointer; flex-shrink: 0; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch .track { width: 40px; height: 22px; border-radius: 999px; background: var(--line-strong); transition: background 0.15s; }
.switch .thumb {
  position: absolute; left: 3px; width: 16px; height: 16px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25); transition: transform 0.15s;
}
.switch input:checked ~ .track { background: var(--brand); }
.switch input:checked ~ .thumb { transform: translateX(18px); }
.switch input:focus-visible ~ .track { outline: 2px solid var(--brand); outline-offset: 2px; }

.setting-row {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1.5rem; padding: 1rem 0; border-bottom: 1px solid var(--line);
}
.setting-row:first-of-type { padding-top: 0; }
.setting-row p { margin: 0.1875rem 0 0; font-size: 0.8125rem; color: var(--fg-muted); }

/* ---------------------------------------------------------------------------
   Badges, status, alerts
--------------------------------------------------------------------------- */
.badge {
  display: inline-flex; align-items: center; gap: 0.25rem;
  padding: 0.125rem 0.4375rem; border-radius: var(--radius-sm);
  background: var(--surface-hover); color: var(--fg-muted);
  font-size: 0.6875rem; font-weight: 500; white-space: nowrap;
}
.badge-brand { background: var(--brand-muted); color: var(--brand); }
.badge-success { background: var(--success-muted); color: var(--success); }
.badge-warning { background: var(--warning-muted); color: var(--warning); }
.badge-danger { background: var(--danger-muted); color: var(--danger); }
.badge-info { background: var(--info-muted); color: var(--info); }

/* Status dot plus text: colour alone would fail colour-blind users. */
.status { display: inline-flex; align-items: center; gap: 0.4375rem; font-size: 0.8125rem; font-weight: 500; }
.status::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
.status-active { color: var(--success); }
.status-disabled { color: var(--fg-subtle); }

.alert {
  display: flex; gap: 0.625rem; align-items: flex-start;
  padding: 0.75rem 0.875rem; margin-bottom: 1rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  font-size: 0.8125rem; line-height: 1.5;
}
.alert .icon { margin-top: 1px; }
.alert-error { background: var(--danger-muted); border-color: color-mix(in srgb, var(--danger) 22%, transparent); color: var(--danger); }
.alert-success { background: var(--success-muted); border-color: color-mix(in srgb, var(--success) 22%, transparent); color: var(--success); }
.alert-info { background: var(--info-muted); border-color: color-mix(in srgb, var(--info) 22%, transparent); color: var(--info); }
.alert-warning { background: var(--warning-muted); border-color: color-mix(in srgb, var(--warning) 26%, transparent); color: var(--warning); }

/* ---------------------------------------------------------------------------
   Progress

   Framed as encouragement, never as a score: no "behind" state, no comparison
   to anyone else.
--------------------------------------------------------------------------- */
.progress-track { height: 6px; border-radius: 999px; background: var(--surface-hover); overflow: hidden; }
.progress-fill { height: 100%; border-radius: 999px; background: var(--brand); transition: width 0.5s ease-out; }
.progress-fill.is-complete { background: var(--success); }

.segments { display: flex; gap: 3px; }
.segments span { flex: 1; height: 4px; border-radius: 999px; background: var(--surface-hover); }
.segments span.on { background: var(--brand); }

/* ---------------------------------------------------------------------------
   Tables and lists
--------------------------------------------------------------------------- */
.table-wrap { overflow-x: auto; }
.table { width: 100%; border-collapse: collapse; font-size: 0.875rem; }
.table th {
  padding: 0.625rem 1.125rem; text-align: left;
  background: var(--surface-muted); border-bottom: 1px solid var(--line);
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--fg-subtle); white-space: nowrap;
}
.table td { padding: 0.75rem 1.125rem; border-bottom: 1px solid var(--line); vertical-align: middle; }
.table tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: var(--surface-sunken); }

.list { list-style: none; margin: 0; padding: 0; }
.list > li { border-bottom: 1px solid var(--line); }
.list > li:last-child { border-bottom: 0; }
.list-row { display: flex; align-items: center; gap: 0.875rem; padding: 0.8125rem 1.125rem; }
a.list-row { color: inherit; }
a.list-row:hover { background: var(--surface-sunken); text-decoration: none; }

.desktop-only { display: none; }
@media (min-width: 768px) {
  .desktop-only { display: block; }
  .mobile-only { display: none; }
}

/* ---------------------------------------------------------------------------
   Empty states

   An empty state should invite rather than report: "No courses yet" alone tells
   a student they have failed at something; pairing it with the next action
   tells them what to do.
--------------------------------------------------------------------------- */
.empty {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  padding: 2.75rem 1.5rem;
  border: 1px dashed var(--line-strong); border-radius: var(--radius-lg);
  background: var(--surface-sunken);
}
.empty-icon {
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; margin-bottom: 0.875rem;
  border-radius: var(--radius); background: var(--surface);
  border: 1px solid var(--line); color: var(--fg-subtle);
}
.empty h3 { margin-bottom: 0.3125rem; }
.empty p { max-width: 25rem; font-size: 0.8125rem; color: var(--fg-muted); line-height: 1.55; }
.empty .btn { margin-top: 1rem; }

/* ---------------------------------------------------------------------------
   Menus
--------------------------------------------------------------------------- */
.menu-wrap { position: relative; }
/*
  Fixed, not absolute.

  An absolutely positioned menu is clipped by any ancestor with `overflow`, and
  the row actions live inside a scrolling table inside a card that hides its
  own overflow — so the menu was cut off at the card edge. Fixed positioning
  takes it out of every clipping context; app.js places it against its trigger
  and flips it above when there is no room below.
*/
.menu {
  position: fixed; z-index: 60; min-width: 200px; padding: 4px;
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--surface); box-shadow: var(--shadow-overlay);
  max-height: min(20rem, 70dvh); overflow-y: auto;
}
.menu form { margin: 0; }
.menu-item {
  display: flex; align-items: center; gap: 0.5625rem; width: 100%;
  padding: 0.4375rem 0.625rem; border: 0; border-radius: var(--radius-sm);
  background: none; color: var(--fg); font: inherit; font-size: 0.8125rem;
  text-align: left; cursor: pointer;
}
.menu-item:hover { background: var(--surface-hover); text-decoration: none; }
.menu-item .icon { color: var(--fg-subtle); }
.menu-item.danger { color: var(--danger); }
.menu-item.danger:hover { background: var(--danger-muted); }
.menu-item.danger .icon { color: var(--danger); }
.menu-sep { height: 1px; margin: 4px 0; background: var(--line); }
.menu-head { padding: 0.5rem 0.625rem; border-bottom: 1px solid var(--line); margin-bottom: 4px; }

.avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 50%; flex-shrink: 0;
  background: var(--brand-muted); color: var(--brand);
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.02em;
}

.profile-button {
  display: flex; align-items: center; gap: 0.5625rem; width: 100%;
  padding: 0.4375rem; border: 0; border-radius: var(--radius);
  background: none; color: inherit; font: inherit; text-align: left; cursor: pointer;
  transition: background 0.12s;
}
.profile-button:hover { background: var(--surface-hover); }
.profile-button .name { display: block; font-size: 0.8125rem; font-weight: 500; }
.profile-button .email { display: block; font-size: 0.6875rem; color: var(--fg-subtle); }

/* ---------------------------------------------------------------------------
   Modals — native <dialog> for focus trapping and Escape handling
--------------------------------------------------------------------------- */
dialog.modal {
  width: calc(100% - 2rem); max-width: 480px; padding: 0;
  border: 1px solid var(--line); border-radius: var(--radius-xl);
  background: var(--surface); color: var(--fg);
  box-shadow: var(--shadow-overlay);
}
dialog.modal::backdrop { background: rgba(8, 10, 14, 0.45); backdrop-filter: blur(2px); }
dialog.modal form { margin: 0; }
dialog.modal .modal-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem;
  padding: 1.125rem 1.25rem 0.875rem;
}
dialog.modal .modal-head p { margin: 0.25rem 0 0; font-size: 0.8125rem; color: var(--fg-muted); }
dialog.modal .modal-body { padding: 0 1.25rem 1.25rem; max-height: 60vh; overflow-y: auto; }
dialog.modal .modal-foot {
  display: flex; justify-content: flex-end; gap: 0.5rem;
  padding: 0.875rem 1.25rem; border-top: 1px solid var(--line);
  background: var(--surface-sunken);
  border-radius: 0 0 var(--radius-xl) var(--radius-xl);
}
@media (max-width: 640px) {
  dialog.modal {
    width: 100%; max-width: 100%; height: 100%; max-height: 100%;
    border-radius: 0; border: 0;
    display: flex; flex-direction: column;
  }
  dialog.modal[open] { display: flex; }
  dialog.modal .modal-body { max-height: none; flex: 1; }
  dialog.modal .modal-foot { flex-direction: column-reverse; border-radius: 0; }
  dialog.modal .modal-foot .btn { width: 100%; }
}

/* ---------------------------------------------------------------------------
   Toasts
--------------------------------------------------------------------------- */
.toasts {
  position: fixed; z-index: 100; display: flex; flex-direction: column; gap: 0.5rem;
  inset-inline: 1rem; bottom: 5rem; align-items: center; pointer-events: none;
}
@media (min-width: 640px) {
  .toasts { inset-inline: auto; right: 1.25rem; top: 1.25rem; bottom: auto; align-items: flex-end; }
}
.toast {
  display: flex; align-items: flex-start; gap: 0.5625rem; pointer-events: auto;
  width: 100%; max-width: 23rem; padding: 0.6875rem 0.875rem;
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--surface); box-shadow: var(--shadow-overlay);
  font-size: 0.8125rem; line-height: 1.5;
}
.toast .icon { margin-top: 1px; }
.toast-success .icon { color: var(--success); }
.toast-error .icon { color: var(--danger); }
.toast-info .icon { color: var(--info); }
.toast-warning .icon { color: var(--warning); }

/* ---------------------------------------------------------------------------
   Login
--------------------------------------------------------------------------- */
.login-layout { display: flex; flex-direction: column; min-height: 100vh; min-height: 100dvh; }
@media (min-width: 1024px) { .login-layout { flex-direction: row; } }

.login-form-side { display: flex; align-items: center; justify-content: center; flex: 1; padding: 3rem 1.25rem; }
@media (min-width: 1024px) { .login-form-side { order: 1; } }
.login-form-inner { width: 100%; max-width: 21rem; }

.login-aside { display: none; }
@media (min-width: 1024px) {
  .login-aside {
    display: flex; flex-direction: column; justify-content: center;
    width: 46%; padding: 4rem 3.5rem;
    background: var(--surface-muted); border-right: 1px solid var(--line);
  }
}
.login-aside h2 { font-size: 1.625rem; line-height: 1.25; letter-spacing: -0.02em; }

.login-points { list-style: none; margin: 2.5rem 0 0; padding: 0; }
.login-points li { display: flex; gap: 0.875rem; margin-bottom: 1.5rem; }
.login-points li:last-child { margin-bottom: 0; }
.login-points .point-icon {
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex-shrink: 0;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--surface); color: var(--brand);
}
.login-points h3 { font-size: 0.8125rem; }
.login-points p { margin: 0.1875rem 0 0; font-size: 0.8125rem; color: var(--fg-muted); line-height: 1.5; }

.login-note {
  margin-top: 1.75rem; padding-top: 1.25rem; border-top: 1px solid var(--line);
  font-size: 0.75rem; line-height: 1.55; color: var(--fg-muted);
}

/* ---------------------------------------------------------------------------
   Tutor

   The chat is the page, so it claims the viewport rather than sitting in a
   column under a heading. Everything else here exists to give the transcript
   more room: a one-line header, a single scrolling row of shortcuts, and a
   conversation list that is a panel on desktop and a sheet on mobile.
--------------------------------------------------------------------------- */
.tutor-page { display: flex; gap: 0.875rem; }

/* Viewport minus: top bar (60px), page padding, and the mobile tab bar. */
.tutor-page { height: calc(100dvh - 60px - 3.5rem - 56px); min-height: 22rem; }
@media (min-width: 1024px) { .tutor-page { height: calc(100dvh - 60px - 5rem); } }

/* Past conversations. A panel beside the chat where there is room, a slide-over
   sheet where there is not. */
.convo-panel {
  display: none; flex-direction: column; width: 232px; flex-shrink: 0;
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--surface); overflow: hidden;
}
@media (min-width: 1024px) { .convo-panel { display: flex; } }

.convo-panel-head {
  display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
  padding: 0.625rem 0.75rem; border-bottom: 1px solid var(--line);
}
.convo-list { flex: 1; min-height: 0; overflow-y: auto; padding: 0.375rem; }

.convo-item {
  display: flex; align-items: center; gap: 0.375rem;
  padding: 0.4375rem 0.5rem; margin-bottom: 1px; border-radius: var(--radius-sm);
  color: var(--fg-muted); font-size: 0.8125rem; cursor: pointer;
  border: 0; background: none; width: 100%; text-align: left; font-family: inherit;
}
.convo-item:hover { background: var(--surface-hover); color: var(--fg); }
.convo-item.is-active { background: var(--brand-muted); color: var(--brand); font-weight: 500; }
.convo-item .convo-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.convo-item .convo-actions { display: none; flex-shrink: 0; }
.convo-item:hover .convo-actions, .convo-item.is-active .convo-actions { display: flex; }
.convo-actions button {
  display: flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; padding: 0; border: 0; border-radius: 5px;
  background: none; color: var(--fg-subtle); cursor: pointer;
}
.convo-actions button:hover { background: var(--surface); color: var(--danger); }

/* Mobile: the same list as a sheet from the bottom. */
.convo-sheet {
  position: fixed; inset-inline: 0; bottom: 0; z-index: 55;
  max-height: 70dvh; display: flex; flex-direction: column;
  border-top: 1px solid var(--line);
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  background: var(--surface); box-shadow: var(--shadow-overlay);
}
@media (min-width: 1024px) { .convo-sheet, .convo-sheet-backdrop { display: none !important; } }
.convo-sheet-backdrop { position: fixed; inset: 0; z-index: 54; background: rgba(8, 10, 14, 0.45); }

.chat-shell {
  display: flex; flex-direction: column; flex: 1; min-width: 0;
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--surface); overflow: hidden;
}

.chat-toolbar {
  display: flex; gap: 0.375rem; align-items: center;
  padding: 0.5rem 0.625rem; border-bottom: 1px solid var(--line);
  overflow-x: auto; scrollbar-width: none;
}
.chat-toolbar::-webkit-scrollbar { display: none; }
.chat-toolbar .select {
  width: auto; min-width: 0; max-width: 12rem; height: 30px;
  padding-block: 0; font-size: 0.75rem; flex-shrink: 0;
}

.chat-messages { flex: 1; min-height: 0; overflow-y: auto; padding: 0.875rem; }
@media (min-width: 640px) { .chat-messages { padding: 1.125rem; } }

.chat-composer { border-top: 1px solid var(--line); padding: 0.5rem 0.625rem; background: var(--surface-sunken); }

/* One scrolling row rather than a wrapping block: nine shortcuts on two lines
   were costing the transcript about 70px of height. */
.quick-actions {
  display: flex; gap: 0.3125rem; margin-bottom: 0.5rem;
  overflow-x: auto; padding-bottom: 2px;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
.quick-actions::-webkit-scrollbar { display: none; }
.quick-actions .btn { height: 26px; padding: 0 0.5625rem; font-size: 0.75rem; flex-shrink: 0; }

.chat-form { display: flex; gap: 0.5rem; align-items: flex-end; }
.chat-form .textarea {
  min-height: 38px; max-height: 8rem; padding: 0.4375rem 0.625rem;
  font-size: 0.875rem; line-height: 1.45; resize: none;
}

.bubble { margin-bottom: 0.875rem; }
.bubble:last-child { margin-bottom: 0; }
.bubble-user {
  margin-left: auto; max-width: 85%; width: fit-content;
  padding: 0.5rem 0.75rem; border-radius: var(--radius-lg) var(--radius-lg) 4px var(--radius-lg);
  background: var(--brand); color: var(--brand-fg); font-size: 0.875rem;
}
.bubble-user p { margin: 0; }
.bubble-assistant { max-width: 100%; }

/* Tutor responses render as sections rather than one block of prose, so a long
   explanation can be navigated instead of read start to finish. */
.ai-section {
  padding: 0.75rem 0.875rem; margin-bottom: 0.5rem;
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--surface); font-size: 0.875rem; line-height: 1.6;
}
.ai-section:last-child { margin-bottom: 0; }
.ai-section h3 {
  margin: 0 0 0.4375rem; font-size: 0.6875rem; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--fg-subtle);
}
.ai-section p { margin: 0 0 0.4375rem; }
.ai-section p:last-child { margin-bottom: 0; }
.ai-section ul { margin: 0 0 0.4375rem; padding-left: 1.125rem; }
.ai-section li { margin: 0.125rem 0; }
.ai-section code { padding: 0.0625rem 0.3125rem; border-radius: 4px; background: var(--surface-hover); }

.ai-big-idea { border-color: var(--brand-line); background: var(--brand-muted); }
.ai-big-idea h3 { color: var(--brand); }
.ai-analogy { border-color: color-mix(in srgb, var(--warning) 25%, transparent); background: var(--warning-muted); }
.ai-analogy h3 { color: var(--warning); }
.ai-mistakes { border-color: color-mix(in srgb, var(--danger) 22%, transparent); background: var(--danger-muted); }
.ai-mistakes h3 { color: var(--danger); }
.ai-your-turn { border-color: color-mix(in srgb, var(--info) 25%, transparent); background: var(--info-muted); }
.ai-your-turn h3 { color: var(--info); }
.ai-summary { border-color: color-mix(in srgb, var(--success) 25%, transparent); background: var(--success-muted); }
.ai-summary h3 { color: var(--success); }
.ai-hint { background: var(--surface-muted); }

.ai-error { color: var(--danger); font-size: 0.875rem; }

.code-block {
  margin: 0.5rem 0; padding: 0.625rem 0.75rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface-sunken); overflow-x: auto;
  font-size: 0.8125rem; line-height: 1.5;
}
html[data-theme="dark"] .code-block { background: #0d1015; }

/* A page whose content fills the viewport rather than flowing under a heading. */
.page-compact { margin-bottom: 0.875rem; }
.page-compact h1 { font-size: 1.125rem; }

/* ---------------------------------------------------------------------------
   Motion: respect the OS setting. Long study sessions plus vestibular triggers
   is a bad combination.
--------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Set while the mobile drawer is open. Locking the body rather than the drawer
   keeps the page behind from scrolling under the overlay on iOS. */
body.is-locked { overflow: hidden; }

/* ---------------------------------------------------------------------------
   Impersonation banner

   Amber rather than the brand colour: this is a state the administrator needs
   to notice, not a feature to admire. It sits above the shell and pushes the
   whole layout down, so it cannot be scrolled away and forgotten.
--------------------------------------------------------------------------- */
.impersonation-bar {
  position: sticky; top: 0; z-index: 45;
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.75rem; flex-wrap: wrap;
  padding: 0.5rem 0.875rem;
  background: var(--warning-muted); color: var(--warning);
  border-bottom: 1px solid color-mix(in srgb, var(--warning) 30%, transparent);
  font-size: 0.8125rem;
}
@media (min-width: 640px) { .impersonation-bar { padding: 0.5rem 1.5rem; } }
.impersonation-bar form { margin: 0; }

.impersonation-exit {
  background: var(--warning); color: #fff; border-color: transparent;
}
.impersonation-exit:hover { filter: brightness(0.94); }

/* The email is useful confirmation on a wide screen and noise on a phone. */
.impersonation-detail { display: none; }
@media (min-width: 640px) { .impersonation-detail { display: inline; } }

/* ---------------------------------------------------------------------------
   Quizzes
--------------------------------------------------------------------------- */
.quiz-option {
  display: flex; align-items: flex-start; gap: 0.5rem;
  padding: 0.5rem 0.625rem; margin-bottom: 0.25rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  font-size: 0.875rem; cursor: pointer;
  transition: border-color 0.12s, background 0.12s;
}
.quiz-option:hover { border-color: var(--line-strong); background: var(--surface-sunken); }
.quiz-option input { margin-top: 0.2rem; flex-shrink: 0; accent-color: var(--brand); }
.quiz-option:has(input:checked) { border-color: var(--brand); background: var(--brand-muted); }

/* A mark plus a colour, never colour alone. */
.result-mark {
  display: flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; flex-shrink: 0; border-radius: 50%;
  background: var(--danger); color: #fff;
}
.result-mark.is-correct { background: var(--success); }

.quiz-result.is-correct { border-left: 3px solid var(--success); }
.quiz-result.is-wrong { border-left: 3px solid var(--danger); }

/* ---------------------------------------------------------------------------
   Flashcards
--------------------------------------------------------------------------- */
.flashcard-stage { max-width: 32rem; margin: 0 auto; }

.flashcard {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1rem; width: 100%; min-height: 15rem; padding: 2rem 1.5rem;
  border: 1px solid var(--line); border-radius: var(--radius-xl);
  background: var(--surface); box-shadow: var(--shadow-card);
  color: var(--fg); font: inherit; text-align: center; cursor: pointer;
  transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
}
.flashcard:hover { border-color: var(--line-strong); box-shadow: var(--shadow-lifted); }
.flashcard.is-flipped { background: var(--brand-muted); border-color: var(--brand-line); }

.flashcard-face { font-size: 1.0625rem; line-height: 1.5; white-space: pre-wrap; }
.flashcard.is-flipped .flashcard-face { font-size: 0.9375rem; }
.flashcard-hint { font-size: 0.75rem; color: var(--fg-subtle); }

/* Reuses the chevron for "previous" rather than carrying a second icon. */
.flip-x { transform: scaleX(-1); }

/* ---------------------------------------------------------------------------
   Saving an explanation from the chat
--------------------------------------------------------------------------- */
.bubble-tools {
  display: flex; gap: 0.25rem; margin-top: 0.375rem;
  opacity: 0; transition: opacity 0.12s;
}
.bubble-assistant:hover .bubble-tools,
.bubble-tools:focus-within { opacity: 1; }
/* Touch has no hover, so the tools stay visible there. */
@media (hover: none) { .bubble-tools { opacity: 1; } }

.bubble-tools button {
  display: inline-flex; align-items: center; gap: 0.25rem;
  padding: 0.1875rem 0.4375rem; border: 1px solid var(--line);
  border-radius: var(--radius-sm); background: var(--surface);
  color: var(--fg-muted); font: inherit; font-size: 0.6875rem; cursor: pointer;
}
.bubble-tools button:hover { background: var(--surface-hover); color: var(--fg); }
.bubble-tools button.is-done { color: var(--success); border-color: var(--success); }
