/* StudyBuddy — layout & components. Consumes css/tokens.css. */

* { box-sizing: border-box; margin: 0; padding: 0; }
[hidden] { display: none !important; }

html, body { height: 100%; }

body {
  font-family: var(--font-body);
  font-size: var(--fs-md);
  line-height: var(--lh-body);
  color: var(--ink);
  background: var(--canvas);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, .display { font-family: var(--font-display); line-height: var(--lh-tight); font-weight: 700; }
h1 { font-size: var(--fs-xxl); }
h2 { font-size: var(--fs-xl); }
h3 { font-size: var(--fs-lg); }

a { color: var(--brand-ink); }

.tabular { font-variant-numeric: tabular-nums; }

:focus-visible {
  outline: none;
  box-shadow: var(--ring);
  border-radius: var(--r-sm);
}

/* Elements with tabindex="-1" are only ever focused programmatically — we move
   focus to a view's heading on navigation so keyboard and screen-reader users
   start at the new content. A focus ring there is noise: the user didn't press
   anything, and they can't Tab to it either. Genuine focus rings are untouched. */
[tabindex="-1"]:focus,
[tabindex="-1"]:focus-visible {
  outline: none;
  box-shadow: none;
}

/* Visible only to assistive tech. */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

.skiplink {
  position: absolute; left: 50%; top: 0; transform: translate(-50%, -120%);
  z-index: 100; background: var(--brand); color: #fff;
  padding: var(--s-2) var(--s-4); border-radius: 0 0 var(--r-md) var(--r-md);
  font-weight: 700; text-decoration: none;
  transition: transform var(--dur-fast) var(--ease-out);
}
.skiplink:focus { transform: translate(-50%, 0); }

/* Dark ink on the bright orange, not white — white on this amber is only
   2.7:1. Keeps the colour, passes AA. */
.streakbadge {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px var(--s-3); border-radius: var(--r-full);
  background: var(--retry); color: #2B2B3A;
  font-size: var(--fs-sm); font-weight: 700; text-decoration: none;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--retry) 60%, transparent),
              0 2px 16px -3px color-mix(in srgb, var(--retry) 85%, transparent);
  transition: box-shadow var(--dur-fast), transform var(--dur-fast) var(--ease-spring);
}
a.streakbadge:hover {
  transform: translateY(-1px);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--retry) 75%, transparent),
              0 3px 22px -3px color-mix(in srgb, var(--retry) 95%, transparent);
}
:root[data-theme="dark"] .streakbadge { color: #21170C; }
/* At risk: a freeze is standing by. Cool blue instead of the warm streak amber. */
.streakbadge--risk {
  background: var(--info); color: #fff;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--info) 60%, transparent),
              0 2px 16px -3px color-mix(in srgb, var(--info) 80%, transparent);
}
:root[data-theme="dark"] .streakbadge--risk { color: #08222E; }

.resume h3 { margin-bottom: 0; }

/* ---------- app shell ---------- */
#app { min-height: 100%; display: flex; flex-direction: column; }
.shell { flex: 1; display: flex; align-items: stretch; }
.shell__main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

/* Left sidebar — the whole nav on desktop. Hidden below 900px, where the
   topbar (and its ⋮ menu) covers the same ground. */
.sidebar { display: none; }
.sidebar__brand {
  display: flex; align-items: center; gap: var(--s-2);
  font-family: var(--font-display); font-weight: 700; font-size: var(--fs-lg);
  color: var(--ink); text-decoration: none;
  padding: 0 var(--s-2); margin-bottom: var(--s-5);
}
.sidebar__brand img { width: 28px; height: 28px; }
.sidebar__nav { display: flex; flex-direction: column; gap: 2px; flex: 1; }
.sidebar__link {
  display: flex; align-items: center; gap: var(--s-3);
  padding: 9px var(--s-3); border-radius: var(--r-md);
  color: var(--ink-soft); text-decoration: none; font-weight: 600; font-size: var(--fs-sm);
  transition: background var(--dur-fast), color var(--dur-fast);
}
.sidebar__link svg { flex: none; }
.sidebar__link:hover { background: var(--surface-sunk); color: var(--ink); }
.sidebar__link.is-active { background: var(--brand-tint); color: var(--brand-ink); }
.sidebar__link.is-active svg { color: var(--brand-ink); }
.sidebar__group {
  margin: var(--s-4) var(--s-3) 4px; font-size: 10px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em; color: var(--ink-faint);
}
.sidebar__nav > .sidebar__group:first-child { margin-top: 0; }
.sidebar__div { height: 1px; margin: var(--s-3) var(--s-3) var(--s-2); background: var(--line); }
.sidebar__foot { display: flex; flex-direction: column; gap: var(--s-2); margin-top: var(--s-4); }

/* Fuller streak row (icon + the day count in words) — sidebar only, the
   topbar keeps the compact .streakbadge pill. */
.sidebar__streak {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3); border-radius: var(--r-md);
  background: var(--surface-sunk); color: var(--ink);
  font-size: var(--fs-sm); font-weight: 700; text-decoration: none;
  transition: background var(--dur-fast);
}
.sidebar__streak:hover { background: var(--line); }
/* The flame disc breathes a warm glow — an ember, not a whole-button halo. */
.sidebar__streak-icon {
  width: 32px; height: 32px; flex: none; border-radius: var(--r-full);
  display: grid; place-items: center; color: var(--retry); background: var(--retry-tint);
  --glow: var(--retry);
  box-shadow: 0 0 8px 0 color-mix(in srgb, var(--glow) 40%, transparent);
  animation: streak-ember 2.4s var(--ease-out) infinite;
}
@keyframes streak-ember {
  0%, 100% { box-shadow: 0 0 7px 0 color-mix(in srgb, var(--glow) 42%, transparent); }
  50%      { box-shadow: 0 0 18px 3px color-mix(in srgb, var(--glow) 70%, transparent); }
}
.sidebar__streak--risk .sidebar__streak-icon {
  color: var(--info); background: var(--info-tint); --glow: var(--info);
}
/* Zero-streak: same fixed spot in the footer, just muted rather than the
   vivid orange — no ember until there's a streak to celebrate. */
.sidebar__streak--none { color: var(--ink-soft); }
.sidebar__streak--none .sidebar__streak-icon {
  color: var(--ink-faint); background: var(--surface); box-shadow: none; animation: none;
}

/* Segmented light/system/dark switcher, sidebar footer. */
.sidebar__theme { display: flex; gap: 2px; padding: 3px; border-radius: var(--r-full); background: var(--surface-sunk); }
.sidebar__theme-btn {
  flex: 1; display: grid; place-items: center; height: 30px; font: inherit;
  border: none; border-radius: var(--r-full); background: transparent; color: var(--ink-faint);
  cursor: pointer; transition: background var(--dur-fast), color var(--dur-fast);
}
.sidebar__theme-btn:hover { color: var(--ink); }
.sidebar__theme-btn[aria-pressed="true"] { background: var(--surface); color: var(--brand-ink); box-shadow: var(--shadow-1); }

/* No visible scrollbar on the sidebar. It still scrolls (wheel, trackpad,
   keyboard) when the nav outgrows a short screen — only the bar is hidden,
   so nothing at the bottom ever becomes unreachable. */
.sidebar { scrollbar-width: none; -ms-overflow-style: none; }
.sidebar::-webkit-scrollbar { display: none; }

@media (min-width: 900px) {
  .sidebar {
    display: flex; flex-direction: column; flex: none;
    width: 236px; position: sticky; top: var(--s-4); align-self: flex-start;
    height: calc(100vh - var(--s-4) * 2); overflow-y: auto;
    margin: var(--s-4) 0 var(--s-4) var(--s-4);
    padding: var(--s-5) var(--s-3);
    background: var(--surface);
    border: 1px solid var(--line); border-radius: var(--r-lg);
  }
  /* Topbar stays on desktop but collapses to the language + bell + account
     controls, floating top-right over the content — the sidebar carries
     brand/nav/streak, and the language switcher lives up here. */
  .topbar { background: transparent; backdrop-filter: none; border-bottom: none; }
  .topbar__inner { justify-content: flex-end; padding-top: var(--s-4); padding-right: var(--s-6); }
  .topbar .brand,
  .topbar .streakbadge { display: none; }
}

.topbar {
  position: sticky; top: 0; z-index: 20;
  background: color-mix(in srgb, var(--canvas) 88%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}
.topbar__inner {
  max-width: var(--maxw); margin: 0 auto;
  padding: var(--s-3) var(--s-4);
  display: flex; align-items: center; gap: var(--s-4);
}
.brand {
  display: flex; align-items: center; gap: var(--s-2);
  font-family: var(--font-display); font-weight: 700; font-size: var(--fs-lg);
  color: var(--ink); text-decoration: none;
}
.brand img { width: 30px; height: 30px; }
.topbar__spacer { flex: 1; }

/* Bottom tab bar — mobile's primary nav (see js/main.js tabBar()). Fixed and
   thumb-reachable; hidden on desktop by the min-width:900px block above. */
.tabbar {
  display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 25;
  background: color-mix(in srgb, var(--surface) 97%, transparent);
  backdrop-filter: blur(8px);
  border-top: 1px solid var(--line);
  padding-bottom: env(safe-area-inset-bottom, 0);
}
.tabbar__item {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; padding: 8px 0 7px; border: none; background: none; font: inherit; cursor: pointer;
  color: var(--ink-faint); text-decoration: none;
}
.tabbar__item svg { flex: none; }
.tabbar__item span { font-size: 10.5px; font-weight: 600; }
.tabbar__item.is-active { color: var(--brand-ink); }
.tabbar__more { position: relative; flex: 1; display: flex; }
/* Desktop carries the whole nav in the sidebar — hide the mobile bar. Placed
   after .tabbar's base rule so it wins the cascade (media queries add no
   specificity, so source order decides). */
@media (min-width: 900px) { .tabbar { display: none; } }

/* The "Meny" tab's popover — the full nav, opened upward from the bottom bar. */
.topmenu__list {
  position: absolute; right: 8px; bottom: calc(100% + 10px); z-index: 30;
  min-width: 190px; padding: 6px;
  max-height: calc(100vh - 90px); overflow-y: auto; overscroll-behavior: contain;
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--r-md); box-shadow: var(--shadow-2);
  display: flex; flex-direction: column; gap: 2px;
}
.topmenu__item {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 10px; border-radius: var(--r-md);
  color: var(--ink); text-decoration: none; font-size: var(--fs-sm); font-weight: 600;
}
.topmenu__item:hover { background: var(--surface-sunk); }
.topmenu__item svg { flex: none; color: var(--ink-soft); }
.topmenu__item.is-active { background: var(--brand-tint); color: var(--brand-ink); }
.topmenu__item.is-active svg { color: var(--brand-ink); }
.topmenu__group {
  margin: 8px 10px 2px; font-size: 10px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em; color: var(--ink-faint);
}
.topmenu__list > .topmenu__group:first-child { margin-top: 2px; }
.topmenu__div { height: 1px; margin: 6px 10px; background: var(--line); }
/* Theme switcher tucked in the ⋮ menu's footer — the sidebar carries it on
   desktop, but there's no sidebar on mobile. */
.topmenu__foot { padding: 2px 10px 6px; }
.topmenu__foot .topmenu__group { margin: 4px 0 6px; }
.topmenu__foot .sidebar__theme { width: 100%; }

@media (max-width: 560px) {
  .topbar__inner { gap: var(--s-2); padding-left: var(--s-3); padding-right: var(--s-3); }
  .topbar__inner .brand { font-size: var(--fs-md); }
  .topbar__inner .brand img { width: 26px; height: 26px; }
  /* Bell + account + ⋮ need the room; the streak already shows in the
     "Idag" panel and the language toggle stays. */
  .topbar__inner > .streakbadge { display: none; }
}
@media (max-width: 430px) {
  /* Leaderboard + notifications + account + language + ⋮ is a lot of chrome on
     a small phone — drop the wordmark and let the logo carry the home link. */
  .topbar__inner .brand__name { display: none; }
}

/* "← Home" control at the top of every sub-screen. */
.homebtn {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-sm); font-weight: 600; color: var(--ink-soft);
  padding: 5px 12px 5px 8px; border-radius: var(--r-full);
  border: 1px solid var(--line-strong); background: var(--surface);
  margin-bottom: var(--s-4);
}
.homebtn:hover { color: var(--brand); border-color: var(--brand); }
.homebtn svg { flex: none; }
.psheet__bar .homebtn { margin-bottom: 0; }

.iconbtn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: var(--r-full);
  border: 1px solid var(--line-strong); background: var(--surface);
  color: var(--ink-soft); cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-spring), background var(--dur-fast);
}
.iconbtn:hover { transform: translateY(-1px); background: var(--surface-sunk); }

/* Language toggle: the globe alone is ambiguous, so it carries the code it
   will switch to. */
.langbtn { position: relative; width: auto; min-width: 40px; gap: 5px; padding: 0 10px; }
.langbtn__flag { display: inline-flex; flex: none; }
.langbtn__flag svg { width: 18px; height: auto; display: block; border-radius: 2px; }
.langbtn__code { font-size: 11px; font-weight: 700; letter-spacing: .03em; }

.content {
  flex: 1;
  max-width: var(--maxw); width: 100%; margin: 0 auto;
  padding: var(--s-6) var(--s-4) var(--s-8);
}
@media (max-width: 899.98px) {
  /* Room for the fixed bottom tab bar so page content — and anything that
     floats at the bottom, like the solve-screen hint — never sits under it.
     Immersive routes (a running session, print) drop the tab bar, so no
     clearance is needed and the session's own hint button owns the bottom. */
  .shell:not(.shell--immersive) .content { padding-bottom: calc(70px + env(safe-area-inset-bottom, 0)); }
}

/* ---------- buttons ---------- */
.btn {
  font-family: var(--font-display); font-weight: 600; font-size: var(--fs-md);
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: var(--s-3) var(--s-5); border-radius: var(--r-full);
  border: none; cursor: pointer; text-decoration: none;
  background: var(--brand); color: #fff;
  box-shadow: var(--shadow-1);
  transition: transform var(--dur-fast) var(--ease-spring), box-shadow var(--dur-fast), background var(--dur-fast);
}
.btn:hover { transform: translateY(-2px); box-shadow: var(--shadow-2); }
.btn:active { transform: translateY(0); }
.btn--ghost { background: var(--surface); color: var(--ink); border: 1px solid var(--line-strong); box-shadow: none; }
.btn--ghost:hover { background: var(--surface-sunk); }
.btn--ok { background: var(--ok); }
.btn--danger { background: var(--c-berry-ink); color: #fff; }
.btn--sm { padding: var(--s-2) var(--s-4); font-size: var(--fs-sm); }
/* Not opacity — 0.5 on a coloured button drops the label to ~2.3:1 and reads as
   broken rather than "not yet". Explicit muted colours stay legible. */
.btn:disabled {
  cursor: not-allowed; transform: none; box-shadow: none;
  background: var(--surface-sunk); color: var(--ink-faint);
  /* Softer edge than a ghost button's --line-strong, so "disabled" and
     "secondary action" do not end up looking like the same control. */
  border: 1px solid var(--line);
}
.btn:disabled:hover { transform: none; box-shadow: none; background: var(--surface-sunk); }

/* ---------- cards & panels ---------- */
.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--shadow-1);
}
.panel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--s-5); }

/* ---------- menu / home ---------- */
.home__head { margin-bottom: var(--s-5); }
.home__hi { color: var(--ink-soft); margin-top: 2px; }
/* Solve / Library / New-set — a row on its own, between the "Idag" panel
   and "Dina set". */
.home__actions { display: flex; gap: var(--s-2); flex-wrap: wrap; margin-bottom: var(--s-5); }

.tabs { display: inline-flex; background: var(--surface-sunk); border-radius: var(--r-full); padding: 4px; gap: 4px; }
.tab {
  font-family: var(--font-display); font-weight: 600; font-size: var(--fs-sm);
  border: none; background: transparent; color: var(--ink-soft);
  padding: var(--s-2) var(--s-4); border-radius: var(--r-full); cursor: pointer;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.tab[aria-selected="true"] { background: var(--surface); color: var(--ink); box-shadow: var(--shadow-1); }

.chips { display: flex; flex-wrap: wrap; gap: var(--s-2); margin: 0 0 var(--s-4); }
.chip {
  font-size: var(--fs-sm); font-weight: 600;
  border: 1px solid var(--line-strong); background: var(--surface);
  color: var(--ink-soft); padding: 6px var(--s-3); border-radius: var(--r-full); cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-spring);
  display: inline-flex; align-items: center; gap: 6px;
}
.chip:hover { transform: translateY(-1px); }
.chip__dot { width: 10px; height: 10px; border-radius: var(--r-full); background: var(--subject, var(--ink-faint)); }
.chip[aria-pressed="true"] { color: #fff; background: var(--subject-ink, var(--brand)); border-color: transparent; }
.chip[aria-pressed="true"] .chip__dot { background: rgba(255,255,255,.85); }
/* A subject the user pre-added — no sets under it yet. */
.chip--empty:not([aria-pressed="true"]) { opacity: .5; }
/* "+ add subject" control at the end of the row, and its inline input. */
.chip--add { color: var(--ink-faint); border-style: dashed; padding-inline: 9px; }
.chip--add:hover { color: var(--brand); border-color: var(--brand); }
.chip--add svg { display: block; }
.chip--addinput { width: 130px; font: inherit; font-size: var(--fs-sm); font-weight: 600; padding: 5px var(--s-3); color: var(--ink); }
.chip--addinput:focus-visible { border-color: var(--brand); outline: none; }

/* ---------- today strip ---------- */
.today { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: var(--s-3); margin-bottom: var(--s-4); }
.tile {
  display: flex; align-items: center; gap: var(--s-3); text-decoration: none; color: inherit;
  padding: var(--s-3) var(--s-4); border-radius: var(--r-md);
  background: var(--surface); border: 1px solid var(--line); box-shadow: var(--shadow-1);
  transition: transform var(--dur-fast) var(--ease-spring), box-shadow var(--dur-fast);
}
.tile:hover { transform: translateY(-2px); box-shadow: var(--shadow-2); }
.tile strong { display: block; font-family: var(--font-display); font-size: var(--fs-md); }
.tile__sub { display: block; font-size: var(--fs-sm); color: var(--ink-soft); }
.tile__icon {
  width: 36px; height: 36px; flex: none; border-radius: var(--r-full);
  display: grid; place-items: center; background: var(--surface-sunk); color: var(--ink-soft);
}
.tile--accent { border-color: var(--brand); background: var(--brand-tint); }
.tile--accent .tile__icon { background: var(--brand); color: #fff; }

/* "Continue where you left off" — the loudest tile on the strip. */
.tile--resume {
  border-color: var(--brand); border-left-width: 4px;
  background: var(--brand-tint);
  box-shadow: 0 3px 18px -5px color-mix(in srgb, var(--brand) 55%, transparent);
}
.tile--resume:hover { box-shadow: 0 6px 24px -6px color-mix(in srgb, var(--brand) 60%, transparent); }
.tile--resume .tile__icon { background: var(--brand); color: #fff; }
.tile--resume strong { color: var(--brand-ink); }

/* Streak / goal / weak-spot tiles all get the same glowing treatment as the
   top-bar streak badge, each in its own colour. */
.tile--streak {
  border-color: color-mix(in srgb, var(--retry) 55%, var(--line));
  box-shadow: 0 2px 16px -4px color-mix(in srgb, var(--retry) 60%, transparent);
}
.tile--streak .tile__icon { background: var(--retry); color: #2B2B3A; }
:root[data-theme="dark"] .tile--streak .tile__icon { color: #21170C; }

.tile--goal {
  border-color: color-mix(in srgb, var(--ok-bright) 55%, var(--line));
  box-shadow: 0 2px 16px -4px color-mix(in srgb, var(--ok-bright) 60%, transparent);
}
.tile--goal:hover { box-shadow: 0 5px 22px -5px color-mix(in srgb, var(--ok-bright) 65%, transparent); }

.tile--weak {
  border-color: color-mix(in srgb, var(--c-berry) 55%, var(--line));
  box-shadow: 0 2px 16px -4px color-mix(in srgb, var(--c-berry) 60%, transparent);
}
.tile--weak:hover { box-shadow: 0 5px 22px -5px color-mix(in srgb, var(--c-berry) 65%, transparent); }
.tile--weak .tile__icon { background: var(--c-berry); color: #fff; }
:root[data-theme="dark"] .tile--weak .tile__icon { color: #3B1E2B; }

/* Calendar day chooser: subject dot in each row. */
.daymenu { z-index: 60; min-width: 190px; }
.daymenu__dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }

/* ---------- home: "Today" + "Your sets" panels ---------- */
.home-panel {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--shadow-1);
  padding: var(--s-4) var(--s-5); margin-bottom: var(--s-5);
}
.home-panel__label {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  font-family: var(--font-display); font-weight: 700; font-size: var(--fs-md);
  color: var(--ink); margin-bottom: var(--s-3);
}
/* Quieter link-style text on the home page specifically (the achievements
   count, "switch to Tests", "Show all") — same elsewhere (Study, Progress,
   Settings, the Kommande dialog), just not underlined here. */
.home-panel .linkbtn, .home-panel .upcoming__more { text-decoration: none; }

/* Backup nudge — a warm-tinted card so it reads as "attention" without alarm. */
.home-panel--backup { border-color: color-mix(in srgb, var(--retry) 40%, var(--line)); background: var(--retry-tint); }
.home-panel--backup .home-panel__label { align-items: flex-start; }
.home-panel--backup .home-panel__label span { color: var(--retry-ink); }
.home-panel--backup .home-panel__label .linkbtn { flex: none; white-space: nowrap; font-size: var(--fs-sm); }
.home-panel--backup .note { color: var(--ink-soft); }

/* Two-column home page: the panels on the left, a sticky rail (upcoming
   deadlines + achievements teaser) on the right. Falls back to one column
   below 1100px, or whenever homeRail() has nothing to show. */
.home-layout { display: grid; grid-template-columns: minmax(0, 1fr) 288px; gap: var(--s-5); align-items: start; }
.home-layout--solo { grid-template-columns: minmax(0, 1fr); }
.home-main { min-width: 0; }
.home-rail { display: flex; flex-direction: column; gap: var(--s-5); position: sticky; top: calc(var(--s-4) + 16px); }
.home-rail .home-panel { margin-bottom: 0; }
.home-rail .upcoming { margin-bottom: 0; }
@media (max-width: 1100px) {
  .home-layout { grid-template-columns: minmax(0, 1fr); }
  .home-rail { position: static; }
}

/* Achievements teaser: the badge closest to unlocking, with a slim bar. */
.ach-next { display: flex; gap: var(--s-3); align-items: flex-start; }
.ach-next__emoji { font-size: 26px; line-height: 1.1; flex: none; }
.ach-next__bar {
  height: 6px; border-radius: var(--r-full); background: var(--surface-sunk);
  overflow: hidden; margin: 6px 0 4px;
}
.ach-next__fill { height: 100%; background: var(--brand); border-radius: inherit; }

/* "Continue where you left off" — the wide banner at the top of Today. */
.continue-banner {
  display: flex; align-items: center; gap: var(--s-3); text-decoration: none;
  padding: var(--s-3) var(--s-4); border-radius: var(--r-md);
  background: var(--brand-tint); border: 1px solid var(--brand); border-left-width: 4px;
  color: inherit; margin-bottom: var(--s-3);
  transition: box-shadow var(--dur-fast);
}
.continue-banner:hover { box-shadow: 0 4px 20px -6px color-mix(in srgb, var(--brand) 55%, transparent); }
.continue-banner__ic {
  width: 34px; height: 34px; flex: none; border-radius: var(--r-full);
  display: grid; place-items: center; background: var(--brand); color: #fff;
}
.continue-banner__body { flex: 1; min-width: 0; }
.continue-banner__body strong { display: block; font-family: var(--font-display); color: var(--brand-ink); }
.continue-banner__body span { display: block; font-size: var(--fs-sm); color: var(--ink-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.continue-banner__go { flex: none; font-family: var(--font-display); font-weight: 700; font-size: var(--fs-sm); color: var(--brand-ink); }
.continue-banner__go::after { content: " ›"; }

/* the small stat pills below the banner */
.pillrow { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.pill {
  display: inline-flex; align-items: center; gap: var(--s-2); text-decoration: none;
  font-size: var(--fs-sm); font-weight: 700; color: var(--ink-soft);
  padding: 6px var(--s-3); border-radius: var(--r-full);
  background: var(--surface); border: 1px solid var(--line-strong);
  transition: transform var(--dur-fast) var(--ease-spring);
}
.pill:hover { transform: translateY(-1px); color: var(--ink); }
.pill__ic { width: 18px; height: 18px; flex: none; border-radius: var(--r-full); display: grid; place-items: center; background: var(--surface-sunk); color: var(--ink-soft); }
.pill .ring { width: 26px; height: 26px; flex: none; }
.pill--due .pill__ic { background: var(--brand); color: #fff; }
.pill--weak { border-color: color-mix(in srgb, var(--c-berry) 45%, var(--line-strong)); }
.pill--weak .pill__ic { background: var(--c-berry); color: #fff; }
/* The streak pill glows in the same warm amber as the top-bar badge — a
   running streak should read as "lit", not just another stat. */
.pill--streak {
  border-color: color-mix(in srgb, var(--retry) 55%, var(--line-strong));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--retry) 30%, transparent),
              0 2px 16px -4px color-mix(in srgb, var(--retry) 60%, transparent);
}
.pill--streak:hover {
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--retry) 45%, transparent),
              0 4px 22px -4px color-mix(in srgb, var(--retry) 78%, transparent);
}
.pill--streak .pill__ic { background: var(--retry); color: #2B2B3A; }
.pill--goal { border-color: color-mix(in srgb, var(--ok-bright) 45%, var(--line-strong)); }
:root[data-theme="dark"] .pill--streak .pill__ic { color: #21170C; }

/* "Filter" toggle + the collapsible drawer holding sort + subject chips */
.filtertoggle .caret { display: inline-block; margin-left: 4px; transition: transform var(--dur-fast); }
.filtertoggle.is-on .caret { transform: rotate(180deg); }
.filterdrawer { display: flex; flex-direction: column; gap: var(--s-3); margin: var(--s-2) 0 var(--s-3); }
.filterdrawer .sortsel { align-self: flex-start; }

/* calendar / upcoming, shown in a dialog off the Today panel */
.caldialog { max-width: 460px; }
.caldialog__head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); margin-bottom: var(--s-3); }
.caldialog__head h3 { display: flex; align-items: center; gap: var(--s-2); }
.caldialog .upcoming { margin-bottom: 0; }

@media (max-width: 480px) {
  /* In the calendar dialog on a phone the month grid is more bulk than help —
     the Upcoming list already says when everything is due. */
  .caldialog .cal { display: none; }
}

/* ---------- calendar page: the month planner (js/views/calendar.js) ---------- */
.planner-page { display: flex; flex-direction: column; gap: var(--s-3); }
.planner-page > .homebtn { align-self: flex-start; }   /* don't let the column stretch it */
.planner-page__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-3); flex-wrap: wrap;
}
.planner-page__head h1 { margin: 0; }
.planner-page__sub { margin: 0 0 var(--s-2); max-width: 60ch; }

.planner { display: grid; gap: var(--s-4); align-items: start; }
@media (min-width: 1180px) { .planner { grid-template-columns: minmax(0, 1fr) 300px; } }

/* --- month grid --- */
.plan-cal {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: var(--s-4);
}
.plan-cal__head { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-3); }
.plan-nav { display: flex; gap: var(--s-1); flex: none; }
.plan-nav__btn {
  width: 30px; height: 30px; border-radius: var(--r-sm);
  border: 1px solid var(--line-strong); background: var(--surface);
  color: var(--ink-soft); cursor: pointer; font-size: 17px; line-height: 1;
  display: grid; place-items: center; transition: background var(--dur-fast), color var(--dur-fast);
}
.plan-nav__btn:hover { background: var(--surface-sunk); color: var(--ink); }
.plan-cal__month {
  flex: 1; min-width: 0;
  font-family: var(--font-display); font-weight: 700; font-size: var(--fs-lg);
  text-transform: capitalize; letter-spacing: -.01em;
}
.plan-cal__today { flex: none; }

.plan-cal__wd, .plan-cal__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--s-1); }
.plan-cal__wd { margin-bottom: var(--s-1); }
.plan-cal__wd span {
  padding-left: 4px; font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em; color: var(--ink-faint);
}
.plan-cal__cell {
  position: relative; display: flex; flex-direction: column; gap: 2px;
  min-height: 86px; padding: 4px; text-align: left;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface); overflow: hidden; font: inherit; color: inherit;
}
button.plan-cal__cell { cursor: pointer; transition: background var(--dur-fast), border-color var(--dur-fast); }
button.plan-cal__cell:hover { background: var(--brand-tint); border-color: var(--brand-bright); }
.plan-cal__cell.is-outside { background: var(--surface-sunk); }
.plan-cal__cell.is-outside .plan-cal__date { opacity: .55; }
.plan-cal__cell.is-past:not(.is-today) { background: var(--canvas); }
.plan-cal__cell.is-past .plan-cal__chip { opacity: .5; }
.plan-cal__date {
  align-self: flex-end; padding: 1px 3px;
  font-size: 12px; font-weight: 700; color: var(--ink-soft); font-variant-numeric: tabular-nums;
}
.plan-cal__cell.is-today .plan-cal__date {
  background: var(--brand); color: #fff; border-radius: var(--r-full);
  min-width: 20px; height: 20px; display: grid; place-items: center; padding: 0 5px;
}

.plan-cal__chip {
  display: flex; align-items: center; gap: 5px;
  padding: 2px 5px; border-radius: 5px;
  background: var(--subject-tint); color: var(--subject-ink);
  font-size: 11px; font-weight: 600; line-height: 1.35; text-decoration: none;
  overflow: hidden;
}
.plan-cal__chip:hover { outline: 1px solid var(--subject); }
.plan-cal__chip-dot { width: 5px; height: 5px; border-radius: 2px; background: var(--subject); flex: none; }
.plan-cal__chip.is-test .plan-cal__chip-dot { border-radius: var(--r-full); }
.plan-cal__chip-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.plan-cal__more {
  align-self: flex-start; padding: 1px 3px; font: inherit; font-size: 10.5px; font-weight: 700;
  color: var(--ink-faint); background: none; border: none; cursor: pointer;
}
.plan-cal__more:hover { color: var(--brand-ink); }

/* The "+" grows to fill whatever height the chips leave, so any empty part of
   a today/future cell is a click target. Hidden until the cell is hovered. */
.plan-cal__add {
  flex: 1 1 auto; align-self: stretch; min-height: 14px;
  border: none; background: none; cursor: pointer; border-radius: 4px;
  display: grid; place-items: center; color: var(--ink-faint);
  opacity: 0; transition: opacity var(--dur-fast), background var(--dur-fast);
}
.plan-cal__cell:hover .plan-cal__add, .plan-cal__add:focus-visible { opacity: .75; }
.plan-cal__add:hover { background: var(--brand-tint); color: var(--brand-ink); opacity: 1; }
/* Touch, wide enough for it not to be clutter (tablet): a faint standing "+". */
@media (hover: none) and (min-width: 561px) { .plan-cal__add { opacity: .4; } }

/* --- agenda rail --- */
.plan-agenda { display: flex; flex-direction: column; gap: var(--s-4); }
.plan-agenda__head { margin: 0; font-size: var(--fs-md); color: var(--ink-soft); }
.plan-agenda__group { display: flex; flex-direction: column; gap: var(--s-2); }
.plan-agenda__gtitle {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-2); margin: 0;
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-faint);
}
.plan-agenda__group.is-overdue .plan-agenda__gtitle { color: var(--retry-ink); }
.plan-agenda__count {
  font-size: 10.5px; font-weight: 700; color: var(--ink-faint);
  background: var(--surface-sunk); border-radius: var(--r-full); padding: 0 6px;
}
.plan-agenda__rows { display: flex; flex-direction: column; gap: var(--s-2); }
.plan-row {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-2) var(--s-3); border-radius: var(--r-sm);
  background: var(--surface); border: 1px solid var(--line);
  border-left: 3px solid var(--subject, var(--brand));
  text-decoration: none; color: inherit;
  transition: background var(--dur-fast), border-color var(--dur-fast);
}
.plan-row:hover { background: var(--surface-sunk); }
.plan-row.is-soon { border-color: var(--retry); border-left-color: var(--retry); }
.plan-row.is-overdue { border-color: var(--retry); border-left-color: var(--retry); background: var(--retry-tint); }
.plan-row__body { flex: 1; min-width: 0; }
.plan-row__title {
  display: block; font-family: var(--font-display); font-weight: 600; font-size: var(--fs-sm);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.plan-row__meta { display: flex; align-items: center; gap: var(--s-2); font-size: 12px; color: var(--ink-soft); }
.plan-row.is-soon .plan-row__meta, .plan-row.is-overdue .plan-row__meta { color: var(--retry-ink); }
.plan-row__type {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  color: var(--brand-ink); background: var(--brand-tint); border-radius: var(--r-full); padding: 0 5px;
}
.plan-row.is-soon .plan-row__type, .plan-row.is-overdue .plan-row__type {
  color: var(--retry-ink); background: color-mix(in srgb, var(--retry) 22%, transparent);
}
.plan-row__go { flex: none; color: var(--ink-faint); display: grid; place-items: center; }
.plan-row:hover .plan-row__go { color: var(--brand); }

.plan-agenda__empty {
  display: flex; flex-direction: column; align-items: center; text-align: center; gap: var(--s-2);
  padding: var(--s-6) var(--s-4); color: var(--ink-faint);
  border: 1px dashed var(--line-strong); border-radius: var(--r-md);
}
.plan-agenda__empty-t { margin: 0; font-family: var(--font-display); font-weight: 600; color: var(--ink); }
.plan-agenda__empty .note { margin: 0; max-width: 34ch; }

@media (max-width: 560px) {
  .plan-cal { padding: var(--s-3); }
  .plan-cal__cell { min-height: 54px; }
  /* No room for titles on a phone — each deadline is a thin colour bar; the
     agenda below carries the detail. */
  .plan-cal__chip { padding: 0; height: 5px; border-radius: 3px; background: var(--subject); }
  .plan-cal__chip-t, .plan-cal__chip-dot { display: none; }
  .plan-cal__more { font-size: 9px; }
}

/* ---------- upcoming deadlines ---------- */
.upcoming { margin-bottom: var(--s-4); }
.upcoming__title {
  display: flex; align-items: center; gap: var(--s-2);
  font-size: var(--fs-md); margin-bottom: var(--s-3); color: var(--ink-soft);
}
.upcoming .cal { margin-bottom: var(--s-4); }
.upcoming__list { display: flex; flex-direction: column; gap: var(--s-2); }
.upcoming__row {
  display: flex; align-items: center; gap: var(--s-3);
  padding: 8px var(--s-3); border-radius: var(--r-md);
  background: var(--surface); border: 1px solid var(--line);
  text-decoration: none; color: inherit;
  transition: border-color var(--dur-fast), background var(--dur-fast);
}
.upcoming__row:hover { border-color: var(--brand); }
.upcoming__row.is-soon { border-color: var(--retry); }
.upcoming__row.is-overdue { border-color: var(--retry); background: var(--retry-tint); }
.upcoming__dot {
  width: 8px; height: 8px; flex: none; border-radius: var(--r-full);
  background: var(--subject, var(--brand));
}
.upcoming__main { flex: 1; min-width: 0; }
.upcoming__main strong {
  display: block; font-family: var(--font-display); font-weight: 600; font-size: var(--fs-sm);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.upcoming__meta { display: block; font-size: 12px; color: var(--ink-soft); }
.upcoming__row.is-soon .upcoming__meta,
.upcoming__row.is-overdue .upcoming__meta { color: var(--retry-ink); }
.upcoming__go { flex: none; color: var(--ink-faint); display: grid; place-items: center; }
.upcoming__row:hover .upcoming__go { color: var(--brand); }
.upcoming__more {
  margin-top: var(--s-2); padding: 6px 2px;
  border: none; background: none; cursor: pointer;
  font-family: var(--font-display); font-weight: 600; font-size: var(--fs-sm);
  color: var(--brand-ink); text-decoration: underline; text-underline-offset: 3px;
}
.upcoming__more:hover { color: var(--ink); }

/* Home-rail calendar collapse — a single, navigable week by default (the
   "Show full month" link sits in the panel label, next to "Upcoming"); a
   note up top when the week in view has nothing due. The dialog version
   gets neither. Kept plain (no underline, regular weight) so the label row
   stays quiet next to the bold "Upcoming" heading. */
.cal-collapse__toggle { font-weight: 400; text-decoration: none; }
.cal-empty { margin: 0 0 var(--s-2); }

/* Countdown + one-tap study shortcut to the soonest test, directly under
   the calendar. Built like an .upcoming__row, tinted red once it's within
   a day of the deadline. */
.next-test-wrap { display: flex; flex-direction: column; gap: 4px; margin: 0 0 var(--s-3); }
a.next-test {
  display: grid; grid-template-columns: 1fr auto; gap: 1px var(--s-2);
  padding: 8px var(--s-3);
  border-radius: var(--r-md); background: var(--surface); border: 1px solid var(--line);
  text-decoration: none; color: inherit;
  transition: border-color var(--dur-fast), background var(--dur-fast);
}
.next-test__prep {
  align-self: flex-start; display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 4px; font-size: 12px; font-weight: 600;
  color: var(--brand-ink); text-decoration: none;
}
.next-test__prep:hover { text-decoration: underline; }
.next-test__prep svg { flex: none; }
a.next-test:hover { border-color: var(--brand); }
.next-test__when {
  display: flex; align-items: center; gap: 6px;
  font-size: 12px; font-weight: 600; color: var(--ink-soft);
}
.next-test__when svg { flex: none; color: var(--ink-faint); }
.next-test__title {
  font-family: var(--font-display); font-weight: 600; font-size: var(--fs-sm);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.next-test__go {
  grid-column: 2; grid-row: 1 / 3; align-self: center;
  color: var(--ink-faint); display: grid; place-items: center;
}
a.next-test:hover .next-test__go { color: var(--brand); }
a.next-test--soon { border-color: var(--retry); background: var(--retry-tint); }
.next-test--soon .next-test__when,
.next-test--soon .next-test__when svg { color: var(--retry-ink); }

.acard__tag--due { background: var(--surface-sunk); color: var(--ink-soft); }
.acard__tag--overdue { background: var(--retry-tint); color: var(--retry-ink); }

/* ---------- daily goal ring ---------- */
.ring--goal { width: 34px; height: 34px; }
.ring--goal .ring__fg { stroke: var(--brand); }
.ring--goal .ring__label { font-size: 13px; fill: var(--ink); }
/* Goal met: full ring goes green and the count is replaced by a check (the
   number moves to the label beside the ring). */
.ring--goal-done .ring__fg { stroke: var(--ok); }
.ring--goal .ring__check { stroke: var(--ok); }
.tile--goal .ring { flex: none; }

/* ---------- calendar (js/components/calendar.js) ---------- */
.cal__nav { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--s-2); }
.cal__month {
  font-family: var(--font-display); font-weight: 700; font-size: var(--fs-sm);
  text-transform: capitalize; letter-spacing: .01em;
}
.cal__navbtn {
  width: 30px; height: 30px; flex: none; border-radius: var(--r-sm);
  border: 1px solid var(--line-strong); background: var(--surface);
  color: var(--ink-soft); cursor: pointer; font-size: 18px; line-height: 1;
  display: grid; place-items: center;
}
.cal__navbtn:hover { background: var(--surface-sunk); color: var(--ink); }
.cal__week, .cal__grid { display: grid; grid-template-columns: repeat(7, 1fr); }
.cal__week { margin-bottom: 4px; }
.cal__wd {
  text-align: center; font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .03em; color: var(--ink-faint); padding: 4px 0;
}
.cal__grid { gap: 2px; }
.cal__cell {
  position: relative; aspect-ratio: 1; min-height: 34px;
  display: grid; place-items: center;
  border: 1px solid transparent; border-radius: var(--r-sm);
  background: none; font: inherit; font-size: var(--fs-sm); font-variant-numeric: tabular-nums;
  color: var(--ink); cursor: default;
}
button.cal__cell { cursor: pointer; }
button.cal__cell:hover:not(:disabled) { background: var(--surface-sunk); }
.cal__cell.is-outside { color: var(--ink-faint); }
.cal__cell.is-today { font-weight: 700; box-shadow: inset 0 0 0 1px var(--line-strong); }
.cal__cell.is-selected {
  background: var(--brand); color: #fff; font-weight: 700;
  box-shadow: none;
}
.cal__cell:disabled { color: var(--ink-faint); opacity: .4; }
.cal__cell:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.cal__dot {
  position: absolute; bottom: 4px; left: 50%; transform: translateX(-50%);
  width: 5px; height: 5px; border-radius: var(--r-full); background: var(--brand);
}
/* More than one deadline that day — a wider pill hints "there's a few". */
.cal__dot--multi { width: 12px; }
.cal__cell.is-marked { color: var(--brand-ink); font-weight: 700; }
.cal--mini .cal__cell.is-marked:hover { background: var(--brand-tint); }

/* Empty future day — a faint "+" on hover/focus says "tap to add a set here". */
.cal__cell.is-addable .cal__add {
  position: absolute; bottom: 2px; left: 50%; transform: translateX(-50%);
  font-size: 12px; line-height: 1; color: var(--ink-faint); opacity: 0;
  transition: opacity var(--dur-fast);
}
.cal__cell.is-addable:hover .cal__add,
.cal__cell.is-addable:focus-visible .cal__add { opacity: 1; }
.cal__cell.is-addable:hover { background: var(--brand-tint); color: var(--brand-ink); }

.datepick { display: block; }
.datepick__chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: var(--s-3); }
.datepick__chip {
  font-size: var(--fs-sm); font-weight: 600; font-family: var(--font-display);
  border: 1px solid var(--line-strong); background: var(--surface);
  color: var(--ink-soft); padding: 5px var(--s-3); border-radius: var(--r-full);
  cursor: pointer; transition: transform var(--dur-fast) var(--ease-spring), border-color var(--dur-fast);
}
.datepick__chip:hover { transform: translateY(-1px); border-color: var(--brand); color: var(--ink); }

/* ---------- search & sort ---------- */
/* Tabs on the left, search + sort on the right, wrapping to two rows when
   there is not enough width for both. */
.librarybar {
  display: flex; align-items: center; gap: var(--s-3);
  flex-wrap: wrap; margin-bottom: var(--s-3);
}
.librarybar .tabs { flex: none; }
.librarybar .tools { flex: 1; min-width: 260px; margin-bottom: 0; justify-content: flex-end; }
.tools { display: flex; gap: var(--s-3); margin-bottom: var(--s-4); flex-wrap: wrap; }
.search { position: relative; display: flex; align-items: center; flex: 1; min-width: 180px; color: var(--ink-faint); }
.search svg { position: absolute; left: 12px; pointer-events: none; }
.search__input {
  width: 100%; font: inherit; color: var(--ink);
  padding: var(--s-2) var(--s-3) var(--s-2) 38px;
  border-radius: var(--r-full); border: 1px solid var(--line-strong); background: var(--surface);
}
.search__input:focus-visible { border-color: var(--brand); box-shadow: none; }
.linkbtn {
  font: inherit; font-size: inherit; padding: 0; border: none; background: none;
  color: var(--brand-ink); text-decoration: underline; cursor: pointer;
}
.linkbtn:hover { color: var(--brand); }

.sortsel {
  font: inherit; padding: var(--s-2) var(--s-3); border-radius: var(--r-full);
  border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink);
}

.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--s-4); }

.acard {
  position: relative; text-align: left; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line);
  border-left: 6px solid var(--subject, var(--brand));
  border-radius: var(--r-md); padding: var(--s-4);
  display: flex; flex-direction: column; gap: var(--s-3); min-height: 148px;
  box-shadow: var(--shadow-1);
  transition: transform var(--dur-mid) var(--ease-spring), box-shadow var(--dur-mid);
  animation: pop-in var(--dur-mid) var(--ease-out) both;
}
.acard:hover { transform: translateY(-4px); box-shadow: var(--shadow-2); }
.acard:hover .acard__go { transform: translateX(3px); }
.acard__tag {
  align-self: flex-start; font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  padding: 3px 8px; border-radius: var(--r-full);
  background: var(--subject-tint, var(--brand-tint)); color: var(--subject-ink, var(--brand-ink));
}
.acard__tag--open { background: var(--ok-tint); color: var(--ok-ink); }
.acard__title { font-family: var(--font-display); font-weight: 600; font-size: var(--fs-lg); }
.acard__meta { margin-top: auto; display: flex; align-items: center; justify-content: space-between; color: var(--ink-faint); font-size: var(--fs-sm); }
.acard__go { transition: transform var(--dur-mid) var(--ease-spring); }

.acard--new {
  border: 2px dashed var(--line-strong); border-left: 2px dashed var(--line-strong);
  align-items: center; justify-content: center; color: var(--ink-soft);
  font-family: var(--font-display); font-weight: 600; gap: var(--s-2); box-shadow: none;
}
.acard--new:hover { border-color: var(--brand); color: var(--brand-ink); }

.empty { text-align: center; color: var(--ink-soft); padding: var(--s-7) var(--s-4); }
.empty svg { margin-bottom: var(--s-3); }

/* ---------- card ⋮ menu ---------- */
.acard { position: relative; }
.acard__menu {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  width: 30px; height: 30px; border-radius: var(--r-full);
  display: grid; place-items: center; cursor: pointer;
  border: none; background: transparent; color: var(--ink-faint);
  opacity: 0; transition: opacity var(--dur-fast), background var(--dur-fast);
}
.acard:hover .acard__menu, .acard__menu:focus-visible { opacity: 1; }
.acard__menu:hover { background: var(--surface-sunk); color: var(--ink); }
@media (hover: none) { .acard__menu { opacity: 1; } }

.cardmenu {
  position: absolute; z-index: 60; min-width: 180px;
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--r-md); box-shadow: var(--shadow-2);
  padding: 6px; display: flex; flex-direction: column; gap: 2px;
}
.cardmenu__item {
  display: flex; align-items: center; gap: var(--s-3);
  font: inherit; font-size: var(--fs-sm); text-align: left;
  padding: var(--s-2) var(--s-3); border: none; border-radius: var(--r-sm);
  background: transparent; color: var(--ink); cursor: pointer;
}
.cardmenu__item:hover { background: var(--surface-sunk); }
.cardmenu__item--danger { color: var(--retry-ink); }
.cardmenu__item--danger:hover { background: var(--retry-tint); }

.iconbtn--sm { width: 30px; height: 30px; }

/* ---------- mastery ring (small, on cards) ---------- */
.ring { --v: 0; width: 38px; height: 38px; flex: none; }
.ring circle { fill: none; stroke-width: 5; }
.ring__label {
  font-family: var(--font-display); font-weight: 700; font-size: 11px;
  fill: var(--ink-soft); font-variant-numeric: tabular-nums;
}
.ring .ring__bg { stroke: var(--line); }
.ring .ring__fg { stroke: var(--subject, var(--ok)); stroke-linecap: round;
  stroke-dasharray: 100 100; stroke-dashoffset: calc(100 - var(--v));
  transition: stroke-dashoffset var(--dur-slow) var(--ease-out); }

/* ---------- session ---------- */
.session__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4); margin-bottom: var(--s-2); flex-wrap: wrap;
}
.session__headright { display: flex; align-items: center; gap: var(--s-2); }
/* The shortcuts button is the only affordance for the keyboard layer, so it
   stays visible at every width rather than hiding on small screens. */
.shortcutsbtn, .readingbtn { width: 36px; height: 36px; flex: none; }
/* Keyboard shortcuts are desktop-only value; on a phone the reading control
   is the one that earns its place. */
@media (max-width: 560px) { .shortcutsbtn { display: none; } }

/* Reading-comfort popover (anchored from the session header) */
.reading__t { font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--ink-faint); padding: 4px 6px 6px; margin: 0; }
.reading__themes { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 6px; padding: 0 4px 6px; }
.reading__sw {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 8px 4px 6px; border-radius: 10px; border: 1px solid var(--line-strong);
  background: var(--surface); cursor: pointer; font: inherit; font-size: 11px; font-weight: 600; color: var(--ink-soft);
}
.reading__chip { width: 100%; height: 20px; border-radius: 6px; border: 1px solid var(--line); }
.reading__sw--light .reading__chip { background: #F5F7FC; }
.reading__sw--paper .reading__chip { background: #F1E7D0; }
.reading__sw--dark  .reading__chip { background: #0A0C11; }
.reading__sw.is-on { border-color: var(--brand); color: var(--brand-ink); box-shadow: 0 0 0 1px var(--brand); }
.reading__row { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); padding: 8px 6px; border-top: 1px solid var(--line); }
.reading__k { font-size: var(--fs-sm); color: var(--ink-soft); }
.reading__seg { display: inline-flex; gap: 2px; padding: 2px; border-radius: var(--r-full); background: var(--surface-sunk); }
.reading__seg button { min-width: 30px; padding: 3px 0; border: none; background: transparent; border-radius: var(--r-full); cursor: pointer; color: var(--ink-faint); font-family: var(--font-display); line-height: 1; }
.reading__seg button.is-on { background: var(--surface); color: var(--brand-ink); font-weight: 700; box-shadow: var(--shadow-1); }
.reading__toggle { width: 40px; height: 22px; border-radius: var(--r-full); border: none; background: var(--line-strong); cursor: pointer; position: relative; flex: none; transition: background var(--dur-fast); }
.reading__toggle.is-on { background: var(--brand); }
.reading__knob { position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%; background: #fff; transition: transform var(--dur-fast); }
.reading__toggle.is-on .reading__knob { transform: translateX(18px); }

.session { display: grid; grid-template-columns: 1fr 380px; gap: var(--s-5); align-items: start; }
/* The empty track was 1.05:1 against the page — invisible until it had fill.
   The border gives the shape an edge without shouting. */
.progressbar {
  height: 10px; border-radius: var(--r-full); overflow: hidden; margin-bottom: var(--s-2);
  background: color-mix(in srgb, var(--ink) 20%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--ink) 52%, var(--surface));
}
.progressbar__fill { height: 100%; border-radius: var(--r-full);
  background: linear-gradient(90deg, var(--c-sky), var(--c-grape));
  width: 0%; transition: width var(--dur-slow) var(--ease-spring); }
.progress-label { font-size: var(--fs-sm); color: var(--ink-faint); }

.question { margin-top: var(--s-4); }

/* ---------- Högskoleprovet: figure + stimulus + KVA/NOG panels ---------- */
.question__figure { margin: 0 0 var(--s-4); }
.question__figure img {
  display: block; max-width: 100%; height: auto;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface);
}
.question__figure figcaption { font-size: var(--fs-sm); color: var(--ink-faint); margin-top: var(--s-2); }
.question__figure--missing {
  border: 1px dashed var(--line-strong); border-radius: var(--r-md);
  padding: var(--s-3) var(--s-4); background: var(--surface-sunk); color: var(--ink-soft);
  font-size: var(--fs-sm);
}
.question__figure--missing b {
  display: block; text-transform: uppercase; letter-spacing: .05em; font-size: 11px;
  color: var(--ink-faint); margin-bottom: var(--s-1);
}
.question__stimulus {
  margin: 0 0 var(--s-4); padding: var(--s-3) var(--s-4);
  border: 1px solid var(--line); border-left: 3px solid var(--brand);
  border-radius: var(--r-sm); background: var(--surface-sunk);
  max-height: 42vh; overflow: auto;
}
.question__stimulus-label {
  margin: 0 0 var(--s-2); font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--brand-ink); font-weight: 700;
}
.question__stimulus-body { font-size: var(--fs-sm); line-height: 1.6; color: var(--ink-soft); }
.question__stimulus-body br { content: ""; display: block; margin-bottom: .5em; }

.hp-kva {
  display: grid; grid-template-columns: 1fr auto 1fr; gap: 0;
  border: 1px solid var(--line-strong); border-radius: var(--r-md); overflow: hidden;
  margin: 0 0 var(--s-4);
}
.hp-kva__col { padding: var(--s-3) var(--s-4); background: var(--surface-sunk); }
.hp-kva__col h4 {
  margin: 0 0 var(--s-1); font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-faint); font-weight: 700;
}
.hp-kva__q { font-size: var(--fs-lg); color: var(--ink); }
.hp-kva__div { width: 1px; background: var(--line-strong); }
.hp-kva__given {
  grid-column: 1 / -1; border-top: 1px dashed var(--line-strong);
  padding: var(--s-2) var(--s-4); font-size: var(--fs-sm); color: var(--ink-soft); background: var(--surface);
}
@media (max-width: 480px) {
  .hp-kva { grid-template-columns: 1fr; }
  .hp-kva__div { width: auto; height: 1px; }
}

.hp-nog__stmts { display: flex; flex-direction: column; gap: var(--s-2); margin: 0 0 var(--s-4); }
.hp-nog__stmt {
  display: flex; gap: var(--s-3); padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line-strong); border-radius: var(--r-sm); background: var(--surface-sunk);
  font-size: var(--fs-sm); color: var(--ink-soft);
}
.hp-nog__stmt b { flex: none; color: var(--ink); }

/* the normed-score reveal, sibling of .gradereveal */
.gradereveal--hp .gradereveal__letter { font-variant-numeric: tabular-nums; }
.gradereveal--hp:not(.gradereveal--low):not(.gradereveal--high) {
  background: var(--brand-tint); border-color: var(--brand);
}

/* #/hp dashboard */
.hp-dash { display: grid; gap: var(--s-4); }
.hp-dash > h1 { margin-bottom: 0; }
.hp-dash__when { display: flex; align-items: center; gap: 6px; font-weight: 600; flex-wrap: wrap; }
.hp-dash__prognosis { display: flex; align-items: baseline; gap: var(--s-5); flex-wrap: wrap; }
.hp-dash__big { font-family: var(--font-display); font-weight: 700; font-size: 3rem; color: var(--brand-ink); font-variant-numeric: tabular-nums; }
.hp-dash__split { font-size: var(--fs-sm); color: var(--ink-soft); line-height: 1.7; }
.hp-dash__split b { color: var(--ink); font-variant-numeric: tabular-nums; }

.hp-readiness { display: flex; flex-direction: column; gap: var(--s-2); }
.hp-readiness__row {
  display: grid; grid-template-columns: 48px 1fr 42px; gap: var(--s-3); align-items: center;
  text-decoration: none; color: inherit; padding: 4px 0;
}
.hp-readiness__row:hover .hp-readiness__k { color: var(--brand); }
.hp-readiness__k { font-weight: 700; font-size: var(--fs-sm); }
.hp-readiness__n { text-align: right; font-size: var(--fs-sm); color: var(--ink-faint); font-variant-numeric: tabular-nums; }

/* print */
.psheet__stimulus {
  border: 1px solid var(--line-strong); border-radius: 6px; padding: 8px 10px;
  margin: 0 0 8px; font-size: 13px; background: #f6f6f6;
}
.psheet__fig { display: block; max-width: 100%; max-height: 110mm; margin: 0 0 8px; }

.question__topline { display: flex; align-items: flex-start; gap: var(--s-3); margin-bottom: var(--s-4); }
.question__topline .question__prompt { margin-bottom: 0; flex: 1; min-width: 0; }
.question__prompt { font-size: var(--fs-lg); font-family: var(--font-display); font-weight: 600; margin-bottom: var(--s-4); }
/* Read-aloud button on the question card. */
.q-speak {
  flex: none; width: 32px; height: 32px; border-radius: var(--r-full);
  border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink-soft);
  display: grid; place-items: center; cursor: pointer; margin-top: 2px;
  transition: background var(--dur-fast), color var(--dur-fast), border-color var(--dur-fast);
}
.q-speak:hover { color: var(--brand); border-color: var(--brand); }
.q-speak.is-on { background: var(--brand); border-color: var(--brand); color: #fff; }
.reading__row--stack { flex-direction: column; align-items: stretch; gap: 6px; }
.reading__voice { width: 100%; font: inherit; font-size: var(--fs-sm); padding: 6px 8px; border-radius: var(--r-sm); border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink); }

.choices { display: flex; flex-direction: column; gap: var(--s-3); }
.choice {
  text-align: left; font: inherit; cursor: pointer;
  padding: var(--s-3) var(--s-4); border-radius: var(--r-md);
  border: 2px solid var(--line-strong); background: var(--surface);
  display: flex; align-items: center; gap: var(--s-3);
  transition: border-color var(--dur-fast), background var(--dur-fast), transform var(--dur-fast) var(--ease-spring);
}
.choice:hover { transform: translateY(-1px); border-color: var(--brand); }
.choice__key {
  width: 28px; height: 28px; flex: none; border-radius: var(--r-full);
  display: grid; place-items: center; font-weight: 700; font-family: var(--font-display);
  background: var(--surface-sunk); color: var(--ink-soft);
}
.choice[aria-pressed="true"] { border-color: var(--brand); background: var(--brand-tint); }
.choice.is-correct { border-color: var(--ok); background: var(--ok-tint); animation: pop var(--dur-mid) var(--ease-spring); }
.choice.is-wrong  { border-color: var(--retry); background: var(--retry-tint); animation: nudge var(--dur-mid) var(--ease-out); }
.choice.is-correct .choice__key { background: var(--ok); color: #fff; }
.choice.is-wrong .choice__key { background: var(--retry); color: #fff; }

.answerbox {
  width: 100%; font: inherit; padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md); border: 2px solid var(--line-strong);
  background: var(--surface); resize: vertical; min-height: 92px;
}
.answerbox:focus-visible { border-color: var(--brand); box-shadow: none; }

.feedback { margin-top: var(--s-4); padding: var(--s-4); border-radius: var(--r-md); font-size: var(--fs-sm); }
.feedback.ok { background: var(--ok-tint); color: var(--ok-ink); }
.feedback.retry { background: var(--retry-tint); color: var(--retry-ink); }

.flashcard { perspective: 1200px; }
.flashcard__inner {
  position: relative; min-height: 200px; border-radius: var(--r-lg);
  transition: transform var(--dur-slow) var(--ease-spring); transform-style: preserve-3d; cursor: pointer;
}
.flashcard.is-flipped .flashcard__inner { transform: rotateY(180deg); }
.flashcard__face {
  position: absolute; inset: 0; backface-visibility: hidden;
  display: grid; place-items: center; text-align: center; padding: var(--s-5);
  border-radius: var(--r-lg); border: 2px solid var(--line-strong); background: var(--surface);
  font-size: var(--fs-lg); font-family: var(--font-display); font-weight: 600;
}
.flashcard__face--back { transform: rotateY(180deg); background: var(--brand-tint); }
.selfrate { display: flex; gap: var(--s-2); margin-top: var(--s-4); flex-wrap: wrap; }

/* "how sure were you?" row after a practice answer */
.confrow { display: flex; align-items: center; gap: var(--s-2); margin-top: var(--s-4); flex-wrap: wrap; }
.confrow__q { font-size: var(--fs-sm); font-weight: 700; color: var(--ink-soft); }
.confrow .btn.is-picked { background: var(--brand); color: #fff; border-color: transparent; }

/* cloze fill-in-the-blank */
.cloze { font-size: var(--fs-md); line-height: 2.1; }
.cloze__blank {
  font: inherit; width: auto; min-width: 60px;
  border: none; border-bottom: 2px solid var(--brand);
  background: var(--surface-sunk); border-radius: var(--r-sm) var(--r-sm) 0 0;
  padding: 2px 6px; margin: 0 2px; color: var(--ink);
}
.cloze__blank:focus-visible { outline: none; background: var(--brand-tint); }
.cloze__blank.is-correct { border-color: var(--ok); background: var(--ok-tint); color: var(--ok-ink); }
.cloze__blank.is-wrong { border-color: var(--retry); background: var(--retry-tint); color: var(--retry-ink); }

/* math keypad */
.mathpad {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(40px, 1fr));
  gap: 4px; margin-top: var(--s-3); max-width: 340px;
}
.mathpad__key {
  font: inherit; font-size: var(--fs-md); cursor: pointer;
  aspect-ratio: 1; border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  background: var(--surface); color: var(--ink);
}
.mathpad__key:hover { background: var(--surface-sunk); border-color: var(--brand); }

/* adaptive-pacing note in a session */
.adaptive:not(:empty) { margin-bottom: var(--s-3); }

/* test-mode banner + its "turn off / turn on" toggle */
.testbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px 16px; flex-wrap: wrap; margin-bottom: var(--s-3);
}
.testbar .btn { flex: none; }

/* Pomodoro countdown in the session header */
.pomo {
  font-variant-numeric: tabular-nums; font-weight: 700; font-size: var(--fs-sm);
  padding: 4px var(--s-2); border-radius: var(--r-sm);
  background: var(--surface-sunk); color: var(--ink-soft);
}
.pomo.is-up { background: var(--retry-tint); color: var(--retry-ink); }

/* exam clock in the session header */
.examtimer {
  display: inline-flex; align-items: center; gap: 5px;
  font-variant-numeric: tabular-nums; font-weight: 700; font-size: var(--fs-sm);
  padding: 4px var(--s-2); border-radius: var(--r-sm);
  background: var(--surface-sunk); color: var(--ink-soft);
}
.examtimer--warn { background: var(--retry-tint); color: var(--retry-ink); }
.examtimer svg { flex: none; }

/* editor: drag handle + reorder */
.qedit__grip { cursor: grab; color: var(--ink-faint); font-size: 16px; user-select: none; padding: 0 4px; }
.qedit.is-dragging { opacity: .5; }
.qedit.is-dragover { outline: 2px dashed var(--brand); outline-offset: 2px; }

/* sample gallery */
.gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--s-4); }
.gallery__card {
  display: flex; flex-direction: column; gap: 6px; align-items: flex-start;
  padding: var(--s-4); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface);
}
.gallery__card h3 { font-size: var(--fs-md); }
.gallery__card .btn { margin-top: auto; }
.gallery__preview { font-style: italic; color: var(--ink-soft); font-size: var(--fs-sm); }

/* practice library */
.libgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: var(--s-3); margin-top: var(--s-4); }
.libcard {
  border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--s-4);
  background: var(--surface); display: flex; flex-direction: column; gap: var(--s-3);
  justify-content: space-between; min-width: 0;
}
.libcard__title { font-family: var(--font-display); font-weight: 600; font-size: var(--fs-md); }
.libcard__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2); flex-wrap: wrap; }
/* An already-added set: clear "in your library" state + Study as the primary. */
.libcard--added { border-color: color-mix(in srgb, var(--ok-bright) 38%, var(--line)); }
.libcard__added { display: inline-flex; align-items: center; gap: 4px; font-size: var(--fs-sm); font-weight: 700; color: var(--ok-ink); }
.libcard__added svg { flex: none; }
.libcard__count { font-weight: 400; color: var(--ink-faint); }
/* "Exam-mode options" disclosure above the set grid. */
.libexam { margin-top: var(--s-3); }
.libexam summary, .set-ai-how summary {
  cursor: pointer; list-style: none; display: inline-flex; align-items: center; gap: 6px;
  font-weight: 600; font-size: var(--fs-sm); color: var(--ink-soft);
}
.libexam summary::-webkit-details-marker, .set-ai-how summary::-webkit-details-marker { display: none; }
.libexam summary::before, .set-ai-how summary::before {
  content: "▸"; font-size: 10px; color: var(--ink-faint); transition: transform var(--dur-fast);
}
.libexam[open] summary::before, .set-ai-how[open] summary::before { transform: rotate(90deg); }
.libexam summary svg { flex: none; }

/* first-run walkthrough */
.onb .modal__card { max-width: 400px; text-align: center; position: relative; }
.onb__emoji { line-height: 1; margin-bottom: var(--s-3); color: var(--brand); }
.onb__emoji svg { display: inline-block; }
.onb__body h2 { font-size: var(--fs-lg); margin-bottom: var(--s-2); }
.onb__body p { color: var(--ink-soft); }
.onb__dots { display: flex; gap: 6px; justify-content: center; margin: var(--s-4) 0; }
.onb__dot { width: 7px; height: 7px; border-radius: var(--r-full); background: var(--line-strong); }
.onb__dot.is-on { background: var(--brand); }
.onb__nav { display: flex; align-items: center; gap: var(--s-2); }
.onb__x { position: absolute; top: 8px; right: 12px; border: none; background: none; font-size: 20px; cursor: pointer; color: var(--ink-faint); }

/* command palette */
.cmd { align-items: flex-start; padding-top: 12vh; }
.cmd__card { background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--r-lg); box-shadow: var(--shadow-2); width: 100%; max-width: 480px; overflow: hidden; }
.cmd__input { width: 100%; border: none; border-bottom: 1px solid var(--line); padding: var(--s-4); font: inherit; font-size: var(--fs-md); background: var(--surface); color: var(--ink); }
.cmd__input:focus-visible { outline: none; }
.cmd__list { max-height: 320px; overflow-y: auto; }
.cmd__row { width: 100%; display: flex; justify-content: space-between; gap: var(--s-3); align-items: center; padding: 10px var(--s-4); border: none; background: none; font: inherit; color: var(--ink); cursor: pointer; text-align: left; }
.cmd__row.is-sel { background: var(--brand-tint); }
.cmd__sub { font-size: 12px; color: var(--ink-faint); flex: none; }

/* Weekly recap — a slim line at the top of the Today panel */
.recap {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  padding: 6px var(--s-3); margin-bottom: var(--s-3);
  border-radius: var(--r-sm); border: 1px solid var(--line);
  background: var(--surface-sunk); font-size: var(--fs-sm);
}
.recap > div { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; min-width: 0; }
.recap strong { font-family: var(--font-display); }
.recap__body { color: var(--ink-soft); }
.recap__x {
  flex: none; border: none; background: none; cursor: pointer;
  font-size: 18px; line-height: 1; color: var(--ink-faint); padding: 2px 4px;
}
.recap__x:hover { color: var(--ink); }
/* In the "Idag" panel the recap sits last now — after the resume banner and
   the stat pills — so it needs a top gap, not a bottom one. */
.home-panel--today > .recap { margin: var(--s-3) 0 0; }

.nav-row { display: flex; justify-content: space-between; gap: var(--s-3); margin-top: var(--s-5); }

/* ---------- tutor chat ---------- */
.mascot { position: relative; display: inline-flex; flex: none; }
.mascot__dot {
  position: absolute; right: -2px; bottom: -2px; width: 10px; height: 10px;
  border-radius: var(--r-full); border: 2px solid var(--surface);
}
.mascot__dot--pulse { animation: mascot-pulse 1.1s ease-in-out infinite; }
@keyframes mascot-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

.tutor { position: sticky; top: 76px; display: flex; flex-direction: column; max-height: calc(100vh - 100px); }
.tutor__head { display: flex; align-items: center; gap: var(--s-3); padding: var(--s-4); border-bottom: 1px solid var(--line); }
.tutor__title { font-family: var(--font-display); font-weight: 600; }
.tutor__sub { font-size: var(--fs-sm); color: var(--ink-faint); }
.tutor__log { flex: 1; overflow-y: auto; padding: var(--s-4); display: flex; flex-direction: column; gap: var(--s-3); }
.msg { max-width: 92%; padding: var(--s-3) var(--s-4); border-radius: var(--r-md); font-size: var(--fs-sm); line-height: 1.5; }
.msg p + p { margin-top: .5em; }
.msg.ai { background: var(--surface-sunk); align-self: flex-start; border-bottom-left-radius: 4px; }
.msg.me { background: var(--brand); color: #fff; align-self: flex-end; border-bottom-right-radius: 4px; }
.msg.ai code, .msg.me code { background: rgba(0,0,0,.08); padding: 1px 4px; border-radius: 4px; }

/* typing indicator — the tutor thread and the site help chat both use it */
.typing span { display: inline-block; width: 6px; height: 6px; margin: 0 1px; border-radius: 50%; background: var(--ink-faint); animation: blink 1.2s infinite both; }
.typing span:nth-child(2) { animation-delay: .2s; }
.typing span:nth-child(3) { animation-delay: .4s; }
@keyframes blink { 0%,80%,100% { opacity: .2; } 40% { opacity: 1; } }

/* ---------- site help chat (floating widget, any page) ---------- */
.sitechat__fab {
  position: fixed; right: var(--s-4); bottom: var(--s-4); z-index: 40;
  width: 56px; height: 56px; border-radius: var(--r-full);
  display: grid; place-items: center; border: none; cursor: pointer;
  background: var(--brand); color: #fff; box-shadow: var(--shadow-2);
  transition: transform var(--dur-fast) var(--ease-spring);
}
.sitechat__fab:hover { transform: translateY(-2px) scale(1.05); }
.sitechat__panel {
  position: fixed; right: var(--s-4); bottom: calc(var(--s-4) + 56px + var(--s-3)); z-index: 40;
  width: min(360px, calc(100vw - var(--s-4) * 2));
  height: min(520px, calc(100vh - 160px));
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--shadow-2); overflow: hidden;
}
.sitechat__head { display: flex; align-items: center; gap: var(--s-3); padding: var(--s-4); border-bottom: 1px solid var(--line); }
.sitechat__title { font-family: var(--font-display); font-weight: 700; }
.sitechat__sub { font-size: var(--fs-sm); color: var(--ink-faint); }
.sitechat__log { flex: 1; overflow-y: auto; padding: var(--s-4); display: flex; flex-direction: column; gap: var(--s-3); }
.sitechat__form { display: flex; gap: var(--s-2); padding: var(--s-3); border-top: 1px solid var(--line); }
.sitechat__input { flex: 1; font: inherit; padding: var(--s-2) var(--s-3); border-radius: var(--r-full); border: 1px solid var(--line-strong); background: var(--surface); }
.sitechat__input:focus-visible { border-color: var(--brand); box-shadow: none; }
.sitechat__dormant { padding: var(--s-4); text-align: center; color: var(--ink-soft); }
.sitechat__dormant p:first-child { font-family: var(--font-display); font-weight: 600; margin-bottom: var(--s-2); color: var(--ink); }
/* Out of the way during a running session / worksheet. */
body.route-immersive .sitechat__fab,
body.route-immersive .sitechat__panel { display: none; }
/* Clear of the fixed bottom tab bar on mobile. */
@media (max-width: 899.98px) {
  .sitechat__fab { bottom: calc(70px + var(--s-3) + env(safe-area-inset-bottom, 0)); }
  .sitechat__panel { bottom: calc(70px + 56px + var(--s-4) + env(safe-area-inset-bottom, 0)); }
}
@media (max-width: 480px) {
  .sitechat__panel { right: var(--s-3); left: var(--s-3); width: auto; }
}
.tutor__form { display: flex; gap: var(--s-2); padding: var(--s-3); border-top: 1px solid var(--line); }
.tutor__input { flex: 1; font: inherit; padding: var(--s-2) var(--s-3); border-radius: var(--r-full); border: 1px solid var(--line-strong); background: var(--surface); }
.tutor__input:focus-visible { border-color: var(--brand); box-shadow: none; }
.voice-btn { position: relative; color: var(--ink-faint); }
.voice-btn:hover { color: var(--ink-soft); }
.voice-btn:disabled { opacity: .4; cursor: not-allowed; }
.voice-btn.is-listening {
  color: var(--retry-ink); background: var(--retry);
  animation: voicepulse 1.3s ease-in-out infinite;
}
@keyframes voicepulse { 0%, 100% { box-shadow: 0 0 0 0 var(--retry); } 50% { box-shadow: 0 0 0 5px transparent; } }
@media (prefers-reduced-motion: reduce) { .voice-btn.is-listening { animation: none; } }
.voice-soon {
  position: absolute; bottom: -3px; right: -3px;
  font-size: 8px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
  background: var(--retry); color: #2B2B3A; padding: 1px 4px; border-radius: var(--r-full);
  line-height: 1.3;
}
.tooltip { position: relative; }
.tooltip[data-tip]:hover::after {
  content: attr(data-tip); position: absolute; bottom: calc(100% + 6px); right: 0;
  background: var(--ink); color: #fff; font-size: 12px; font-weight: 600;
  padding: 4px 8px; border-radius: 6px; white-space: nowrap;
}
.tutor__locked { padding: var(--s-4); text-align: center; color: var(--ink-soft); }
.tutor__locked p:first-child { font-family: var(--font-display); font-weight: 600; margin-bottom: var(--s-2); }

.typing span { display: inline-block; width: 6px; height: 6px; margin: 0 1px; border-radius: 50%; background: var(--ink-faint); animation: blink 1.2s infinite both; }
.typing span:nth-child(2) { animation-delay: .2s; }
.typing span:nth-child(3) { animation-delay: .4s; }

/* ---------- results ---------- */
.results { text-align: center; max-width: 560px; margin: 0 auto; }
.scorering { width: 180px; height: 180px; margin: var(--s-4) auto; }
.scorering circle { fill: none; stroke-width: 14; }
.scorering .bg { stroke: var(--surface-sunk); }
.scorering .fg { stroke: var(--ok); stroke-linecap: round; transform: rotate(-90deg); transform-origin: 50% 50%;
  transition: stroke-dashoffset 900ms var(--ease-out); }
.scorering text { font-family: var(--font-display); font-weight: 700; fill: var(--ink); }
.delta-list { display: flex; flex-direction: column; gap: var(--s-2); margin: var(--s-5) 0; text-align: left; }
.delta { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); padding: var(--s-3) var(--s-4); border-radius: var(--r-md); background: var(--surface); border: 1px solid var(--line); }
.delta__bar { flex: 1; height: 8px; border-radius: var(--r-full); background: var(--surface-sunk); overflow: hidden; margin: 0 var(--s-3); }
.delta__bar i { display: block; height: 100%; background: var(--ok); border-radius: inherit; transition: width var(--dur-slow) var(--ease-out); }
.delta__n { font-size: var(--fs-sm); font-weight: 700; }
.delta__n.up { color: var(--ok); } .delta__n.down { color: var(--retry-ink); }

/* Wrong-answer review on the results screen — the correct answer + any
   authored explanation, reusing the .delta-list card look but stacked
   instead of the topic-mastery rows' left/right layout. */
.delta-list--review .delta--review { flex-direction: column; align-items: flex-start; gap: 6px; }
.delta__prompt { font-weight: 600; }
.delta__answer, .delta__explain { margin: 0; font-size: var(--fs-sm); }
.delta__explain { color: var(--ink-faint); }

/* "Scheduled for review" summary on the results screen. */
.results .srs-next { color: var(--ink); font-weight: 700; }

/* ---------- progress dashboard ---------- */
.dash { display: grid; gap: var(--s-5); }
.streak { display: flex; gap: 6px; flex-wrap: wrap; }
.streak__day { width: 30px; height: 30px; flex: none; border-radius: var(--r-sm); background: var(--surface-sunk); display: grid; place-items: center; font-size: 11px; color: var(--ink-faint); }
.streak__day.on { background: var(--ok); color: #fff; font-weight: 700; }
.streak__day.frozen { background: var(--info); color: #fff; font-weight: 700; }
/* "is-today", not ".today" — a bare .today rule elsewhere is a 210px grid. */
.streak__day.is-today { outline: 2px solid var(--brand); outline-offset: 1px; }

/* A banked streak freeze, shown next to the streak badge. */
.freezechip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px var(--s-2); border-radius: var(--r-full);
  background: var(--info-tint); color: var(--info);
  font-size: var(--fs-sm); font-weight: 700;
}

/* Achievements shelf */
.badges { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: var(--s-3); }
.badge-card {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4); border-radius: var(--r-md);
  border: 1px solid var(--line); background: var(--surface);
}
.badge-card__emoji { font-size: 26px; flex: none; line-height: 1; }
.badge-card__text { display: flex; flex-direction: column; min-width: 0; }
.badge-card__text strong { font-family: var(--font-display); font-size: var(--fs-md); }
.badge-card.is-locked { background: var(--surface-sunk); border-style: dashed; }
.badge-card.is-locked .badge-card__emoji { filter: grayscale(1); opacity: .45; }
.badge-card.is-locked .badge-card__text strong { color: var(--ink-soft); }

/* 12-week study heatmap — 7 rows (Mon-Sun), columns are weeks, oldest left. */
.heatmap {
  display: grid; grid-template-rows: repeat(7, 13px); grid-auto-flow: column;
  grid-auto-columns: 13px; gap: 3px; width: max-content; max-width: 100%;
}
.heatmap__cell { width: 13px; height: 13px; border-radius: 3px; background: var(--surface-sunk); }
.heatmap__cell.on { background: var(--ok); }
.heatmap__cell.frozen { background: var(--info); }
.heatmap__cell.is-today { outline: 2px solid var(--brand); outline-offset: 1px; }
.meter { display: grid; grid-template-columns: 120px 1fr 44px 26px; align-items: center; gap: var(--s-3); padding: var(--s-2) 0; }
.meter__track { height: 10px; border-radius: var(--r-full); background: var(--surface-sunk); overflow: hidden; }
.meter__fill { height: 100%; border-radius: inherit; background: var(--subject, var(--brand)); transition: width var(--dur-slow) var(--ease-out); }
/* "A long-term measure — your last result was X%" under a subject's meter, so
   the mastery % doesn't read as contradicting a recent test score. */
.meter__context { margin: 2px 0 6px; font-size: 12px; color: var(--ink-faint); }
.due-list { display: flex; flex-direction: column; gap: var(--s-2); }
.due-item { display: flex; justify-content: space-between; gap: var(--s-3); padding: var(--s-3) var(--s-4); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); }
.due-item__main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.due-item__why { font-size: var(--fs-sm); color: var(--ink-faint); }

/* Results "what you'll still remember" — two forgetting curves. */
.retention { width: 100%; max-width: 380px; height: auto; display: block; }
.retention__grid { stroke: var(--line); stroke-width: 1; }
.retention__lab { fill: var(--ink-faint); font-size: 9px; font-family: var(--font-body); }
.retention__without { fill: none; stroke: var(--retry); stroke-width: 2; stroke-dasharray: 4 3; stroke-linecap: round; }
.retention__with { fill: none; stroke: var(--brand); stroke-width: 2.5; stroke-linecap: round; }
.retention__dot { fill: var(--brand); }
.retention__legend { display: flex; gap: var(--s-4); flex-wrap: wrap; font-size: var(--fs-sm); color: var(--ink-soft); margin-top: 4px; }
.retention__k { display: inline-block; width: 16px; border-top: 2px solid; vertical-align: middle; margin-right: 6px; }
.retention__k--without { border-top-style: dashed; color: var(--retry); }
.retention__k--with { color: var(--brand); }
.retention__cap { font-size: 12px; color: var(--ink-faint); margin-top: 6px; }

/* Review-recall sparkline — bare line chart, scales to the panel width. */
.sparkline { width: 100%; height: auto; display: block; }
.sparkline polyline { fill: none; stroke: var(--brand); stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
.sparkline circle { fill: var(--brand); }
.sparkline circle.last { fill: var(--ok); }

/* ---------- exam prep ---------- */
.exam-prep { display: grid; gap: var(--s-4); max-width: 640px; }
.exam-prep h1 { margin: 0; }
.exam-prep .panel { margin: 0; }
.exam-prep__head { border-left: 3px solid var(--brand); }
.exam-prep__when { margin: 0; font-weight: 700; display: flex; align-items: center; gap: 6px; }
.exam-prep__when svg { flex: none; color: var(--brand-ink); }
.exam-prep__sets { display: flex; flex-direction: column; gap: var(--s-2); }
.exam-prep__set {
  display: flex; align-items: center; gap: var(--s-2);
  padding: 4px 8px 4px var(--s-3); border-radius: var(--r-md);
  background: var(--surface); border: 1px solid var(--line);
  transition: border-color var(--dur-fast);
}
.exam-prep__set:hover { border-color: var(--brand); }
.exam-prep__set-link { display: flex; align-items: center; gap: var(--s-3); flex: 1; min-width: 0; text-decoration: none; color: inherit; padding: 6px 0; }
.exam-prep__set-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.exam-prep__set-pct { flex: none; font-weight: 700; color: var(--ink-soft); }
.exam-prep__set svg { flex: none; color: var(--ink-faint); }
.exam-prep__actions { display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: center; }
.exam-prep__mock { display: flex; gap: var(--s-2); align-items: flex-end; flex-wrap: wrap; }
.exam-prep__pick { display: flex; flex-direction: column; gap: var(--s-2); margin-top: var(--s-3); }
.exam-prep__pick-card {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4); border-radius: var(--r-md);
  background: var(--surface); border: 1px solid var(--line);
  border-left: 3px solid var(--subject, var(--brand));
  text-decoration: none; color: inherit;
  transition: border-color var(--dur-fast), background var(--dur-fast);
}
.exam-prep__pick-card:hover { background: var(--surface-sunk); }
.exam-prep__pick-name { flex: 1; min-width: 0; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.exam-prep__pick-when {
  flex: none; font-size: 12px; font-weight: 600; color: var(--ink-soft);
  background: var(--surface-sunk); padding: 2px 8px; border-radius: var(--r-full);
}
.exam-prep__pick-pct { flex: none; font-weight: 700; color: var(--ink-soft); }
.exam-prep__pick-card svg { flex: none; color: var(--ink-faint); }

/* Subject % sits in the heading; the body is a per-topic breakdown, weakest
   first — one whole-subject bar isn't much to prep from. */
.exam-prep__mastery-head { display: flex; align-items: baseline; gap: var(--s-2); flex-wrap: wrap; }
.exam-prep__pct { font-family: var(--font-display); font-weight: 700; font-size: var(--fs-md); color: var(--ink-soft); }
.exam-prep__topics { display: flex; flex-direction: column; gap: 2px; margin-top: var(--s-2); }
.exam-prep__topics .meter { grid-template-columns: minmax(88px, 42%) 1fr 44px; }

/* The dated study plan — today → test day, one task per row. */
.exam-prep__plan { border-left: 3px solid var(--brand); }
.exam-prep__days { list-style: none; margin: var(--s-3) 0 0; padding: 0; display: grid; gap: 2px; }
.exam-prep__day {
  display: grid; grid-template-columns: 66px 1fr auto; align-items: center;
  gap: var(--s-3); padding: 8px 10px; border-radius: var(--r-sm); font-size: var(--fs-sm);
}
.exam-prep__day-when { color: var(--ink-faint); font-weight: 700; font-size: 12px; text-transform: capitalize; }
.exam-prep__day-task { min-width: 0; color: var(--ink-soft); }
.exam-prep__day-min { color: var(--ink-faint); font-size: 12px; flex: none; }
.exam-prep__day.is-today { background: var(--brand-tint); }
.exam-prep__day.is-today .exam-prep__day-task { color: var(--ink); font-weight: 600; }
.exam-prep__day.is-today .exam-prep__day-when { color: var(--brand-ink); }
.exam-prep__day.is-test .exam-prep__day-task { color: var(--ink-faint); font-style: italic; }
.exam-prep__day .btn--sm { padding: 4px 12px; white-space: nowrap; }

/* ---------- formula sheet ---------- */
.ref { max-width: 760px; }
.ref__tabs { margin-bottom: var(--s-4); flex-wrap: wrap; }
.ref__group { margin-bottom: var(--s-5); }
.ref__group h3 { font-size: var(--fs-md); color: var(--ink-soft); margin-bottom: var(--s-3); }
.ref__items { display: grid; gap: var(--s-2); }
.ref__item {
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface);
  padding: var(--s-3) var(--s-4);
}
.ref__item-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; }
.ref__name { font-weight: 600; font-size: var(--fs-sm); }
.ref__formula { font-size: var(--fs-md); overflow-x: auto; max-width: 100%; }
.ref__note { margin: 6px 0 0; font-size: 12px; color: var(--ink-faint); }

/* ---------- calculator & graph plotter ---------- */
.calc { max-width: 560px; }
.calc__screen { background: var(--surface-sunk); border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--s-3); }
.calc__screentop { display: flex; gap: var(--s-2); align-items: center; }
.calc__input {
  flex: 1; min-width: 0; font: inherit; font-size: var(--fs-md); font-family: ui-monospace, "SF Mono", Menlo, monospace;
  border: 1px solid var(--line-strong); border-radius: var(--r-sm); padding: 8px 10px;
  background: var(--surface); color: var(--ink);
}
.calc__output {
  margin-top: var(--s-2); text-align: right; font-size: var(--fs-xl); font-weight: 700;
  font-family: ui-monospace, "SF Mono", Menlo, monospace; color: var(--ink);
  overflow-x: auto; min-height: 1.4em;
}
.calc__output.is-error { color: var(--retry-ink); font-size: var(--fs-md); font-weight: 500; }
.calc__pad { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; margin-top: var(--s-3); }
.calc__key {
  font: inherit; font-size: var(--fs-md); font-weight: 600; padding: 12px 0;
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  background: var(--surface); color: var(--ink); cursor: pointer;
  transition: background var(--dur-fast);
}
.calc__key:hover { background: var(--surface-sunk); }
.calc__key--op { color: var(--brand-ink); }
.calc__key--eq { background: var(--brand); color: #fff; border-color: var(--brand); }
.calc__key--eq:hover { background: var(--brand-bright); }
.calc__history { margin-top: var(--s-3); display: grid; gap: 4px; }
.calc__hrow {
  display: flex; justify-content: space-between; gap: var(--s-3); width: 100%;
  border: none; background: none; cursor: pointer; padding: 4px 6px; border-radius: var(--r-sm);
  font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12px; color: var(--ink-soft); text-align: left;
}
.calc__hrow:hover { background: var(--surface-sunk); }
.calc__hval { color: var(--ink); font-weight: 700; flex: none; }
.calc__grange { display: flex; gap: var(--s-3); align-items: end; flex-wrap: wrap; margin-bottom: var(--s-3); }
.calc__grange label { font-size: var(--fs-sm); color: var(--ink-soft); display: flex; flex-direction: column; gap: 4px; }
.calc__num { width: 90px; font: inherit; padding: 6px 8px; border: 1px solid var(--line-strong); border-radius: var(--r-sm); background: var(--surface); color: var(--ink); }
.calc__canvaswrap { border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; background: var(--surface); }
.calc__canvas { display: block; width: 100%; height: auto; }
.calc__gmsg.is-error { color: var(--retry-ink); }

/* ---------- create flow ---------- */
.steps { display: flex; gap: var(--s-2); margin-bottom: var(--s-5); flex-wrap: wrap; }
.step { font-size: var(--fs-sm); font-weight: 700; color: var(--ink-faint); display: flex; align-items: center; gap: 6px; }
.step.on { color: var(--brand-ink); }
.step__n { width: 22px; height: 22px; border-radius: var(--r-full); background: var(--surface-sunk); display: grid; place-items: center; font-size: 12px; }
.step.on .step__n { background: var(--brand); color: #fff; }

.source-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--s-3); }
.source-opt { position: relative; cursor: pointer; padding: var(--s-5) var(--s-4); border-radius: var(--r-md); border: 2px solid var(--line-strong); background: var(--surface); text-align: center; font-family: var(--font-display); font-weight: 600; transition: transform var(--dur-fast) var(--ease-spring), border-color var(--dur-fast); }
.source-opt:hover { transform: translateY(-2px); border-color: var(--brand); }
.source-opt svg { display: block; margin: 0 auto var(--s-2); }
/* An AI-generated source with no server to run it — still openable (you can
   prepare the material), just clearly marked as not ready to generate. */
.source-opt--locked { border-style: dashed; border-color: var(--line); opacity: .82; }
.source-opt--locked svg { opacity: .7; }
.source-opt__tag {
  display: inline-block; margin-top: var(--s-2); font-size: 11px; font-weight: 700;
  color: var(--ink-faint); background: var(--surface-sunk);
  padding: 2px 8px; border-radius: var(--r-full);
}

/* Library's subject picker only — every subject otherwise looked identical
   (same brand-blue book icon), which read as flat. Same colour-accent idiom
   .acard already uses (a solid stripe, not a full-saturation border all
   round), so it matches how subjects look everywhere else in the app. */
.source-opt--subject { border-left: 6px solid var(--subject, var(--line-strong)); }
.source-opt--subject svg { color: var(--subject, var(--brand)); }
.source-opt--subject:hover { border-color: var(--subject, var(--brand)); }

/* Library's subject picker on a phone: the big centred cards meant three
   subjects filled the screen and åk 9 (thirteen subjects) was ~4 screens of
   scrolling. Collapse to single-line rows — colour stripe + icon + name; the
   blurb shows on the set-list header you land on. Desktop keeps the cards. */
@media (max-width: 560px) {
  .source-grid--subjects { display: flex; flex-direction: column; gap: var(--s-2); }
  .source-grid--subjects .source-opt--subject {
    display: flex; align-items: center; gap: var(--s-3);
    text-align: left; padding: var(--s-3) var(--s-4);
  }
  .source-grid--subjects .source-opt--subject svg { margin: 0; flex: none; width: 20px; height: 20px; }
  .source-grid--subjects .source-opt--subject .note { display: none; }
}

/* Subject field: text input + one-tap chips for the subjects that exist. */
.subjectfield { display: flex; flex-direction: column; gap: var(--s-2); }
.subjchips { display: flex; flex-wrap: wrap; gap: 6px; }
.subjchips .chip { font-size: var(--fs-sm); font-weight: 600; }

.field { display: block; margin-bottom: var(--s-4); }
.field > span { display: block; font-weight: 700; font-size: var(--fs-sm); margin-bottom: var(--s-2); }
.field input[type=text], .field input[type=number], .field input[type=email], .field input[type=password], .field select, .field textarea {
  width: 100%; font: inherit; padding: var(--s-3); border-radius: var(--r-md);
  border: 1px solid var(--line-strong); background: var(--surface);
}
.field textarea { min-height: 160px; resize: vertical; }

.qedit { border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--s-4); margin-bottom: var(--s-3); background: var(--surface); }
.qedit__row { display: flex; gap: var(--s-2); align-items: center; margin-bottom: var(--s-2); }
.badge { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; padding: 2px 8px; border-radius: var(--r-full); background: var(--surface-sunk); color: var(--ink-soft); }
.dot { display: inline-block; width: 8px; height: 8px; flex: none; border-radius: 50%; }

.spinner { width: 42px; height: 42px; border-radius: 50%; border: 4px solid var(--surface-sunk); border-top-color: var(--brand); animation: spin .8s linear infinite; margin: var(--s-6) auto; }

/* ---------- settings ---------- */

/* The three "how it looks and sounds" controls sit side by side and wrap on
   narrow screens, instead of stacking as three separate panels. */
.settings__row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--s-3); margin-top: var(--s-3);
}
.settings__row .field { margin-bottom: 0; }
.settings__row + .note { margin-top: var(--s-3); }
.settings__row + .note + .note { margin-top: var(--s-2); }

/* Each control with its own help text directly beneath it — one field per
   grid cell — instead of a row of controls and a clump of notes under them. */
.settings__fields {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: var(--s-4) var(--s-5); align-items: start; margin-top: var(--s-3);
}
.settings__fields .field { margin-bottom: 0; }
.panel > .note + .settings__fields { margin-top: var(--s-3); }
.field__note { margin-top: var(--s-2); }

/* Offline-library download progress in Settings. */
.offline-bar { height: 8px; border-radius: var(--r-full); background: var(--surface-sunk); overflow: hidden; margin-bottom: var(--s-3); }
.offline-bar i { display: block; height: 100%; width: 0; background: var(--brand); border-radius: inherit; transition: width var(--dur-mid) var(--ease-out); }

/* Sub-heading inside a merged panel: a hairline rule carries the grouping so
   the panel does not need to become two panels again. */
.settings__sub {
  font-family: var(--font-display); font-size: var(--fs-md); font-weight: 700;
  margin-top: var(--s-5); padding-top: var(--s-4); border-top: 1px solid var(--line);
}
.panel > .settings__sub:first-of-type { margin-top: var(--s-4); padding-top: 0; border-top: none; }

/* Not a panel — the roadmap is information, not something you can change. */
.roadmapbox > h3 { font-size: var(--fs-md); color: var(--ink-soft); margin-bottom: var(--s-3); }
.settings { display: grid; gap: var(--s-5); max-width: 620px; }
.keyrow { display: flex; gap: var(--s-2); }
.note { font-size: var(--fs-sm); color: var(--ink-soft); }
.note--warn { background: var(--retry-tint); color: var(--retry-ink); padding: var(--s-3) var(--s-4); border-radius: var(--r-md); }
.preset-table { width: 100%; border-collapse: collapse; margin-top: var(--s-3); font-size: var(--fs-sm); }
.preset-table th, .preset-table td { text-align: left; padding: 6px 8px; border-top: 1px solid var(--line); }
.preset-table th { font-weight: 600; color: var(--ink-soft); width: 40%; }
.preset-table td:nth-child(2) { font-weight: 700; }

.roadmap { list-style: none; display: grid; gap: var(--s-2); }
.roadmap li { display: flex; align-items: center; gap: var(--s-3); padding: var(--s-3) var(--s-4); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); color: var(--ink-soft); }
.roadmap li::before { content: "🔜"; }

.toast {
  position: fixed; left: 50%; bottom: var(--s-5); transform: translateX(-50%) translateY(20px);
  /* Fixed dark pill regardless of theme — var(--ink) flips to near-white in
     dark mode, which left the hardcoded white text unreadable against it. */
  background: #2B2B3A; color: #fff; padding: var(--s-3) var(--s-5); border-radius: var(--r-full);
  font-weight: 600; font-size: var(--fs-sm); box-shadow: var(--shadow-2);
  opacity: 0; pointer-events: none; transition: opacity var(--dur-mid), transform var(--dur-mid) var(--ease-spring);
  z-index: 50;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.toast:has(.toast__action) { display: flex; align-items: center; gap: var(--s-3); }
.toast:has(.toast__action).show { pointer-events: auto; }
.toast__action {
  font: inherit; font-weight: 700; cursor: pointer;
  background: none; border: none; color: #9FBCFF;
  text-decoration: underline; text-underline-offset: 3px; padding: 0;
}
.toast__action:hover { color: #fff; }

/* Persistent top banner (js/lib/dom.js showBanner) — unlike .toast, never
   auto-dismisses. Reserved for things the student must actually see, like a
   failed save. */
.savebar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 110;
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  background: var(--retry-tint); color: var(--retry-ink);
  font-weight: 600; font-size: var(--fs-sm);
  transform: translateY(-100%); transition: transform var(--dur-mid) var(--ease-out);
}
.savebar.show { transform: translateY(0); }
.savebar__action {
  margin-left: auto; font: inherit; font-weight: 700; cursor: pointer;
  background: none; border: none; color: inherit;
  text-decoration: underline; text-underline-offset: 3px; padding: 0;
}
.savebar__close {
  font: inherit; cursor: pointer; background: none; border: none; color: inherit;
  font-size: 18px; line-height: 1; padding: 0 2px; flex: none;
}

/* ---------- animations ---------- */
@keyframes pop-in { from { opacity: 0; transform: translateY(8px) scale(.98); } to { opacity: 1; transform: none; } }
@keyframes pop { 0% { transform: scale(1); } 40% { transform: scale(1.04); } 100% { transform: scale(1); } }
@keyframes nudge { 0%,100% { transform: translateX(0); } 25% { transform: translateX(-4px); } 75% { transform: translateX(4px); } }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes blink { 0%,80%,100% { opacity: .2; } 40% { opacity: 1; } }

/* ---------- shortcuts modal ---------- */
.modal {
  position: fixed; inset: 0; z-index: 80;
  background: rgba(20, 18, 28, .5); backdrop-filter: blur(3px);
  display: grid; place-items: center; padding: var(--s-4);
}
.modal__card {
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--r-lg); box-shadow: var(--shadow-2);
  padding: var(--s-5); max-width: 420px; width: 100%;
}

/* "Review answered" panel — practice / review look-back at what's been done. */
.review-so-far { max-width: 560px; max-height: 82vh; display: flex; flex-direction: column; }
.review-so-far__list { overflow-y: auto; display: flex; flex-direction: column; gap: var(--s-3); }
.review-so-far__item {
  border: 1px solid var(--line); border-left: 3px solid var(--line-strong);
  border-radius: var(--r-md); padding: var(--s-3) var(--s-4); background: var(--surface);
}
.review-so-far__item.is-ok { border-left-color: var(--ok); }
.review-so-far__item.is-miss { border-left-color: var(--retry); }
.review-so-far__q { display: flex; gap: var(--s-2); font-weight: 600; font-size: var(--fs-sm); }
.review-so-far__n { flex: none; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.review-so-far__verdict { display: flex; align-items: center; gap: 5px; margin-top: 6px; font-size: 12px; font-weight: 700; color: var(--ink-soft); }
.review-so-far__item.is-ok .review-so-far__verdict { color: var(--ok-ink); }
.review-so-far__item.is-miss .review-so-far__verdict { color: var(--retry-ink); }
.review-so-far__ans { margin: 6px 0 0; font-size: var(--fs-sm); color: var(--ink); }
.review-so-far__exp { margin: 4px 0 0; font-size: 12px; color: var(--ink-soft); line-height: 1.5; }

/* "This is a test" toggle in the Deadline dialog. */
.due-dialog__test { display: flex; align-items: flex-start; gap: var(--s-2); margin-top: var(--s-3); font-size: var(--fs-sm); color: var(--ink-soft); cursor: pointer; }
.due-dialog__test input { margin-top: 2px; flex: none; }
.due-dialog__test span { display: flex; align-items: center; gap: 6px; }
.due-dialog__test svg { flex: none; color: var(--ink-faint); }

/* Centred yes/no dialog — replaces window.confirm. */
.confirmdlg__card { max-width: 400px; }
.confirmdlg__body {
  font-family: var(--font-display); font-weight: 600; font-size: var(--fs-lg);
  line-height: 1.3; color: var(--ink);
}
.confirmdlg__note {
  margin-top: var(--s-3); padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md); background: var(--ok-tint); color: var(--ok-ink);
  font-size: var(--fs-sm); line-height: 1.4;
}
.confirmdlg--danger .confirmdlg__note { background: var(--retry-tint); color: var(--retry-ink); }
.confirmdlg__actions {
  display: flex; justify-content: flex-end; gap: var(--s-3);
  margin-top: var(--s-5); flex-wrap: wrap;
}
.confirmdlg__actions .btn { flex: 1 1 auto; justify-content: center; min-width: 120px; }
kbd {
  font-family: ui-monospace, "Cascadia Code", Consolas, monospace; font-size: 12px;
  background: color-mix(in srgb, var(--ink) 20%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--ink) 52%, var(--surface));
  border-bottom-width: 2px; border-radius: 6px; padding: 2px 6px; color: var(--ink);
  white-space: nowrap;
}
/* .kbdhint removed — the shortcuts button in the session header replaced it. */

/* The floating hint button only exists on narrow screens. */
.hintfab { display: none; }

/* ---------- responsive ---------- */
@media (max-width: 860px) {
  .session { grid-template-columns: 1fr; }

  /* Tutor becomes a slide-up sheet so a stuck student never has to scroll
     away from the question to reach help. */
  .tutor {
    /* top: auto matters — the desktop rule sets top for sticky, and a fixed
       box with both top and bottom set ignores bottom. */
    position: fixed; top: auto; left: 0; right: 0; bottom: 0; z-index: 70;
    max-height: min(70vh, 520px);
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    border-bottom: none;
    box-shadow: var(--shadow-2);
    transform: translateY(101%);
    transition: transform var(--dur-mid) var(--ease-out);
  }
  .tutor.is-open { transform: translateY(0); }

  .hintfab {
    display: block; position: fixed; z-index: 71;
    left: 50%; bottom: var(--s-4); transform: translateX(-50%);
    font-family: var(--font-display); font-weight: 600; font-size: var(--fs-sm);
    padding: var(--s-3) var(--s-5); border-radius: var(--r-full);
    border: none; background: var(--brand); color: #fff;
    box-shadow: var(--shadow-2); cursor: pointer;
  }
  .tutor.is-open ~ .hintfab { bottom: calc(min(70vh, 520px) + var(--s-3)); }

  /* Keep the last controls clear of the floating button. */
  .nav-row { margin-bottom: var(--s-8); }
}
@media (max-width: 540px) {
  .content { padding: var(--s-5) var(--s-3) var(--s-7); }
  h1 { font-size: 1.6rem; }
  .grid { grid-template-columns: 1fr 1fr; }
  .meter { grid-template-columns: 88px 1fr 40px 26px; gap: var(--s-2); }
}
@media (max-width: 380px) {
  .grid { grid-template-columns: 1fr; }
}

/* ---------- print worksheet ---------- */
.psheet__bar { display: flex; gap: 10px; margin-bottom: var(--s-5); }
.psheet__title { font-size: var(--fs-xl); margin-bottom: 4px; }
.psheet__meta { color: var(--ink-soft); font-size: var(--fs-sm); margin-bottom: var(--s-5); }
.psheet__item { margin-bottom: var(--s-5); break-inside: avoid; }
.psheet__q { margin-bottom: 8px; }
.psheet__choices { margin: 4px 0 0 22px; }
.psheet__choices li { list-style: upper-alpha; margin: 2px 0; }
.psheet__lines { height: 64px; border-bottom: 1px solid var(--line-strong);
  background: repeating-linear-gradient(var(--surface), var(--surface) 27px, var(--line) 28px); }
.psheet__break { break-before: page; margin-top: var(--s-8); }
.psheet__akey { margin: 3px 0; font-size: var(--fs-sm); }

/* ---------- shell: leaderboard + bell + account actions ---------- */
.topbar__actions { display: flex; align-items: center; gap: var(--s-2); }
.topbar__actions .iconbtn.is-active { color: var(--brand); border-color: var(--brand); }
.topbar__bell { position: relative; }
.topbar__dot {
  position: absolute; top: 5px; right: 5px; width: 9px; height: 9px;
  border-radius: var(--r-full); background: var(--retry); border: 2px solid var(--surface);
}

.popover {
  position: absolute; z-index: 60; max-width: calc(100vw - 16px);
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--r-md); box-shadow: var(--shadow-2);
  padding: 6px; display: flex; flex-direction: column; gap: 2px;
}
.popover .note { margin: 0; }

/* ---------- notification panel ---------- */
.notifpanel__head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2); padding: var(--s-2) var(--s-2) var(--s-3); }
.notifpanel__head h3 { font-size: var(--fs-md); }
.notiftabs { display: flex; gap: 4px; padding: 0 var(--s-1) var(--s-2); border-bottom: 1px solid var(--line); margin-bottom: var(--s-2); }
.notiftab {
  flex: 1; display: flex; align-items: center; justify-content: center; gap: 6px;
  font: inherit; font-size: var(--fs-sm); font-weight: 600;
  padding: var(--s-2) var(--s-3); border: none; border-radius: var(--r-full);
  background: transparent; color: var(--ink-soft); cursor: pointer;
}
.notiftab[aria-selected="true"] { background: var(--surface-sunk); color: var(--ink); }
.notiftab__count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: var(--r-full);
  background: var(--brand); color: #fff; font-size: 11px; font-weight: 700;
}
.notifpanel__body { max-height: min(420px, 60vh); overflow-y: auto; display: flex; flex-direction: column; gap: 4px; }
.notifcard { display: flex; gap: var(--s-3); padding: var(--s-3); border-radius: var(--r-md); background: transparent; }
.notifcard--unread { background: var(--brand-tint); }
.notifcard__icon {
  flex: none; width: 34px; height: 34px; border-radius: var(--r-full);
  display: flex; align-items: center; justify-content: center;
  background: var(--surface-sunk); color: var(--ink-soft);
}
.notifcard--unread .notifcard__icon { background: var(--brand); color: #fff; }
.notifcard__main { flex: 1; min-width: 0; }
.notifcard__title { font-weight: 700; font-size: var(--fs-sm); margin-bottom: 2px; }
.notifcard__text { font-size: var(--fs-sm); color: var(--ink-soft); margin-bottom: var(--s-2); }
.notifcard__footer { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2); font-size: var(--fs-sm); flex-wrap: wrap; }
.notifcard__link { color: var(--brand-ink); font-weight: 600; text-decoration: underline; }
.notifcard__meta { color: var(--ink-faint); white-space: nowrap; }
.notifcard__mark {
  flex: none; align-self: flex-start; width: 30px; height: 30px; border-radius: var(--r-full);
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink-soft);
  cursor: pointer; transition: background var(--dur-fast), color var(--dur-fast), border-color var(--dur-fast);
}
.notifcard__mark:hover { background: var(--ok); color: #fff; border-color: transparent; }

/* ---------- achievements page ---------- */
.achievements-page { display: flex; flex-direction: column; gap: var(--s-5); }
.achievements-head h1 { margin-bottom: 4px; }
.ach-summary__bar { margin-top: var(--s-3); height: 8px; border-radius: var(--r-full); background: var(--surface-sunk); overflow: hidden; max-width: 360px; }
.ach-summary__bar i { display: block; height: 100%; border-radius: inherit; background: var(--brand); transition: width var(--dur-slow) var(--ease-out); }
.achgroup__title { display: flex; align-items: center; gap: var(--s-2); margin-bottom: var(--s-4); color: var(--ink-soft); font-size: var(--fs-md); }
.achrow { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: var(--s-3); }

/* "Next up" hero — leads the page so day one isn't just a wall of locked
   grey. The one badge closest to earned (nextAchievement), with its bar. */
.ach-hero {
  display: flex; gap: var(--s-3); align-items: center;
  padding: var(--s-4); border-radius: var(--r-md);
  background: linear-gradient(135deg, var(--brand-tint), var(--surface-sunk));
  border: 1px solid color-mix(in srgb, var(--brand) 24%, transparent);
}
.ach-hero__icon {
  flex: none; width: 44px; height: 44px; border-radius: var(--r-full);
  display: grid; place-items: center; background: var(--brand); color: #fff;
}
.ach-hero__body { min-width: 0; flex: 1; }
.ach-hero__eyebrow { margin: 0; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--brand-ink); }
.ach-hero__name { display: block; font-family: var(--font-display); margin: 2px 0; }
.ach-hero__desc { margin: 0 0 var(--s-2); font-size: var(--fs-sm); color: var(--ink-soft); }
.ach-hero__bar { height: 6px; border-radius: var(--r-full); background: var(--surface-sunk); overflow: hidden; margin-bottom: 3px; }
.ach-hero__bar i { display: block; height: 100%; background: var(--brand-bright); transition: width var(--dur-slow) var(--ease-out); }

/* Per-track "show the tiers you haven't reached yet" — only the next unearned
   tier shows by default, the rest fold away so the page reads at a glance. */
.achgroup__more { margin-top: var(--s-3); }
.achgroup__more > summary {
  cursor: pointer; font-size: var(--fs-sm); font-weight: 600; color: var(--brand-ink);
  list-style: none; padding: 6px 0;
}
.achgroup__more > summary::-webkit-details-marker { display: none; }
.achgroup__more > summary::after { content: " ⌄"; }
.achgroup__more[open] > summary::after { content: " ⌃"; }
.achbadge {
  display: flex; gap: var(--s-3); align-items: flex-start;
  padding: var(--s-4); border-radius: var(--r-md);
  background: var(--surface); border: 1px solid var(--line);
  animation: pop-in var(--dur-mid) var(--ease-out) both;
}
.achbadge__icon {
  flex: none; width: 44px; height: 44px; border-radius: var(--r-full);
  display: grid; place-items: center;
  background: var(--surface-sunk); color: var(--ink-faint); border: 1px solid var(--line-strong);
}
.achbadge__body { min-width: 0; }
.achbadge__top { display: flex; align-items: center; gap: 6px; margin-bottom: 2px; }
.achbadge__tiername { font-weight: 700; font-size: var(--fs-sm); color: var(--ink-faint); text-transform: uppercase; letter-spacing: .03em; }
.achbadge__check { display: grid; place-items: center; flex: none; width: 18px; height: 18px; border-radius: var(--r-full); background: var(--ok); color: #fff; }
.achbadge__desc { font-size: var(--fs-sm); color: var(--ink-soft); line-height: 1.4; margin: 0 0 var(--s-2); }
.achbadge__unlockdate { font-size: var(--fs-sm); color: var(--ink-faint); font-weight: 600; margin: 0; }
.achbadge__progress { display: flex; align-items: center; gap: var(--s-2); }
.achbadge__bar { flex: 1; height: 6px; border-radius: var(--r-full); background: var(--surface-sunk); overflow: hidden; }
.achbadge__bar i { display: block; height: 100%; border-radius: inherit; background: var(--ink-faint); transition: width var(--dur-slow) var(--ease-out); }
.achbadge__fraction { font-size: var(--fs-sm); color: var(--ink-faint); font-variant-numeric: tabular-nums; flex: none; }
.achbadge--unlocked { border-color: transparent; }
.achbadge--unlocked.achbadge--bronze { background: var(--ach-bronze-tint); }
.achbadge--unlocked.achbadge--bronze .achbadge__icon { background: var(--ach-bronze); color: #fff; border-color: transparent; }
.achbadge--unlocked.achbadge--bronze .achbadge__tiername { color: var(--ach-bronze-ink); }
.achbadge--unlocked.achbadge--silver { background: var(--ach-silver-tint); }
.achbadge--unlocked.achbadge--silver .achbadge__icon { background: var(--ach-silver); color: #fff; border-color: transparent; }
.achbadge--unlocked.achbadge--silver .achbadge__tiername { color: var(--ach-silver-ink); }
.achbadge--unlocked.achbadge--gold { background: var(--ach-gold-tint); }
.achbadge--unlocked.achbadge--gold .achbadge__icon { background: var(--ach-gold); color: #fff; border-color: transparent; }
.achbadge--unlocked.achbadge--gold .achbadge__tiername { color: var(--ach-gold-ink); }
.achbadge--unlocked.achbadge--platinum { background: linear-gradient(135deg, var(--brand-tint), var(--c-ocean-tint) 60%, var(--c-berry-tint)); }
.achbadge--unlocked.achbadge--platinum .achbadge__icon {
  background: linear-gradient(135deg, var(--brand), var(--c-ocean) 55%, var(--c-berry));
  color: #fff; border-color: transparent;
  box-shadow: 0 0 0 3px var(--surface), 0 0 14px rgba(58, 106, 224, .38);
}
.achbadge--unlocked.achbadge--platinum .achbadge__tiername { color: var(--brand-ink); }

/* Milestones — the one-off badges carried over from the old set. Neutral
   brand colour when earned, no tier ladder. */
.achbadge--unlocked.achbadge--milestone { background: var(--brand-tint); }
.achbadge--unlocked.achbadge--milestone .achbadge__icon { background: var(--brand); color: #fff; border-color: transparent; }
.achbadge--unlocked.achbadge--milestone .achbadge__tiername { color: var(--brand-ink); }
.achtoast--milestone .achtoast__icon { background: var(--brand); color: #fff; }

/* Progress-page teaser card linking to the full page. */
.achteaser { display: block; text-decoration: none; color: inherit; transition: transform var(--dur-fast) var(--ease-spring), box-shadow var(--dur-fast); }
.achteaser:hover { transform: translateY(-2px); box-shadow: var(--shadow-2); }
.achteaser__top { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2); margin-bottom: var(--s-2); }
.achteaser__top h3 { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-faint); margin: 0; }
.achteaser__top .badge { background: var(--brand-tint); color: var(--brand-ink); }

/* Unlock celebration — dropped in from the top, appended to <body> so it
   survives the navigation to the results page. */
.achtoast {
  position: fixed; left: 50%; top: var(--s-5); z-index: 90;
  display: flex; align-items: center; gap: var(--s-3);
  min-width: 280px; max-width: min(400px, calc(100vw - 32px));
  padding: var(--s-4) var(--s-5); border-radius: var(--r-lg);
  background: var(--surface); border: 1px solid var(--line-strong); box-shadow: var(--shadow-2);
  transform: translate(-50%, -20px); opacity: 0; pointer-events: none;
  transition: transform var(--dur-mid) var(--ease-spring), opacity var(--dur-mid);
}
.achtoast.show { transform: translate(-50%, 0); opacity: 1; }
.achtoast__icon { flex: none; width: 48px; height: 48px; border-radius: var(--r-full); display: grid; place-items: center; background: var(--surface-sunk); color: var(--ink-soft); }
.achtoast__eyebrow { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-faint); margin: 0; }
.achtoast__name { font-family: var(--font-display); font-weight: 700; margin: 1px 0; }
.achtoast__desc { font-size: var(--fs-sm); color: var(--ink-soft); margin: 0; }
.achtoast--bronze .achtoast__icon { background: var(--ach-bronze); color: #fff; }
.achtoast--silver .achtoast__icon { background: var(--ach-silver); color: #fff; }
.achtoast--gold .achtoast__icon { background: var(--ach-gold); color: #fff; }
.achtoast--platinum .achtoast__icon { background: linear-gradient(135deg, var(--brand), var(--c-ocean) 55%, var(--c-berry)); color: #fff; }
.achtoast--gold { border-color: var(--ach-gold); }
.achtoast--platinum { border-color: var(--brand-bright); }

/* ---------- estimated grade ---------- */
.gradepill {
  display: grid; place-items: center; width: 26px; height: 26px;
  border-radius: var(--r-full); font-family: var(--font-display); font-weight: 700; font-size: 13px;
  background: var(--surface-sunk); color: var(--ink-soft); cursor: default;
}
.gradepill--low { background: var(--retry-tint); color: var(--retry-ink); }
.gradepill--high { background: var(--ok-tint); color: var(--ok-ink); }

.gradereveal {
  display: inline-block; margin: var(--s-2) auto var(--s-5); padding: var(--s-5) var(--s-6);
  min-width: 220px; border-radius: var(--r-lg); border: 1px solid var(--line);
  background: var(--surface-sunk);
  animation: pop-in var(--dur-slow) var(--ease-spring) 650ms both;
}
.gradereveal--low { background: var(--retry-tint); border-color: var(--retry); }
.gradereveal--high { background: var(--ok-tint); border-color: var(--ok); }
.gradereveal__eyebrow { display: block; font-size: 12px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--ink-faint); }
.gradereveal__letter { font-family: var(--font-display); font-weight: 700; font-size: 4rem; line-height: 1.2; color: var(--ink); }
.gradereveal--low .gradereveal__letter { color: var(--retry-ink); }
.gradereveal--high .gradereveal__letter { color: var(--ok-ink); }
.gradereveal__compare { font-weight: 600; margin-top: var(--s-1); }
.gradereveal__compare.up { color: var(--ok-ink); }
.gradereveal__compare.down { color: var(--ink-soft); }
.gradereveal__caption { font-size: var(--fs-sm); color: var(--ink-faint); margin-top: 2px; }

/* ---------- instant solve ---------- */
.solve-dropzone {
  display: flex; align-items: center; justify-content: center;
  min-height: 180px; border: 2px dashed var(--line-strong); border-radius: var(--r-lg);
  background: var(--surface-sunk); cursor: pointer; overflow: hidden;
  transition: transform var(--dur-fast) var(--ease-spring), border-color var(--dur-fast), background var(--dur-fast);
}
.solve-dropzone:hover { transform: translateY(-2px); border-color: var(--brand); background: var(--brand-tint); }
.solve-dropzone.has-image { border-style: solid; border-color: var(--line); background: var(--surface); padding: var(--s-3); }
.solve-dropzone__inner { display: flex; flex-direction: column; align-items: center; gap: var(--s-2); color: var(--ink-soft); text-align: center; padding: var(--s-5); }
.solve-dropzone__cta { font-family: var(--font-display); font-weight: 700; color: var(--ink); }
.solve-dropzone svg { color: var(--brand); }
.solve-dropzone img { max-width: 100%; max-height: 320px; display: block; border-radius: var(--r-md); object-fit: contain; }
.solve-result { display: flex; gap: var(--s-5); align-items: flex-start; flex-wrap: wrap; text-align: left; }
.solve-result__thumb { width: 120px; height: 120px; object-fit: cover; border-radius: var(--r-md); border: 1px solid var(--line); flex: none; }
.solve-restated { font-style: italic; color: var(--ink-soft); font-size: var(--fs-sm); margin: 0 0 var(--s-3); }
.solve-answer { padding: var(--s-4) var(--s-5); border-radius: var(--r-lg); border: 1px solid var(--ok); background: var(--ok-tint); }
.solve-answer__label { display: block; font-size: 12px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--ok-ink); margin-bottom: 4px; }
.solve-answer__value { font-family: var(--font-display); font-weight: 700; font-size: var(--fs-xl); color: var(--ink); }
.solve-steps { margin: 12px 0 0; padding-left: 22px; display: flex; flex-direction: column; gap: var(--s-2); }
.solve-steps li { padding-left: 4px; }
/* The method is collapsible — the answer is already shown, so seeing the
   working is opt-in (hide it and try to reconstruct it). */
.solve-method { margin-top: 20px; text-align: left; border-top: 1px solid var(--line); padding-top: var(--s-4); }
.solve-method > summary {
  cursor: pointer; list-style: none; font-family: var(--font-display); font-weight: 700;
  font-size: var(--fs-md); color: var(--ink); display: inline-flex; align-items: center; gap: 8px;
}
.solve-method > summary::-webkit-details-marker { display: none; }
.solve-method > summary::before { content: "▸"; font-size: 11px; color: var(--ink-faint); transition: transform var(--dur-fast); }
.solve-method[open] > summary::before { transform: rotate(90deg); }
/* Push toward practising, not just reading the answer. */
.solve-next { margin-top: 20px; padding: var(--s-4); border-radius: var(--r-md); background: var(--brand-tint); border: 1px solid color-mix(in srgb, var(--brand) 24%, transparent); text-align: left; }
.solve-next__lead { margin: 0 0 var(--s-3); font-weight: 600; color: var(--ink); }

/* ---------- calendar quick-add dialog ---------- */
.quickadd { max-width: 380px; }
.quickadd__types { display: flex; gap: var(--s-2); }
.quickadd__type {
  flex: 1; font: inherit; font-weight: 600; font-size: var(--fs-sm);
  padding: var(--s-3); border-radius: var(--r-md);
  border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink-soft);
  cursor: pointer; transition: background var(--dur-fast), border-color var(--dur-fast), color var(--dur-fast);
}
.quickadd__type:hover { background: var(--surface-sunk); }
.quickadd__type[aria-pressed="true"] { background: var(--brand-tint); border-color: var(--brand); color: var(--brand-ink); }

/* ---------- friend leaderboard ---------- */
.lbrows { display: flex; flex-direction: column; gap: var(--s-2); }
.lbrow {
  display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
  padding: var(--s-3) var(--s-4); border-radius: var(--r-md);
  background: var(--surface); border: 1px solid var(--line);
}
.lbrow--me { border-color: var(--brand); background: var(--brand-tint); }
/* Same gold/silver/bronze as the achievement badges — one ranked-tier
   visual language for the whole app. */
.lbrank {
  flex: none; width: 28px; height: 28px; border-radius: var(--r-full);
  display: grid; place-items: center; font-weight: 700; font-family: var(--font-display);
  background: var(--surface-sunk); color: var(--ink-soft); font-size: var(--fs-sm);
}
.lbrank--gold { background: var(--ach-gold); color: #fff; }
.lbrank--silver { background: var(--ach-silver); color: #fff; }
.lbrank--bronze { background: var(--ach-bronze); color: #fff; }
.lbrow__who { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.lbrow__handle { font-weight: 700; font-family: var(--font-display); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lbrow__stats { display: flex; align-items: center; gap: var(--s-4); flex: none; }
.lbrow__streak { display: inline-flex; align-items: center; gap: 4px; color: var(--retry-ink); font-weight: 700; font-size: var(--fs-sm); }
.lbrow__questions { font-weight: 700; font-size: var(--fs-sm); color: var(--ink-soft); white-space: nowrap; }
.lbcode {
  font-size: 22px; font-weight: 700; letter-spacing: .12em; font-family: ui-monospace, "SF Mono", Menlo, monospace;
  background: var(--surface-sunk); border: 1px dashed var(--line-strong); border-radius: var(--r-md);
  padding: var(--s-3); text-align: center; margin: var(--s-2) 0 var(--s-3);
}

/* ---------- share card ---------- */
.sharecard { text-align: center; position: relative; max-width: 380px; }
.sharecard__close { position: absolute; top: var(--s-2); right: var(--s-2); }
.sharecard__preview { width: 100%; border-radius: var(--r-md); margin: var(--s-3) 0; display: block; }
.sharecard__actions { display: flex; gap: var(--s-3); justify-content: center; flex-wrap: wrap; }
.achbadge__share { margin-left: auto; width: 26px; height: 26px; color: var(--ink-faint); }
.achbadge__share:hover { color: var(--brand); }

@media print {
  .topbar, .hintfab, [data-noprint], .toast, .savebar, .achtoast, .popover, .sharecard-backdrop { display: none !important; }
  body { background: #fff; color: #000; }
  .content { max-width: none; padding: 0; }
  .psheet__lines { background: none; height: 60px; border-bottom: 1px solid #999; }
  a[href]::after { content: ""; }
}
