/* Components (spec §5, §7). Logical properties only -- no left/right, so RTL
 * and any future LTR locale both work without a mirrored stylesheet. */

.wrap { max-inline-size: 640px; margin-inline: auto; padding-inline: var(--space-4); }
.wrap--wide { max-inline-size: 1040px; }

/* ---------- Chrome ---------- */

.site-header {
  position: sticky; inset-block-start: 0; z-index: 10;
  background: var(--surface); border-block-end: 1.5px solid var(--line);
}
/* The brand cannot shrink (nowrap) and five nav items plus the theme toggle do
   not fit beside it at 375px -- the row overflowed the viewport by 22px. The
   nav wraps to its own line instead, which also gives the links room to be a
   real touch target rather than 19px of text. */
.site-header__inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-2) var(--space-3); padding-block: var(--space-2);
  flex-wrap: wrap;
}
.brand {
  text-decoration: none; color: var(--text);
  display: flex; align-items: center;
  min-block-size: 44px; flex: none;
}
.brand__mark { display: block; block-size: 44px; inline-size: auto; }

/* One mark per theme. Both are in the DOM so the swap is instant on toggle,
   with no second network request at the moment the page changes colour. */
.brand__mark--night { display: none; }
[data-theme="night"] .brand__mark--day { display: none; }
[data-theme="night"] .brand__mark--night { display: block; }

/* The store link sits between the mark and the nav. `margin-inline: auto`
   centres it in whatever space those two leave rather than pinning it to a
   fixed position, so it stays put as the nav grows by one item for admins. */
.store-link {
  margin-inline: auto;
  color: var(--text-muted); text-decoration: none;
  font-size: var(--t-label); white-space: nowrap;
  padding: var(--space-2);
  border-radius: var(--r-sm);
}
.store-link:hover, .store-link:focus-visible { color: var(--accent); }
/* Deliberately not hidden on phones. The nav already wraps to its own line at
   this width, which leaves the mark and this link alone on the first row with
   room to spare -- and a link to the store that only desktop users can see
   would be pointless on a product used almost entirely on phones. */
.site-nav { display: flex; align-items: center; gap: var(--space-2); font-size: 0.75rem; flex-wrap: wrap; justify-content: flex-end; }
@media (min-width: 480px) { .site-nav { gap: var(--space-3); font-size: var(--t-label); } }
/* 44px tall including padding (spec §9), while the text stays small. */
.site-nav a,
.site-nav .linkish {
  color: var(--text-muted); text-decoration: none;
  display: inline-flex; align-items: center;
  min-block-size: 44px; padding-inline: var(--space-2);
}
.site-nav a:hover { color: var(--text); }
.linkish { background: none; border: 0; color: var(--text-muted); font: inherit; }
.site-nav .linkish:hover { color: var(--wrong-600); }

.theme-toggle {
  inline-size: 30px; block-size: 30px; border-radius: var(--r-pill);
  border: 1.5px solid var(--line); background: var(--surface-2); flex: none;
  display: grid; place-items: center;
}
.theme-toggle::after {
  content: ""; inline-size: 12px; block-size: 12px;
  border-radius: var(--r-pill); background: var(--maroon-800);
  box-shadow: inset -4px -2px 0 0 var(--surface-2);
}
[data-theme="night"] .theme-toggle::after { background: var(--orange-500); box-shadow: none; }

/* ---------- Buttons ---------- */
/* Maroon text on orange: white on orange fails contrast (spec §2.1). */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  font-family: var(--font-display); font-weight: 600; font-size: 1.0625rem;
  min-block-size: 52px; padding-inline: var(--space-5); border-radius: var(--r-md);
  border: 0; text-decoration: none; transition: box-shadow 80ms linear, transform 80ms linear;
}
.btn--block { display: flex; inline-size: 100%; }
.btn--sm { min-block-size: 38px; font-size: var(--t-label); padding-inline: var(--space-4); }

.btn--primary {
  background: var(--orange-500); color: var(--maroon-800);
  border: var(--stroke-w) solid var(--stroke-c); box-shadow: var(--shadow-cut);
}
.btn--secondary {
  background: var(--surface); color: var(--text);
  border: 1.5px solid var(--maroon-600); box-shadow: var(--shadow-soft);
}
/* The whole button animation: it presses into the page. No hover lift. */
.btn--primary:active { box-shadow: var(--shadow-cut-sm); transform: translate(2px, 2px); }
.btn--secondary:active { transform: translate(1px, 1px); }

.link { color: var(--text); text-underline-offset: 3px; }
.link:hover { text-decoration-color: var(--orange-500); }

/* ---------- Chip ---------- */

.chip {
  display: inline-flex; align-items: center; gap: var(--space-2);
  background: var(--surface-2); color: var(--text);
  border: 0; border-radius: var(--r-sm); min-block-size: 30px;
  padding-inline: var(--space-3); font-size: var(--t-label); font-weight: 500;
  transition: background 150ms linear;
}
.chip--toggle[aria-pressed="true"] { background: var(--guess-100); color: var(--guess-600); }

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

.progress { block-size: 6px; background: var(--line); border-radius: var(--r-pill); overflow: hidden; }
.progress__fill {
  block-size: 100%; background: var(--orange-500); border-radius: var(--r-pill);
  transition: inline-size 400ms var(--ease-out);
}

/* ---------- The card stack (spec §5.1) ---------- */

.stack { position: relative; margin-block: var(--space-5); }

.card { background: var(--surface); border-radius: var(--r-lg); padding: var(--space-5); }

/* Ghosts sit behind, scaled and pushed down, so the pile reads as physical.
   They are aria-hidden and hold no content. When the deck runs low they are
   removed one by one, so the stack visibly thins. */
.card--ghost {
  position: absolute; inset-inline: 0; inset-block-start: 0; block-size: 100%;
  background: var(--surface-2); box-shadow: var(--shadow-soft); z-index: 0;
  /* The spec's fill is peach on cream, which at this scale is invisible. A
     hairline edge is what actually makes the pile read as a pile. */
  border: 1.5px solid var(--line);
  /* Scaling from the top keeps the offset the translate intended: scaling from
     the centre pulls the bottom edge back up and eats most of it. */
  transform-origin: top center;
}
.card--ghost-1 { transform: translateY(12px) scale(0.96); opacity: .9; }
.card--ghost-2 { transform: translateY(24px) scale(0.92); opacity: .7; }

.card--active {
  position: relative; z-index: 1;
  border: var(--stroke-w) solid var(--stroke-c); box-shadow: var(--shadow-cut);
}

.card__question {
  font-size: var(--t-question); font-weight: 500; line-height: 1.6;
  margin-block: var(--space-4) var(--space-5);
}

/* ---------- Option rows (spec §5.2) ---------- */

.options { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }

.option {
  display: flex; align-items: center; gap: var(--space-3); inline-size: 100%;
  min-block-size: 56px; padding: var(--space-3) var(--space-4);
  background: var(--surface); color: var(--text);
  border: 1.5px solid var(--line); border-radius: var(--r-md);
  font: inherit; text-align: start;
  transition: background 180ms var(--ease-out), border-color 180ms var(--ease-out),
              opacity 180ms linear, transform 80ms linear;
}
@media (hover: hover) {
  .option:not(:disabled):hover { background: var(--surface-2); border-color: var(--maroon-600); }
}
.option:not(:disabled):active { background: var(--peach-200); transform: scale(0.985); }

.option__key {
  flex: none; inline-size: 32px; block-size: 32px; border-radius: var(--r-pill);
  display: grid; place-items: center; background: var(--peach-200);
  color: var(--maroon-800); font-weight: 600; font-size: var(--t-label);
  transition: background 180ms var(--ease-out);
}
.option__text { line-height: 1.5; }
.option__mark { flex: none; margin-inline-start: auto; display: flex; }

.option--correct { background: var(--correct-100); border: 2px solid var(--correct-600); }
.option--correct .option__key { background: var(--correct-600); color: #fff; }
.option--wrong { background: var(--wrong-100); border: 2px solid var(--wrong-600); }
.option--wrong .option__key { background: var(--wrong-600); color: #fff; }
/* Revealed but not chosen: dashed, so it reads differently from what was picked. */
.option--reveal { background: var(--correct-100); border: 2px dashed var(--correct-600); }
.option--dim { opacity: .55; }

/* ---------- Explanation sheet (spec §5.3) ---------- */
/* Animating grid-template-rows grows the card without a hard-coded height, so
   a long explanation never clips. */

.sheet { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 260ms var(--ease-out); }
.sheet[data-open="true"] { grid-template-rows: 1fr; }
/* `overflow: hidden` is what makes the grid-row grow animation work, but it
   also clips anything that reaches past the box -- the primary button's 4px
   cutout shadow and its 3px focus ring both did. The padding below reserves
   room for them; without it the button looks sliced off. */
.sheet__inner { overflow: hidden; min-block-size: 0; }
.sheet__body {
  padding-block-start: var(--space-4); margin-block-start: var(--space-4);
  border-block-start: 1.5px solid var(--line);
}
.sheet__result { font-family: var(--font-display); font-weight: 600; font-size: var(--t-h2); }
.sheet__result--ok { color: var(--correct-600); }
.sheet__result--no { color: var(--wrong-600); }
.sheet__actions {
  display: flex; align-items: center; gap: var(--space-3);
  margin-block-start: var(--space-4);
  /* Room for the cutout shadow (4px) and the focus ring (3px + 2px offset). */
  padding: 2px 6px 8px;
}
.sheet__actions .btn { margin-inline-start: auto; }

/* ---------- Feedback motion (spec §6.1) ---------- */

.shake { animation: shake 320ms linear; }
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-6px); }
  40% { transform: translateX(6px); }
  60% { transform: translateX(-4px); }
  80% { transform: translateX(4px); }
}

.sparkle-burst {
  position: absolute; inline-size: 14px; block-size: 14px;
  color: var(--correct-600); pointer-events: none;
}
.squiggle-mark {
  inline-size: 34px; block-size: 12px; color: var(--wrong-600); flex: none;
  animation: squiggle-in 400ms var(--ease-out);
}
@keyframes squiggle-in { from { opacity: 0; transform: translateY(4px); } }

/* ---------- Misc ---------- */

.muted { color: var(--text-muted); }
.small { font-size: var(--t-small); }
.label { font-size: var(--t-label); }
.display { font-family: var(--font-display); }
.hero-num { font-family: var(--font-display); font-weight: 800; font-size: var(--t-hero); line-height: 1; }

.surface { background: var(--surface); border-radius: var(--r-lg); padding: var(--space-5); box-shadow: var(--shadow-soft); }
.surface--flat { background: var(--surface-2); box-shadow: none; }
.surface--cut { border: var(--stroke-w) solid var(--stroke-c); box-shadow: var(--shadow-cut); }

/* `minmax(0, 1fr)` rather than the default `auto`: a grid track sized `auto`
   refuses to go below its widest child's min-content, so one long unit name
   made every sibling card 381px in a 375px viewport. Same reason `.row`
   children get `min-inline-size: 0` -- flex items have the identical default. */
.stack-v { display: grid; gap: var(--space-4); grid-template-columns: minmax(0, 1fr); }
.row { display: flex; align-items: center; gap: var(--space-3); min-inline-size: 0; }
.row > * { min-inline-size: 0; }
.row--between { justify-content: space-between; }
.push { margin-inline-start: auto; }

.doodle-corner {
  position: absolute; inset-block-start: 0; inset-inline-end: 0;
  inline-size: 64px; block-size: 64px; color: var(--orange-300); pointer-events: none;
}

.sr-only {
  position: absolute; inline-size: 1px; block-size: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ---------- Review chrome (spec §4.3) ---------- */
/* The review screen drops the site nav; nothing competes with the card. */

.review-bar { position: sticky; inset-block-start: 0; z-index: 10; background: var(--bg); }
.review-bar__inner { display: flex; align-items: center; gap: var(--space-3); padding-block: var(--space-3); }
.review-bar__close { color: var(--text-muted); text-decoration: none; font-size: 1.25rem; line-height: 1; padding: var(--space-2); }
.review-bar__count { color: var(--text-muted); flex: none; }
.review-bar__progress { flex: 1; }

[data-desktop-only] { display: none; }
@media (hover: hover) and (pointer: fine) { [data-desktop-only] { display: block; } }

/* ---------- Session complete (spec §4.4) ---------- */

.done { text-align: center; padding-block: var(--space-7); position: relative; }
.done__sun { inline-size: 72px; block-size: 72px; margin-inline: auto; color: var(--orange-500); }
.done__num { color: var(--orange-500); margin-block: var(--space-4) 0; }
.done__title { font-size: var(--t-h1); font-weight: 700; margin-block: var(--space-2); }
.done__sub { margin-block: 0 var(--space-6); }
.done__actions {
  display: grid; gap: var(--space-3); max-inline-size: 320px;
  margin-inline: auto; padding: 2px 6px 10px;
}
.done__more { font-size: var(--t-small); text-decoration: underline; text-underline-offset: 3px; }

/* ---------- Auth (spec §4.1) ---------- */

.auth { max-inline-size: 360px; margin-inline: auto; padding-block: var(--space-7); text-align: center; }
.auth__sun { inline-size: 56px; block-size: 56px; margin-inline: auto; color: var(--orange-300); }
.auth__title { font-size: var(--t-h1); font-weight: 800; margin-block: var(--space-4) 0; }
.auth__sub { margin-block: var(--space-2) var(--space-6); }
.auth__form { display: grid; gap: var(--space-4); text-align: start; }
.auth__note {
  background: var(--guess-100); color: var(--guess-600);
  border-radius: var(--r-sm); padding: var(--space-3); text-align: center;
}
.auth__foot { margin-block-start: var(--space-6); }

/* ---------- Fields (spec §7) ---------- */
/* Labels are always visible: a placeholder disappears the moment you type. */

.field { display: grid; gap: var(--space-2); }
.field__label { font-weight: 500; font-size: var(--t-small); }
.field__hint { font-size: var(--t-label); color: var(--text-muted); }

.input, .select {
  min-block-size: 56px; inline-size: 100%;
  background: var(--surface); color: var(--text);
  border: 1.5px solid var(--line); border-radius: var(--r-sm);
  padding-inline: var(--space-4); font: inherit;
}
/* Digits read left-to-right even inside an RTL page. */
.input--ltr { direction: ltr; text-align: start; }

/* ---------- Readiness sun (spec §7) ---------- */

.sun { position: relative; margin: 0 auto; max-inline-size: 100%; }
.sun svg { display: block; inline-size: 100%; height: auto; }
/* The label sits inside the ring's clear middle, not on top of the rays. */
.sun__label {
  position: absolute; inset-inline: 12%; inset-block-end: 4%;
  display: grid; gap: 2px; text-align: center;
}
.sun__cap { line-height: 1.35; }
.sun__num { color: var(--maroon-800); }
[data-theme="night"] .sun__num { color: var(--peach-100); }

/* ---------- Dashboard (spec §4.2) ---------- */

.dash__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding-block: var(--space-5) var(--space-2); }
.dash__hi { font-size: var(--t-h1); font-weight: 700; margin: 0; }

.streak { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--orange-500); font-family: var(--font-display); font-weight: 700; }
.streak__icon { inline-size: 18px; block-size: 18px; flex: none; }
.streak--bump { animation: streak-bump 300ms var(--ease-snap); }
@keyframes streak-bump { 50% { transform: scale(1.12); } }

.gauge-wrap { position: relative; display: grid; place-items: center; padding-block: var(--space-4) var(--space-6); }
.gauge-wrap__blob {
  position: absolute; inline-size: 210px; block-size: 190px;
  color: var(--surface-2); z-index: -1; opacity: .45;
}

.course-card { display: grid; gap: var(--space-3); }
.course-card__name { font-family: var(--font-display); font-weight: 700; font-size: var(--t-h2); margin: 0; }
.course-card__code { font-size: var(--t-label); color: var(--text-muted); }
.course-card__nums { display: flex; gap: var(--space-6); }
.course-card__num { font-family: var(--font-display); font-weight: 800; font-size: 1.75rem; line-height: 1; }
.course-card__num--new { color: var(--orange-500); }

.exam-pill { flex: none; border-radius: var(--r-sm); padding: var(--space-1) var(--space-3); font-size: var(--t-label); font-weight: 600; background: var(--surface-2); }
.exam-pill--near { background: var(--wrong-100); color: var(--wrong-600); }


/* ---------- Charts (spec §4.5: two simple charts, orange bars) ---------- */

.bars { display: flex; align-items: flex-end; gap: 3px; block-size: 96px; }
.bars__bar { flex: 1; min-block-size: 2px; background: var(--orange-500); border-radius: 3px 3px 0 0; opacity: .85; }

.weakest {
  background: var(--guess-100); color: var(--guess-600);
  border-radius: var(--r-sm); padding: var(--space-3); margin: 0; font-size: var(--t-small);
}

/* ---------- Admin (spec §4.5: same tokens, denser rhythm) ---------- */

.admin-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
.admin-grid--3 { grid-template-columns: repeat(3, 1fr); }
.admin-row { padding-block: var(--space-3); border-block-end: 1.5px solid var(--line); }
.admin-row:last-child { border-block-end: 0; }
.admin-flag {
  background: var(--wrong-100); color: var(--wrong-600);
  border-radius: var(--r-sm); padding: var(--space-4); font-size: var(--t-small);
}

/* ---------- Landing ---------- */

.hero { position: relative; text-align: center; padding-block: var(--space-8) var(--space-7); }
.hero__title { font-size: var(--t-hero); font-weight: 800; margin: 0; line-height: 1.2; }
.hero__sub { max-inline-size: 30ch; margin: var(--space-4) auto var(--space-6); }

.features { display: grid; gap: var(--space-3); }
@media (min-width: 700px) { .features { grid-template-columns: repeat(3, 1fr); } }
.features__icon { display: block; inline-size: 28px; block-size: 28px; color: var(--orange-500); }
.features__title { font-family: var(--font-display); font-size: 1.0625rem; font-weight: 700; margin-block: var(--space-3) var(--space-2); }

.cta {
  margin-block-start: var(--space-7); text-align: center;
  background: var(--maroon-800); color: var(--peach-100);
  border-radius: var(--r-lg); padding: var(--space-7) var(--space-5);
}
.cta__title { font-size: var(--t-h2); margin: 0; }
.cta__sub { margin-block: var(--space-2) var(--space-5); opacity: .8; }
.cta__btn { background: var(--orange-500); color: var(--maroon-800); box-shadow: none; }

.quick-banner {
  background: var(--guess-100); color: var(--guess-600);
  border-radius: var(--r-sm); padding: var(--space-3);
  text-align: center; font-size: var(--t-label); margin-block: 0 var(--space-4);
}

/* ---------- Theme control ---------- */

.icon-18 { inline-size: 18px; block-size: 18px; display: block; }

.theme-toggle { color: var(--text-muted); }
.theme-toggle::after { content: none; }
/* Show the state you would switch *to*, which is what a toggle icon means. */
.theme-toggle__sun { display: none; }
.theme-toggle__moon { display: block; }
[data-theme="night"] .theme-toggle__sun { display: block; color: var(--orange-500); }
[data-theme="night"] .theme-toggle__moon { display: none; }

.theme-picker { border: 0; padding: 0; margin: 0; }
.theme-opt {
  display: flex; align-items: center; gap: var(--space-3);
  min-block-size: 44px; cursor: pointer;
}
.theme-opt input { inline-size: 20px; block-size: 20px; accent-color: var(--orange-500); }

/* ---------- Session complete (spec §4.4) ---------- */

/* Contains the confetti. Same clipping caveat as the sheet, so the actions
   below carry their own breathing room. */
.done { position: relative; overflow: hidden; }

.tally {
  display: grid; gap: var(--space-2); margin: var(--space-5) auto var(--space-6);
  max-inline-size: 240px; text-align: start;
}
.tally__row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.tally__row dt { display: flex; align-items: center; gap: var(--space-2); color: var(--text-muted); font-size: var(--t-small); }
.tally__row dd { margin: 0; font-family: var(--font-display); font-weight: 700; }
.tally__icon { display: flex; inline-size: 18px; block-size: 18px; }
.tally__icon--ok { color: var(--correct-600); }
.tally__icon--no { color: var(--wrong-600); }
.tally__icon--guess { color: var(--guess-600); }

.confetti {
  position: absolute; inline-size: 14px; block-size: 14px;
  pointer-events: none; z-index: 2;
}
.confetti svg { inline-size: 100%; block-size: 100%; display: block; }

/* ---------- Touch targets ---------- */
/*
 * §7 specifies a 30px chip; §9 requires a 44px touch target. Both are right:
 * a 44px-tall chip looks like a button, and a 30px hit area is a miss on a
 * phone. The fix is to keep the drawn size and grow the *hit* area with a
 * transparent overlay, so the visual language and the thumb both get what
 * they need.
 */
.chip--toggle,
.review-bar__close,
.theme-toggle { position: relative; }

.chip--toggle::after,
.review-bar__close::after,
.theme-toggle::before {
  content: "";
  position: absolute;
  /* Collapse to the centre, then grow 22px in every direction. `transform:
     translate()` is physical, so in RTL it pushed the box off-centre and out
     of the viewport instead of centring it -- inset plus a negative margin is
     direction-agnostic and needs no left/right. */
  inset: 50%;
  margin: -22px;
  inline-size: 44px; block-size: 44px;
}

/* ---------- Truncation ---------- */
/*
 * A flex item will not shrink below its content's min-content width, so
 * `white-space: nowrap` alone makes a long unit name push its whole card past
 * the viewport instead of ellipsising. `min-inline-size: 0` is the part that
 * actually lets it shrink.
 */
.truncate {
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A slider's 16px thumb track is far below a usable touch target; padding
   gives the thumb room without changing how the track looks. */
.range {
  inline-size: 100%;
  accent-color: var(--orange-500);
  min-block-size: 44px;
}

/* §9's 44px applies to small buttons too -- 38px is a miss on a phone. */
.btn--sm { min-block-size: 44px; }

/* Card links in the admin list are the tap target for opening a card, so they
   need the same 44px as everything else. */
.admin-row .link {
  display: inline-flex; align-items: center;
  min-block-size: 44px; padding-block: var(--space-1);
}

/* ---------- Courses: two sections (spec §4.5) ---------- */

.section-title {
  font-family: var(--font-display); font-size: var(--t-h2); font-weight: 700;
  margin-block: var(--space-6) var(--space-4);
}
.section-title:first-of-type { margin-block-start: var(--space-5); }

/* Locked cards sit flat and stay closed: no outline, no shadow, no numbers. */
.course-locked {
  display: flex; align-items: center; gap: var(--space-3);
  inline-size: 100%; text-align: start; font: inherit; color: inherit;
  border: 0; padding: var(--space-4); min-block-size: 44px;
}
.course-locked__icon { flex: none; display: flex; color: var(--text-muted); }
.course-locked__body { display: grid; gap: 2px; min-inline-size: 0; }
.course-locked .course-card__name { font-size: var(--t-body); }

.course-settings summary { cursor: pointer; min-block-size: 44px; display: flex; align-items: center; }
.course-settings summary::marker { color: var(--text-muted); }

/* ---------- Bottom sheet (spec §4.5) ---------- */

.scrim {
  position: fixed; inset: 0; z-index: 40;
  background: color-mix(in srgb, var(--maroon-900) 40%, transparent);
}

.sheet-bottom {
  position: fixed; inset-inline: 0; inset-block-end: 0; z-index: 41;
  background: var(--surface); color: var(--text);
  border-start-start-radius: var(--r-lg); border-start-end-radius: var(--r-lg);
  box-shadow: var(--shadow-soft);
  padding: var(--space-4) var(--space-5) calc(var(--space-6) + env(safe-area-inset-bottom));
  max-inline-size: 640px; margin-inline: auto;
}
.sheet-bottom__handle {
  display: block; inline-size: 44px; block-size: 4px; border-radius: var(--r-pill);
  background: var(--line); margin: 0 auto var(--space-5);
}
.sheet-bottom__title {
  font-family: var(--font-display); font-size: var(--t-h2); font-weight: 700;
  margin-block: 0 var(--space-3);
}
.sheet-bottom__title:focus-visible { outline-offset: 4px; }
.sheet-bottom__body { margin-block: 0 var(--space-2); }
.sheet-bottom__foot { margin-block: var(--space-4) 0; text-align: center; }

/* ---------- Code entry (spec §4.1) ---------- */

.code-boxes { display: flex; gap: var(--space-2); justify-content: center; }
.code-box {
  inline-size: 46px; block-size: 56px; text-align: center;
  font-family: var(--font-display); font-size: 1.5rem; font-weight: 700;
  background: var(--surface); color: var(--text);
  border: 1.5px solid var(--line); border-radius: var(--r-sm);
}
.code-box:focus-visible { border-color: var(--focus); }

.field__error { color: var(--wrong-600); font-size: var(--t-small); }
.auth__dev {
  display: grid; gap: var(--space-3); margin-block-start: var(--space-7);
  padding-block-start: var(--space-5); border-block-start: 1.5px solid var(--line);
}
.ltr { direction: ltr; unicode-bidi: isolate; }

@media (max-width: 380px) {
  /* Six 46px boxes plus gaps do not fit a 360px screen. */
  .code-box { inline-size: 40px; block-size: 52px; font-size: 1.25rem; }
}

/* --- Mock exam ---------------------------------------------------------- */

.exam-clock { flex: none; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.exam-clock--low { color: var(--wrong-600); font-weight: 600; }
.exam__hand-in { display: flex; justify-content: center; margin-block-start: var(--space-5); }
.exam__hand-in .linkish { color: var(--text-muted); }

/* Unit rows on /stats link into a focused session. The text is one line tall,
 * which is not something a thumb can be asked to hit, so the link itself
 * carries the 44px target rather than the row around it. */
.unit-link {
  display: flex; align-items: center;
  min-block-size: 44px;
}

/* Text buttons in the header ("خروج") are as wide as their word, which for a
 * short Arabic word is under the 44px minimum. */
.site-nav .linkish { min-inline-size: 44px; }

/* ---------- Recall trend (spec §4.6) ---------- */
/*
 * The line is drawn in a 0-100 box and stretched to whatever width the card
 * has, which is why every stroke carries `vector-effect: non-scaling-stroke`:
 * without it a 300px-wide card draws a 1px-tall horizontal line and a 6px
 * vertical one.
 */
.trend { margin: var(--space-4) 0 0; }
.trend svg { inline-size: 100%; block-size: 96px; display: block; overflow: visible; }
.trend__line {
  fill: none; stroke: var(--maroon-800); stroke-width: 2;
  stroke-linejoin: round; stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.trend__target {
  stroke: var(--orange-500); stroke-width: 1.5; stroke-dasharray: 5 4;
  vector-effect: non-scaling-stroke;
}
.trend__dot { fill: var(--maroon-800); }
.trend figcaption { margin-block-start: var(--space-2); }

/* On maroon the line has to be the light one, or it disappears into the card.
 * Only the attribute is needed: base.html sets data-theme="night" for the
 * system preference too, so a media query here would be a second source of
 * truth for the same question. */
[data-theme="night"] .trend__line { stroke: var(--peach-200); }
[data-theme="night"] .trend__dot { fill: var(--peach-200); }

/* ---------- Course covers ---------- */
/*
 * The ملزمة's own cover, so the card a student taps looks like the book they
 * bought. Portrait mock-ups, so the box is taller than it is wide; the border
 * keeps a pale cover from dissolving into a pale card.
 */
.course-cover {
  flex: none;
  inline-size: 56px; block-size: 72px;
  object-fit: cover;
  border-radius: var(--r-sm);
  border: 1.5px solid var(--line);
  background: var(--surface-2);
}
.course-cover--locked { inline-size: 48px; block-size: 62px; }
