/* REM-8.1: .nav-brand unified with the landing lockup; .brand-beta; ink-3/ink-4 and footer legibility */
/* ============================================================
   Analyst Quorum — Global Design System
   ============================================================ */

:root {
  --bg:          #0b1528;
  --bg2:         #101c32;
  --surface:     #16243c;
  --card:        #1a2a42;
  --card-hover:  #1f3250;
  --border:      #253655;
  --gold:        #c8a044;
  --gold-light:  #f0c455;
  --gold-dim:    rgba(200,160,68,0.12);
  --text:        #dce8f8;
  --text-muted:  #7a94b8;
  --text-dim:    #6f8bb0;   /* REM-6.4 — lifted off navy: #46607e was ~2.6:1 on --bg (WCAG fail for the legal/footer text set in it); #6f8bb0 reads ~4.5:1 while staying clearly dimmer than --text-muted */
  --success:     #1f7a52;   /* REM-2.4b: re-pointed to --pos — the muted institutional green. The old #2ecc71 was the consumer palette v2 retired. */
  --success-bg:  rgba(31,122,82,0.09);
  --error:       #a8352b;   /* REM-2.4b: re-pointed to --neg. */
  --error-bg:    rgba(168,53,43,0.08);
  --info:        #3498db;
  --radius:      10px;
  --radius-sm:   6px;
  --shadow:      0 4px 28px rgba(0,0,0,0.45);
  --font:        'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;

  /* ══ DESIGN SYSTEM v2 (2026-07-12) ═══════════════════════════════════════════
     ONE accent, TWO type families, no monospace.

     Source Serif 4 replaces Georgia as the display face. Georgia is a system font and reads as
     "web page"; Source Serif 4 is an editorial text serif and reads as "institution". Inter
     carries every other word on the site.

     The orange (#D97706) is RETIRED. It was the loudest thing on every screen and it was not the
     brand colour — two warm accents at high saturation is what made the site read as consumer
     SaaS. Gold is now the single accent, used sparingly: the one number that matters in a card,
     the active state, the primary action. Hierarchy comes from weight, size and whitespace.
     ═══════════════════════════════════════════════════════════════════════════ */
  --serif:       'Source Serif 4', Georgia, 'Times New Roman', serif;

  /* Light surface palette (the cream world the app actually lives in) */
  --ink:         #16233c;   /* headings, primary type on light */
  --ink-2:       #3d4b63;   /* body on light */
  --ink-3:       #5d6a7d;   /* secondary / meta      REM-8.1: 3.59 -> 5.41:1 */
  --ink-4:       #66727f;   /* tertiary / disabled   REM-8.1: 2.15 -> 4.83:1 */
  --paper:       #fffdfa;   /* card surface */
  --paper-2:     #f5f2ec;   /* page background */
  --rule:        #e6e0d6;   /* hairline borders */
  --rule-strong: #d6cec1;

  /* Semantic (kept muted — institutional, not traffic-light) */
  --pos:         #1f7a52;
  --pos-bg:      #e8f2ed;
  --neg:         #a8352b;
  --neg-bg:      #f7ebe9;
  --neutral:     #7b8798;
  --neutral-bg:  #eeece7;

  /* Motion — one vocabulary, used everywhere */
  --ease:        cubic-bezier(0.4, 0.0, 0.2, 1);
  --t-fast:      140ms;
  --t-base:      220ms;
  --t-slow:      380ms;
}

/* ── Motion primitives ───────────────────────────────────────
   Reused by every page. Respects prefers-reduced-motion. */
@keyframes aq-rise   { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes aq-fade   { from { opacity: 0; } to { opacity: 1; } }
@keyframes aq-shimmer{ 0% { background-position: -420px 0; } 100% { background-position: 420px 0; } }

.aq-rise { animation: aq-rise var(--t-base) var(--ease) both; }
.aq-fade { animation: aq-fade var(--t-base) var(--ease) both; }
/* Stagger children: .aq-stagger > * gets a 45ms cascade. */
.aq-stagger > *          { animation: aq-rise var(--t-base) var(--ease) both; }
.aq-stagger > *:nth-child(1){ animation-delay: 0ms;   }
.aq-stagger > *:nth-child(2){ animation-delay: 45ms;  }
.aq-stagger > *:nth-child(3){ animation-delay: 90ms;  }
.aq-stagger > *:nth-child(4){ animation-delay: 135ms; }
.aq-stagger > *:nth-child(5){ animation-delay: 180ms; }
.aq-stagger > *:nth-child(6){ animation-delay: 225ms; }
.aq-stagger > *:nth-child(7){ animation-delay: 270ms; }
.aq-stagger > *:nth-child(8){ animation-delay: 315ms; }
.aq-stagger > *:nth-child(9){ animation-delay: 360ms; }
.aq-stagger > *:nth-child(10){ animation-delay: 405ms; }
/* Clamp: a 20-topic grid must not cascade for a second and a half. Everything from the
   11th child on lands together at 450ms. */
.aq-stagger > *:nth-child(n+11){ animation-delay: 450ms; }

/* Numbers that count up. tabular-nums is not decorative here: without it the glyph widths
   change on every frame and the number visibly jitters as it climbs. */
.aq-count { font-variant-numeric: tabular-nums; }

/* ── Skeletons ───────────────────────────────────────────────
   Replaces literal "Loading..." text, which made first paint look BROKEN rather than busy.
   A skeleton is a grey shape the size of the thing that is coming. */
.sk {
  background: linear-gradient(90deg, #ece8e0 25%, #f4f1ea 50%, #ece8e0 75%);
  background-size: 420px 100%;
  animation: aq-shimmer 1.3s linear infinite;
  border-radius: 4px;
  color: transparent !important;
  user-select: none;
}
.sk-line  { height: 0.7rem; margin: 0.4rem 0; }
.sk-line.w-40 { width: 40%; } .sk-line.w-60 { width: 60%; } .sk-line.w-80 { width: 80%; }
.sk-title { height: 1.1rem; width: 55%; margin-bottom: 0.6rem; }
.sk-block { height: 4.5rem; }
.sk-num   { height: 1.8rem; width: 4.5rem; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
  html { scroll-behavior: auto; }
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html { font-size: 16px; scroll-behavior: smooth; }

body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  line-height: 1.6;
  min-height: 100vh;
}

/* ── Typography ─────────────────────────────────────────── */
h1 { font-family: var(--serif); font-size: 2.4rem; font-weight: 700; line-height: 1.2; }
h2 { font-family: var(--serif); font-size: 1.6rem; font-weight: 600; line-height: 1.3; }
h3 { font-family: var(--serif); font-size: 1.15rem; font-weight: 600; }
h4 { font-size: 0.95rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-muted); }
p  { color: var(--ink-3); }   /* REM-8.1: --text-muted is a navy-surface token; every page linking this sheet is cream-bodied */

/* ── Links ──────────────────────────────────────────────── */
a { color: var(--gold); text-decoration: none; transition: color 0.2s; }
a:hover { color: var(--gold-light); }

/* ── Buttons ────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.6rem 1.35rem;
  border-radius: var(--radius-sm);
  font-family: var(--font);
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  border: none;
  transition: all 0.2s;
  text-decoration: none;
}
.btn-primary {
  background: var(--gold);
  color: #0b1528;
}
.btn-primary:hover { background: var(--gold-light); color: #0b1528; }
.btn-ghost {
  background: transparent;
  border: 1.5px solid var(--border);
  color: var(--text);
}
/* REM-8.1: gold on cream was 2.19:1. The dark gold reads on cream.
   REM-8.2: the nav is cream now too, so the scoped pair below no longer has to hold
   light gold for a navy bar - it carries the same dark gold as everything else, and
   still covers the nav.js "Log out" ghost button on account.html, the one page with
   no local .nav .btn-ghost override of its own. */
.btn-ghost:hover { border-color: #7a5f22; color: #7a5f22; }
.nav .btn-ghost { color: #3d4b63; border-color: rgba(22,35,60,.28); }
.nav .btn-ghost:hover { border-color: #7a5f22; color: #7a5f22; background: rgba(200,160,68,.06); }
.btn-danger  { background: var(--error); color: #fff; }
.btn-danger:hover { opacity: 0.85; }
.btn-sm { padding: 0.4rem 0.9rem; font-size: 0.82rem; }
.btn-lg { padding: 0.8rem 1.8rem; font-size: 1rem; }
.btn:disabled { opacity: 0.4; cursor: not-allowed; }

/* ── Cards ──────────────────────────────────────────────── */
.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.5rem;
}
.card-hover { transition: border-color 0.2s, transform 0.2s; }
.card-hover:hover { border-color: var(--gold); transform: translateY(-2px); }

/* ── Form elements ──────────────────────────────────────── */
input, select, textarea {
  width: 100%;
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: var(--font);
  font-size: 0.95rem;
  padding: 0.65rem 0.9rem;
  transition: border-color 0.2s;
  outline: none;
}
input:focus, select:focus, textarea:focus { border-color: var(--gold); }
input::placeholder { color: var(--text-dim); }

/* REM-6.7 — the exam-BUDGET clock (per question on L1, per item set on L2/L3).
   Informational only. Colour walks the brand: muted → gold at 75% of the exam
   budget → institutional red past it. Never blocks anything. */
.aq-exam-clock { font-size: 0.72rem; color: var(--text-dim); font-variant-numeric: tabular-nums; margin-right: 0.55rem; user-select: none; transition: color .3s; }
.aq-exam-clock.warn { color: var(--gold); font-weight: 600; }
.aq-exam-clock.over { color: var(--error); font-weight: 700; }

/* ── REM-6.3 — keyboard hint pill + armed item-set question ─────────────────
   The hint floats like the nav pill and dies forever on first dismissal; the
   armed card is how a candidate sees which sub-question 1–4 / arrows selected. */
.aq-kbd-hint { position: fixed; top: 54px; left: 50%; transform: translateX(-50%); z-index: 320;
  display: flex; gap: 0.7rem; align-items: center; background: #1a2a42; color: #dce8f8;
  border: 1px solid #253655; border-radius: 999px; padding: 0.45rem 1rem; font-size: 0.78rem;
  box-shadow: 0 8px 24px rgba(0,0,0,0.35); max-width: 94vw; }
.aq-kbd-hint kbd { background: #0b1528; border: 1px solid #46607e; border-bottom-width: 2px;
  border-radius: 4px; padding: 0 0.35rem; font-family: inherit; font-size: 0.72rem; }
.aq-kbd-hint button { background: none; border: 0; color: #7a94b8; cursor: pointer;
  font-size: 0.8rem; padding: 0 0 0 0.2rem; line-height: 1; }
.aq-kbd-hint button:hover { color: #dce8f8; }
.kb-armed { outline: 2px solid var(--gold); outline-offset: 3px; }

/* ── REM-6.4 — global keyboard + selection affordances ──────────────────────
   One focus ring everywhere (buttons, links, inputs, the aq-dd) instead of the
   scattered per-component ones; a brand-tinted text selection. :focus-visible
   only — mouse clicks stay ring-free. */
:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; border-radius: 2px; }
::selection { background: rgba(200,160,68,0.35); }
select option { background: var(--card); color: var(--text); }
label { display: block; font-size: 0.85rem; font-weight: 600; color: var(--text-muted); margin-bottom: 0.4rem; letter-spacing: 0.03em; }
.form-group { margin-bottom: 1.2rem; }

/* ── Custom dropdown (aq-dd) — replaces native <select> popups platform-wide ─
 *
 * PALETTE. This component was built before the v2 light redesign and was still painted in
 * the DARK-theme tokens (--surface #16243c, --text #dce8f8, --border #253655). On the cream
 * dashboard that rendered as a navy slab sitting on a light card — the same mistake the
 * footer made with --text-dim. The LIGHT treatment below is now the DEFAULT, because the
 * dashboard and all four practice pages are light.
 *
 * register.html was the last page that still wanted the navy treatment via .on-dark (set
 * from a data-dd-dark attribute on the <select>; see dropdown.js). REM-7.6 turned it cream to
 * match the landing page and dropped that attribute from all three of its selects, so the
 * .on-dark branch below is now reached only by admin.html. Same pattern as .aq-footer.
 *
 * HEIGHT IS FIXED. The label is nowrap + ellipsis AND the button has an explicit height, so
 * the card that contains it cannot change size with the length of the selected topic.
 */
select.aq-dd-native { position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); border:0; }
.aq-dd { position:relative; width:100%; }

.aq-dd-btn {
  position:relative;                     /* the chevron is positioned against this */
  width:100%; height:38px;               /* FIXED — sized in the layout sandbox, 2026-07-13 */
  display:flex; align-items:center; justify-content:center;   /* label optically centred */
  padding:0 2rem;                        /* symmetric, so centring is true and the chevron never overlaps */
  background:var(--paper); border:1.5px solid var(--rule); border-radius:var(--radius-sm);
  color:var(--ink); font-family:var(--font); font-size:14px; font-weight:500;
  cursor:pointer; text-align:center;
  transition:border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.aq-dd-btn:hover { border-color:var(--gold); }
.aq-dd.open .aq-dd-btn, .aq-dd-btn:focus-visible {
  border-color:var(--gold); outline:none; box-shadow:0 0 0 3px rgba(200,160,68,0.18);
}
.aq-dd-label { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:100%; }
.aq-dd-label.placeholder { color:var(--ink-4); font-weight:400; }
.aq-dd-chev {
  position:absolute; right:0.8rem; top:50%; margin-top:-3px;
  width:0; height:0; border-left:5px solid transparent; border-right:5px solid transparent;
  border-top:6px solid var(--gold); transition:transform var(--t-fast) var(--ease);
}
.aq-dd.open .aq-dd-chev { transform:rotate(180deg); }

.aq-dd-menu {
  display:none; position:absolute; top:calc(100% + 5px); left:0; right:0; z-index:1600;
  background:var(--paper); border:1px solid var(--rule); border-radius:var(--radius-sm);
  box-shadow:0 14px 38px -12px rgba(26,55,94,0.35);
  /* 264px scrolled with only 9 topics — a scrollbar on a nine-item list is a papercut.
     Fits the full L1/L2 topic list; still caps on a long L3 pathway list. */
  max-height:380px; overflow-y:auto; padding:4px;
}
/* aq-FADE, not aq-rise: aq-rise animates transform, and a transform on a popover (or on any
   of its ancestors) creates a stacking context that can fence it in. Opacity only. */
.aq-dd.open .aq-dd-menu { display:block; animation:aq-fade var(--t-fast) var(--ease) both; }
.aq-dd-opt {
  padding:0.45rem 0.7rem; border-radius:6px; color:var(--ink); cursor:pointer;
  font-size:0.86rem; line-height:1.3; text-align:center;
}
.aq-dd-opt:hover { background:var(--gold-dim); }
.aq-dd-opt.active { background:var(--gold-dim); color:#7a5f14; font-weight:600; }
.aq-dd-opt.disabled { opacity:0.45; cursor:not-allowed; }
.aq-dd-menu::-webkit-scrollbar { width:9px; }
.aq-dd-menu::-webkit-scrollbar-thumb { background:var(--rule); border-radius:5px; }

/* Dark variant — register.html (navy page). Retains the original treatment. */
.aq-dd.on-dark .aq-dd-btn  { background:var(--surface); border-color:var(--border); color:var(--text); }
.aq-dd.on-dark .aq-dd-label.placeholder { color:var(--text-dim); }
.aq-dd.on-dark .aq-dd-menu { background:var(--card); border-color:var(--border); box-shadow:0 12px 34px rgba(0,0,0,0.45); }
.aq-dd.on-dark .aq-dd-opt  { color:var(--text); }
.aq-dd.on-dark .aq-dd-opt:hover  { background:var(--surface); }
.aq-dd.on-dark .aq-dd-opt.active { background:var(--gold-dim); color:var(--gold); }
.aq-dd.on-dark .aq-dd-menu::-webkit-scrollbar-thumb { background:var(--border); }

/* ── Nav ────────────────────────────────────────────────── */
.nav {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0 1.5rem;
  height: 58px;
  /* REM-8.2 - the app chrome joins the cream world. This bar was navy while login,
     register and contact carried the very same lockup on cream paper, so the wordmark
     changed colourway depending on which page you were standing on. One lockup now,
     everywhere: ink "Analyst", dark-gold "Quorum", dark-gold tagline and beta chip.
     Navy survives only where the surface is genuinely dark - the email header, the OG
     image, the favicon - and cream would disappear. */
  background: rgba(255,253,249,.94);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid rgba(200,160,68,.38);
  box-shadow: 0 10px 30px -24px rgba(11,21,40,.5);
  position: sticky;
  top: 0;
  z-index: 100;
}
/* REM-8.1 - matched to the landing / SEO lockups. Was 1.05rem/700/no tracking, and
   display:flex + gap:0.5rem, which turned the bare text node "Analyst " into an anonymous
   flex item and opened a 0.5rem hole between the two words. inline-block closes it and
   makes vertical-align work for the beta chip, exactly as on every other page. */
.nav-brand {
  text-align: center;   /* REM-9.8 - centre the tagline under the wordmark, like the register lockup */
  font-size: 1.12rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.1;
  color: #16233c;
  display: inline-block;
  text-decoration: none;
}
.nav-brand span { color: #7a5f22; }
/* REM-9.8 - the brand lockup never changes colour on hover/focus. The global
   a:hover (gold-light, line ~139) was gilding the wordmark on any app page whose
   inline .nav-brand !important guard was missing (account.html had none). A matched
   :hover rule (0,2,0 > a:hover 0,1,1) fixes every page and retires those per-page guards. */
.nav-brand:hover, .nav-brand:focus { color: #16233c; }
.nav-brand:hover span, .nav-brand:focus span { color: #7a5f22; }
/* The chip is a <b>, not a <span>: every brand rule on every page paints its spans gold
   (.nav .brand span is 0-2-1), so a span chip would inherit that and need a specificity
   fight on each page. No rule anywhere targets <b> inside a brand lockup. */
.brand-beta{display:inline-block;font-size:.5rem;font-weight:700;letter-spacing:.12em;
  text-transform:uppercase;vertical-align:.42em;margin-left:.4em;padding:1px 5px;
  border:1px solid rgba(122,95,34,.45);border-radius:3px;color:#7a5f22;line-height:1.4;}
/* REM-8.2 - the tagline was missing from the app chrome entirely: nav.js mounted
   "Analyst Quorum beta" while every marketing and auth page mounted the same lockup
   WITH "Secure your seat." under it. It is part of the lockup, not decoration. */
.brand-tag{display:block;font-size:.53rem;font-weight:700;letter-spacing:.28em;
  color:#7a5f22;margin-top:2px;text-transform:uppercase;}
.nav-links { display: flex; align-items: center; gap: 0.2rem; margin-left: auto; }
.nav-link {
  padding: 0.4rem 0.8rem;
  border-radius: var(--radius-sm);
  color: #3d4b63;
  font-size: 0.88rem;
  font-weight: 500;
  text-decoration: none;
  transition: all 0.2s;
}
.nav-link:hover, .nav-link.active { color: #7a5f22; background: rgba(200,160,68,.10); }
.nav-sep { width: 1px; height: 20px; background: #e6e0d6; margin: 0 0.4rem; }

/* ══ HIDING NAV ══════════════════════════════════════════════════════════════
   The nav is now HIDDEN by default and slides down on demand — worth ~70px of vertical room
   on every page. Markup and behaviour live in assets/js/nav.js (ONE definition; it used to be
   copy-pasted into five files, which is how `.sticky-bar` ended up hidden three different ways
   on one page and not at all on another).

   The nav is `position: fixed` and translated out of view. It is NOT `display:none` — a display
   toggle cannot animate, and more importantly it would remove the links from the accessibility
   tree entirely, so a screen-reader user would have no nav at all rather than a hidden one.

   ⚠ OPT-IN, NOT GLOBAL. Every rule below is scoped to `body.aq-chrome`, a class that nav.js adds
   to <body> when it mounts. It must NOT be a bare `.nav { transform: translateY(-100%) }`:
   index.html — the public landing page — carries its own static `<nav class="nav">` and never
   mounts nav.js, so a global rule would translate the marketing site's nav off-screen with no
   pull-tab to bring it back. The nav would simply cease to exist for every visitor who is not
   logged in. A style that hides things must only reach the pages that also know how to show
   them again. */
body.aq-chrome .nav {
  position: fixed; top: 0; left: 0; right: 0;
  z-index: 1100;
  transform: translateY(-100%);
  transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
  will-change: transform;
}
body.aq-chrome.nav-open .nav { transform: translateY(0); }
@media (prefers-reduced-motion: reduce) { body.aq-chrome .nav { transition: none; } }

/* The invisible strip along the very top edge. Hovering it reveals the nav. */
#aqNavHot { position: fixed; top: 0; left: 0; right: 0; height: 14px; z-index: 1050; }

/* ── The pull-tab ────────────────────────────────────────────
   The ONE element that never hides. On practice pages it carries the Daily Goal — with the F6
   ceremony gone, that label is the only signal of session progress, so it must not be the thing
   that disappears. Making it the affordance as well means the element a student always sees is
   also the element that teaches them the nav is up there. */
#aqNavTab {
  position: fixed; top: 4px; left: 50%; transform: translateX(-50%);
  z-index: 1200; cursor: pointer; user-select: none;
  display: flex; align-items: center; gap: 0.45rem;
  height: 22px; padding: 0 0.7rem; border: 0;
  /* REM-8.2 - the tab overlaps the nav while the nav is open, so a navy chip would
     sit as a dark blot on a cream bar. Cream chip, gold hairline, real shadow: it
     still reads as a control against the warm page behind it. */
  background: #fffdf9; color: #3d4b63;
  border: 1px solid rgba(200,160,68,.45); border-top: 0;
  border-radius: 0 0 9px 9px;
  font-family: inherit; font-size: 0.7rem; font-weight: 600; letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;   /* the count must not jitter as it ticks */
  white-space: nowrap;
  box-shadow: 0 6px 16px -8px rgba(11, 21, 40, 0.4);
  transition: color 0.25s ease, background 0.25s ease;
}
#aqNavTab:hover { background: #fffefc; color: #7a5f22; }
#aqNavTab:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
#aqNavTab.met { color: #7a5f22; }
.aq-tab-grip { display: flex; gap: 2px; }
.aq-tab-grip i { width: 3px; height: 3px; border-radius: 50%; background: currentColor; opacity: 0.75; display: block; }
.aq-tab-chev { font-size: 0.6rem; opacity: 0.8; transition: transform 0.3s ease; }
body.nav-open .aq-tab-chev { transform: rotate(180deg); }

/* A brief breathing nudge after the first-visit reveal retracts, so the eye follows the nav up
   to where it went. Three cycles, then it stops — an animation that never ends is noise. */
@keyframes aqTabHint {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  50%      { transform: translateX(-50%) translateY(2px); }
}
#aqNavTab.hint { animation: aqTabHint 1.6s ease-in-out 3; }
@media (prefers-reduced-motion: reduce) { #aqNavTab.hint { animation: none; } }

/* ── Account menu ────────────────────────────────────────────
   Persistent. Log Out and Account settings must never sit behind a hover the student has to
   discover first. Steps aside while the nav is open so the two never duplicate each other. */
.aq-avatar-wrap { position: fixed; top: 10px; right: 16px; z-index: 1250; }
.aq-avatar {
  width: 32px; height: 32px; border-radius: 50%; cursor: pointer;
  background: #101c32; color: var(--gold);
  display: flex; align-items: center; justify-content: center;
  font-family: inherit; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.02em;
  border: 1px solid rgba(200, 160, 68, 0.45);
  box-shadow: 0 2px 10px rgba(10, 31, 68, 0.18);
  transition: transform 0.18s ease;
}
.aq-avatar:hover { transform: scale(1.06); }
.aq-avatar:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.aq-avatar-menu {
  position: absolute; top: 40px; right: 0; min-width: 186px;
  background: var(--paper, #FFFCF8);
  border: 1px solid var(--rule, rgba(200,185,165,0.45));
  border-radius: 8px;
  box-shadow: 0 12px 32px rgba(42, 34, 24, 0.16);
  padding: 0.4rem; display: none;
}
.aq-avatar-menu.open { display: block; }
.aq-am-head { padding: 0.45rem 0.6rem 0.5rem; border-bottom: 1px solid var(--rule, rgba(200,185,165,0.45)); margin-bottom: 0.3rem; }
.aq-am-name { font-size: 0.82rem; font-weight: 700; color: #2a2218; }
.aq-am-lvl  { font-size: 0.68rem; color: var(--gold); font-weight: 700; letter-spacing: 0.04em; margin-top: 1px; }
.aq-am-item {
  display: block; width: 100%; text-align: left;
  background: none; border: 0; cursor: pointer;
  padding: 0.45rem 0.6rem; border-radius: 5px;
  font-family: inherit; font-size: 0.8rem; color: #2a2218; text-decoration: none;
}
.aq-am-item:hover { background: rgba(200, 160, 68, 0.10); }
.aq-am-item.danger { color: #a8352b; }
.aq-am-item.danger:hover { background: rgba(168, 53, 43, 0.08); }
.aq-am-sep { height: 1px; background: var(--rule, rgba(200,185,165,0.45)); margin: 0.3rem 0; }
body.nav-open .aq-avatar-wrap { opacity: 0; pointer-events: none; transition: opacity 0.18s; }

/* ⚠ THE AVATAR OWNS THE TOP-RIGHT CORNER. RESERVE IT — WHEREVER IT EXISTS.
   A position:fixed badge in a corner CLAIMS that corner from the page underneath it. On the
   vignette pages it was landing on the "Next →" button, covering 16px of it, so clicking Next's
   right edge hit the avatar instead. The avatar is now removed from practice pages entirely
   (nav.js: `showAvatar = !practice`) and the account controls moved into the nav.

   It remains on the dashboard and account page, and the dashboard ALSO puts something in that
   corner — the brand lockup. So the reservation follows the avatar rather than being pinned to
   any one page: reserve the corner where the avatar actually is, and the collision cannot recur
   when a future header grows a control on its right-hand end. */
:root { --aq-corner: 56px; }   /* 32px avatar + 16px inset + breathing room */
body.aq-chrome .dash-head { padding-right: var(--aq-corner); }

/* ── "← Dashboard" in the practice sub-header ────────────────
   Moved out of the nav, because a student must never have to hunt for the exit. It sits in the
   always-visible sub-header row instead. guardExit() still intercepts it wherever it lives — it
   is an <a href>, so the flush-before-navigate protection is unaffected. */
.aq-back {
  display: inline-flex; align-items: center; gap: 0.3rem;
  font-size: 0.8rem; font-weight: 600;
  color: #0A1F44; text-decoration: none;
  padding: 0.3rem 0.6rem; border-radius: 6px;
  border: 1px solid var(--rule, rgba(200,185,165,0.45));
  background: #fff;
  transition: background 0.18s ease;
}
.aq-back:hover { background: rgba(200, 160, 68, 0.10); color: #0A1F44; }   /* REM-8.1: 1.65 -> 13.4:1 */

/* ══ REPORT-AN-ISSUE MODAL (aq-rm) ═══════════════════════════════════════════
   Was: FOUR copies (one per practice page), all painted with DARK-THEME tokens —
   var(--card), var(--text), var(--border), var(--surface) — on pages whose background is cream.
   It rendered as a navy box in the middle of a warm page, on every level. Nobody caught it for
   months precisely BECAUSE it was in four places: a bug that appears four times looks like the
   design.

   Same failure as the aq-dd dropdown, which inherited the same dark tokens onto the same light
   surface. Fixed the same way: theme it in the warm palette, and define it ONCE.

   Markup and behaviour now come from assets/js/report.js. */
.aq-rm {
  display: none; position: fixed; inset: 0; z-index: 3000;
  background: rgba(42, 34, 24, 0.42);
  backdrop-filter: blur(3px);
  align-items: center; justify-content: center; padding: 1rem;
}
.aq-rm.open { display: flex; animation: aq-fade 0.18s ease-out; }

.aq-rm-card {
  background: var(--paper, #FFFCF8);
  color: #2a2218;
  border: 1px solid var(--rule, rgba(200,185,165,0.45));
  border-top: 3px solid var(--gold);          /* the same gold accent every card on the platform carries */
  max-width: 460px; width: 100%;
  border-radius: 10px;
  padding: 1.35rem 1.5rem;
  box-shadow: 0 20px 56px rgba(42, 34, 24, 0.24);
  font-family: inherit;
}
.aq-rm-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.1rem; }
.aq-rm-head strong { font-family: var(--serif); font-size: 1rem; font-weight: 700; color: #2a2218; }
.aq-rm-x {
  background: none; border: none; font-size: 1.5rem; line-height: 1;
  color: #a89878; cursor: pointer; padding: 0 0.2rem;
}
.aq-rm-x:hover { color: #2a2218; }

.aq-rm-label { display: block; font-size: 0.78rem; font-weight: 700; color: #5b5346; margin: 0 0 0.4rem; letter-spacing: 0.02em; }
.aq-rm-label span { font-weight: 400; color: #a89878; }
.aq-rm .aq-dd { margin-bottom: 1rem; }

.aq-rm-in {
  width: 100%; box-sizing: border-box;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--rule, rgba(200,185,165,0.45));
  border-radius: 6px;
  font-size: 0.92rem; font-family: inherit;
  color: #2a2218; background: #fff;
  margin-bottom: 1rem;
}
.aq-rm-in:focus { outline: none; border-color: var(--gold); box-shadow: 0 0 0 3px rgba(200,160,68,0.15); }
textarea.aq-rm-in { resize: vertical; }
.aq-rm-in::placeholder { color: #b9a894; }

.aq-rm-status { display: none; font-size: 0.84rem; padding: 0.55rem 0.75rem; border-radius: 6px; margin-bottom: 0.85rem; }
.aq-rm-status.ok  { display: block; background: rgba(31,122,82,0.10);  border: 1px solid rgba(31,122,82,0.35);  color: #1f7a52; }
.aq-rm-status.err { display: block; background: rgba(168,53,43,0.08);  border: 1px solid rgba(168,53,43,0.32);  color: #a8352b; }

.aq-rm-actions { display: flex; gap: 0.6rem; justify-content: flex-end; }
.aq-rm-ghost {
  background: #fff; color: #5b5346;
  border: 1px solid var(--rule, rgba(200,185,165,0.45));
  padding: 0.55rem 1.1rem; border-radius: 6px; cursor: pointer;
  font-family: inherit; font-size: 0.88rem; font-weight: 600;
}
.aq-rm-ghost:hover { background: rgba(200,160,68,0.08); color: #2a2218; }
.aq-rm-primary {
  background: var(--gold); color: #2a2218; border: 0;
  padding: 0.55rem 1.15rem; border-radius: 6px; cursor: pointer;
  font-family: inherit; font-size: 0.88rem; font-weight: 700;
}
.aq-rm-primary:hover { filter: brightness(1.06); }
.aq-rm-primary:disabled { opacity: 0.55; cursor: not-allowed; }

/* The trigger. It was a .feedback-btn on L1, inline styles on L2 and L3-MCQ, and a
   .results-report-btn on the essay page — three different looks for one action. */
.aq-report-btn {
  background: none; border: none; padding: 0; margin-top: 0.9rem;
  font-family: inherit; font-size: 0.76rem; color: #9a8a78;
  cursor: pointer; text-decoration: underline;
}
.aq-report-btn:hover { color: #a8352b; }

/* ── Page wrapper ───────────────────────────────────────── */
.page { max-width: 1200px; margin: 0 auto; padding: 2rem 1.5rem; }
.page-sm { max-width: 480px; margin: 0 auto; padding: 3rem 1.5rem; }

/* ── Badges ─────────────────────────────────────────────── */
.badge {
  display: inline-block;
  padding: 0.2rem 0.65rem;
  border-radius: 99px;
  font-size: 0.75rem;
  font-weight: 600;
}
.badge-gold { background: var(--gold-dim); color: var(--gold); border: 1px solid var(--gold); }
.badge-green { background: var(--success-bg); color: var(--success); }
.badge-red   { background: var(--error-bg); color: var(--error); }

/* ── Progress bar ───────────────────────────────────────── */
.progress-bar { height: 6px; background: var(--border); border-radius: 99px; overflow: hidden; }
.progress-fill { height: 100%; border-radius: 99px; background: var(--gold); transition: width 0.4s; }
.progress-fill.green { background: var(--success); }

/* ── Alert / Toast ──────────────────────────────────────── */
.alert {
  padding: 0.8rem 1rem;
  border-radius: var(--radius-sm);
  font-size: 0.9rem;
  margin-bottom: 1rem;
  display: none;
}
.alert.show { display: block; }
.alert-error   { background: var(--error-bg); border: 1px solid var(--error); color: var(--error); }
.alert-success { background: var(--success-bg); border: 1px solid var(--success); color: var(--success); }
.alert-info    { background: rgba(52,152,219,0.1); border: 1px solid var(--info); color: var(--info); }

/* ── Spinner ────────────────────────────────────────────── */
.spinner {
  width: 22px; height: 22px;
  border: 2.5px solid var(--border);
  border-top-color: var(--gold);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
  display: inline-block;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ── Section divider ────────────────────────────────────── */
.divider { height: 1px; background: var(--border); margin: 2rem 0; }

/* ── Practice layout (80/20) ────────────────────────────── */
.practice-wrap {
  display: grid;
  grid-template-columns: 1fr 220px;
  max-width: 1440px;
  margin-left: auto;
  margin-right: auto;
  width: 100%;
  gap: 1.25rem;
  align-items: start;
  min-height: calc(100vh - 58px);
  padding: 1.25rem;
}
.practice-main { min-width: 0; }
.practice-ad {
  position: sticky;
  top: 70px;
  background: var(--surface);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  min-height: 400px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: var(--text-dim);
  font-size: 0.78rem;
  text-align: center;
  padding: 1rem;
  gap: 0.5rem;
}

/* ── Vignette split (40/40/20) ──────────────────────────── */
.vignette-wrap {
  display: grid;
  grid-template-columns: 1fr 1fr 220px;
  gap: 1.25rem;
  align-items: start;
  min-height: calc(100vh - 58px);
  padding: 1.25rem;
}
.vignette-panel {
  position: sticky;
  top: 70px;
  max-height: calc(100vh - 90px);
  overflow-y: auto;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.25rem;
  font-size: 0.92rem;
  line-height: 1.7;
  color: var(--text);
}
.vignette-panel::-webkit-scrollbar { width: 5px; }
.vignette-panel::-webkit-scrollbar-track { background: transparent; }
.vignette-panel::-webkit-scrollbar-thumb { background: var(--border); border-radius: 99px; }

/* ── Essay layout ───────────────────────────────────────── */
.essay-wrap {
  display: flex;
  flex-direction: column;
  min-height: calc(100vh - 58px);
}
.essay-content { flex: 1; padding: 1.25rem; max-width: 960px; margin: 0 auto; width: 100%; }
.essay-ad-bottom {
  background: var(--surface);
  border-top: 1px dashed var(--border);
  height: 90px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-dim);
  font-size: 0.78rem;
  flex-shrink: 0;
}

/* ══ GLOBAL BOTTOM AD BAR (728×90) ═══════════════════════════════════════════
   Was: FOUR divergent copies of the same idea. `.sticky-bar` on login and register (hidden via
   `display:none !important`), `.sticky-bar` on the dashboard (hidden THREE separate ways —
   a rule, an override, and an inline style), and `#bottomSticky` on the essay page under a
   different name entirely... which was NOT hidden, so essay candidates were being shown a
   dashed "Ad · 728×90" placeholder box. Four copies, and the one that diverged is the one that
   leaked. That is the whole argument against duplicated markup in a sentence.

   Now: ONE component, ONE switch (AQ_ADS_ENABLED in assets/js/api.js).

   THE DEFAULT IS HIDDEN, and the bar is revealed only by a class the switch adds to <body>.
   That polarity is deliberate. If the script fails, a page forgets to load it, or someone adds
   a new page and wires nothing up, the failure mode is "no ad" — never "a placeholder box in
   front of a paying candidate". The thing that must never happen must require an action. */
.aq-adbar { display: none; }

body.aq-ads-on .aq-adbar {
  position: fixed; bottom: 0; left: 0; right: 0; height: 58px; z-index: 100;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255,252,248,0.97);
  border-top: 1px solid rgba(200,185,165,0.4);
  backdrop-filter: blur(8px);
}
/* Reserve the space ONLY when the bar is actually there. A permanent 70px gutter under every
   page "just in case" would be dead space on every page for as long as ads stay off. */
body.aq-ads-on { padding-bottom: 70px; }

.aq-adbar-slot {
  max-width: 728px; width: 100%; height: 56px;
  display: flex; align-items: center; justify-content: center;
  border: 1px dashed rgba(200,185,165,0.4); border-radius: 4px;
  color: #746959; font-size: 0.72rem; letter-spacing: 0.04em;
}

/* ══ FOOTER / LEGAL DISCLAIMER ═══════════════════════════════════════════════
   Was: four duplicate .aq-footer definitions (one per practice page), plus a bare <p> with
   inline styles on the dashboard, login and register, plus a different footer again on the
   landing page. Colour was var(--text-dim) — a DARK-theme value — printed on cream, so the
   legal text was a muddy low-contrast blue-grey. Size was 0.6rem (~9.6px): below the readable
   minimum and it looked cheap on the one block of text a regulator would actually read.

   Now: ONE definition. Semantic <footer>. A comfortable measure, a hairline rule, and a size
   the text can actually be read at. The disclaimer is the last thing a candidate sees — it
   should look like a prospectus footnote, not an afterthought. */
.aq-footer {
  margin-top: 2.5rem;
  padding: 1.4rem 1.5rem 1.8rem;
  border-top: 1px solid var(--rule);
  background: transparent;
}
.aq-footer-in { max-width: 940px; margin: 0 auto; text-align: center; }
.aq-foot-brand {
  font-size: 0.75rem; font-weight: 500; color: var(--ink-3);
  margin-bottom: 0.45rem; letter-spacing: 0.01em;
}
.aq-foot-legal {
  font-size: 0.7rem; line-height: 1.7; color: #746959;   /* REM-8.1: 1.96 -> 4.81:1 */
  max-width: 820px; margin: 0 auto;
}
.aq-foot-legal sup { font-size: 0.62em; }
.aq-footer a { color: var(--ink-3); text-decoration: underline; text-underline-offset: 2px; }
.aq-footer a:hover { color: #6b5119; }   /* REM-8.1: 2.19 -> 6.67:1 */

/* Inverted footer for a dark page. The landing, sign-in and register pages were the
   original users; all three are cream since REM-7.6, so only admin.html sets .on-dark. */
.aq-footer.on-dark            { border-top-color: var(--border); }
.aq-footer.on-dark .aq-foot-brand { color: var(--text-muted); }
.aq-footer.on-dark .aq-foot-legal { color: var(--text-dim); }
.aq-footer.on-dark a          { color: var(--text-muted); }
.aq-footer.on-dark a:hover    { color: var(--gold); }

/* ── Question option ────────────────────────────────────── */
.option {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 0.85rem 1rem;
  border: 1.5px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: all 0.2s;
  margin-bottom: 0.6rem;
  background: var(--surface);
}
/* ══ THE ANSWER REVEAL ═══════════════════════════════════════════════════════
   This is the core interaction of the entire platform and it had NO motion — options snapped
   to colour instantly and the explanation appeared with no entrance. It is the single moment a
   candidate feels most, so it is the one that most repays being designed.

   Correct: the option settles (a brief lift, then rest) and its letter badge pops.
   Wrong:   a short, restrained shake. Not punitive — just legible.
   Neither is celebratory. This is a professional exam tool, not a game. */
.option {
  transition: border-color var(--t-base) var(--ease),
              background   var(--t-base) var(--ease),
              transform    var(--t-fast) var(--ease),
              opacity      var(--t-base) var(--ease);
}
.option:hover:not(.correct):not(.wrong):not(.dismissed) {
  border-color: var(--gold); background: var(--card); transform: translateY(-1px);
}
.option:active:not(.correct):not(.wrong):not(.dismissed) { transform: translateY(0); }
.option.selected { border-color: var(--gold); background: var(--gold-dim); }
.option.correct  { border-color: var(--success); background: var(--success-bg); animation: aq-settle 420ms var(--ease); }
.option.wrong    { border-color: var(--error);   background: var(--error-bg);   animation: aq-nudge 300ms var(--ease); }
.option.correct .option-letter { animation: aq-pop 380ms var(--ease) 60ms both; }

@keyframes aq-settle { 0% { transform: translateY(0) scale(1); } 35% { transform: translateY(-2px) scale(1.012); } 100% { transform: none; } }
@keyframes aq-nudge  { 0%,100% { transform: translateX(0); } 22% { transform: translateX(-3px); } 55% { transform: translateX(2px); } 80% { transform: translateX(-1px); } }
@keyframes aq-pop    { 0% { transform: scale(0.6); opacity: 0; } 60% { transform: scale(1.12); opacity: 1; } 100% { transform: scale(1); } }

/* Explanation + why-wrongs enter rather than appear. */
.explanations-section { animation: aq-rise var(--t-slow) var(--ease) 120ms both; }
.expl-box.show        { animation: aq-rise var(--t-base) var(--ease) both; }
.why-wrong-item       { animation: aq-rise var(--t-base) var(--ease) both; }
.why-wrong-item:nth-of-type(1) { animation-delay: 180ms; }
.why-wrong-item:nth-of-type(2) { animation-delay: 240ms; }
.why-wrong-item:nth-of-type(3) { animation-delay: 300ms; }
.option-letter {
  width: 26px; height: 26px;
  min-width: 26px;
  border-radius: 50%;
  background: var(--border);
  display: flex; align-items: center; justify-content: center;
  font-size: 0.8rem; font-weight: 700;
  transition: all 0.2s;
}
.option.selected .option-letter { background: var(--gold); color: #0b1528; }
.option.correct  .option-letter { background: var(--success); color: #fff; }
.option.wrong    .option-letter { background: var(--error); color: #fff; }
.option-text { font-size: 0.93rem; line-height: 1.55; padding-top: 2px; }

/* ── "Saving…" toast ────────────────────────────────────────
   Shown only when leaving a practice page with answers still un-flushed, while we wait for
   the write to land. It exists so the delay is EXPLAINED rather than felt as a hang. */
.aq-saving {
  position: fixed; left: 50%; bottom: 28px; z-index: 9999;
  transform: translateX(-50%) translateY(8px); opacity: 0;
  display: flex; align-items: center; gap: 0.55rem;
  background: var(--ink); color: #fffdfa;
  font-size: 0.8rem; font-weight: 600; letter-spacing: 0.01em;
  padding: 0.6rem 1.15rem; border-radius: 999px;
  box-shadow: 0 10px 30px -10px rgba(11,21,40,0.55);
  pointer-events: none;
  transition: opacity var(--t-base) var(--ease), transform var(--t-base) var(--ease);
}
.aq-saving.in { opacity: 1; transform: translateX(-50%) translateY(0); }
.aq-saving-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--gold);
                 animation: aq-pulse 950ms var(--ease) infinite; }
@keyframes aq-pulse { 0%,100% { opacity: 0.35; transform: scale(0.78); } 50% { opacity: 1; transform: scale(1); } }

/* ── QuorumConsensus — empty state ──────────────────────────
   Shared by all four practice pages (the .f1-* chrome is still page-local; only this
   empty state is centralised, because it was the same apologetic italic line on all four).

   Design rule: the empty state is the SAME SKELETON as the filled state — label, number
   slot, track, footnote — so when the question crosses N = 20 nothing on the page moves.
   It also stops apologising. It says what the gate is and how close this question is to it,
   which turns a dead panel into a reason to keep practising. */
.f1-empty {
  background: rgba(255,252,248,0.96);
  border: 1px solid rgba(200,185,165,0.35); border-top: none;
  padding: 0.7rem 0.85rem;
}
.f1-empty-lbl { font-size: 0.68rem; color: #7a6a58; margin-bottom: 0.22rem; }
/* An em-dash sitting in the exact slot the real number will occupy. */
.f1-empty-num {
  font-size: 1.3rem; font-weight: 700; color: #c4b6a4;
  line-height: 1; margin-bottom: 0.45rem;
}
/* Hatched, not empty: a 0%-wide solid bar would read as "the cohort scored zero". */
.f1-empty-track {
  height: 7px; border-radius: 3.5px; overflow: hidden; margin-bottom: 0.5rem;
  background: repeating-linear-gradient(90deg,
    rgba(200,185,165,0.42) 0 4px, rgba(200,185,165,0.14) 4px 8px);
}
.f1-empty-fill {
  height: 100%; border-radius: 3.5px; width: 0%;
  background: rgba(26,55,94,0.30);
  transition: width 0.9s var(--ease);
}
.f1-empty-note { font-size: 0.68rem; color: #8a7a6a; line-height: 1.5; }
.f1-empty-note strong { color: #1A375E; font-weight: 600; font-variant-numeric: tabular-nums; }

/* ── Score chip ─────────────────────────────────────────── */
.score-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.8rem;
  border-radius: 99px;
  font-size: 0.82rem;
  font-weight: 600;
}

/* ── LO box ─────────────────────────────────────────────── */
.lo-box {
  background: var(--surface);
  border-left: 3px solid var(--gold);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: 0.75rem 1rem;
  font-size: 0.85rem;
  color: var(--text-muted);
  margin-top: 1rem;
}
.lo-box strong { color: var(--gold); font-size: 0.75rem; display: block; margin-bottom: 0.2rem; text-transform: uppercase; letter-spacing: 0.06em; }

/* ── Result panel ───────────────────────────────────────── */
.result-panel {
  border-radius: var(--radius);
  padding: 1.1rem 1.25rem;
  margin-top: 1rem;
  display: none;
}
.result-panel.show { display: block; }
.result-panel.correct { background: var(--success-bg); border: 1px solid var(--success); }
.result-panel.wrong   { background: var(--error-bg);   border: 1px solid var(--error);   }

/* ── Grid utilities ─────────────────────────────────────── */
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1rem; }
.flex { display: flex; }
.flex-center { display: flex; align-items: center; }
.flex-between { display: flex; align-items: center; justify-content: space-between; }
.gap-1 { gap: 0.5rem; }
.gap-2 { gap: 1rem; }
.mt-1 { margin-top: 0.5rem; }
.mt-2 { margin-top: 1rem; }
.mt-3 { margin-top: 1.5rem; }
.mb-1 { margin-bottom: 0.5rem; }
.mb-2 { margin-bottom: 1rem; }
.mb-3 { margin-bottom: 1.5rem; }

/* ── Responsive ─────────────────────────────────────────── */
@media (max-width: 900px) {
  .practice-wrap  { grid-template-columns: 1fr; }
  .vignette-wrap  { grid-template-columns: 1fr; }
  .practice-ad    { display: none; }
  .grid-2, .grid-3 { grid-template-columns: 1fr; }
}
@media (max-width: 600px) {
  h1 { font-size: 1.8rem; }
  .page { padding: 1rem; }
}

/* ── Loading overlay ────────────────────────────────────── */
.loading-overlay {
  position: fixed; inset: 0;
  background: rgba(11,21,40,0.85);
  display: flex; align-items: center; justify-content: center;
  z-index: 999;
  display: none;
}
.loading-overlay.show { display: flex; }

/* ── Table ──────────────────────────────────────────────── */
table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
th, td { text-align: left; padding: 0.75rem 1rem; border-bottom: 1px solid var(--border); }
th { color: var(--text-muted); font-weight: 600; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.05em; }
tr:last-child td { border-bottom: none; }
tr:hover td { background: var(--surface); }

/* ── Empty state ────────────────────────────────────────── */
.empty { text-align: center; padding: 3rem; color: var(--ink-3); }   /* REM-8.1 */
.empty-icon { font-size: 2.5rem; margin-bottom: 0.75rem; }

/* ══════════════════════════════════════════════════════════════════════════
   REM-9.3 - WIDTH SYSTEM (app chrome: dashboard, account, login, contact...)
   Matches the tiers introduced on index.html so the marketing site and the
   product stop disagreeing about how wide a page is.

   The defect this fixes on the dashboard: .nav ran edge to edge while .page
   stopped at 1200px, so on any wide monitor the masthead visibly overhung the
   cards beneath it and the side space read as a layout failure rather than as
   margin. The bar KEEPS its full bleed background - that part was right, and
   it is what makes centred content look deliberate. Only the bar's contents
   are pulled in, to exactly the same edge as .page.
   ══════════════════════════════════════════════════════════════════════════ */
:root {
  /* Two tiers, kept deliberately distinct (2026-07-30).
     --w-content is the READING tier: prose and card chrome (dashboard, account,
     login, contact, marketing). 1280px is the modern house standard - GitHub's
     capped app width and Tailwind's max-w-7xl both land here - and it is about
     as wide as a multi-column card grid goes before the eye loses the row.
     --w-wide is the WORKING tier: the split-screen practice surfaces, where the
     student needs case text and answers side by side. It stays at 1440 on
     purpose; pushing it further lengthens each reading column and costs more in
     legibility than it gains in screen use.
     Both are CEILINGS inside a min(), so every screen narrower than the tier is
     already fluid and is untouched by either value. */
  --w-content: 1280px;
  --w-wide:    1440px;
  --gutter:    clamp(1.25rem, 4vw, 3rem);
}

.page {
  width: min(100% - 2 * var(--gutter), var(--w-content));
  max-width: none;
  margin-inline: auto;
  padding-left: 0;
  padding-right: 0;
}

.nav {
  padding-left:  calc((100% - min(100% - 2 * var(--gutter), var(--w-content))) / 2);
  padding-right: calc((100% - min(100% - 2 * var(--gutter), var(--w-content))) / 2);
}

/* The 80/20 practice layout is a working surface, not prose: wide tier. */
.practice-wrap {
  width: min(100% - 2 * var(--gutter), var(--w-wide));
  max-width: none;
  margin-inline: auto;
}

/* ── App nav on phones (2026-08-03) ────────────────────────
   Measured at 390px before this block: the lockup wrapped to THREE lines (80px tall
   inside a 58px bar) and the link row ran 74px off-screen, so "Contact us" could not be
   reached on dashboard, account or any practice page.
   Causes: the wordmark had no nowrap, .brand-beta is position:absolute so its width never
   entered the layout, and there was no breakpoint for the link row at all.
   This nav is an overlay panel opened from the pull-tab, so letting it grow to two rows is
   safer than shrinking type until it only just fits. */
@media (max-width: 640px) {
  .nav { height: auto; min-height: 58px; flex-wrap: wrap; padding: 0.5rem 0.9rem; gap: 0.35rem 0.6rem; }
  .nav-brand { font-size: 0.95rem; white-space: nowrap; line-height: 1.15; }
  .nav-brand .brand-tag, .nav-brand .brand-beta { display: none; }
  .nav-links { margin-left: auto; flex-wrap: wrap; justify-content: flex-end; gap: 0.15rem; }
  .nav-link { padding: 0.35rem 0.55rem; font-size: 0.8rem; }
  .nav-sep { display: none; }
  .aq-avatar-wrap { top: 13px; right: 12px; }
}

/* ══════════════════════════════════════════════════════════════════════════════
   EXHIBIT TABLES — horizontal scroller (2026-08-03)
   ══════════════════════════════════════════════════════════════════════════════
   Measured on practice-l3-essay at 390px: a five-column exhibit needs 505px of
   intrinsic width and the case pane offers 345px. Nothing scrolled sideways, so the
   last two columns were clipped and unreachable — and the table's own background ran
   out past the vignette card's rounded edge. Every exhibit table is now wrapped by
   AQExhibits (assets/js/api.js) in its own scroller.

   The table inside is width:auto + min-width:100%, NOT width:100%. That is the whole
   trick: it stretches to fill the pane when the columns fit and grows past it when
   they do not, instead of crushing five columns into 345px. The !important clears the
   pages' own `#vigText table { width:100% }` rule, which carries id specificity.     */

.aq-exh-wrap { position: relative; margin: 0.9rem 0; }
.aq-exh {
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;   /* a sideways swipe must not chain to the pane behind */
  scrollbar-width: thin;
}
.aq-exh > table { width: auto !important; min-width: 100%; margin: 0 !important; }

/* The fade is the only signal that there is more table to the right. It lives on the
   WRAPPER, not the scroller: an ::after inside an overflow container scrolls away with
   the content and stops marking the edge. It is cleared at the end of the scroll so it
   never points at columns that are not there. */
.aq-exh-wrap::after {
  content: ''; position: absolute; top: 0; right: 0; bottom: 0; width: 36px;
  pointer-events: none; opacity: 0; transition: opacity 0.18s ease;
  background: linear-gradient(to right, rgba(253,250,244,0), rgba(245,240,231,0.96));
  border-radius: 0 6px 6px 0;
}
.aq-exh-wrap.has-more::after { opacity: 1; }
.aq-exh:focus-visible { outline: 2px solid rgba(196,154,44,0.9); outline-offset: 2px; }

/* Tighter type buys back roughly a column before any scrolling is needed at all. */
@media (max-width: 560px) {
  .aq-exh > table { font-size: 0.78rem !important; }
  .aq-exh > table th, .aq-exh > table td { padding: 0.45rem 0.6rem !important; }
}
