:root{
  color-scheme: light;
  --surface-1:#fffefb; --page:#f6f3ec;
  --ink-1:#171310; --ink-2:#5c554c; --ink-3:#928a7d;
  --grid:#e7e2d6; --axis:#c9c0b0; --border:rgba(23,19,16,0.09);
  --s1:#2a78d6; --s2:#d97757; --s3:#1f9d74; --s4:#c98a2b;
  --good:#1f9d4c; --warn:#e2a13d; --serious:#d97757; --crit:#c5453f;
  --seq-100:#d8e8fb; --seq-250:#8fbbf0; --seq-450:#2a78d6; --seq-600:#184f95;
  --radius:12px;
  --shadow-sm:0 1px 2px rgba(23,19,16,.05);
  --shadow-md:0 6px 20px rgba(23,19,16,.06);
}
:root[data-theme="dark"]{
  color-scheme: dark;
  --surface-1:#1e1b17; --page:#14120f;
  --ink-1:#f5f1ea; --ink-2:#c9c0b0; --ink-3:#948c7e;
  --grid:#322e28; --axis:#423d35; --border:rgba(245,241,234,0.09);
  --s1:#4b93ea; --s2:#e08b68; --s3:#2bb583; --s4:#d9a54a;
  --good:#2bb56a; --warn:#e2a13d; --serious:#e08b68; --crit:#e2726b;
  --seq-100:#173a66; --seq-250:#1f5490; --seq-450:#4b93ea; --seq-600:#8fbbf0;
  --shadow-sm:0 1px 2px rgba(0,0,0,.3);
  --shadow-md:0 8px 24px rgba(0,0,0,.35);
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  background:var(--page); color:var(--ink-1);
  /* Devanagari faces are named explicitly because none of the Latin fallbacks
     cover the script: on Windows system-ui resolves to Segoe UI, which has no
     Devanagari, so the browser silently substitutes a face with different
     metrics. Nirmala UI (Windows), Noto Sans Devanagari (Android/Linux) and
     Kohinoar/Devanagari Sangam MN (Apple) are the shipped-with-the-OS options.
     Deliberately no webfont: this is an offline-capable PWA and a Devanagari
     face is a few hundred KB the service worker would have to carry. */
  font-family:system-ui,-apple-system,"Segoe UI",Roboto,
    "Nirmala UI","Noto Sans Devanagari","Kohinoor Devanagari","Devanagari Sangam MN",sans-serif;
  font-size:15px; line-height:1.5;
}
/* Devanagari sets taller than Latin at the same px size: the conjuncts and the
   headstroke need the extra leading or stacked lines touch. */
.mr,[lang="mr"]{line-height:1.75;}
a{color:var(--s1)}
.wrap{max-width:1180px;margin:0 auto;padding:0 20px 80px}

/* ---------- shell / sidebar ---------- */
.shell{display:flex;align-items:stretch;min-height:100vh}
.sidebar{
  flex:none; width:220px; background:var(--surface-1); border-right:1px solid var(--border);
  position:sticky; top:0; align-self:flex-start; height:100vh; overflow-y:auto;
  display:flex; flex-direction:column; transition:width .16s ease; z-index:50;
}
.sidebar-head{display:flex;align-items:center;gap:10px;padding:14px 12px;border-bottom:1px solid var(--border);flex:none}
.sidebar-toggle{
  flex:none;width:32px;height:32px;display:grid;place-items:center;cursor:pointer;
  background:none;border:1px solid var(--axis);border-radius:8px;color:var(--ink-2);
}
.sidebar-toggle:hover{background:var(--page);color:var(--ink-1)}
.sidebar-brand{font-weight:700;font-size:14px;white-space:nowrap;overflow:hidden}
.sidenav{padding:10px 8px 20px;display:flex;flex-direction:column;gap:2px}
.navitem,.navgroup-head{
  display:flex;align-items:center;gap:10px;width:100%;
  font:inherit;font-size:13.5px;font-weight:600;color:var(--ink-2);
  background:none;border:none;border-radius:8px;padding:9px 10px;cursor:pointer;text-align:left;
}
.navgroup-head::-webkit-details-marker,.navgroup-head::marker{display:none;content:""}
.navitem .ico,.navgroup-head .ico{flex:none;width:18px;height:18px}
.navitem .label,.navgroup-head .label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.navgroup-head .chev{flex:none;width:10px;height:10px;color:var(--ink-3);transition:transform .15s}
.navgroup[open]>.navgroup-head .chev{transform:rotate(180deg)}
.navitem:hover,.navgroup-head:hover{background:var(--page);color:var(--ink-1)}
.navitem[aria-selected="true"]{background:color-mix(in srgb,var(--s1) 13%,transparent);color:var(--s1)}
.navgroup-items{display:flex;flex-direction:column;gap:2px;padding-left:15px;margin-top:2px}
.sidebar-foot{margin-top:auto;padding:10px 12px 14px;border-top:1px solid var(--border);flex:none;
  display:flex;flex-direction:column;gap:6px}
.sidebar-foot .btn{width:100%;display:inline-flex;align-items:center;justify-content:center;gap:8px}
/* The reading language, in the footer with the theme toggle and sign-out. It is a
   standing setting rather than a header control: set once, changed rarely, and it
   belongs with the other two rather than competing for a row that has to carry the
   exam clock's buttons mid-paper. */
.sidefield{display:flex;flex-direction:column;gap:4px;width:100%;cursor:pointer}
.sidefield .label{font-size:11px;font-weight:700;letter-spacing:.02em;color:var(--ink-3);
  text-transform:uppercase;white-space:nowrap;overflow:hidden}
.sidefield .langsel{width:100%;min-height:34px}
/* The theme glyph stands in for the sibling button's 16px icon, so both labels
   line up and both collapse to a centred icon on the rail. */
.themeglyph{flex:none;width:16px;display:grid;place-items:center;font-size:15px;line-height:1}
.main{flex:1;min-width:0}

/* Backdrop behind the mobile nav drawer. Inert (and invisible) on desktop. */
.backdrop{
  position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:90;
  opacity:0;pointer-events:none;transition:opacity .22s ease;display:none;
}
/* The right-hand in-progress drawer opens at every width, so the backdrop has
   to be available on desktop too — not only inside the mobile nav breakpoint. */
.backdrop.show{display:block;opacity:1;pointer-events:auto}

/* Desktop: the sidebar collapses in place to an icon rail. */
@media (min-width:861px){
  .sidebar.collapsed{width:60px}
  .sidebar.collapsed .sidebar-head{justify-content:center;padding:14px 8px}
  .sidebar.collapsed .label,.sidebar.collapsed .sidebar-brand,.sidebar.collapsed .chev{display:none}
  .sidebar.collapsed .navitem,.sidebar.collapsed .navgroup-head{justify-content:center;padding:9px}
  .sidebar.collapsed .navgroup-items{padding-left:0}
  .sidebar.collapsed .sidebar-foot{padding:10px 8px 14px}
  .sidebar.collapsed .sidebar-foot .btn{padding:7px}
  /* 44px of usable rail. The native dropdown arrow costs ~14px of that, which is
     the difference between the rail reading "मराठी" and reading "मराठ" — so on the
     rail the control drops the arrow and keeps the word. It is still a select:
     clicking it opens the same list. */
  .sidebar.collapsed .sidefield .langsel{
    padding:6px 2px;font-size:11px;text-align:center;
    appearance:none;-webkit-appearance:none;-moz-appearance:none;
  }
}

/* Mobile: the sidebar becomes an off-canvas drawer over the content, so it
   never eats the viewport. Opened from the header hamburger (#navToggle),
   since the in-sidebar toggle is off-screen at this width. */
@media (max-width:860px){
  .sidebar{
    position:fixed;top:0;left:0;bottom:0;height:100dvh;
    width:264px;max-width:82vw;
    transform:translateX(-100%);transition:transform .22s ease;
    box-shadow:var(--shadow-md);z-index:95;
  }
  .sidebar.open{transform:none}
  .backdrop{display:block}
  .backdrop.show{opacity:1;pointer-events:auto}
}

/* ---------- header ---------- */
header.top{
  background:var(--surface-1); border-bottom:1px solid var(--border);
  position:sticky; top:0; z-index:40;
}
/* Never wraps. It used to, and the last control in the row was the one that
   dropped, landing on a second line flush against the left edge under the exam
   subtitle on every screen at 1280px. A header that changes height when a control
   is added is also a header that shifts the page under a candidate mid-tap, so the
   row is fixed and the *text* gives way instead: the brand and the exam chip shrink
   and ellipsise, the controls keep their size. (The language select used to live
   here and is now in the sidebar footer, which is one fewer control competing for
   this row while a paper is running.) */
.topin{max-width:1180px;margin:0 auto;padding:12px 20px;display:flex;align-items:center;gap:12px;flex-wrap:nowrap}
.brand{font-weight:700;font-size:17px;letter-spacing:-.01em;min-width:0;flex:0 1 auto;overflow:hidden}
.brand small{display:block;font-weight:400;font-size:12px;color:var(--ink-3);letter-spacing:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.spacer{flex:1 1 auto;min-width:0}
.topin > .btn, .topin > .chip, .topin > .savestate{flex:none}
#meChip{max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.examchip{max-width:170px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Header hamburger — only needed once the sidebar is an off-canvas drawer. */
#navToggle{display:none;flex:none;width:38px;height:38px;place-items:center;padding:0}
.onlymobile{display:none}
.brandshort{display:none}
#logoutBtn{display:inline-flex;align-items:center;gap:6px}

/* The header stacked 7 wrapping items ~400px tall on a phone. Drop everything
   that is repeated elsewhere in the UI (exam subtitle, AI status, button
   labels) so it stays a single row. */
@media (max-width:860px){
  #navToggle{display:grid}
  .topin{padding:10px 12px;gap:8px;flex-wrap:nowrap}
  .brand{font-size:15px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .brand small{display:none}
  .brandfull{display:none}
  .brandshort{display:inline}
  .savestate .txt{display:none}
  .btnlabel{display:none}
  .onlymobile{display:inline-flex}
  /* Clipped to 64px this read "u." — two characters of a username, which
     identifies nobody and costs a control's worth of a 390px row. Who is
     signed in is in the sidebar and in Settings; the header does not need it. */
  #meChip{display:none}
  .examchip{max-width:112px}
  /* A label that is hidden by .btnlabel leaves a lopsided box around the
     glyph. Square them so they read as icon buttons and clear 38px of touch. */
  .topin > .btn{min-width:38px;min-height:38px;display:inline-flex;align-items:center;
    justify-content:center;padding:7px 9px}
  /* The drawer is 264px here rather than a 60px rail, so the language field keeps
     its full width and its label; it is the header that had to give up controls at
     this width, not the sidebar. */
}
/* At phone width the same row carries two more buttons during an exam (focus,
   OMR) and overflowed by ~27px, pushing the theme toggle off the right edge.
   Tighten the gaps and the select rather than clipping a control. */
@media (max-width:560px){
  .topin{gap:6px}
  /* Mid-paper the row also carries focus + OMR. Which exam it is was settled
     before the clock started and is named on the paper itself, so the chip and
     the save dot stand down rather than squeezing the controls in use. */
  body.exam-live .examchip, body.exam-live .savestate{display:none}
}
.btn{
  font:inherit;font-size:13px;font-weight:600;cursor:pointer;
  background:var(--surface-1);color:var(--ink-1);
  border:1px solid var(--axis);border-radius:8px;padding:7px 12px;
}
.btn:hover:not(:disabled){background:var(--page)}
.btn:disabled{opacity:.5;cursor:not-allowed}
.btn.pri{background:linear-gradient(135deg,var(--s1),color-mix(in srgb,var(--s1) 80%,#000 10%));border-color:var(--s1);color:#fff;box-shadow:var(--shadow-sm)}
.btn.pri:hover:not(:disabled){filter:brightness(1.08);background:linear-gradient(135deg,var(--s1),color-mix(in srgb,var(--s1) 80%,#000 10%))}
.btn.sm{padding:4px 9px;font-size:12px}

/* saved / offline indicator */
.savestate{font-size:12px;color:var(--ink-3);display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
.savestate .dot{background:var(--good)}
.savestate.saving .dot{background:var(--warn)}
.savestate.error{color:var(--crit)} .savestate.error .dot{background:var(--crit)}

/* ---------- layout ---------- */
.grid{display:grid;gap:16px}
/* Grid items default to min-width:auto, so a wide child (a min-width table,
   a long unbroken string) forces the whole track wider than the viewport
   instead of letting the child scroll. Let them shrink. */
.grid>*{min-width:0}
.g4{grid-template-columns:repeat(4,1fr)}
.g3{grid-template-columns:repeat(3,1fr)}
.g2{grid-template-columns:repeat(2,1fr)}
@media(max-width:900px){.g4{grid-template-columns:repeat(2,1fr)}.g3,.g2{grid-template-columns:1fr}}
@media(max-width:560px){.g4{grid-template-columns:1fr}}
.card{
  background:var(--surface-1);border:1px solid var(--border);border-radius:var(--radius);
  padding:16px 18px;box-shadow:var(--shadow-sm);
}
.card h3{margin:0 0 2px;font-size:13px;font-weight:600;color:var(--ink-2);letter-spacing:.01em}
.card .sub{font-size:12px;color:var(--ink-3);margin:0 0 12px}
h2.sec{font-size:15px;font-weight:700;margin:28px 0 12px;letter-spacing:-.01em}
h2.sec::before{content:"";display:inline-block;width:4px;height:13px;background:var(--s1);border-radius:2px;margin-right:8px;vertical-align:-1px}
.tile .val{font-size:34px;font-weight:700;line-height:1.1;margin-top:6px;letter-spacing:-.02em}
.tile .foot{font-size:12px;color:var(--ink-3);margin-top:4px}

/* ---------- bars ---------- */
.barrow{display:grid;grid-template-columns:minmax(0,170px) 1fr 74px;gap:10px;align-items:center;margin-bottom:7px;font-size:13px}
/* Wraps to a second line rather than clipping. These labels carry the subject
   *and* its weight — "Polity & Governance (13 Q)" — so at 170px the weight was
   the part that got eaten, which is the part the row exists to compare. The
   title= is still there, but a title is a hover affordance and most of this
   app's traffic has no pointer to hover with. */
.barrow .lab{color:var(--ink-2);overflow-wrap:anywhere;line-height:1.35}
.track{background:var(--grid);height:14px;border-radius:4px;overflow:hidden;position:relative}
.fill{height:100%;border-radius:4px;background:linear-gradient(90deg,var(--seq-450),var(--seq-600));transition:width .3s}
.barrow .num{text-align:right;color:var(--ink-2);font-variant-numeric:tabular-nums;font-size:12px}
.progbar{background:var(--grid);height:10px;border-radius:5px;overflow:hidden;margin-top:10px}
.progbar>div{height:100%;background:linear-gradient(90deg,var(--seq-450),var(--seq-600));border-radius:5px;transition:width .3s}
@media(max-width:560px){.barrow{grid-template-columns:110px 1fr 60px}}

/* ---------- table ---------- */
table{border-collapse:collapse;width:100%;font-size:13.5px}
th,td{text-align:left;padding:8px 10px;border-bottom:1px solid var(--grid);vertical-align:middle}
th{font-size:11.5px;text-transform:uppercase;letter-spacing:.04em;color:var(--ink-3);font-weight:600;background:var(--page)}
tbody tr:hover{background:var(--page)}
td.num,th.num{text-align:right;font-variant-numeric:tabular-nums}
select,input[type=text],input[type=password],input[type=number],input[type=date],textarea{
  font:inherit;font-size:13px;background:var(--surface-1);color:var(--ink-1);
  border:1px solid var(--axis);border-radius:6px;padding:5px 7px;
}
textarea{width:100%;min-height:70px;resize:vertical}
.pill{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:600;
  padding:2px 8px;border-radius:999px;border:1px solid var(--border);white-space:nowrap}
.pill.not{color:var(--ink-3)}
.pill.prog{color:#8a5a00;background:rgba(250,178,25,.16)}
.pill.done{color:#0a7a0a;background:rgba(12,163,12,.14)}
.pill.due{color:#a52c2c;background:rgba(208,59,59,.14)}
:root[data-theme="dark"] .pill.prog{color:#fab219}
:root[data-theme="dark"] .pill.done{color:#0ca30c}
:root[data-theme="dark"] .pill.due{color:#ef8080}
.chip{display:inline-block;font-size:11px;color:var(--ink-3);border:1px solid var(--border);
  border-radius:5px;padding:1px 6px;margin-right:5px}
.dot{width:9px;height:9px;border-radius:50%;display:inline-block;flex:none;background:var(--ink-3)}
.muted{color:var(--ink-3);font-size:13px}
.muted.warn{color:var(--warn)}
.empty{color:var(--ink-3);font-size:13px;line-height:1.7;margin:6px 0 2px}
.hidden{display:none !important}
hr.sep{border:none;border-top:1px solid var(--grid);margin:20px 0}

/* ---------- exam ---------- */
.qcard{background:var(--surface-1);border:1px solid var(--border);border-radius:var(--radius);padding:20px 22px;box-shadow:var(--shadow-sm)}
.qmeta{display:flex;gap:8px;align-items:center;flex-wrap:wrap;font-size:12px;color:var(--ink-3);margin-bottom:10px}
/* Printed on the first question only — the rules do not change mid-paper. */
.qmeta .marking{margin-left:auto}
.qtext{font-size:17px;font-weight:600;line-height:1.45;margin:0 0 16px}

/* ---------- the table a "match the pairs" question is made of ----------
   The markup comes from the question itself — `richText()` admits the table
   tags and no attribute, so the stem can say `<table>` but never `width=` or
   `style=`. Everything about how it looks is therefore here.

   Two columns of List I / List II have to stay two columns at 360px: the
   question is which row pairs with which, and a table that wraps into one
   column has thrown that away. So the cells shrink and wrap their text, and
   the stem scrolls sideways rather than the table collapsing. Lighter than
   the stem around it, because the pairs are data, not the instruction. */
.qtext,.qpreview .stem{overflow-x:auto}
.qtext table,.qpreview .stem table,.otext table{
  border-collapse:collapse;margin:9px 0;font-size:.88em;font-weight:500;line-height:1.4;
}
.qtext th,.qtext td,.qpreview .stem th,.qpreview .stem td,.otext th,.otext td{
  border:1px solid var(--grid);padding:5px 9px;text-align:left;vertical-align:top;
}
.qtext th,.qpreview .stem th,.otext th{font-weight:600;color:var(--ink-2);white-space:nowrap}
.qtext caption,.qpreview .stem caption,.otext caption{
  caption-side:top;text-align:left;font-size:.9em;color:var(--ink-2);padding-bottom:4px;
}
.opt{
  display:flex;gap:11px;align-items:flex-start;width:100%;text-align:left;cursor:pointer;
  background:var(--surface-1);border:1px solid var(--axis);border-radius:9px;
  padding:11px 13px;margin-bottom:9px;font:inherit;font-size:14.5px;color:var(--ink-1);
  transition:box-shadow .15s;
}
.opt:hover:not(:disabled){border-color:var(--s1);box-shadow:var(--shadow-md)}
.opt:disabled{cursor:default}
.opt .key{flex:none;width:22px;height:22px;border-radius:50%;border:1px solid var(--axis);
  display:grid;place-items:center;font-size:12px;font-weight:700;color:var(--ink-2)}
.opt.sel{border-color:var(--s1);box-shadow:inset 0 0 0 1px var(--s1)}
.opt.right{border-color:var(--good);background:rgba(12,163,12,.08)}
.opt.wrong{border-color:var(--crit);background:rgba(208,59,59,.08)}
.opt .why{display:block;font-size:12.5px;color:var(--ink-2);margin-top:5px;line-height:1.45}

/* ---------- bilingual rendering ----------
   When both languages are shown they are the same question restated, not two
   questions: the second rendering sits directly under the first, quieter, with
   no separating whitespace that would read as a new item. */
.qtext+.qtext{margin-top:2px;color:var(--ink-2);font-weight:500}
.otext{display:block}
.otext+.otext{margin-top:2px;color:var(--ink-2);font-size:.97em}
.wtext+.wtext{display:block;margin-top:3px}

/* Per-question language switch, sitting in the meta row above the stem. */
.langpill{display:inline-flex;border:1px solid var(--border);border-radius:999px;overflow:hidden}
.langpill button{font:inherit;font-size:11px;line-height:1;padding:4px 9px;border:0;cursor:pointer;
  background:var(--surface-1);color:var(--ink-3)}
.langpill button+button{border-left:1px solid var(--border)}
.langpill button:hover{color:var(--ink-1)}
.langpill button.on{background:var(--s1);color:#fff}

/* The reading default. It lives in the sidebar footer now (.sidefield) rather than
   in the header row. */
.langsel{font:inherit;font-size:12px;padding:5px 8px;border-radius:8px;
  border:1px solid var(--border);background:var(--surface-1);color:var(--ink-2);cursor:pointer}
.langsel:hover{border-color:var(--s1)}

.checkline{display:flex;gap:8px;align-items:flex-start;font-size:13px;line-height:1.45}
.checkline input{margin-top:2px;flex:none}
.verdict{font-size:11.5px;font-weight:700;letter-spacing:.03em;text-transform:uppercase}
.verdict.ok{color:var(--good)} .verdict.no{color:var(--crit)} .verdict.skip{color:var(--ink-3)}

/* Per-option verdict as a mark in the margin rather than a word under the
   option. Four options spelling out CORRECT/INCORRECT cost four lines and read
   as four competing sentences; a tick and a cross scan the way a marked OMR
   sheet does. The word is still there for screen readers and on hover. */
/* The option's text column takes the slack, so the mark sits hard against the
   right edge instead of floating in the middle of a wide row. */
.opt>span:not(.key):not(.verdict){flex:1 1 auto;min-width:0}
.verdict.sym{
  flex:none;align-self:flex-start;
  width:22px;height:22px;border-radius:50%;
  display:grid;place-items:center;
  font-size:13px;line-height:1;letter-spacing:0;text-transform:none;
}
.verdict.sym.ok{color:var(--good);background:rgba(12,163,12,.12)}
.verdict.sym.no{color:var(--crit);background:rgba(208,59,59,.10)}
.examnav{display:flex;gap:10px;align-items:center;margin-top:18px;flex-wrap:wrap}
.navclock{display:none}
.whytoggle{margin:2px 0 14px}

/* Exam header row above the card: label, counter, clock, quit. */
.examtop{display:flex;align-items:center;gap:14px;margin:24px 0 14px;flex-wrap:wrap}
.examlabel{font-size:15px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Question palette. Collapsible because 100 buttons is ~560px — most of a
   phone screen — repeated under every question. */
.palwrap{margin-top:16px;border-top:1px solid var(--border);padding-top:10px}
.palwrap>summary{cursor:pointer;font-size:12.5px;font-weight:600;color:var(--ink-2);
  display:flex;gap:8px;align-items:center;list-style:none;min-height:32px}
.palwrap>summary::-webkit-details-marker{display:none}
.palwrap>summary::after{content:"▾";margin-left:auto;color:var(--ink-3);transition:transform .15s}
.palwrap[open]>summary::after{transform:rotate(180deg)}
.palette{display:flex;flex-wrap:wrap;gap:5px;margin-top:14px}
.palette button{width:30px;height:30px;border-radius:6px;border:1px solid var(--axis);
  background:var(--surface-1);color:var(--ink-2);font:inherit;font-size:12px;cursor:pointer;font-variant-numeric:tabular-nums}
.palette button.a{background:var(--s1);border-color:var(--s1);color:#fff}
.palette button.cur{outline:2px solid var(--ink-1);outline-offset:1px}
.timer{font-variant-numeric:tabular-nums;font-weight:700;font-size:15px}
.timer.low{color:var(--crit)}
.scorehero{font-size:46px;font-weight:700;letter-spacing:-.02em;line-height:1.05}
details.rev{border:1px solid var(--border);border-radius:9px;padding:10px 14px;margin-bottom:9px;background:var(--surface-1);box-shadow:var(--shadow-sm)}
.revctl{display:flex;gap:8px;flex-wrap:wrap;margin:0 0 10px}
/* The two options the candidate did not pick and that were not the key keep
   their verdict but fold away their reasoning. It is one button away, and it
   is the difference between a review page you can scan and one that runs to
   79,000px on a phone. */
body.why-terse details.rev .opt.dim .why{display:none}
details.rev summary{cursor:pointer;font-size:14px;font-weight:600}
details.rev summary::marker{color:var(--ink-3)}
.tip{font-size:12.5px;color:var(--ink-2);background:var(--page);border:1px solid var(--border);
  border-radius:8px;padding:10px 12px;margin:12px 0}
.tip.warn{border-color:var(--warn)}
.tip.bad{border-color:var(--crit)}
.confrow{display:flex;align-items:center;gap:7px;flex-wrap:wrap;margin:2px 0 16px}
.confchip{font:inherit;font-size:12px;color:var(--ink-2);background:var(--surface-1);
  border:1px solid var(--axis);border-radius:999px;padding:4px 11px;cursor:pointer}
.confchip:hover{border-color:var(--s1)}
.confchip.on{background:var(--s1);border-color:var(--s1);color:#fff}
.dayrow{display:grid;grid-template-columns:112px 1fr;gap:12px;padding:9px 0;border-bottom:1px solid var(--grid);font-size:13.5px}
.dayrow .d{color:var(--ink-2);font-weight:600;font-variant-numeric:tabular-nums}
.dayrow.today{background:rgba(42,120,214,.07);border-radius:6px;padding-left:8px;padding-right:8px}
.phase{border-left:3px solid var(--s1);padding-left:12px;margin:18px 0 8px}
.phase b{font-size:14px} .phase div{font-size:12.5px;color:var(--ink-3)}

/* ---------- question bank ---------- */
.genrow{display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap}
.genrow label{display:block;font-size:12px;color:var(--ink-2)}
.genrow select,.genrow input{display:block;margin-top:4px;min-width:130px;max-width:100%}
.genrow select.wide{min-width:280px}
/* Stack the generate controls on a phone — a 280px select does not fit a 320px screen. */
@media(max-width:560px){
  .genrow{flex-direction:column;align-items:stretch}
  .genrow label,.genrow select,.genrow input,.genrow .btn{width:100%}
  .genrow select,.genrow input,.genrow select.wide{min-width:0}
}
.spinner{
  width:13px;height:13px;border-radius:50%;flex:none;
  border:2px solid var(--axis);border-top-color:var(--s1);
  display:inline-block;animation:spin .7s linear infinite;
}
@keyframes spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.spinner{animation-duration:2.4s}}
.log{font-size:12.5px;max-height:230px;overflow-y:auto;margin-top:10px}
.log div{padding:5px 0;border-bottom:1px solid var(--grid);display:flex;gap:8px;align-items:baseline}
.log .t{color:var(--ink-3);font-variant-numeric:tabular-nums;flex:none}
.log .bad{color:var(--crit)}
.qpreview{border:1px solid var(--border);border-radius:9px;padding:12px 14px;margin-bottom:10px;background:var(--surface-1);box-shadow:var(--shadow-sm);transition:box-shadow .15s}
.qpreview:hover{box-shadow:var(--shadow-md)}
.qpreview .stem{font-weight:600;margin-bottom:8px}
.qpreview ol{margin:0;padding-left:22px;font-size:13.5px;color:var(--ink-2)}
.qpreview ol li.ok{color:var(--good);font-weight:600}

/* ---------- tutor ---------- */
.tutor{border:1px solid var(--s1);border-radius:10px;padding:14px 16px;margin-top:12px;background:var(--page);box-shadow:var(--shadow-sm)}
.tutor h4{margin:0 0 8px;font-size:13px;font-weight:700;display:flex;align-items:center;gap:8px}
.tutor .thread{max-height:420px;overflow-y:auto;margin-bottom:10px}
.tutor .msg{margin-bottom:12px;font-size:14px;line-height:1.55}
.tutor .msg.you{color:var(--ink-2)}
.tutor .msg .who{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--ink-3);display:block;margin-bottom:3px}
.tutor .msg p{margin:0 0 8px} .tutor .msg p:last-child{margin-bottom:0}
.tutor .msg code{background:var(--grid);padding:1px 4px;border-radius:4px;font-size:13px}
.tutor .ask{display:flex;gap:8px;align-items:flex-end}
.tutor .ask textarea{min-height:44px;flex:1}
.askbtn{margin-top:10px}

/* ---------- contact admin ----------
   A conversation, not a comment box: the two sides are told apart by which edge
   they sit against and by a label, never by colour alone. The composer is hidden
   rather than merely disabled when it is not the candidate's turn — a greyed-out
   box invites a message they cannot send, and the line above it says why. */
.fbthread{border:1px solid var(--border);border-radius:10px;padding:14px 16px;margin-bottom:14px;
  background:var(--surface-1);box-shadow:var(--shadow-sm)}
.fbthread.closed{opacity:.86;border-style:dashed}
.fbthread-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:10px}
.fbthread-head b{font-size:14px}
.fbthread-head .when{margin-left:auto;font-size:11.5px;color:var(--ink-3);
  font-variant-numeric:tabular-nums}
.fbmsgs{display:flex;flex-direction:column;gap:10px;margin-bottom:12px}
.fbmsg{max-width:min(86%,560px);border:1px solid var(--border);border-radius:10px;
  padding:9px 12px;font-size:14px;line-height:1.55;background:var(--page)}
.fbmsg.mine{align-self:flex-end;border-color:color-mix(in srgb,var(--s1) 45%,var(--border))}
.fbmsg.theirs{align-self:flex-start;background:color-mix(in srgb,var(--s1) 8%,var(--surface-1))}
.fbmsg .who{display:flex;gap:8px;align-items:baseline;font-size:11px;font-weight:700;
  text-transform:uppercase;letter-spacing:.04em;color:var(--ink-3);margin-bottom:4px}
.fbmsg .who .t{font-weight:500;text-transform:none;letter-spacing:0}
/* Somebody's own prose, so newlines are the only formatting it carries — and the
   only formatting it is allowed to carry. It is escaped, never rich text. */
.fbmsg .body{white-space:pre-wrap;overflow-wrap:anywhere}
.fbnote{font-size:12.5px;color:var(--ink-2);background:var(--page);border:1px solid var(--border);
  border-left-width:3px;border-radius:8px;padding:8px 11px}
.fbnote.waiting{border-left-color:var(--warn)}
.fbnote.turn{border-left-color:var(--good)}
.fbcompose{display:flex;flex-direction:column;gap:8px}
.fbcompose textarea{min-height:76px}
.fbcompose .row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.fbcompose .count{margin-left:auto;font-size:11.5px;color:var(--ink-3);
  font-variant-numeric:tabular-nums}
.fbcompose .count.over{color:var(--crit);font-weight:600}

/* ---------- toast ---------- */
#toasts{position:fixed;right:18px;bottom:18px;z-index:200;display:flex;flex-direction:column;gap:8px;max-width:380px}
/* A toast anchored 18px off the bottom lands squarely on the pinned exam
   action bar on a phone, covering Next. Lift it clear while a paper is live,
   and let it use the full width there instead of a 380px column. */
@media (max-width:860px){
  #toasts{left:12px;right:12px;bottom:14px;max-width:none}
  body.exam-live #toasts{bottom:calc(var(--exambar-h) + 12px)}
}
.toast{
  background:var(--surface-1);border:1px solid var(--axis);border-left-width:3px;
  border-radius:8px;padding:10px 14px;font-size:13px;box-shadow:0 6px 24px rgba(0,0,0,.12);
}
.toast.ok{border-left-color:var(--good)}
.toast.err{border-left-color:var(--crit)}
.toast.info{border-left-color:var(--s1)}

/* ---------- focus, touch, motion ---------- */
/* Nothing defined a focus style before, so keyboard users had only the
   browser default — invisible against the custom button backgrounds. */
:where(button,a,input,select,textarea,summary,[tabindex]):focus-visible{
  outline:2px solid var(--s1);outline-offset:2px;border-radius:6px;
}
/* Finger-sized targets on touch devices (was 32px on the nav toggle). */
@media (pointer:coarse){
  .btn{min-height:44px}
  .sidebar-toggle,#navToggle{width:44px;height:44px}
  .navitem,.navgroup-head{min-height:44px}
  .opt{min-height:48px}
  .palette button{width:38px;height:38px}
  .confchip{min-height:36px}
}
@media (prefers-reduced-motion:reduce){
  .sidebar,.backdrop,.opt,.qpreview,.fill,.progbar>div,.navgroup-head .chev{transition:none}
}

/* ---------- exam on a phone ----------
   The action row sat at the bottom of a card that grows by ~600px the moment
   practice mode reveals an answer, so Next was always somewhere below the
   fold. It becomes a pinned bar carrying only what a thumb needs — back,
   clear, next, and the clock — and the desktop-only affordances drop out. */
@media (max-width:860px){
  /* One number, because three things have to agree about it: the bar's own
     height, the room the page leaves under the card so the bar covers nothing,
     and how far a toast has to sit above it. 44px of button (the coarse-pointer
     minimum) + 10px of padding either side, and the home-indicator inset on top
     of that. */
  :root{--exambar-h:calc(64px + env(safe-area-inset-bottom))}

  .qcard{padding:16px 14px}
  .examtop{gap:8px;margin:14px 0 10px;font-size:13px}
  .examtop .timelabel,.examtop .practicenote{display:none}
  .examtop .examlabel{flex:1 1 auto}

  /* Fixed, not sticky. Sticky only holds an element against the viewport while
     its own container still extends past it, so the bar came unstuck at the end
     of the card and sat wherever the content had pushed it — which is to say it
     moved down the screen the moment an answer was revealed, by 84px on a 390px
     phone, and was 89px above the bottom at the foot of the card. The button a
     candidate is reaching for does not move under their thumb on a timed paper.

     Scoped to a live paper: the result screen reuses .examnav for its "Take
     another test" row, and pinning that to the bottom of an 8,000px result page
     would overlay the analysis all the way down. */
  body.exam-live .examnav{
    position:fixed;left:0;right:0;bottom:0;z-index:40;
    margin:0;padding:10px 14px calc(10px + env(safe-area-inset-bottom));
    gap:8px;flex-wrap:nowrap;
    background:var(--surface-1);border-top:1px solid var(--border);
    box-shadow:0 -6px 18px rgba(23,19,16,.06);
  }
  /* Out of flow means the card no longer ends where the page thinks it does, so
     the page leaves the bar's worth of room under it — otherwise the last of the
     question (and the keyboard hint after it) is unreachable behind the bar. */
  body.exam-live .wrap{padding-bottom:calc(var(--exambar-h) + 20px)}
  body.exam-live .examnav .navextra{display:none}         /* both live in the header already */
  body.exam-live .examnav #nextBtn{flex:1 1 auto;justify-content:center}
  body.exam-live .examnav #prevBtn,body.exam-live .examnav #clrBtn{flex:0 0 auto}
  body.exam-live .examnav>div[style]{display:none}        /* the desktop spacer */
  .navclock{
    display:flex;flex-direction:column;align-items:flex-end;line-height:1.15;
    font-size:11.5px;margin-left:2px;font-variant-numeric:tabular-nums;
  }
  .navclock .timer{font-size:14px}

  /* Focus and the OMR sheet keep their header buttons at this width, so the
     pinned bar carries only navigation. */
  .palwrap{margin-top:14px}
}
/* ---------- wide tables ---------- */
/* Wide tables (the 9-column syllabus tracker especially) scroll inside their
   own box rather than forcing the whole page sideways. Applied by
   wrapTables() after each render. */
.tablewrap{overflow-x:auto;overscroll-behavior-x:contain;min-width:0;max-width:100%}
@media (max-width:860px){
  .tablewrap table{min-width:560px}
}

/* ---------- sign in / sign up ---------- */
.authwrap{min-height:100dvh;display:grid;place-items:center;padding:24px}
.authcard{
  width:100%;max-width:400px;background:var(--surface-1);
  border:1px solid var(--border);border-radius:var(--radius);
  box-shadow:var(--shadow-md);padding:28px 26px;
}
.authbrand{display:flex;align-items:center;gap:10px;margin-bottom:22px}
.authmark{
  flex:none;width:34px;height:34px;border-radius:9px;background:var(--s1);
  display:grid;place-items:center;color:#fff;font-weight:700;font-size:13px;letter-spacing:.02em;
}
.authbrand b{font-size:15px;letter-spacing:-.01em;display:block;line-height:1.25}
.authbrand span{font-size:12px;color:var(--ink-3)}
.authcard h1{font-size:19px;font-weight:700;letter-spacing:-.01em;margin:0 0 4px}
.authcard .authsub{font-size:13px;color:var(--ink-3);margin:0 0 20px}
.field{display:block;margin-bottom:14px}
.field span{display:block;font-size:12.5px;font-weight:600;color:var(--ink-2);margin-bottom:5px}
.field input{width:100%;padding:9px 11px;font-size:14px}
.btn.block{width:100%;justify-content:center;padding:10px 14px;font-size:14px}
.autherr{color:var(--crit);font-size:13px;margin:12px 0 0;min-height:19px}
.authalt{margin:18px 0 0;padding-top:16px;border-top:1px solid var(--grid);font-size:13px;color:var(--ink-3)}
.linkbtn{
  background:none;border:none;padding:0;font:inherit;font-size:13px;font-weight:600;
  color:var(--s1);cursor:pointer;text-decoration:underline;text-underline-offset:2px;
}

/* ---------- in-progress mock tests: header button + right drawer ----------
   An unfinished paper is the easiest thing in the app to lose track of, so it
   gets a permanent, countable surface rather than a one-shot prompt on boot. */
.livebtn{position:relative;gap:7px}
.livedot{
  width:7px;height:7px;border-radius:50%;background:var(--warn);flex:none;
  animation:livepulse 2s ease-in-out infinite;
}
@keyframes livepulse{0%,100%{opacity:1}50%{opacity:.35}}
@media (prefers-reduced-motion:reduce){.livedot{animation:none}}
.livecount{
  font-size:11px;font-weight:700;line-height:1;padding:3px 6px;border-radius:999px;
  background:var(--warn);color:#3b2a00;
}
:root[data-theme="dark"] .livecount{color:#241a00}

.livepanel{
  position:fixed;top:0;right:0;bottom:0;width:360px;max-width:88vw;
  background:var(--surface-1);border-left:1px solid var(--border);
  box-shadow:var(--shadow-md);z-index:95;
  transform:translateX(100%);transition:transform .22s ease;
  display:flex;flex-direction:column;
}
.livepanel.open{transform:none}
@media (prefers-reduced-motion:reduce){.livepanel{transition:none}}
.livepanel-head{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:14px 16px;border-bottom:1px solid var(--border);flex:none;
}
.livepanel-head b{font-size:14px}
.livepanel-body{padding:14px 16px 24px;overflow-y:auto;display:flex;flex-direction:column;gap:12px}

.livecard{border:1px solid var(--border);border-radius:10px;padding:12px 13px;background:var(--page)}
.livecard h4{margin:0 0 3px;font-size:13.5px;font-weight:700}
.livecard .livemeta{font-size:12px;color:var(--ink-3);margin:0 0 4px}
.livecard .liveprog{font-size:12.5px;color:var(--ink-2);margin:0 0 10px}
.livecard .liveclock{font-weight:700;font-variant-numeric:tabular-nums}
.livecard .liveclock.low{color:var(--crit)}
.livecard .liveclock.out{color:var(--crit);font-weight:700}
.liveacts{display:flex;flex-wrap:wrap;gap:6px}

/* ============================================================
   UI/UX ELEVATION: MISSION HERO, HABIT RINGS, ZEN MODE & OMR
   ============================================================ */

/* Badges */
.badge{
  display:inline-flex;align-items:center;justify-content:center;
  font-size:11px;font-weight:700;line-height:1;border-radius:999px;
  padding:2px 7px;background:var(--s1);color:#fff;
}
.badge.sm{padding:1.5px 5px;font-size:10px}
.badge.amber{background:var(--warn);color:#241a00}
.badge.emerald{background:var(--good);color:#fff}
.badge.crimson{background:var(--crit);color:#fff}
.badge.blue{background:color-mix(in srgb, var(--s1) 18%, transparent);color:var(--s1)}
.badge.purple{background:color-mix(in srgb, #8b5cf6 20%, transparent);color:#7c3aed}


/* 1. Today's Mission Hero Banner */
.mission-hero{
  position:relative;overflow:hidden;
  background:linear-gradient(135deg, var(--surface-1) 0%, color-mix(in srgb, var(--s1) 8%, var(--surface-1)) 100%);
  border:1px solid color-mix(in srgb, var(--s1) 35%, var(--border));
  border-radius:14px;padding:20px 24px;margin-bottom:18px;
  box-shadow:0 6px 24px rgba(42,120,214,0.09);
}
.mission-hero::before{
  content:"";position:absolute;top:0;left:0;right:0;height:3px;
  background:linear-gradient(90deg, var(--s1), var(--s3), var(--warn));
}
.mission-top{display:flex;align-items:center;gap:10px;margin-bottom:8px;flex-wrap:wrap}
.mission-greeting{font-size:12.5px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--s1)}
.mission-countdown{font-size:12px;color:var(--ink-2);background:var(--page);border:1px solid var(--border);border-radius:999px;padding:2px 10px}
.mission-title{font-size:20px;font-weight:700;letter-spacing:-.02em;margin:0 0 6px;color:var(--ink-1)}
.mission-desc{font-size:14px;color:var(--ink-2);margin:0 0 16px;max-width:720px;line-height:1.5}
.mission-actions{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.btn-mission{
  font-size:14px;font-weight:700;padding:10px 20px;border-radius:9px;
  display:inline-flex;align-items:center;gap:8px;
  box-shadow:0 4px 14px color-mix(in srgb, var(--s1) 40%, transparent);
}

/* 2. Habit Rings & Streak Shield */
.habit-strip{display:grid;grid-template-columns:repeat(3, 1fr);gap:12px;margin-bottom:18px}
@media(max-width:680px){.habit-strip{grid-template-columns:1fr}}
.habit-card{
  background:var(--surface-1);border:1px solid var(--border);border-radius:12px;
  padding:14px 16px;display:flex;align-items:center;gap:14px;box-shadow:var(--shadow-sm);
}
.habit-ring-svg{width:48px;height:48px;flex:none;transform:rotate(-90deg)}
.ring-bg{stroke:var(--grid);stroke-width:5;fill:none}
.ring-fg{stroke-width:5;stroke-linecap:round;fill:none;transition:stroke-dashoffset .4s ease}
.ring-fg.q{stroke:var(--s1)}
.ring-fg.r{stroke:var(--s3)}
.ring-fg.s{stroke:var(--s4)}
.habit-info{min-width:0}
.habit-val{font-size:18px;font-weight:700;line-height:1.2;color:var(--ink-1)}
.habit-lbl{font-size:12px;color:var(--ink-3);margin-top:2px}
.streak-shield-badge{
  display:inline-flex;align-items:center;gap:6px;font-size:11.5px;font-weight:600;
  color:var(--s3);background:color-mix(in srgb, var(--s3) 12%, transparent);
  border:1px solid color-mix(in srgb, var(--s3) 25%, transparent);
  border-radius:999px;padding:3px 10px;margin-top:4px;
}

/* 3. Onboarding Diagnostic Banner */
.onboarding-card{
  background:linear-gradient(135deg, color-mix(in srgb, var(--s3) 10%, var(--surface-1)), var(--surface-1));
  border:1px solid color-mix(in srgb, var(--s3) 35%, var(--border));
  border-radius:14px;padding:20px 24px;margin-bottom:18px;box-shadow:var(--shadow-sm);
}
.onboarding-card h3{font-size:17px;font-weight:700;margin:0 0 6px;color:var(--ink-1)}
.onboarding-card p{font-size:13.5px;color:var(--ink-2);margin:0 0 14px;max-width:680px}

/* 4. 1-Click Quick Exam Presets */
.preset-grid{display:grid;grid-template-columns:repeat(4, 1fr);gap:12px;margin-bottom:18px}
@media(max-width:860px){.preset-grid{grid-template-columns:repeat(2, 1fr)}}
@media(max-width:480px){.preset-grid{grid-template-columns:1fr}}
.preset-card{
  background:var(--surface-1);border:1px solid var(--axis);border-radius:11px;
  padding:14px 16px;text-align:left;cursor:pointer;font:inherit;color:var(--ink-1);
  transition:all .18s ease;display:flex;flex-direction:column;gap:6px;
}
.preset-card:hover:not(:disabled){
  border-color:var(--s1);transform:translateY(-2px);
  box-shadow:0 6px 18px rgba(42,120,214,0.12);
}
/* These are <button>s but not .btn, so :disabled styling did not reach them —
   once the day's papers ran out they still lifted and glowed on hover while
   doing nothing at all. */
.preset-card:disabled{opacity:.5;cursor:not-allowed;transform:none;box-shadow:none}
.preset-icon{font-size:22px;line-height:1}
.preset-title{font-size:14px;font-weight:700}
.preset-meta{font-size:12px;color:var(--ink-3)}
.preset-tag{
  font-size:11px;font-weight:600;padding:2px 7px;border-radius:999px;
  background:var(--page);color:var(--ink-2);border:1px solid var(--border);
  align-self:flex-start;margin-top:auto;
}

/* 6. Distraction-Free Zen Focus Mode */
body.exam-focus .sidebar{display:none !important}
body.exam-focus #navToggle{display:none !important}
body.exam-focus .brand small{display:none !important}
body.exam-focus .savestate, body.exam-focus #meChip{display:none !important}
body.exam-focus .wrap{max-width:840px !important;padding-top:8px}
body.exam-focus .qcard{border:2px solid color-mix(in srgb, var(--s1) 35%, var(--border));box-shadow:0 12px 36px rgba(0,0,0,0.08)}
body.exam-focus .qtext{font-size:19px;line-height:1.55}

/* Timer Urgency Animations */
.timer.warn{color:var(--warn);animation:pulseTimer 2s infinite}
.timer.low{color:var(--crit);animation:pulseTimerFast .8s infinite;font-weight:800}
@keyframes pulseTimer{0%,100%{opacity:1}50%{opacity:.6}}
@keyframes pulseTimerFast{0%,100%{transform:scale(1)}50%{transform:scale(1.06)}}
@media (prefers-reduced-motion:reduce){.timer.warn,.timer.low{animation:none}}

/* 7. Digital OMR Sheet Simulator */
.omr-drawer{
  position:fixed;top:0;right:0;bottom:0;width:380px;max-width:92vw;
  background:var(--surface-1);border-left:1px solid var(--border);
  box-shadow:var(--shadow-md);z-index:96;display:flex;flex-direction:column;
  transform:translateX(100%);transition:transform .22s ease;
}
.omr-drawer.open{transform:none}
.omr-head{
  display:flex;align-items:center;justify-content:space-between;
  padding:14px 16px;border-bottom:1px solid var(--border);background:var(--page);
}
.omr-body{padding:12px 14px 30px;overflow-y:auto;flex:1}
.omr-grid{display:grid;grid-template-columns:repeat(2, 1fr);gap:6px 12px}
.omr-row{display:flex;align-items:center;gap:6px;padding:3px 4px;border-radius:6px}
.omr-row.active{background:color-mix(in srgb, var(--s1) 12%, transparent)}
.omr-num{width:26px;font-size:11.5px;font-weight:700;color:var(--ink-2);text-align:right;font-variant-numeric:tabular-nums}
.omr-bubbles{display:flex;gap:4px}
.omr-bubble{
  width:22px;height:22px;border-radius:50%;border:1.4px solid var(--axis);
  display:grid;place-items:center;font-size:10px;font-weight:700;color:var(--ink-3);
  cursor:pointer;background:var(--surface-1);transition:all .12s ease;
}
.omr-bubble:hover{border-color:var(--s1);color:var(--s1)}
.omr-bubble.filled{
  background:#1a1a1a;border-color:#1a1a1a;color:#fff;
  box-shadow:inset 0 0 0 2px rgba(255,255,255,.3);
}
:root[data-theme="dark"] .omr-bubble.filled{background:#f0f0f0;border-color:#f0f0f0;color:#000}

/* 8. The Mistake Book (माझी चुकांची वही) */
.bookmark-btn{
  font-size:12px;font-weight:600;padding:4px 9px;border-radius:7px;
  display:inline-flex;align-items:center;gap:5px;
}
.bookmark-btn.saved{background:color-mix(in srgb, var(--warn) 15%, transparent);border-color:var(--warn);color:var(--warn)}
.mistake-tag{
  display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:600;
  padding:2px 8px;border-radius:999px;border:1px solid transparent;
}
.tag-silly{background:rgba(226,161,61,.14);color:#b37200;border-color:rgba(226,161,61,.3)}
.tag-concept{background:rgba(197,69,63,.14);color:var(--crit);border-color:rgba(197,69,63,.3)}
.tag-guess{background:rgba(42,120,214,.14);color:var(--s1);border-color:rgba(42,120,214,.3)}
.tag-time{background:rgba(146,138,125,.16);color:var(--ink-2);border-color:rgba(146,138,125,.3)}
:root[data-theme="dark"] .tag-silly{color:#fab219}

.mistake-filter-bar{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:14px}
.mistake-card{
  background:var(--surface-1);border:1px solid var(--border);border-radius:11px;
  padding:16px 18px;margin-bottom:12px;box-shadow:var(--shadow-sm);
}

/* 9. Cutoff Probability Gauge & Recovery Card */
.cutoff-card{
  background:var(--surface-1);border:1px solid var(--border);border-radius:12px;
  padding:18px 20px;margin-top:16px;box-shadow:var(--shadow-sm);
}
.cutoff-gauge-track{
  height:16px;background:var(--grid);border-radius:8px;position:relative;
  overflow:hidden;margin:14px 0 8px;
}
.cutoff-zone{
  position:absolute;top:0;bottom:0;background:rgba(31,157,76,.2);
  border-left:2px dashed var(--good);border-right:2px dashed var(--good);
}
.cutoff-score-pin{
  position:absolute;top:0;bottom:0;width:5px;border-radius:3px;
  background:var(--ink-1);transition:left .4s ease;
}
.recov-grid{display:grid;grid-template-columns:repeat(2, 1fr);gap:12px;margin-top:12px}
@media(max-width:540px){.recov-grid{grid-template-columns:1fr}}
.recov-box{background:var(--page);border:1px solid var(--border);border-radius:9px;padding:10px 14px}
.recov-box b{display:block;font-size:18px;margin-top:2px}

/* 10. Mobile-Friendly Syllabus Accordions */
.syll-acc{
  background:var(--surface-1);border:1px solid var(--border);border-radius:10px;
  margin-bottom:10px;overflow:hidden;
}
.syll-acc-head{
  padding:12px 16px;cursor:pointer;display:flex;align-items:center;gap:10px;
  font-weight:700;font-size:14px;background:var(--surface-1);
}
.syll-acc-head:hover{background:var(--page)}
.topic-card-grid{padding:8px 12px 14px;display:flex;flex-direction:column;gap:8px}
.topic-card-item{
  background:var(--page);border:1px solid var(--border);border-radius:8px;
  padding:10px 14px;display:flex;align-items:center;gap:12px;flex-wrap:wrap;
}
.topic-card-item .tname{font-weight:600;font-size:13.5px;flex:1;min-width:200px}
/* A non-wrapping row of two selects and two buttons crushed the drill button
   to about 30px on a phone, stacking its label one character per line. */
.topic-card-item .tact{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.topic-card-item .tact>*{flex:0 1 auto;min-width:0}
.topic-card-item .tact .btn{white-space:nowrap}


/* ============================================================
   EXAM PICKER & SWITCHER
   ============================================================
   Two surfaces, one card: the full-screen first-run picker, and the modal
   switcher behind the header chip. Cards rather than a <select> because the
   choice is consequential — it decides the marking scheme a candidate will
   train their guessing habits against — and the differences that matter
   (question count, duration, negative marking, whether the pattern is
   confirmed) have to be readable before the click, not after it.
   ============================================================ */

.pickwrap{min-height:100dvh;padding:48px 24px;max-width:1080px;margin:0 auto}
.pickhead{text-align:center;margin-bottom:32px}
.pickhead h1{font-size:24px;font-weight:700;letter-spacing:-.02em;margin:0 0 8px}
.pickhead p{font-size:13.5px;color:var(--ink-3);margin:0 auto;max-width:60ch;line-height:1.6}

.examgrid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}

.examcard{
  display:block;width:100%;text-align:left;cursor:pointer;font:inherit;color:inherit;
  background:var(--surface-1);border:1px solid var(--border);border-radius:12px;
  padding:16px 18px;transition:border-color .15s,box-shadow .15s,transform .15s;
}
.examcard:hover:not(:disabled){border-color:var(--s1);box-shadow:var(--shadow-sm);transform:translateY(-1px)}
.examcard:focus-visible{outline:2px solid var(--s1);outline-offset:2px}
.examcard:disabled{opacity:.55;cursor:progress}
.examcard.current{border-color:var(--s1);background:color-mix(in srgb,var(--s1) 6%,var(--surface-1))}

.examcard-top{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-bottom:8px}
.examcard-short{font-size:15px;font-weight:700;letter-spacing:-.01em;margin-right:2px}
.examcard h3{margin:0 0 6px;font-size:13px;font-weight:600;color:var(--ink-2);line-height:1.4}
.examcard-desc{margin:0 0 10px;font-size:12.5px;color:var(--ink-3);line-height:1.55}
.examcard-meta{font-size:12px;font-weight:600;color:var(--ink-2)}
.examcard-posts{margin-top:6px;font-size:11.5px;color:var(--ink-3);line-height:1.5}
.examcard-warn{
  margin-top:10px;padding-top:9px;border-top:1px solid var(--border);
  font-size:11.5px;color:var(--warn);line-height:1.5;
}

.examchip{cursor:pointer;font-weight:600;color:var(--ink-2)}
.examchip:hover{border-color:var(--s1);color:var(--s1)}

.modalcard.examswitch{max-width:900px;padding:22px;background:var(--surface-1);
  border-radius:12px;box-shadow:0 10px 30px rgba(0,0,0,.2);max-height:86dvh;overflow-y:auto}
.examswitch-head{display:flex;justify-content:space-between;align-items:center;gap:12px}
.examswitch-head h3{margin:0;font-size:16px}
.modalcard.examswitch>.muted{margin:4px 0 16px}

@media (max-width:640px){
  .pickwrap{padding:28px 16px}
  .pickhead h1{font-size:20px}
  .examgrid{grid-template-columns:1fr}
}

/* ============================================================
   MODAL OVERLAY
   ============================================================
   `.modalwrap.open` was being used by the admin user dashboard with no rule
   behind it, so the "modal" was appended to the end of the document and
   rendered as an ordinary block a full page below the fold. It is a real
   overlay now, which fixes that screen as well as the exam switcher.
   ============================================================ */
.modalwrap{
  position:fixed;inset:0;z-index:200;
  display:none;place-items:center;padding:24px;
  background:color-mix(in srgb,#000 45%,transparent);
  overflow-y:auto;
}
.modalwrap.open{display:grid}
.modalcard{width:100%;max-width:620px;margin:auto}

@media (max-width:640px){ .modalwrap{padding:12px} }

/* ---------- plan & upgrade ----------
   The plan card on Settings and the 402 card share these. Meters are the
   `.track` bar the dashboard already uses; the buttons are one per paid plan. */
.plancard .planhead{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap}
.plancard h3{font-size:16px;color:var(--ink-1)}
.meters{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;margin:12px 0 4px}
@media(max-width:560px){.meters{grid-template-columns:1fr}}
.meter .meter-lab{display:flex;justify-content:space-between;font-size:12.5px;color:var(--ink-2);margin-bottom:5px}
.meter .track{height:8px}
.meter .fill{height:100%;background:var(--s1);border-radius:4px;transition:width .3s}
.meter .fill.full{background:var(--warn)}
.planpitch{margin-top:16px;padding-top:14px;border-top:1px solid var(--line-subtle,var(--border))}
.planpitch h4{font-size:13.5px;margin:0 0 6px}
.planpitch ul{margin:0 0 14px;padding-left:18px;font-size:13px;color:var(--ink-2)}
.planpitch li{margin:3px 0}
.planbtns{display:flex;gap:10px;flex-wrap:wrap}
.planbtn{flex:1 1 140px;flex-direction:column;align-items:flex-start;gap:2px;padding:10px 14px;text-align:left}
.planbtn b{font-size:17px;letter-spacing:-.01em}
.planbtn span{font-size:11.5px;opacity:.85}
.preset-tag.pro{background:var(--s4);color:#241a00}

/* ---------- sign-in: the pitch beside the form ---------- */
.authsplit{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,400px);gap:28px;align-items:center;max-width:960px;width:100%}
.authpitch{padding:8px 4px}
.authpitch h2{font-size:24px;line-height:1.2;letter-spacing:-.015em;margin:8px 0 14px}
.authpitch ul{margin:0;padding-left:18px;font-size:13.5px;color:var(--ink-2)}
.authpitch li{margin:6px 0}
.authpitch a{color:var(--ink-3)}
.pricebox{display:grid;gap:8px;margin-top:18px}
.pricebox>div{background:var(--surface-1);border:1px solid var(--border);border-radius:var(--radius-sm,8px);padding:10px 12px;font-size:12.5px;color:var(--ink-2)}
.pricebox b{display:block;color:var(--ink-1);margin-bottom:2px;font-size:13px}
@media(max-width:820px){
  .authsplit{grid-template-columns:1fr;gap:18px}
  .authpitch{order:2}
  .authpitch h2{font-size:20px}
}

/* Availability line under the mock-setup language select. It replaces a
   checkbox that claimed to filter and did not — see langAvailability(). */
.langavail{display:block;margin-top:4px;font-size:12px;line-height:1.45}

/* The exam keyboard shortcuts, named under the nav row. Hidden where there is
   no keyboard: a phone cannot press F, and the line would only crowd the
   palette. */
.shortcuthint{margin:10px 0 0;font-size:11.5px;color:var(--ink-3);display:none}
.shortcuthint kbd{
  font:inherit;font-size:11px;font-weight:600;color:var(--ink-2);
  background:var(--page);border:1px solid var(--axis);border-bottom-width:2px;
  border-radius:4px;padding:1px 5px;margin:0 1px;
}
@media (min-width:861px) and (pointer:fine){ .shortcuthint{display:block} }

/* ---------- exam density on a phone ----------
   Mid-paper the metadata above the question had grown to six wrapping rows —
   subject, topic, difficulty, the language pills, a full-width "Save to
   Mistake Book" and the marking line — roughly 300px of a 844px screen spent
   before the first word of the question, on a clock. None of it is what the
   candidate is reading. It all stays reachable; it just stops outranking the
   question. */
.qmeta .qwhere{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bkmkglyph{display:none}
@media (max-width:560px){
  .qcard{padding:14px 14px}
  .qmeta{gap:6px;margin-bottom:8px}
  .qmeta .qwhere{max-width:62vw;font-size:11px}
  .qmeta .qdiff{font-size:11px}
  /* The star alone, with the words on aria-label and title. The label was the
     single widest thing in the row and the icon reads the same. */
  .bookmark-btn .bkmklabel{display:none}
  .bookmark-btn .bkmkglyph{display:inline;font-size:15px;line-height:1}
  .bookmark-btn{min-width:38px;min-height:32px;display:inline-flex;align-items:center;justify-content:center}
  /* Big enough to hit without zooming: the palette was a grid of 30px squares,
     under every touch-target guideline going, and it is how you get back to a
     question you flagged. */
  .palette button{min-width:40px;min-height:40px}
  .langpill button{padding:8px 11px;font-size:12px}
}

/* Syllabus tracker filter bar. Each control is a label + field pair, so the
   pairs need to stay together when the row wraps — otherwise "Search" ends up
   on one line and its box on the next. */
.syllfilters{display:flex;gap:14px;align-items:center;flex-wrap:wrap}
.syllfilters > label{display:inline-flex;align-items:center;gap:6px;font-size:13px;color:var(--ink-2)}
.syllfilters #syllShowing{font-size:12.5px}
@media (max-width:560px){
  .syllfilters{gap:10px}
  .syllfilters > label{flex:1 1 100%}
  .syllfilters > label select, .syllfilters > label input{flex:1;min-width:0}
}

/* Unambiguous echo under a native date input, whose own rendering follows the
   browser locale rather than the app's. */
.fieldecho{display:block;margin-top:5px;font-size:12.5px}

/* Today's paper allowance, on the tab where papers are started. */
.usagenote{margin:-4px 0 14px;font-size:13px;color:var(--ink-2)}
.usagenote.out{
  color:var(--ink-1);background:color-mix(in srgb,var(--warn) 12%,transparent);
  border:1px solid color-mix(in srgb,var(--warn) 40%,transparent);
  border-radius:var(--radius);padding:10px 14px;margin:0 0 16px;
}
