@font-face {
  font-family: 'Nunito Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/nunito-sans/nunito-sans-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Nunito Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/nunito-sans/nunito-sans-500.woff2') format('woff2');
}
@font-face {
  font-family: 'Nunito Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/nunito-sans/nunito-sans-600.woff2') format('woff2');
}
@font-face {
  font-family: 'Nunito Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/nunito-sans/nunito-sans-700.woff2') format('woff2');
}
@font-face {
  font-family: 'Nunito Sans';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('/fonts/nunito-sans/nunito-sans-800.woff2') format('woff2');
}

:root{
  --ground:#FAF9F6; --surface:#FFFFFF; --surface-2:#F0F2F4; --raise:#FFFFFF;
  --line:#D3D9DF; --line-soft:#E3E7EB;
  --text:#1C2A35; --text-2:#4A5D6E; --text-3:#5E6E7E;
  /* --accent is a fill that carries --brand-text; --accent-2 is a foreground
     that sits on surfaces and on --accent-soft, and is also the hover fill for
     accented buttons. In light mode --accent-2 is therefore *darker* than
     --accent, and in dark mode lighter. That inversion is the whole contract:
     giving both themes the lighter value dropped link text to 1.77:1 and type
     chips to 1.62:1 across 26 usages. tests/unit/palette-contrast.test.js now
     fails if either direction is broken again. */
  --accent:#12456E; --accent-2:#0E3A5E; --accent-soft:#E7F0F7;
  --brand:#12456E; --brand-text:#FFFFFF;
  --focus:#2563A8;
  /* --ok-text and --bad-text are the foregrounds that sit *on* the --ok and
     --bad fills, as --brand-text does on --accent. The answer-key badges in
     exam review were hardcoded to #fff on both, which is 2.34:1 on the dark
     --ok and 2.89:1 on the dark --bad - a learner reading which option was
     correct, at a ratio below the large-text floor. Pre-existing rather than
     introduced here, and fixed because the letter is information. */
  --ok:#1F6F52; --ok-soft:#DEEFE7; --ok-text:#FFFFFF;
  --bad:#A8352E; --bad-soft:#F8E2E0; --bad-text:#FFFFFF;
  --flag:#5B4B9E; --flag-soft:#E9E5F6;
  /* Reserved for the Phase 5 guided-onboarding "next step" affordance; nothing
     consumes it yet. Amber at #D97706 read better but is 2.84:1 on --surface-2
     and 2.86:1 on its own soft background, so it cannot carry text. If Phase 5
     wants a brighter amber it must be for large text or a non-text indicator. */
  --guidance:#9A5406; --guidance-soft:#FEF3C7;
  --shadow:0 1px 2px rgba(20,30,40,.05), 0 4px 8px -4px rgba(20,30,40,.08);
  --shadow-lift:0 2px 4px rgba(20,30,40,.08), 0 18px 40px -18px rgba(20,30,40,.25);
  --r:14px; --card-r:24px; --content-max:1240px; --display-scale:1;

  /* Canvas C spacing scale and radii. Space BETWEEN components comes off this
     scale; a small component's own internals (a chip's 5px/11px padding) may
     sit off it, because the step is coarser than the part. */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px; --s6:32px; --s7:48px; --s8:64px;
  --r-xs:8px; --r-sm:10px; --r-md:14px; --r-lg:20px; --pill:999px;

  /* The five kinds of work, as action blobs. These are NOT status colours and
     a room never repaints them: a learner studying in two rooms must not have
     to relearn the colour language. Every hue clears --ok, --bad and --flag by
     at least 28deg in OKLCH; the tightest pair is --k-cards against --ok. The
     floor the test enforces is 22deg, which is what the palette shipped BEFORE
     this redesign accepted at its tightest (the retired teal --accent #0F766E
     against --ok #1F6F52) - so the floor is precedent and the 6deg above it is
     headroom, deliberately, because a hue nudged later should not land a
     regression on the next commit. Each ink holds at least 5.6:1 on its fill. The prototype's mint and lilac are not used
     verbatim: they sat 6deg from --ok and 5deg from --flag respectively, so in
     dark mode - where the fill goes away and hue carries the meaning alone -
     "cards" would have read as correct and "drill" as flagged. */
  --k-read:#ECCC99;  --k-read-ink:#643F00;
  --k-cards:#95E1DE; --k-cards-ink:#005756;
  --k-drill:#B9D1FF; --k-drill-ink:#2D457D;
  --k-mock:#F6BEDB;  --k-mock-ink:#6C2E53;
  --k-save:#9BDCF6;  --k-save-ink:#005170;
  --mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;
  --ui:'Nunito Sans',ui-sans-serif,system-ui,sans-serif;
  --disp:var(--ui);
  --body:ui-serif,"Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif;
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --ground:#0C1217; --surface:#141C23; --surface-2:#1A242C; --raise:#172028;
    --line:#2A3540; --line-soft:#1F2A34;
    --text:#E8EDEE; --text-2:#A8B8C0; --text-3:#8496A3;
    --accent:#6FB1E8; --accent-2:#9BCBF3; --accent-soft:#0E2A40;
    --brand:#6FB1E8; --brand-text:#04121D;
    --focus:#8CC5FF;
    --ok:#54BC90; --ok-soft:#0D281C; --ok-text:#06231A;
    --bad:#E4796F; --bad-soft:#2D1816; --bad-text:#2B0A06;
    --flag:#9B8CDC; --flag-soft:#1A162E;
    /* #643F00, the light value, is 3.7:1 on this dark --k-read - under the
       4.5:1 body-text floor - because the light pairing was tuned against the
       much lighter #ECCC99. #402800 is 5.5:1 on #CA9C4E.

       Only the Phase 4 reader's traps consume this in dark mode. The other
       consumer, `.blob.read`, maps it to --k-ink, but the dark .blob rule
       drops its fill and draws a ring in --k WITHOUT ever reading --k-ink -
       so this override is invisible there, verified in a browser rather than
       reasoned about. A new dark-mode consumer of --k-read-ink must check
       that for itself instead of assuming the blob's pattern. */
    --k-read:#CA9C4E; --k-read-ink:#402800; --k-cards:#37BAB7; --k-drill:#83A3EA; --k-mock:#D688B3; --k-save:#49B3D8;
    --guidance:#FBBF24; --guidance-soft:#2E2608;
    --shadow:0 1px 2px rgba(0,0,0,.5), 0 8px 24px -12px rgba(0,0,0,.7);
    --shadow-lift:0 2px 4px rgba(0,0,0,.5), 0 18px 40px -18px rgba(0,0,0,.85);
  }
}
:root[data-theme="dark"]{
  --ground:#0C1217; --surface:#141C23; --surface-2:#1A242C; --raise:#172028;
  --line:#2A3540; --line-soft:#1F2A34;
  --text:#E8EDEE; --text-2:#A8B8C0; --text-3:#8496A3;
  --accent:#6FB1E8; --accent-2:#9BCBF3; --accent-soft:#0E2A40;
  --brand:#6FB1E8; --brand-text:#04121D;
  --focus:#8CC5FF;
  --ok:#54BC90; --ok-soft:#0D281C; --ok-text:#06231A;
    --bad:#E4796F; --bad-soft:#2D1816; --bad-text:#2B0A06;
  --flag:#9B8CDC; --flag-soft:#1A162E;
  /* See the matching comment in the prefers-color-scheme block above: #643F00
     is only 3.7:1 on this dark --k-read, so the explicit dark theme needs the
     same #402800 (5.5:1) override. */
  --k-read:#CA9C4E; --k-read-ink:#402800; --k-cards:#37BAB7; --k-drill:#83A3EA; --k-mock:#D688B3; --k-save:#49B3D8;
  --guidance:#FBBF24; --guidance-soft:#2E2608;
  --shadow:0 1px 2px rgba(0,0,0,.5), 0 8px 24px -12px rgba(0,0,0,.7);
  --shadow-lift:0 2px 4px rgba(0,0,0,.5), 0 18px 40px -18px rgba(0,0,0,.85);
}

*{box-sizing:border-box}
body{
  margin:0; background:var(--ground); color:var(--text);
  font-family:var(--body); font-size:18px; line-height:1.72; letter-spacing:.003em;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
h1,h2,h3,h4{font-family:var(--ui); text-wrap:balance; margin:0; line-height:1.16; letter-spacing:-.02em}
p{margin:0}
a{color:var(--accent-2)}
:focus-visible{outline:2px solid var(--accent); outline-offset:2px; border-radius:4px}
h1[tabindex="-1"]:focus,h2[tabindex="-1"]:focus{outline:none}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}

.eyebrow{
  font-family:var(--ui); font-size:11px; font-weight:700; letter-spacing:.08em;
  text-transform:uppercase; color:var(--text-3);
}
.mono{font-family:var(--mono); font-variant-numeric:tabular-nums}
.content-status{
  max-width:var(--content-max); min-height:calc(100vh - 66px); margin:0 auto; padding:clamp(56px,12vh,150px) 22px 90px;
}
.noscript-note{max-width:720px; margin:0 auto; padding:56px 22px}
.content-status h1{max-width:620px; margin-top:8px; font-size:clamp(30px,4.4vw,46px)}
.content-status p{max-width:560px; margin-top:16px; color:var(--text-2); font-size:18px; line-height:1.68}
.content-status .btn{margin-top:24px}

/* ---------- top bar ---------- */
/* Signed out with the feature available, this menu is the only way to keep
   progress off a single device, so it earns the accent treatment the rest of
   the topbar reserves for clear actions rather than decoration. */
.account-panel h2{margin:0 0 8px}
.account-backup-note{font-family:var(--ui); font-size:13px; color:var(--text-2); margin:0 0 12px; line-height:1.5}
/* Account sync (Google) is a second section in the same panel, not a
   separate menu, so it earns a divider rather than blending into the local
   backup controls above it. */
#account-sync{margin-top:18px; padding-top:16px; border-top:1px solid var(--line-soft)}
.account-state{font-family:var(--ui); font-size:14px; color:var(--text-2); margin:0 0 8px; line-height:1.5}
.account-status{font-family:var(--ui); font-size:13px; color:var(--text-3); margin:0 0 12px; line-height:1.5; min-height:1em}
.account-status.is-warn{color:var(--bad)}
.account-actions{display:flex; flex-wrap:wrap; gap:8px; margin:0 0 12px}
.account-actions:empty{margin:0}
.account-turnstile{margin:0 0 12px}
.account-turnstile form{display:flex; flex-direction:column; gap:10px; align-items:flex-start}
.account-turnstile .btn[disabled]{opacity:.5; cursor:not-allowed}
.account-fineprint{font-family:var(--ui); font-size:12px; color:var(--text-3); margin:0; line-height:1.5}
.account-panel .btn{
  font-family:var(--ui); font-weight:600; font-size:14px; min-height:38px;
  padding:0 14px; border:1px solid var(--line); border-radius:6px;
  background:var(--accent); color:var(--brand-text); cursor:pointer;
}
.account-panel .btn:hover:not(:disabled){background:var(--accent-2); color:var(--brand-text)}
.account-panel .btn.ghost{background:none; color:var(--text-2)}
.account-panel .btn.ghost:hover:not(:disabled){color:var(--text)}
.account-panel .btn[hidden]{display:none}
.account-panel .account-danger{color:var(--bad)}
.text-size-status{min-width:38px; color:var(--text-3); font-family:var(--mono); font-size:11px; font-weight:600; text-align:center}
.display-stepper,.theme-options{display:flex; gap:4px; padding:3px; border-radius:7px; background:var(--surface-2)}
.theme-options button{min-width:64px}
.theme-options button[aria-pressed="true"]{background:var(--surface); color:var(--text); box-shadow:0 1px 2px rgba(0,0,0,.12)}
:root[data-text-size="x-small"]{--display-scale:.8}
:root[data-text-size="small"]{--display-scale:.9}
:root[data-text-size="large"]{--display-scale:1.1}
:root[data-text-size="x-large"]{--display-scale:1.2}
:root[data-text-size="xx-large"]{--display-scale:1.3}
:root[data-text-size="xxx-large"]{--display-scale:1.4}
:root[data-text-size="huge"]{--display-scale:1.5}
:root[data-text-size="x-huge"]{--display-scale:1.6}
:root[data-text-size="xx-huge"]{--display-scale:1.7}


/* ---------- today ---------- */
.today-wrap{max-width:var(--content-max); margin:0 auto; padding:48px 22px 90px}
#today-app{display:flex; flex-direction:column; gap:20px}
.today-card{border:1px solid var(--line); border-radius:var(--r); background:var(--surface); box-shadow:var(--shadow); padding:24px}
.today-card h2{font-size:21px}
.today-card p{margin-top:8px; color:var(--text-2); font-size:15px}
.today-next .eyebrow{color:var(--accent-2)}
.today-next h2{margin-top:8px; font-size:26px}
.today-next .startbar{margin-top:18px}
/* An inline text action inside a sentence of prose, pointing at the one
   central Backup menu rather than repeating its buttons at every call site. */
.linklike{
  font:inherit; font-weight:600; color:var(--accent-2); background:none; border:0;
  padding:0; margin:0; cursor:pointer; text-decoration:underline; text-underline-offset:2px;
}
.linklike:hover{color:var(--accent)}
.storage-notice{margin-top:16px; padding:11px 13px; border-left:3px solid var(--bad); background:var(--bad-soft); color:var(--text); font-family:var(--disp); font-size:13px; line-height:1.5}
.certification-brief{margin-top:28px; padding:22px 24px}
.certification-brief summary{cursor:pointer}
.certification-brief summary .eyebrow{display:inline}
.certification-brief[open] summary{margin-bottom:14px}
.certification-brief h2{font-size:21px}
.certification-brief > p{margin-top:7px; color:var(--text-2); font-size:15px}
.certification-facts{display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:1px; margin-top:17px; background:var(--line); border:1px solid var(--line); border-radius:8px; overflow:hidden}
.certification-facts div{padding:12px 14px; background:var(--surface)}
.certification-facts dt{font-family:var(--disp); font-size:11px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--text-3)}
.certification-facts dd{margin:4px 0 0; font-family:var(--disp); font-size:15px; font-weight:700; line-height:1.35}
.certification-domains{margin-top:15px; color:var(--text-2); font-family:var(--disp); font-size:14px; line-height:1.55}

/* ---------- hero ---------- */
.hero{max-width:var(--content-max); margin:0 auto; padding:44px 22px 26px}
.hero h2{font-size:clamp(29px,4.4vw,46px); font-weight:700}
.hero .lede{margin-top:16px; color:var(--text-2); font-size:18px; line-height:1.68}
/* The domain's weight and description, lifted out of Material's own content
   so they read once, above the tabs, and stay in view on Flashcards and Quiz
   too - rather than Material repeating the page heading under a heavy rule
   that outweighs the tabs themselves. */
.learn-domain-intro:empty{display:none}
.learn-domain-intro{margin-top:14px}
.learn-domain-intro p{margin-top:8px; color:var(--text-2); font-size:15.5px; line-height:1.6}
/* ---------- learn tabs (Domain page) ---------- */
/* Subsection heading + its "mark read" control share one row, control at the
   top right in front of the heading - not a form checkbox floating under it. */
.sub-head{display:flex; align-items:center; justify-content:space-between; gap:16px; margin:34px 0 11px}
.sub-head h3.sub{margin:0; flex:1; min-width:0}
.read-toggle{
  flex:none; display:inline-flex; align-items:center; gap:5px;
  font-family:var(--ui); font-size:12.5px; font-weight:600; color:var(--text-3);
  background:none; border:0; padding:4px 2px; margin-top:2px; cursor:pointer; border-radius:4px;
  transition:color .15s;
}
.read-toggle span{text-decoration:underline; text-underline-offset:3px; text-decoration-color:var(--line)}
.read-toggle:hover{color:var(--text-2)}
.read-toggle:hover span{text-decoration-color:currentColor}
.read-toggle svg{flex:none; color:var(--ok)}
.read-toggle.is-read{color:var(--ok)}
.read-toggle.is-read span{text-decoration:none}

/* ---------- today overview / mock list (shared row pattern) ---------- */
/* var(--bad), not a hardcoded red. #b3382c had no dark-mode counterpart, so the
   "weak" verdict read at 2.64:1 on --surface-2 in dark mode while its
   verdict-ready sibling on the line above correctly used a token. */

/* ---------- today overview disclosure ---------- */

/* ---------- guided tour card ---------- */


/* ---------- study layout ---------- */
.study-wrap{max-width:var(--content-max); margin:0 auto; padding:8px 22px 90px}
.rail{position:sticky; top:78px; display:flex; flex-direction:column; gap:5px}
.rail a{
  display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center;
  column-gap:9px; row-gap:7px; text-decoration:none; color:var(--text-2);
  padding:6px 4px 8px; border-radius:6px;
  font-family:var(--disp); font-size:13.5px; font-weight:500; transition:color .15s;
}
.rail a .n{
  font-family:var(--mono); font-size:10.5px; font-weight:600; width:20px; height:20px;
  border-radius:5px; display:grid; place-items:center;
  background:var(--surface-2); color:var(--text-3);
  transition:background .15s, color .15s;
}
.rail a:hover{color:var(--text)}
.rail a:hover .n{background:var(--line)}
.rail a.on{color:var(--text)}
.rail a.on .t{font-weight:600}
.rail a.on .n{background:var(--accent); color:var(--surface)}
.rail a.ref .n{background:none}
.rail a.ref .n::before{content:""; width:5px; height:5px; border-radius:1px; background:var(--text-3); transition:background .15s}
.rail a.ref:hover .n::before{background:var(--text-2)}
.rail a.ref.on .n::before{background:var(--accent)}

.doc{min-width:0; max-width:none}
section.dom{scroll-margin-top:86px; padding-top:18px}
.doc > section.dom[hidden]{display:none}
/* The lede (weight badge + description) now lives in the hero above the
   tabs, not here, so the first thing in the section's own content should
   not carry the spacing that used to separate it from that lede. */
section.dom > *:first-child{margin-top:0}
.dom-weight{
  font-family:var(--mono); font-size:11.5px; font-weight:600; color:var(--accent-2);
  background:var(--accent-soft); padding:3px 8px; border-radius:20px; white-space:nowrap;
}
h3.sub{font-size:18px; font-weight:650; margin:34px 0 11px; letter-spacing:-.01em}
h3.sub::before{content:""; display:inline-block; width:14px; height:2px; background:var(--accent); vertical-align:middle; margin-right:9px; transform:translateY(-2px)}
.doc p{margin:12px 0; color:var(--text-2); line-height:1.74}
.doc ul{margin:10px 0; padding-left:0; list-style:none}
.doc ul li{position:relative; padding-left:19px; margin:9px 0; color:var(--text-2); line-height:1.68}
.doc ul li::before{content:""; position:absolute; left:4px; top:.72em; width:5px; height:5px; border-radius:50%; background:var(--line); }
.doc strong{color:var(--text); font-weight:600}
code{font-family:var(--mono); font-size:.86em; background:var(--surface-2); padding:1.5px 5px; border-radius:4px; color:var(--text)}

.tblwrap{overflow-x:auto; margin:14px 0; border:1px solid var(--line); border-radius:var(--r); background:var(--surface)}
table{border-collapse:collapse; width:100%; font-family:var(--disp); font-size:15px; line-height:1.55; letter-spacing:0}
th,td{text-align:left; padding:11px 14px; border-bottom:1px solid var(--line-soft); vertical-align:top}
thead th{
  font-family:var(--mono); font-size:10.5px; letter-spacing:.1em; text-transform:uppercase;
  color:var(--text-3); font-weight:600; background:var(--surface-2); border-bottom:1px solid var(--line);
  position:sticky; top:0;
}
tbody tr:last-child td{border-bottom:0}
td:first-child{font-weight:600; color:var(--text); white-space:nowrap}
td em{font-style:normal; color:var(--text-3)}

.callout{
  border:1px solid var(--line); background:var(--surface);
  border-radius:0 var(--r) var(--r) 0; padding:13px 16px; margin:18px 0;
}
.callout .eyebrow{color:var(--accent-2); margin-bottom:5px}
.callout p{margin:0; font-size:16px; line-height:1.65}

.vs{display:grid; gap:1px; background:var(--line); border:1px solid var(--line); border-radius:var(--r); overflow:hidden; margin:14px 0; grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}
.vs > div{background:var(--surface); padding:13px 15px}
.vs h4{font-family:var(--mono); font-size:11.5px; letter-spacing:.06em; color:var(--accent-2); margin-bottom:6px}
.vs p{margin:0; font-size:15px; line-height:1.6}

/* ---------- exam ---------- */
.exam-wrap{max-width:var(--content-max); margin:0 auto; padding:0 22px 90px}
.hidden{display:none!important}

.start{max-width:var(--content-max); margin:44px auto; }
/* Flashcards/Quiz are the same #view-recall/#view-exam nodes used standalone
   on Review/Practice, relocated into .study-wrap by embedContainer(). Their
   own full-page max-width/padding/margin would stack with .study-wrap's,
   which already centers and insets this column - neutralize them here so
   all three tabs start at the same offset Material's plain section.dom does. */
.study-wrap .exam-wrap{max-width:none; margin:0; padding:0}
.study-wrap .start{max-width:none; margin:18px auto 0}
.start h2{font-size:clamp(29px,4.4vw,42px); font-weight:700}
.start > p{color:var(--text-2); margin-top:14px; font-size:18px; line-height:1.68}
.optbox{margin-top:26px; border:1px solid var(--line); border-radius:var(--r); background:var(--surface); overflow:hidden}
.optrow{display:flex; align-items:center; gap:16px; padding:15px 18px; border-bottom:1px solid var(--line-soft); flex-wrap:wrap}
.optrow:last-child{border-bottom:0}
.optrow .lbl{font-family:var(--disp); font-weight:600; font-size:14.5px; min-width:150px}
.optrow .hint{font-size:13.5px; color:var(--text-3); flex:1 1 220px; font-family:var(--disp)}
.seg{display:flex; gap:2px; background:var(--surface-2); padding:3px; border-radius:8px; flex-wrap:wrap}
.seg button{
  font-family:var(--disp); font-weight:600; font-size:12.5px; border:0; background:none; color:var(--text-2);
  padding:6px 12px; border-radius:6px; cursor:pointer;
}
.seg button[aria-pressed="true"]{background:var(--surface); color:var(--text); box-shadow:0 1px 2px rgba(0,0,0,.12)}
.btn{
  font-family:var(--ui); font-weight:700; font-size:15px; border:1px solid var(--line);
  background:var(--surface); color:var(--text); padding:12px 22px; border-radius:8px; cursor:pointer;
  transition:background .15s,border-color .15s,transform .1s, box-shadow .15s;
}
.btn{min-height:46px}
.seg button,.grade,.recall-filter button,.errtax-btn,.tree-opt,.case-opt,.ordbtns button,.matchrow select{min-height:40px}
.btn:hover{border-color:var(--text-3)}
.btn:active{transform:translateY(1px)}
/* --brand-text, not a hardcoded pair. Phase 1 replaced three rules that
   hardcoded #fff for light and #0D1D14 for dark - a dark *green* left from the
   pre-teal palette, which rendered green-black on SAA-C03's blue accent - while
   the room-theme contract published --brand-text for exactly this purpose and
   .account-panel .btn already read it. Same visual role, two different text
   colours, and the room had no way to influence the primary one.
   The lift and pulse below are this phase's; the colour token is not negotiable
   and tests/unit/palette-contrast.test.js fails if it is hardcoded again. */
.btn.primary{
  background:var(--accent); border-color:var(--accent); color:var(--brand-text);
  box-shadow:var(--shadow-lift);
  position:relative;
  overflow:visible;
}
.btn.primary::before{
  content:""; position:absolute; inset:-2px; border-radius:10px;
  background:var(--accent); opacity:0; transition:opacity .3s, transform .3s;
  transform:scale(1.1);
  pointer-events:none;
  z-index:-1;
}
@media (prefers-reduced-motion:reduce){
  .btn.primary::before{display:none}
}
.btn.primary:hover{filter:brightness(1.07)}
.btn:disabled{opacity:.45; cursor:not-allowed}
.btn.ghost{background:none}
.startbar{margin-top:24px; display:flex; gap:12px; align-items:center; flex-wrap:wrap}

/* exam bar */
.exambar{
  position:sticky; top:60px; z-index:50; background:var(--surface); border-bottom:1px solid var(--line);
  margin:0 -22px 0; padding:0 22px;
}
.exambar-in{display:flex; align-items:center; gap:18px; height:58px; max-width:1196px; margin:0 auto; flex-wrap:nowrap}
.timer{display:flex; align-items:center; gap:9px}
.timer svg{display:block; flex:none}
.timer .t{font-family:var(--mono); font-size:17px; font-weight:600; font-variant-numeric:tabular-nums; letter-spacing:-.02em}
.timer.warn .t{color:var(--bad)}
.exambar .prog{flex:1; min-width:60px; height:4px; background:var(--surface-2); border-radius:3px; overflow:hidden}
.exambar .prog i{display:block; height:100%; background:var(--accent); border-radius:3px; transition:width .25s}
.exambar .count{font-family:var(--mono); font-size:12px; color:var(--text-3); white-space:nowrap}
.exambar .spacer{flex:none}

.qlayout{display:grid; grid-template-columns:minmax(0,1fr) 224px; gap:36px; align-items:start; padding-top:26px}
.qcard{background:var(--surface); border:1px solid var(--line); border-radius:var(--r); box-shadow:var(--shadow); padding:26px 30px 24px; min-width:0}
.qmeta{display:flex; align-items:center; gap:9px; flex-wrap:wrap; margin-bottom:14px}
.chip{
  font-family:var(--mono); font-size:10px; font-weight:600; letter-spacing:.1em; text-transform:uppercase;
  padding:3px 7px; border-radius:4px; background:var(--surface-2); color:var(--text-2); white-space:nowrap;
}
.chip.type{background:var(--accent-soft); color:var(--accent-2)}
.chip.flagged{background:var(--flag-soft); color:var(--flag)}
.qnum{font-family:var(--mono); font-size:12px; font-weight:600; color:var(--text-3); margin-right:auto}
.scenario{
  border-left:3px solid var(--line); padding:2px 0 2px 15px; margin:0 0 16px; color:var(--text-2);
  font-size:15.5px;
}
.scenario .eyebrow{display:block; margin-bottom:4px}
.stem{font-size:19px; line-height:1.62; color:var(--text)}
.instr{font-family:var(--mono); font-size:11.5px; letter-spacing:.06em; text-transform:uppercase; color:var(--accent-2); margin-top:10px; font-weight:600}

.opts{margin-top:18px; display:flex; flex-direction:column; gap:9px}
.opt{
  display:flex; gap:13px; align-items:flex-start; padding:12px 15px; border:1px solid var(--line);
  border-radius:9px; cursor:pointer; background:var(--surface); transition:border-color .13s, background .13s;
  font-size:15.5px; line-height:1.5; position:relative;
}
.opt:hover{border-color:var(--text-3); background:var(--surface-2)}
.opt input{position:absolute; opacity:0; width:1px; height:1px}
.opt:has(input:focus-visible){outline:3px solid var(--focus); outline-offset:2px}
.opt .key{
  font-family:var(--mono); font-size:12px; font-weight:600; width:23px; height:23px; flex:none;
  border:1px solid var(--line); border-radius:5px; display:grid; place-items:center; color:var(--text-2);
  background:var(--surface); margin-top:1px;
}
.opt.sel{border-color:var(--accent); background:var(--accent-soft)}
.opt.sel .key{background:var(--accent); border-color:var(--accent); color:var(--brand-text)}
.opt.locked{cursor:default}
.opt.locked:hover{border-color:var(--line); background:var(--surface)}
.opt.correct{border-color:var(--ok); background:var(--ok-soft)}
.opt.correct .key{background:var(--ok); border-color:var(--ok); color:var(--ok-text)}
.opt.wrong{border-color:var(--bad); background:var(--bad-soft)}
.opt.wrong{border-color:var(--bad); background:var(--bad-soft)}
.opt.wrong .key{background:var(--bad); border-color:var(--bad); color:var(--bad-text)}
.choice-fieldset{border:0; margin:18px 0 0; padding:0}
.sr-only{position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0}
.selection-status{margin:10px 0 0; color:var(--text-2); font-family:var(--ui); font-size:13px}
.selection-status.limit{color:var(--bad); font-weight:600}

/* Answer selection animation */
.opt.sel{
  animation: optSelect 0.2s ease-out;
}
@keyframes optSelect{
  0%{transform:scale(0.98); background:var(--surface)}
  50%{transform:scale(1.01)}
  100%{transform:scale(1); background:var(--accent-soft)}
}
@media (prefers-reduced-motion:reduce){
  .opt.sel{animation:none}
}

/* Empty state */

/* Results dial animation */
.dial circle:last-child{
  transition:stroke-dashoffset 0.8s cubic-bezier(0.4,0,0.2,1);
}
@media (prefers-reduced-motion:reduce){
  .dial circle:last-child{transition:none}
}

/* Account menu icon */

/* ordering */
/* minmax(0,...) rather than 1fr: a bare 1fr track will not shrink below its
   content's minimum, so at 320px a step with one long word pushed the row
   ~100px past the card. */
.ordwrap{margin-top:18px; display:grid; grid-template-columns:minmax(0,1fr); gap:9px}
.orditem{
  display:flex; gap:12px; align-items:center; padding:11px 14px; border:1px solid var(--line);
  border-radius:9px; background:var(--surface); font-size:15px;
}
.orditem .pos{
  font-family:var(--mono); font-size:12px; font-weight:600; width:24px; height:24px; flex:none;
  border-radius:5px; display:grid; place-items:center; background:var(--surface-2); color:var(--text-2);
}
.orditem .txt{flex:1; min-width:0; overflow-wrap:anywhere}
/* The step's words need a column of their own: beside the position badge and two
   44px move buttons a phone leaves them ~30px, which breaks a sentence one
   letter at a time. Below this the buttons drop under the text instead. */
@media (max-width:420px){
  .orditem{flex-wrap:wrap}
  .orditem .txt{flex:1 1 9em}
  .orditem .ordbtns{margin-left:auto}
}
.ordbtns{display:flex; gap:4px; flex:none}
.ordbtns button{
  width:26px; height:26px; border:1px solid var(--line); background:var(--surface); border-radius:6px;
  cursor:pointer; color:var(--text-2); font-size:11px; line-height:1; display:grid; place-items:center;
}
.ordbtns button:hover:not(:disabled){border-color:var(--text-3); color:var(--text)}
.ordbtns button:disabled{opacity:.3; cursor:not-allowed}
.orditem.correct{border-color:var(--ok); background:var(--ok-soft)}
.orditem.correct .pos{background:var(--ok); color:var(--ok-text)}
.orditem.wrong{border-color:var(--bad); background:var(--bad-soft)}
.orditem.wrong .pos{background:var(--bad); color:var(--bad-text)}

/* matching */
.matchwrap{margin-top:18px; display:flex; flex-direction:column; gap:9px}
.matchrow{
  display:grid; grid-template-columns:minmax(0,.72fr) minmax(0,1.28fr); gap:13px; align-items:center;
  padding:11px 14px; border:1px solid var(--line); border-radius:9px; background:var(--surface);
}
.matchrow .prompt{font-size:15px; line-height:1.45}
.matchrow select{
  font-family:var(--disp); font-size:13.5px; padding:8px 9px; border-radius:7px; border:1px solid var(--line);
  background:var(--surface-2); color:var(--text); width:100%; cursor:pointer;
}
.matchrow.correct{border-color:var(--ok); background:var(--ok-soft)}
.matchrow.wrong{border-color:var(--bad); background:var(--bad-soft)}
.matchrow .truth{font-family:var(--mono); font-size:11px; color:var(--ok); margin-top:5px; display:block}


/* explanation */
.explain{margin-top:20px; border-top:1px solid var(--line-soft); padding-top:18px}
.verdict{display:flex; align-items:center; gap:9px; font-family:var(--disp); font-weight:600; font-size:15px; margin-bottom:12px}
.verdict .dot{width:9px; height:9px; border-radius:50%; flex:none}
.verdict.good{color:var(--ok)} .verdict.good .dot{background:var(--ok)}
.verdict.bad{color:var(--bad)} .verdict.bad .dot{background:var(--bad)}
.explain .why{background:var(--surface-2); border-radius:9px; padding:13px 16px; font-size:15px; color:var(--text)}
.optnotes{margin-top:14px; display:flex; flex-direction:column; gap:8px}
.optnote{display:flex; gap:11px; font-size:14.5px; line-height:1.5; color:var(--text-2)}
.optnote .k{
  font-family:var(--mono); font-size:11px; font-weight:600; flex:none; width:21px; height:21px;
  border-radius:5px; display:grid; place-items:center; margin-top:2px;
  background:var(--bad-soft); color:var(--bad);
}
.optnote.is-right .k{background:var(--ok-soft); color:var(--ok)}
/* Three cards of padding leave ~146px for the words at 320px, and a token like
   "prompt/completion" has no break opportunity, so without this it painted over
   the card's edge. */
.optnote > span:last-child{min-width:0; overflow-wrap:anywhere}

/* navigator */
.nav{position:sticky; top:130px; display:flex; flex-direction:column; gap:12px}
.nav-summary{
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  cursor:pointer; list-style:none; padding:8px 0; border-bottom:1px solid var(--line-soft);
}
.nav-summary::-webkit-details-marker{display:none}
.nav-summary::after{content:""; width:6px; height:6px; margin:0 0 3px 7px; border-right:1.5px solid currentColor; border-bottom:1.5px solid currentColor; transform:rotate(45deg); transition:transform 0.2s}
.nav-details[open] .nav-summary::after{transform:rotate(-135deg)}
.nav-summary .eyebrow{margin:0}
.nav-count{font-family:var(--mono); font-size:11px; color:var(--text-3); background:var(--surface-2); padding:2px 8px; border-radius:var(--r-sm)}
.navgrid{display:grid; grid-template-columns:repeat(6,1fr); gap:5px}
.navcell{
  aspect-ratio:1; border:1px dashed var(--text-3); border-radius:6px; background:var(--ground);
  font-family:var(--mono); font-size:11px; font-weight:500; color:var(--text-3); cursor:pointer;
  display:grid; place-items:center; position:relative; padding:0;
  transition:border-color .12s, background .12s;
}
.navcell:hover{border-color:var(--text-3)}
.navcell.answered{background:var(--ok-soft); color:var(--ok); border:1px solid var(--ok); font-weight:700}
.navcell.answered::before{
  content:""; position:absolute; bottom:7px; width:7px; height:4px; border-left:1.5px solid currentColor;
  border-bottom:1.5px solid currentColor; transform:rotate(-45deg);
}
.navcell.flagged::after{
  content:""; position:absolute; top:0; right:0; border-width:0 7px 7px 0; border-style:solid;
  border-color:transparent var(--flag) transparent transparent; border-radius:0 6px 0 0;
  animation: flagDrop 0.3s ease-out;
}
@keyframes flagDrop{
  0%{opacity:0; transform:translateY(-10px) rotate(45deg)}
  100%{opacity:1; transform:translateY(0) rotate(45deg)}
}
@media (prefers-reduced-motion:reduce){
  .navcell.flagged::after{animation:none}
}
.navcell.cur{border-color:var(--accent); box-shadow:0 0 0 2px var(--accent-soft); color:var(--text); font-weight:600}
.navcell.rw{background:var(--ok-soft); border-color:var(--ok); color:var(--ok)}
.navcell.rx{background:var(--bad-soft); border-color:var(--bad); color:var(--bad)}
.navlegend{display:flex; flex-wrap:wrap; gap:9px; font-family:var(--mono); font-size:10px; color:var(--text-3)}
.navlegend span{display:flex; align-items:center; gap:5px}
.navlegend i{width:10px; height:10px; border-radius:3px; display:block}
.navlegend .key-answered{background:var(--ok-soft); border:1px solid var(--ok)}
.navlegend .key-flagged{background:var(--flag); border:1px solid var(--flag)}
.navlegend .key-skipped{background:var(--ground); border:1px dashed var(--text-3)}

/* results */
.results{max-width:var(--content-max); margin:0 auto; padding-top:34px}
.dial{position:relative; width:150px; height:150px; flex:none}
.dial svg{transform:rotate(-90deg)}
.dial .mid{position:absolute; inset:0; display:grid; place-items:center; text-align:center}
.dial .mid b{font-family:var(--disp); font-size:38px; font-weight:700; line-height:1; letter-spacing:-.03em}
.dial .mid span{font-family:var(--mono); font-size:10px; letter-spacing:.12em; text-transform:uppercase; color:var(--text-3); display:block; margin-top:5px}


.rev{display:flex; flex-direction:column; gap:14px}
.revq{border:1px solid var(--line); border-radius:var(--r); background:var(--surface); padding:20px 24px}

/* recall lab */
.recall-wrap{max-width:var(--content-max); margin:0 auto; padding:44px 22px 90px}
/* Same relocation as .exam-wrap above: neutralize the standalone-page frame
   when this is the Flashcards tab, and drop its heading - the domain hub's
   own hero already shows the domain name and description above the tabs. */
.study-wrap .recall-wrap{max-width:none; margin:0; padding:18px 0 0}

/* Recall focus mode - card 100% width, filters dropdown, sidebar as bottom sheet */
.recall-filter select{
  width:100%; font-family:var(--ui); font-size:14px; font-weight:600; color:var(--text);
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r);
  padding:10px 14px; cursor:pointer; appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 12px center; background-size:18px;
  padding-right:44px;
}
.recall-filter select:focus{outline:2px solid var(--focus); outline-offset:2px}

.recall-domain{margin:25px 0 18px; padding:14px 16px; border:1px solid var(--line); border-radius:var(--r); background:var(--surface)}
.recall-domain .focus-row{display:flex; align-items:center; gap:12px; flex-wrap:wrap}
.recall-domain .seg{margin-left:auto}
.recall-filter{display:flex; gap:5px; flex-wrap:wrap; margin-bottom:14px}
.recall-filter button{font-family:var(--disp); font-size:12.5px; font-weight:600; color:var(--text-2); border:1px solid var(--line); border-radius:20px; padding:6px 10px; background:var(--surface); cursor:pointer}
.recall-filter button[aria-pressed="true"]{background:var(--accent-soft); border-color:var(--accent); color:var(--accent-2)}
/* A flashcard reads as an object, not a content block: full-width so it fills
   its column at any viewport, a real card radius and enough lift to look
   liftable. */
/* The reveal button sits right under the question, sized to itself rather
   than stretched into a full-width bar - it is one choice, not a form. */
/* The exam cue lives inside the answer face, so it only shows once the
   card is flipped - it is a reveal, not a giveaway printed on the front. */
/* Two-choice self-grade, right under the answer it grades - not anchored to
   the bottom of the card, and sized to the label rather than stretched. */
.grade{
  font-family:var(--disp); font-size:14.5px; font-weight:700; border:1px solid var(--line);
  background:var(--surface); color:var(--text); border-radius:9px; padding:12px 20px; cursor:pointer;
  transition:background .15s, border-color .15s;
}
.grade:hover{border-color:var(--text-3)}
.grade.again{color:var(--bad); border-color:var(--bad)}
.grade.again:hover{background:var(--bad-soft)}
.grade.good{color:var(--ok); border-color:var(--ok)}
.grade.good:hover{background:var(--ok-soft)}
.recall-notice{font-family:var(--disp); font-size:12.5px; color:var(--text-3); margin-top:10px}

.overlay{
  position:fixed; inset:0; z-index:100; background:rgba(10,16,22,.55);
  display:grid; place-items:center; padding:22px; backdrop-filter:blur(2px);
}
.dialog{
  background:var(--surface); border:1px solid var(--line); border-radius:12px; box-shadow:var(--shadow-lift);
  padding:26px 28px; max-width:480px; width:100%;
}
.dialog:focus{outline:3px solid var(--focus); outline-offset:3px}
.dialog h3{font-size:19px; font-weight:700}
.dialog p{margin-top:10px; color:var(--text-2); font-size:15px}
.dlgbtns{display:flex; gap:9px; margin-top:20px; flex-wrap:wrap; justify-content:flex-end}
.dlgbtns .btn{font-size:13.5px; padding:9px 15px}

.footer{max-width:var(--content-max); margin:0 auto; padding:34px 22px 60px; border-top:1px solid var(--line); color:var(--text-3); font-size:13.5px; font-family:var(--disp); text-align:center}
.footer a{color:var(--text-2)}
.footer p + p{margin-top:10px}
.footer-links a{white-space:nowrap}

@media (max-width:1000px){
  .study-wrap{grid-template-columns:1fr; gap:0}
  .rail{position:static; flex-direction:row; overflow-x:auto; gap:8px; padding:14px 0 6px; border-bottom:1px solid var(--line)}
  .rail a{
    white-space:nowrap; grid-template-columns:auto auto auto; column-gap:7px;
    padding:6px 10px; background:var(--surface-2);
  }
  .rail a.on{background:var(--accent-soft)}
  .rail a.ref .n{display:none}
  .rail a.ref{grid-template-columns:auto}
  .qlayout{grid-template-columns:1fr; gap:24px}
  .nav{position:static}
  .navgrid{grid-template-columns:repeat(10,1fr)}
}
@media (max-width:640px){
  body{font-size:17px; line-height:1.68; padding-bottom:76px}
  .qcard{padding:20px 18px}
  .display-stepper,.theme-options{justify-content:space-between}
  .display-stepper button,.theme-options button{flex:1}
  .today-wrap{padding:34px 14px 36px}
  /* The trailing meta - a nowrap status badge, the three track dots and the
     chevron - none of which shrink, so below this width they took the row's
     whole inline axis and left .grow about 60px to set a domain's name in,
     one word per line. Wrapping moves them under the text instead. */
  .row{flex-wrap:wrap}
  .row .grow{flex:1 1 100%}
  .today-card{padding:20px 18px}
  .certification-brief{margin-top:22px}
  .certification-facts{grid-template-columns:1fr 1fr}
  .exambar-in{gap:12px}
  .exambar .prog{display:none}
  .hero,.study-wrap,.exam-wrap,.footer{padding-left:14px; padding-right:14px}
  .exambar{margin:0 -14px; padding:0 14px}
  .matchrow{grid-template-columns:1fr; gap:8px}
  .navgrid{grid-template-columns:repeat(7,1fr)}
  .recall-wrap{padding-left:14px; padding-right:14px}
  .recall-domain .seg{margin-left:0}
  .btn,.seg button,.grade,.recall-filter button,.errtax-btn,.tree-opt,.case-opt,.navcell,.ordbtns button,.matchrow select{min-height:44px}
  .ordbtns button{width:44px; height:44px}
  .tblwrap{position:relative; padding-bottom:28px}
  .tblwrap::after{
    content:"Swipe sideways to compare all columns";
    position:absolute; bottom:7px; left:12px; color:var(--text-3); font-family:var(--disp); font-size:12px; font-weight:600;
  }
}
@media (min-width:641px) and (max-width:700px){
  body{padding-bottom:76px}
}
@media (max-width:700px){
  body.exam-active{padding-bottom:0}
}
@media (max-width:380px){
  .navgrid{grid-template-columns:repeat(5,1fr)}
}

/* practice-exam form tag + per-form attempt history */
.formtag{color:var(--text-3)}
.history{margin-top:30px}

/* ============================================================
   Adaptive learning layer — error taxonomy, mastery map,
   decision trees, case lab
   ============================================================ */

/* --- confidence + task chips in review --- */
.chip.task{background:var(--flag-soft); color:var(--flag); font-family:var(--mono)}

/* --- error taxonomy control in review --- */
.errtax{margin-top:16px; padding-top:14px; border-top:1px dashed var(--line-soft)}
.errtax-h{font-family:var(--disp); font-size:12.5px; color:var(--text-2); margin-bottom:9px}
.errtax-h .eyebrow{display:inline; margin-right:6px}
.errtax-h em{font-style:normal; color:var(--text-3)}
.errtax-btns{display:flex; gap:7px; flex-wrap:wrap}
.errtax-btn{
  font-family:var(--disp); font-weight:600; font-size:12px; border:1px solid var(--line);
  background:var(--surface); color:var(--text-2); padding:6px 11px; border-radius:20px; cursor:pointer;
  transition:border-color .15s,background .15s,color .15s;
}
.errtax-btn:hover{border-color:var(--text-3); color:var(--text)}
.errtax-btn.on{background:var(--accent-soft); border-color:var(--accent); color:var(--accent-2)}
.errtax-btn.clear{border-style:dashed; color:var(--text-3)}

/* --- drill banner on results --- */

/* --- proactive save-to-account prompt, shown only while signed out --- */
.save-prompt{
  margin-bottom:22px; padding:14px 18px; border:1px solid var(--accent);
  border-radius:var(--r); background:var(--accent-soft);
}
.save-prompt .eyebrow{color:var(--accent-2)}
.save-prompt p{margin:5px 0 12px; font-family:var(--disp); font-size:13.5px; color:var(--text-2); line-height:1.5}
.save-prompt .btn{margin:0}

/* --- mastery view shell --- */
.mastery-wrap{max-width:var(--content-max); margin:0 auto; padding:44px 22px 90px}
#mastery-app{margin-top:26px; display:flex; flex-direction:column; gap:22px}
.m-sec{background:var(--surface); border:1px solid var(--line); border-radius:var(--r); box-shadow:var(--shadow); padding:22px 24px}
.m-sec-h h2{font-size:18.5px; font-weight:700; font-family:var(--disp)}
.m-sec-h p{margin-top:6px; color:var(--text-2); font-family:var(--disp); font-size:13.5px; line-height:1.5; max-width:100%}
.m-note{margin-top:14px; font-family:var(--disp); font-size:12.5px; color:var(--text-3); line-height:1.5}
.m-sec .startbar{margin-top:18px}
.m-disclosure{border:1px solid var(--line); border-radius:var(--r); background:var(--surface); box-shadow:var(--shadow)}
.m-disclosure summary{padding:19px 22px; cursor:pointer; list-style:none}
.m-disclosure summary::-webkit-details-marker{display:none}
.m-disclosure summary::after{content:"+"; float:right; color:var(--accent-2); font-family:var(--disp); font-size:22px; line-height:.8}
.m-disclosure[open] summary{border-bottom:1px solid var(--line-soft)}
.m-disclosure[open] summary::after{content:"−"}
.m-disclosure summary h2{display:inline; font-family:var(--disp); font-size:18px}
.m-disclosure summary p{display:inline; margin-left:9px; color:var(--text-2); font-family:var(--disp); font-size:13.5px; line-height:1.5}
.m-disclosure-body{padding:20px 22px}
/* Readiness as one bar a learner can read at a glance, with the two
   supporting counts beside it rather than a third equal-weight table cell. */
.progress-stats{display:flex; gap:26px; flex-wrap:wrap}
.progress-stats dt{font-family:var(--mono); font-size:10.5px; letter-spacing:.12em; text-transform:uppercase; color:var(--text-3)}
.progress-stats dd{margin:4px 0 0; font-family:var(--disp); font-weight:700; font-size:23px}
.trend-list, .spot-list{margin-top:14px; display:flex; flex-direction:column; gap:1px; background:var(--line); border:1px solid var(--line); border-radius:var(--r); overflow:hidden}
.trend-row, .spot-row{background:var(--surface); padding:10px 14px; display:flex; justify-content:space-between; gap:12px; font-family:var(--disp); font-size:13.5px}
.trend-date{color:var(--text-3); font-family:var(--mono); font-size:12px}
.trend-label{flex:1; margin-left:10px}
.trend-score{font-weight:600}
.spot-name{font-weight:600}
.spot-detail{color:var(--text-2); text-align:right}
.m-disclosure-body .m-sec{box-shadow:none}

/* --- state badges --- */

/* --- readiness gate --- */

/* --- today's focus drill --- */

/* --- task mastery map --- */
.task-list{margin-top:16px; display:flex; flex-direction:column; gap:10px}

/* --- concept-family scorecards --- */

/* --- error taxonomy rollup --- */

/* --- decision trees + case lab shared grids --- */
.tree-grid,.case-grid{margin-top:16px; display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); gap:14px}
.tree,.case{border:1px solid var(--line); border-radius:10px; background:var(--raise); padding:16px 17px; display:flex; flex-direction:column}
.tree-head h4,.case-head h4{font-size:15.5px; font-weight:700; font-family:var(--disp); margin-top:4px}
.tree-trail{display:flex; align-items:center; gap:6px; flex-wrap:wrap; margin:11px 0 3px; font-family:var(--mono); font-size:10.5px; color:var(--text-3)}
.tree-trail span{background:var(--surface-2); padding:2px 7px; border-radius:4px}
.tree-trail b{color:var(--text-3)}
.tree-q,.case-q{margin-top:12px; font-family:var(--disp); font-weight:600; font-size:14.5px; line-height:1.45}
.tree-opts,.case-opts{margin-top:11px; display:flex; flex-direction:column; gap:7px}
.tree-opt,.case-opt{
  text-align:left; font-family:var(--disp); font-size:13.5px; border:1px solid var(--line);
  background:var(--surface); color:var(--text); padding:10px 13px; border-radius:8px; cursor:pointer;
  transition:border-color .15s,background .15s; line-height:1.4;
}
.tree-opt:hover,.case-opt:not(:disabled):hover{border-color:var(--accent); background:var(--accent-soft)}
.case-opt:disabled{cursor:default}
.case-opt.correct{border-color:var(--ok); background:var(--ok-soft)}
.case-opt.wrong{border-color:var(--bad); background:var(--bad-soft)}
.case-opt.dim{opacity:.55}
.tree-concl{margin-top:13px; padding:14px 15px; border:1px solid var(--line); border-radius:9px; background:var(--surface)}
.tree-badge{
  display:inline-block; font-family:var(--mono); font-size:10px; font-weight:600; letter-spacing:.09em;
  text-transform:uppercase; color:var(--accent-2); background:var(--accent-soft); padding:2px 8px; border-radius:20px;
}
.tree-answer{margin-top:9px; font-family:var(--disp); font-weight:700; font-size:16px}
.tree-why{margin-top:6px; font-family:var(--disp); font-size:13px; color:var(--text-2); line-height:1.5}
.tree-tag{margin-top:8px; font-family:var(--mono); font-size:11px; color:var(--text-3)}
.tree-foot,.case-progress{margin-top:auto}
.tree-foot{padding-top:13px}
.tree-foot .btn,.case .btn{font-size:12.5px; padding:7px 13px}

/* --- case lab specifics --- */
.case-progress{display:flex; align-items:center; justify-content:space-between; gap:10px; margin-top:12px; font-family:var(--mono); font-size:11px; color:var(--text-3)}
.case-dots{display:flex; gap:5px}
.case-dots i{width:9px; height:9px; border-radius:50%; background:var(--surface-2)}
.case-dots i.done{background:var(--ok)}
.case-dots i.cur{background:var(--accent)}
.case-blurb{margin-top:10px; font-family:var(--disp); font-size:12.5px; color:var(--text-3); font-style:italic; line-height:1.45}
.case-fb{margin-top:11px; padding:12px 14px; border-radius:8px; font-family:var(--disp); font-size:13px; line-height:1.5}
.case-fb.good{background:var(--ok-soft); color:var(--text)}
.case-fb.bad{background:var(--bad-soft); color:var(--text)}
.case-fb strong{font-family:var(--disp)}
.case-fb-correct{display:block; margin-top:7px; color:var(--text-2); font-size:12.5px}
.case-fb + .tree-foot{margin-top:12px}
.case-wrap-up{margin-top:13px; padding:15px 16px; border:1px solid var(--ok); border-radius:9px; background:var(--ok-soft)}
.case-wrap-up p{margin-top:9px; font-family:var(--disp); font-size:13px; color:var(--text-2); line-height:1.55}
.case-score{margin-top:9px; font-family:var(--disp); font-weight:700; font-size:16px}

/* --- catalog review dashboard --- */
.catalog-summary{display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:10px; margin-top:17px}
.catalog-summary div{padding:12px 14px; border:1px solid var(--line); border-radius:8px; background:var(--raise)}
.catalog-summary span{display:block; font-family:var(--mono); font-size:10.5px; color:var(--text-3); text-transform:uppercase; letter-spacing:.06em}
.catalog-summary strong{display:block; margin-top:5px; font-family:var(--disp); font-size:22px}
.catalog-sources,.catalog-quality,.catalog-performance{margin-top:24px; padding-top:20px; border-top:1px solid var(--line-soft)}
.catalog-sources h3,.catalog-quality h3,.catalog-performance h3,.catalog-table h3{font-family:var(--disp); font-size:15px}
.catalog-sources ul{margin:10px 0 0; padding-left:20px; font-family:var(--disp); font-size:13px; line-height:1.7}
.catalog-sources span{font-family:var(--mono); font-size:10.5px; color:var(--text-3); text-transform:uppercase}
.catalog-coverage{display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:18px; margin-top:24px}
.catalog-table{min-width:0}
.catalog-table .tblwrap{margin-top:10px}
.catalog-table table,.catalog-performance table{width:100%; border-collapse:collapse; font-family:var(--disp); font-size:12.5px}
.catalog-table th,.catalog-table td,.catalog-performance th,.catalog-performance td{padding:8px 9px; border:1px solid var(--line-soft); text-align:left; vertical-align:top}
.catalog-table thead,.catalog-performance thead{background:var(--surface-2); font-family:var(--mono); font-size:10.5px; text-transform:uppercase; letter-spacing:.04em}
.catalog-quality>p,.catalog-performance>p{margin-top:7px; font-family:var(--disp); font-size:13px; color:var(--text-2); line-height:1.5}
.catalog-flag-grid{display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:12px; margin-top:14px}
.catalog-flag-grid>div{padding:13px 15px; border:1px solid var(--line); border-radius:8px; background:var(--raise)}
.catalog-flag-grid h4,.catalog-performance h4{font-family:var(--disp); font-size:13.5px}
.catalog-performance h4{margin-top:16px}
.catalog-flags{margin:8px 0 0; padding-left:18px; font-family:var(--mono); font-size:11.5px; color:var(--text-2); line-height:1.65}
.catalog-flags span{color:var(--text-3)}

@media (max-width:1000px){
  .mastery-wrap{padding:34px 18px 80px}
  .catalog-summary{grid-template-columns:repeat(2,minmax(0,1fr))}
  .catalog-coverage{grid-template-columns:1fr}
}
@media (max-width:640px){
  .mastery-wrap{padding-left:14px; padding-right:14px}
  .tree-grid,.case-grid{grid-template-columns:1fr}
  .catalog-flag-grid{grid-template-columns:1fr}
}

/* ===========================================================================
 * Canvas C component system (docs/plans/canvas-c-rollout.md, Phase 1).
 *
 * Nothing renders these yet - Phase 2 onwards adopts them view by view. They
 * are appended at the end of the stylesheet on purpose: a room's theme.css is
 * appended after this point and carries colour tokens only, so it can retint
 * these components without any of them knowing a room exists.
 *
 * Names were chosen against the shipped markup rather than copied from the
 * prototype. `.chip` and `.eyebrow` already exist and are load-bearing (10 and
 * 31 uses), so the prototype's status chip lands here as `.badge` and its
 * eyebrow reuses the shipped one, which is already the same component. The
 * prototype's `.row` collided with one legacy element inside `.recall-domain`;
 * that one was renamed `.focus-row` so `.row` could be the general case.
 * ======================================================================== */

/* A surface. Cards do not nest: a nested <section> in study material must not
   render as a card inside a card, which is why there is no `.card .card` rule
   to lean on. */
.card{
  background:var(--ground); border:1px solid var(--line); border-radius:var(--r-lg);
  box-shadow:var(--shadow); padding:var(--s5);
}
.card.plain{box-shadow:none}
.card.quiet{background:var(--surface-2); box-shadow:none}

/* An interactive card: blob, text, status, chevron. Always a button or an
   anchor - never a div with a handler - so it is reachable by keyboard and
   announced as what it is. 44px minimum in both directions on a phone. */
.row{
  display:flex; align-items:center; gap:var(--s4); width:100%; min-height:44px;
  text-align:left; padding:var(--s4) var(--s5); border:1px solid var(--line);
  border-radius:var(--r-lg); background:var(--ground); box-shadow:var(--shadow);
  color:inherit; font:inherit; cursor:pointer;
  transition:box-shadow .18s ease-out, transform .18s ease-out;
}
.row:hover{box-shadow:var(--shadow-lift)}
.row .grow{flex:1 1 auto; min-width:0}
.row .chev{flex:0 0 auto; color:var(--text-3)}
@media (prefers-reduced-motion:reduce){ .row{transition:none} }

/* The kind-of-work blob. The kind class sets --k and --k-ink ON THE ELEMENT;
   light mode fills with --k and draws the glyph in --k-ink, dark mode drops the
   fill and draws a ring in --k itself.
   
   The mode switch is a rule per palette context rather than a token, and that
   is not a style preference. A token holding `var(--k)` is substituted where it
   is DECLARED - at :root, where --k does not exist - so it resolves to nothing
   and every blob renders the same grey. That version passed the whole unit
   suite, because the tests read token values and only a browser renders them. */
.blob{
  width:42px; height:42px; flex:0 0 auto; border-radius:var(--r-md);
  display:grid; place-items:center;
  background:var(--k); color:var(--k-ink);
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .blob{background:transparent; color:var(--k); box-shadow:inset 0 0 0 2px currentColor}
}
:root[data-theme="dark"] .blob{background:transparent; color:var(--k); box-shadow:inset 0 0 0 2px currentColor}
.blob.read{--k:var(--k-read); --k-ink:var(--k-read-ink)}
.blob.cards{--k:var(--k-cards); --k-ink:var(--k-cards-ink)}
.blob.drill{--k:var(--k-drill); --k-ink:var(--k-drill-ink)}
.blob.mock{--k:var(--k-mock); --k-ink:var(--k-mock-ink)}
.blob.save{--k:var(--k-save); --k-ink:var(--k-save-ink)}

/* A status badge: verdicts and states, never actions. The leading dot is the
   status; the word beside it is what a screen reader gets, so the dot is
   decorative by construction. */
.badge{
  display:inline-flex; align-items:center; gap:6px; padding:5px 11px;
  border-radius:var(--pill); font-family:var(--ui); font-size:12px; font-weight:800;
  white-space:nowrap; background:var(--surface-2); color:var(--text-2);
}
.badge::before{content:""; width:7px; height:7px; border-radius:50%; background:currentColor; flex:0 0 auto}
.badge.ready{background:var(--ok-soft); color:var(--ok)}
.badge.weak{background:var(--bad-soft); color:var(--bad)}
.badge.flagged{background:var(--flag-soft); color:var(--flag)}
.badge.none{background:transparent; color:var(--text-3); box-shadow:inset 0 0 0 1px var(--line)}

/* Readiness, 0-5. The meter shows the spread; the verdict word carries the
   meaning, so this never has to be read on its own. */
.meter{display:flex; gap:5px}
.meter i{height:9px; flex:1; border-radius:var(--pill); background:var(--surface-2)}
.meter i.on{background:currentColor}

/* Three track dots per domain row: read, cards, drill. Progress, not a tick to
   collect. */
.dots{display:flex; gap:5px}
.dots b{
  width:9px; height:9px; border-radius:50%; background:transparent;
  box-shadow:inset 0 0 0 1.5px var(--line);
}
.dots b.half{background:linear-gradient(90deg, currentColor 50%, transparent 50%)}
.dots b.full{background:currentColor}

/* The sentence under a heading, and the smaller line that says what a verdict
   rests on. `.hero .lede` predates this and keeps its own size by specificity. */
.lede{color:var(--text-2); font-size:15px; margin-top:var(--s2)}
.evidence{font-family:var(--ui); font-size:13px; font-weight:600; color:var(--text-3)}

/* Sprite glyphs. Every icon is paired with text and hidden from assistive
   technology; there is no icon-only control anywhere in the UI. */
.ico{width:20px; height:20px; flex:0 0 auto; fill:none; stroke:currentColor; stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round}
.ico-sm{width:16px; height:16px}
.ico-lg{width:24px; height:24px}

/* Derived numbers are opt-in: scores, percentages, counts of your own work.
   Structural numbers - question 3 of 10, Mock 2, 90 minutes - are never tagged
   with these and so are never hidden. See docs/ui-guidelines.md. */
/* Written as body:not(.numbers-on) rather than a bare .num so the utility
   out-specifies the component rules it has to beat: a two-class descendant
   selector on the tagged element would otherwise win, which is exactly how a
   tagged number stayed on screen with the switch off while every test still
   passed. */
body:not(.numbers-on) .num,
body:not(.numbers-on) .num-block{display:none}
/* The words that stand in for a hidden number. A verdict, a meter and a
   sentence have to carry the meaning with the digits off; this is the inverse
   pair, shown only while they are off. */
body.numbers-on .num-off{display:none}

/* ===========================================================================
 * Canvas C shell (Phase 2). This is the only shell now - the classic shell
 * and the ?ui=studio flag that used to gate this one were removed when the
 * cutover landed, so these rules apply unconditionally.
 * ======================================================================== */


/* Full-bleed, because the 1240px content column cannot hold a long
   certification name at any desktop width - which is why the prototype's
   SAA-C03 chip overflowed structurally rather than at a breakpoint. */
.studio-bar{
  position:sticky; top:0; z-index:40; background:var(--surface);
  border-bottom:1px solid var(--line);
}
.studio-bar-in{display:flex; align-items:center; gap:var(--s4); padding:var(--s3) var(--s5); min-width:0}
/* Everything in the bar may shrink; the room chip's name is what gives first,
   because the code pill beside it still says which room this is. */
.studio-bar-in > *{min-width:0}
/* One chip now holds the glyph and the word together, the same lockup shape
   the apex uses - previously the chip wrapped only the glyph, with the word
   sitting bare beside it. Colour still comes from --brand/--brand-text, the
   room's own accent: only the apex is neutral. */
.studio-brand{
  display:flex; align-items:center; gap:var(--s2); min-height:44px;
  padding:var(--s2) var(--s3); border-radius:var(--r-sm);
  background:var(--brand); color:var(--brand-text); text-decoration:none;
}
/* Matches .ico-lg exactly - the markup's own icon size, not a guess. */
.studio-mark{width:24px; height:24px; display:grid; place-items:center; flex:0 0 auto}
.studio-word{font-family:var(--ui); font-weight:800; font-size:18px; letter-spacing:-.01em; color:inherit}
.roomchip{
  display:inline-flex; align-items:center; gap:var(--s2); padding:5px 11px;
  border:1px solid var(--line); border-radius:var(--pill); color:var(--accent-2);
}
.roomchip .cert{font-family:var(--ui); font-weight:800; font-size:14px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.roomchip .code{
  font-family:var(--mono); font-size:11px; font-weight:800; letter-spacing:.04em; white-space:nowrap;
  background:var(--accent-soft); color:var(--accent-2); padding:2px 7px; border-radius:var(--pill);
}
.studio-nav{display:flex; gap:var(--s1); margin-left:var(--s3)}
.studio-tab{
  display:inline-flex; align-items:center; gap:var(--s2); min-height:44px; padding:0 var(--s4);
  border:0; border-radius:var(--pill); background:none; color:var(--text-2);
  font-family:var(--ui); font-size:15px; font-weight:700; cursor:pointer;
}
.studio-tab:hover{background:var(--surface-2); color:var(--text)}
.studio-tab[aria-current="page"]{background:var(--accent-soft); color:var(--accent-2)}
.studio-tools{display:flex; align-items:center; gap:var(--s2); margin-left:auto; flex:0 0 auto}
.studio-icon{
  display:inline-flex; align-items:center; gap:var(--s2); min-width:44px; min-height:44px;
  justify-content:center; padding:0 var(--s3); border:1px solid var(--line); border-radius:var(--r-sm);
  background:var(--surface); color:var(--text-2); font-family:var(--ui); font-weight:700; font-size:14px; cursor:pointer;
}
.studio-icon:hover{border-color:var(--accent); color:var(--accent-2)}

/* A view toggle, closer to a zoom control than a setting, so it is visible in
   the bar itself above 1100px and lives in the sheet below that. */
.numbers-switch{
  display:inline-flex; align-items:center; gap:var(--s2); min-height:44px; padding:0 var(--s3);
  border:1px solid var(--line); border-radius:var(--pill); background:var(--surface);
  color:var(--text-2); font-family:var(--ui); font-weight:700; font-size:14px; cursor:pointer;
}
.numbers-track{width:34px; height:18px; border-radius:var(--pill); background:var(--surface-2); display:inline-flex; align-items:center; padding:2px; transition:background .15s ease-out}
.numbers-track i{width:14px; height:14px; border-radius:50%; background:var(--text-3); transition:transform .15s ease-out}
.numbers-switch[aria-pressed="true"]{color:var(--accent-2); border-color:var(--accent)}
.numbers-switch[aria-pressed="true"] .numbers-track{background:var(--accent-soft)}
.numbers-switch[aria-pressed="true"] .numbers-track i{background:var(--accent); transform:translateX(16px)}
@media (prefers-reduced-motion:reduce){ .numbers-track, .numbers-track i{transition:none} }

/* Bottom bar under 640px, with the safe-area inset reserved so it can never
   cover a final control. Hidden while a question is on screen: the exam must
   not compete with global destinations. */
.studio-bottom{
  display:none; position:fixed; left:0; right:0; bottom:0; z-index:40;
  background:var(--surface); border-top:1px solid var(--line);
  padding-bottom:env(safe-area-inset-bottom);
}
.studio-bottom button{
  flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:2px;
  min-height:56px; border:0; background:none; color:var(--text-3);
  font-family:var(--ui); font-size:12px; font-weight:700; cursor:pointer;
}
.studio-bottom button[aria-current="page"]{color:var(--accent-2)}
body.exam-active .studio-bottom{display:none !important}

@media (max-width:1180px){
  .studio-icon-label{position:absolute; width:1px; height:1px; overflow:hidden; clip-path:inset(50%); white-space:nowrap}
}
/* What one row of the bar holds, at the widths that have to hold it: the brand
   (~150px), the room chip (~215px with its name), three labelled tabs (~315px),
   the numbers switch (~110px) and two 44px tools, plus gaps and padding. That
   is ~1050px with the switch and ~890px without, so each breakpoint below is the
   width at which the thing it removes stops fitting - not a device. Boxes here
   are min-width:0 and shrink, which is how an overfull bar paints text over its
   neighbours instead of overflowing the page, and why the page-level overflow
   check never saw it. tests/e2e/studio-shell.spec.js asserts the content fits
   its box at each of these widths. */
@media (max-width:1100px){
  .studio-bar .numbers-switch{display:none}
}
@media (max-width:980px){
  .studio-nav{margin-left:0}
}
/* Tabs move to the bottom bar (already built for phones) rather than shrinking:
   an unfolded foldable in portrait is ~700px wide and a tablet ~770-830px, both
   too narrow for the tabs and the chip in one row. Text scaled to 110% still
   fits above this width. */
@media (max-width:960px){
  .studio-bar .studio-nav{display:none}
  .studio-bottom{display:flex}
  #app-shell{padding-bottom:72px}
}
@media (max-width:640px){
  .studio-icon-label{position:absolute; width:1px; height:1px; overflow:hidden; clip-path:inset(50%); white-space:nowrap}
  .studio-bar-in{gap:var(--s2); padding:var(--s2) var(--s3)}
  .roomchip .cert{display:none}
}
/* Brand tile, room chip and two tools need ~380px. Below that the word goes and
   the tile stays: the room chip is the part that says which certification this
   is, and the tile still names MockRoom and still goes home. */
@media (max-width:400px){
  .studio-word{position:absolute; width:1px; height:1px; overflow:hidden; clip-path:inset(50%); white-space:nowrap}
  .studio-brand{padding:var(--s2); min-width:44px; justify-content:center}
}

/* The Settings sheet is a native <dialog> opened with showModal(), so Escape,
   focus containment and an inert background come from the platform rather than
   from handwritten focus-trap code. */
.sheet{
  border:0; border-radius:var(--r-lg); padding:0; width:min(420px, calc(100vw - 32px));
  background:var(--ground); color:var(--text); box-shadow:var(--shadow-lift);
  /* The UA's 100%-based cap leaves the sheet taller than a short window once the
     reading size is raised, and then only the sheet's own scrollbar is left to
     reach what is below. Cap it to the viewport and let it scroll inside. */
  max-height:calc(100vh - 32px); max-height:calc(100dvh - 32px);
  overflow-y:auto;
}
.sheet::backdrop{background:rgba(12,18,23,.45)}
.sheet-in{padding:var(--s5); display:grid; gap:var(--s4)}
/* Sticky, so the close button stays on screen while the body scrolls under it.
   The negative margin lets the header's own padding stand in for the sheet's
   top and side padding, which would otherwise scroll away above a stuck bar. */
.sheet-head{
  display:flex; align-items:center; justify-content:space-between; gap:var(--s3);
  position:sticky; top:0; z-index:1; background:var(--ground);
  margin:calc(-1 * var(--s5)) calc(-1 * var(--s5)) 0; padding:var(--s5) var(--s5) var(--s3);
}
.sheet-head h2{min-width:0; overflow-wrap:anywhere}
.sheet-head .studio-icon{flex:none}
.sheet-head h2{margin:0; font-family:var(--ui); font-size:19px}
.sheet-row{display:flex; align-items:center; justify-content:space-between; gap:var(--s4); flex-wrap:wrap}
.sheet-label{font-family:var(--ui); font-weight:700; font-size:15px; display:grid; gap:2px}
.sheet-hint{font-family:var(--ui); font-weight:600; font-size:12px; color:var(--text-3); max-width:34ch}

/* Canvas C: Today and the Domains index (Phase 3). */
#today-app,
#domains-app{display:grid; gap:var(--s5)}
/* Today renders inside .today-wrap, which carries the frame and the side
   gutter. The Domains index has no such wrapper - it was added as a bare
   container in Phase 3 - so it took the gutter from nothing and sat flush
   against both edges on a phone. Same frame, stated here rather than by
   wrapping the markup, because the container is what the renderer targets. */
#view-domains{max-width:var(--content-max); margin:0 auto; padding:48px 22px 90px}
/* Stated after the rule above, not up with the other narrow overrides: both
   selectors are a bare id, so at equal specificity the later declaration is
   the one that wins. */
@media (max-width:640px){ #view-domains{padding:34px 14px 36px} }
#today-app h1{margin:0}
/* The grid's gap already spaces the note. It is drawn as a plain card rather
   than the tinted, accent-ringed box it is on Results: here it sits directly
   under "Do this next", and two identical accent boxes would make an aside read
   as a second instruction. */
#today-app .save-prompt{border-radius:var(--r-lg); margin:0; background:var(--surface); border-color:var(--line)}
.rowset{display:grid; gap:var(--s3)}
.today-readiness .verdict-word{margin:var(--s1) 0 var(--s3); font-size:2rem; letter-spacing:-.02em}
.today-readiness .meter{max-width:420px}
.today-readiness .evidence{margin-top:var(--s3)}
.today-readiness .lede{max-width:62ch}
.today-next{background:var(--accent-soft); border-color:var(--accent)}
.today-next h2{margin:var(--s1) 0 var(--s2)}
.today-domains .lede{margin-bottom:var(--s3)}
.shelf{display:grid; gap:var(--s3)}
.shelf h2{margin:0}
/* The classic Today's static furniture. The studio page supplies its own H1 -
   the certification's name - and replaces the overview toggle with always
   visible ranked rows, so leaving these would mean two <h1>s on one view and a
   duplicate domain list below it. */
/* A row is a control, not prose: an anchor row must not wear link underlines
   or the accent's link colour. */
a.row, a.row:hover{text-decoration:none; color:inherit}
a.row strong{text-decoration:none}
/* Inside a row the lede is the second line, not a continuation of the first. */
.row .lede{display:block; margin-top:2px}
/* A verdict built on thin evidence says so. The plan's rule is that not enough
   yet must never read as a confident verdict. */
.verdict-word .provisional{font-size:.9rem; vertical-align:middle; margin-left:var(--s2); font-weight:700}
.domain-tasks .task-list{list-style:none; margin:var(--s2) 0 0; padding:0; display:grid; gap:var(--s2)}
.domain-tasks .task-code{font-family:var(--mono); font-size:12px; color:var(--accent-2); margin-right:var(--s2)}
.domain-tracks{margin-top:var(--s4)}

/* The Domain page's track switcher: Read / Cards / Quiz as one row of three
   equal links. Shaped like the .seg control (a --surface-2 trough holding
   segments) but links, not toggles - each is a real path - so the current one
   is aria-current rather than aria-pressed. The dot is the row's own .dots,
   one circle wide; its empty ring is drawn from --text-3 here because --line
   is too faint against the trough. */
.track-seg{
  display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:2px;
  max-width:440px; padding:2px; background:var(--surface-2); border-radius:10px;
}
.track-seg-item{
  position:relative; display:flex; min-width:0; min-height:40px; border-radius:8px;
  color:var(--text-2); text-decoration:none;
  font-family:var(--ui); font-size:14px; font-weight:500;
}
.track-seg-item:hover{color:var(--text); text-decoration:none}
.track-seg-item[aria-current="page"]{
  background:var(--accent-soft); color:var(--accent-2); font-weight:600;
  box-shadow:inset 0 0 0 1px var(--accent);
}
.track-seg-in{display:flex; flex:1; align-items:center; justify-content:center; gap:var(--s2); min-width:0; padding:0 var(--s2)}
.track-seg .dots{color:var(--accent)}
.track-seg .dots b{box-shadow:inset 0 0 0 1.5px var(--text-3)}
.track-seg .dots b.half{background:linear-gradient(90deg, currentColor 50%, transparent 50%)}
.track-seg .dots b.full{background:currentColor; box-shadow:none}
/* Tooltip: the detail sentence from data-tip, below the segment. Middle one is
   centred; the first and last anchor to their own outer edge so a 320px
   viewport never clips them. Pseudo-element, so it is not read twice (the
   aria-label is the accessible name). :focus covers a tap on touch. */
.track-seg-item::after{
  content:attr(data-tip); position:absolute; top:calc(100% + var(--s2)); left:50%;
  transform:translateX(-50%); z-index:20; box-sizing:border-box;
  width:max-content; max-width:min(260px, 90vw); padding:var(--s2) var(--s3);
  background:var(--text); color:var(--ground); border-radius:var(--r-xs);
  font:500 13px/1.4 var(--ui); text-align:left; white-space:normal;
  box-shadow:var(--shadow-lift); pointer-events:none;
  opacity:0; visibility:hidden; transition:opacity .1s ease, visibility 0s linear .1s;
}
.track-seg-item:first-child::after{left:0; transform:none}
.track-seg-item:last-child::after{left:auto; right:0; transform:none}
.track-seg-item:hover::after,
.track-seg-item:focus::after,
.track-seg-item:focus-visible::after{opacity:1; visibility:visible; transition-delay:0s}
@media (max-width:640px){
  .track-seg{max-width:none}
  .track-seg-item{min-height:44px}
}

/* ===========================================================================
 * Canvas C: the material reader (Phase 4). readerTrapsHTML/readerContentsNav
 * in app.js prepend the traps block and the contents nav to #study-doc, ahead
 * of the same validated section clone the classic shell has always rendered
 * there - so this only adds reader chrome around that clone. The clone's own
 * content (headings, paragraphs, lists, tables) keeps using the .doc/.tblwrap
 * rules above unscoped: one source of truth for both shells, per the ADR 0002
 * allowlist those rules already respect.
 * ======================================================================== */

/* The sticky bar's real height, not the prototype's hardcoded 80px. Every
   .studio-bar-in child sets min-height:44px, so 44px is the tallest thing in
   the bar regardless of which children are hidden at a given width; add the
   bar's own block padding and its 1px bottom border. This has to be declared
   here, where the literal padding numbers are known, rather than composed
   with var(--s3)/var(--s5) at :root - a var() inside a custom property
   resolves at the property's OWN declaration site, not the token's, so
   routing it through :root would just as silently yield nothing as the --k
   token indirection the .blob comment above already warns about. Recompute
   by hand if .studio-bar-in's padding ever changes.

   Each carries 3px over the bar's own height. Landing a heading exactly flush
   with the bar leaves it looking clipped, and the bar's height is fractional
   at some widths - flush measured 60.5px against a 61px bar on the phone
   viewport, so the heading sat half a pixel UNDER the thing it was meant to
   clear. The slack buys the rounding and the breathing room at once. */
:root{ --reader-offset:72px; }
@media (max-width:640px){
  /* .studio-bar-in drops to var(--s2) var(--s3) padding here (8px), so the
     bar is 44px + 8px + 8px + 1px border = 61px, not 69px. */
  :root{ --reader-offset:64px; }
}

#study-doc{display:grid; gap:var(--s5); grid-template-columns:minmax(0,1fr)}
/* A grid item defaults to min-width:auto, so a wide table inside a section
   pushes the track past the viewport instead of scrolling inside its own
   .tblwrap - 375px of phone showing 573px of page. The same idiom the top
   bar already uses on its own children. */
#study-doc > *{min-width:0}
#study-doc > section{
  background:var(--ground); border:1px solid var(--line); border-radius:var(--r-lg);
  padding:var(--s5) var(--s6); scroll-margin-top:var(--reader-offset);
}
/* No catalogue nests a <section> today, but a card rendered inside a card
   would be a silent regression the moment one does, so this is defensive
   rather than dead weight. */
#study-doc section section{
  border:0; padding:0; background:none; border-radius:0;
}
/* The scroll target a TOC click and a tick both use - the app-created
   .sub-head wrapper, never h3.sub itself, because an id or tabindex on parsed
   content is exactly what the learn-markup allowlist exists to keep out. */
.sub-head{scroll-margin-top:var(--reader-offset)}

/* A header row shared by the traps block and the contents nav: a title on the
   left, a short evidence string flush right. */
.rows-head{
  display:flex; align-items:baseline; justify-content:space-between; gap:var(--s4);
  margin:0 0 var(--s3);
}

/* Traps: cloneNode(true) copies of this section's own .callout children,
   surfaced before the material so a learner who only skims still hits them.
   Absent entirely when a section flags none (most do, in both rooms) - see
   readerTrapsHTML's early return, which this stylesheet does not need to
   defend against with an empty-state rule. */
.traps{display:grid; gap:var(--s3)}
.trap{
  background:var(--k-read); color:var(--k-read-ink); border-radius:var(--r-md);
  padding:var(--s4) var(--s5); margin:0;
}
.trap .eyebrow{color:var(--k-read-ink); opacity:.8}
.trap p{color:var(--k-read-ink); margin:var(--s1) 0 0}
.trap p:first-child{margin-top:0}
/* The emphasis inside a trap has to take the trap's own ink. `.doc strong`
   sets --text for the document, and on the sand fill that lands at 2.12:1 in
   dark mode - legible in light, invisible in dark, and a contrast failure the
   trap's own colour pair had already been chosen to avoid. */
.trap strong,
.trap em{color:var(--k-read-ink)}

/* Contents: a card, same shape as .card, holding a tick list. The "N of M
   read" count and each tick come from materialTrack/readSubsectionCount via
   paintReaderContents - no second counter lives here. */
.reader-toc{
  background:var(--ground); border:1px solid var(--line); border-radius:var(--r-lg);
  box-shadow:var(--shadow); padding:var(--s5);
}
.toc{margin:var(--s3) 0 0; padding:0; list-style:none; display:grid; gap:2px}
/* A <button> standing in for the prototype's <a> - the app routes on the
   hash, so a real link here would fire the router as well as scroll. Reset
   what a button gives for free, then rebuild the same full-width flex row so
   it reads and looks the same; :focus-visible falls through to the global
   rule at the top of this file, which already draws a ring outside the
   button's own box. */
.toc-link{
  display:flex; align-items:center; gap:var(--s3); width:100%; min-height:44px;
  border:0; background:none; padding:9px var(--s3); border-radius:var(--r-sm);
  font-family:var(--ui); font-size:15px; font-weight:600; color:var(--text);
  text-align:left; cursor:pointer;
}
.toc-link:hover{background:var(--surface-2)}
.toc .tick{
  width:18px; height:18px; border-radius:50%; border:2px solid var(--line); flex:0 0 auto;
}
.toc li.done .tick{background:var(--ok); border-color:var(--ok)}
.toc li.done .toc-link{color:var(--text-2)}

/* ===========================================================================
 * Canvas C player (Phase 5) - STRUCTURE.
 *
 * These rules decide what exists on screen, and nothing else. #studio-mocks
 * is shown and the rest of the classic start screen's own controls are
 * hidden. Appearance is in the block after this one.
 * ======================================================================== */

/* The two Mocks pages share #screen-start, so exactly one of them renders. */
#screen-start > :not(#studio-mocks){display:none}

/* Drill and exam show different chrome in the same bar. A drill is linear and
   untimed unless it is also a timed Domain Quiz, so the clock, the counter and
   the navigator have nothing to say for untimed drills; a mock keeps all three,
   with the navigator inside its <details> as it is today, because the real exam
   has one and people practise using it. */
body.player-drill:not(.player-timed) .exambar .timer,
body.player-drill .exambar .count:not(.formtag),
body.player-drill .exambar .prog,
body.player-drill .exambar .spacer,
body.player-drill .nav{display:none}
body.player-exam #pbar{display:none}

/* The player is the whole screen while it is up: the tab bar and the masthead
   would only offer a way to walk out of a running clock, and the bottom nav
   already hides in exam-active. */
body.exam-active .studio-bar{display:none}

/* ===========================================================================
 * Canvas C player (Phase 5) - APPEARANCE.
 *
 * Everything below applies unconditionally (plus body.player-drill /
 * body.player-exam, which only ever exist alongside it), reusing the
 * Phase 1 token set and card/row/blob system rather than inventing a second
 * one. These rules override the earlier unscoped .exambar/.qcard/.opt/etc
 * base rules by source order.
 * ======================================================================== */

/* ---------- the exam bar : full-bleed, flush at top, sticky ----------
   The bar hides its own masthead in exam-active (see the STRUCTURE rule
   above this block), so the bar has to sit flush at 0 instead - and BE the
   top of the page. */
.exambar{
  position:sticky; top:0; z-index:60; margin:0; padding:0;
  background:var(--surface); border-bottom:1px solid var(--line);
}
.exambar-in{
  max-width:none; height:auto; min-height:0; gap:var(--s3);
  padding:var(--s3) var(--s5); flex-wrap:nowrap;
}
.exambar-in > *{min-width:0; flex:0 0 auto}
/* Every direct child of the bar keeps a 44px tap target regardless of its own
   padding, matching the .studio-bar-in idiom this bar borrows its height math
   from - see --reader-offset's own comment for why the height has to be
   worked out by hand rather than composed from a token at :root. */
.exambar-in > *{min-height:44px}
.exambar .iconbtn{
  display:inline-flex; align-items:center; gap:var(--s2); min-height:44px;
  padding:0 var(--s3); border:1px solid var(--line); border-radius:var(--r-sm);
  background:var(--surface); color:var(--text-2); font-family:var(--ui);
  font-weight:700; font-size:14px; cursor:pointer; white-space:nowrap;
}
.exambar .iconbtn:hover{border-color:var(--accent); color:var(--accent-2)}
.exambar #player-flag[aria-pressed="true"],
.exambar #player-flag.on{
  background:var(--flag-soft); border-color:var(--flag); color:var(--flag);
}
.exambar .timer{flex:0 0 auto; gap:var(--s2)}
.exambar .timer .t{font-size:15px}
.exambar .count{font-size:13px}
/* The drill progress pips - .pbar i - live only in drill mode; #pbar is
   display:none in exam mode by the STRUCTURE block above. */
.exambar .pbar{
  flex:1 1 auto; min-width:60px; max-width:380px; display:flex; gap:4px;
}
.exambar .pbar i{
  height:8px; flex:1; min-width:6px; background:var(--surface-2); border-radius:var(--pill);
}
.exambar .pbar i.now{background:var(--accent)}
.exambar .pbar i.done{background:var(--ok)}
.exambar .pbar i.wrong{background:var(--bad)}
.exambar .prog{
  flex:1 1 auto; min-width:40px; height:4px; background:var(--surface-2); border-radius:var(--pill);
}
.exambar .spacer{flex:1 1 auto; min-width:0}
.exambar .btn.ghost.spacer{
  flex:0 0 auto; margin-left:auto; min-height:44px; padding:0 var(--s4);
  font-size:14px; border-radius:var(--pill);
}
/* Below 700px the bar sheds the form context first - it is the one item on
   the bar that names WHICH form this is rather than WHERE the learner is in
   it, and "Question 5 of 65" (structural, always visible) already answers
   the question the bar exists to answer. Everything else that remains
   (Leave, timer, count, flag, submit) is the minimum a learner needs while a
   clock is running and stays put.

   It was 480px, which left a gap: a drill's tag ("Targeted practice - Focused
   drill") plus its progress pips need ~650px, so from 481 to 559px the bar was
   wider than the page and the pips were painted over the Flag button up to
   ~650px. A mock overflowed by a few pixels until 508px. Nothing tested those
   widths. */
@media (max-width:700px){
  .exambar .formtag{display:none}
}
@media (max-width:480px){
  .exambar-in{gap:6px; padding:var(--s2) 10px}
  .exambar .iconbtn{padding:0 8px; font-size:12.5px; gap:6px}
  /* The ring is a nice-to-have restatement of the same number the digits
     already carry - "90:00" is the structural fact, the ring is decoration on
     top of it - so it is what gives way when five controls will not fit
     beside a certification-length exam bar on a phone. */
  .exambar .timer svg{display:none}
  .exambar .timer{gap:0}
  .exambar .timer .t{font-size:14px}
  .exambar .count{font-size:12px}
  .exambar .btn.ghost.spacer{padding:0 10px; font-size:12.5px}
}
@media (max-width:340px){
  /* Every icon here is already paired with a word - Leave, Flag - so the
     glyph itself, not the label, is what gives way once five controls will
     not fit beside a running clock at 320px. Text-only still names the
     control; icon-only would not. */
  .exambar .iconbtn .ico{display:none}
  .exambar-in{gap:4px; padding:var(--s2) 8px}
}

/* The bar's real height, computed the same way --reader-offset is: every
   direct child holds min-height:44px, so 44px is the tallest thing in the
   bar regardless of which optional children are hidden at a given width; add
   the bar's own block padding and its 1px border, then carry ~3px of slack
   so the qcard's first line is never flush with (or under) the sticky bar -
   this exact defect shipped twice before on the reader and is not to repeat
   here. Desktop: 44 + 12+12 (--s3 padding) + 1 = 69, +3 slack = 72. Below
   480px the padding drops to --s2 (8px): 44 + 8+8 + 1 = 61, +3 = 64. */
:root{ --player-offset:72px; }
@media (max-width:480px){
  :root{ --player-offset:64px; }
}
.qlayout{padding-top:var(--s6)}
#qcard{scroll-margin-top:var(--player-offset)}

/* ---------- the question card : the prototype's .qbox ---------- */
.qlayout{
  max-width:720px; margin:0 auto; display:block; gap:0; padding-left:var(--s5); padding-right:var(--s5);
}
#qcard{
  background:var(--ground); border:1px solid var(--line); border-radius:var(--r-lg);
  box-shadow:var(--shadow); padding:var(--s6) var(--s5); min-width:0;
}
.qmeta{margin-bottom:var(--s4)}
.qmeta .chip{
  font-family:var(--ui); font-size:12px; letter-spacing:0; text-transform:none;
  padding:5px 11px; border-radius:var(--pill); font-weight:800;
}
#q-heading{margin-bottom:var(--s3)}
.stem{font-size:19px; line-height:1.62; font-weight:500; font-family:var(--body)}
.instr{
  font-family:var(--ui); text-transform:none; letter-spacing:0; font-size:13px; font-weight:700;
}
.scenario{
  border-left:3px solid var(--k-read); border-radius:0 var(--r-sm) var(--r-sm) 0;
  background:var(--surface); padding:var(--s3) var(--s4); margin:0 0 var(--s4);
}

.opts{gap:var(--s3); margin-top:var(--s5)}
.opt{
  border-width:2px; border-radius:var(--r-md); padding:var(--s4); gap:var(--s3);
}
.opt:hover{border-color:var(--k-drill); background:var(--surface)}
.opt .key{
  width:28px; height:28px; border-radius:50%; border:0; background:var(--surface-2); font-family:var(--ui);
}
.opt.sel .key{background:var(--accent); color:var(--brand-text)}
.opt.correct .key{background:var(--ok); color:var(--ok-text)}
.opt.wrong .key{background:var(--bad); color:var(--bad-text)}
.selection-status{font-family:var(--ui)}

/* ordering + matching : the same card treatment as .opt, so a drill reads as
   one family of controls rather than three different widgets bolted on. */
.orditem,
.matchrow{
  border-radius:var(--r-md); padding:var(--s3) var(--s4);
}
.orditem .pos{border-radius:50%}
.ordbtns button{border-radius:50%}
.matchrow select{border-radius:var(--r-sm)}
.truth{font-family:var(--ui); font-weight:700}

/* ---------- the explanation panel : B's plain-English card ---------- */
.explain{
  margin-top:var(--s5); border-top:0; padding:var(--s5); border-radius:var(--r-lg);
  background:var(--k-drill); color:var(--k-drill-ink);
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .explain{background:var(--surface-2); color:var(--text)}
}
:root[data-theme="dark"] .explain{background:var(--surface-2); color:var(--text)}
.verdict{font-family:var(--ui); font-size:16px}
.verdict.good{color:var(--ok)}
.verdict.bad{color:var(--bad)}
.explain .why{background:var(--ground); color:var(--text)}
.optnotes{margin-top:var(--s4)}
.errtax{margin-top:var(--s4); padding-top:var(--s4); border-top-color:var(--line)}
.errtax-btn{border-radius:var(--pill)}
/* The remedy line a miss earns once a reason is named - new in Phase 5, so
   there is no classic equivalent to keep parity with. */
.errtax-remedy{
  margin:var(--s3) 0 0; padding:var(--s3) var(--s4); border-radius:var(--r-sm);
  background:var(--ground); color:var(--text); font-family:var(--ui); font-size:14px; line-height:1.5;
}
.errtax-remedy strong{color:var(--text)}

/* ---------- the player foot : buttons + the keyboard hint ----------
   --text-3 clears 4.5:1 on both --ground and --surface (light and dark - see
   the palette-contrast test), but the hint is read at the end of every
   question, on a phone, so it gets the more legible --text-2 rather than
   riding on the token that merely clears the floor. */
.player-foot{
  /* No component in the Phase 1 set lays this out - .player-foot is new in
     Phase 5 - so it declares its own row. Without it the buttons and the hint
     flow as inline content and the hint reads as though it starts inside the
     Next button. */
  display:flex; align-items:center; flex-wrap:wrap; gap:var(--s3); margin-top:var(--s5);
}
.player-foot .hint{
  font-family:var(--mono); font-size:12.5px; color:var(--text-2); flex:1 1 220px;
}
@media (max-width:480px){
  .player-foot{flex-direction:column; align-items:stretch}
  .player-foot .hint{order:99; text-align:center}
}

/* ---------- the navigator, in exam mode ---------- */
.nav{top:calc(var(--player-offset) + var(--s4))}
.nav-details{
  background:var(--ground); border:1px solid var(--line); border-radius:var(--r-lg);
  box-shadow:var(--shadow); padding:var(--s4);
}
.nav-summary{border-bottom:0; padding:0 0 var(--s2)}
.nav-count{font-family:var(--ui); font-weight:700}
.navgrid{margin-top:var(--s2); gap:var(--s2)}
.navcell{border-radius:var(--r-sm); font-family:var(--ui)}
.navcell.cur{box-shadow:0 0 0 2px var(--accent-soft)}
.navlegend{font-family:var(--ui); margin-top:var(--s3)}
.nav-details > .btn{width:100%; margin-top:var(--s3); border-radius:var(--pill)}

/* ---------- a grid item's default min-width:auto, again -----------------
   The same pitfall the reader's own comment already names for a wide table:
   a `display:grid` track with no explicit sizing takes its MINIMUM from the
   widest thing inside it, and a row's own badge/chip is deliberately
   `white-space:nowrap` (it is a short status word, never meant to wrap). At
   320px that pushed the Mocks row - and with it the whole document, because
   a fixed-position bottom nav's containing block grows right along with an
   overflowing page - past the viewport. `.row`'s own flex children already
   know how to shrink (`.row .grow` carries `min-width:0`); the grid track
   just has to be told it is allowed to ask them to. */
.rowset{grid-template-columns:minmax(0,1fr)}
.rowset > *{min-width:0}

/* ---------- the Mocks page and its one-question start screen ---------- */
#studio-mocks .lede{margin-top:var(--s2)}
#studio-mocks .rowset{margin-top:var(--s5)}
#screen-mock-start .optbox{
  border-radius:var(--r-lg); box-shadow:var(--shadow); background:var(--ground); margin-top:var(--s5);
}
#screen-mock-start .optrow{padding:var(--s4) var(--s5)}
#screen-mock-start .lbl{font-family:var(--ui)}
#screen-mock-start #studio-seg-timer button,
#screen-mock-start .startbar .btn{min-height:44px}

/* ---------- results ---------- */
#screen-results{max-width:860px; margin:0 auto; padding-top:0; display:grid; gap:var(--s6)}
.result-head{
  display:flex; gap:var(--s6); align-items:center; flex-wrap:wrap; padding:var(--s6) var(--s5);
}
.result-head .dial{position:relative; width:150px; height:150px; flex:0 0 auto}
.result-head .mid{
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center; text-align:center;
}
.result-head .mid b{font-family:var(--ui); font-size:26px; letter-spacing:-.02em; display:block}
.result-head .mid span{
  font-family:var(--ui); font-size:12px; font-weight:700; color:var(--text-3); text-transform:uppercase; letter-spacing:.06em;
}
.result-say{flex:1 1 300px; min-width:0}
.result-say h1{margin-top:var(--s1); font-size:var(--fs-h2, 26px); line-height:1.2}
.result-say .lede{max-width:60ch}
.result-say .player-foot{margin-top:var(--s4)}

.result-domains .rowset{margin-top:var(--s3)}

.raw-numbers{padding:var(--s5)}
.raw-numbers summary{cursor:pointer; font-family:var(--ui); font-weight:700; font-size:15px}
.raw-body{margin-top:var(--s3)}
.line{
  display:flex; justify-content:space-between; gap:var(--s4); padding:9px 0; font-family:var(--ui);
  font-size:14px; color:var(--text-2); border-top:1px solid var(--line);
}
.line:first-child{border-top:0}
.line strong{color:var(--text); font-weight:700}

.result-review .rows-head{margin-bottom:var(--s3)}
/* The review filters are quiet toggles, not primary buttons - the missed
   list is already open by default (renderStudioResults sets reviewFilter),
   so these exist to widen the view, not to choose it for the first time. */
.seg.quiet{
  background:none; padding:0; gap:var(--s2); flex-wrap:wrap;
}
.seg.quiet button{
  min-height:36px; padding:6px 14px; border:1px solid var(--line); border-radius:var(--pill);
  background:var(--ground); color:var(--text-2); font-family:var(--ui); font-size:13px; font-weight:700;
}
.seg.quiet button[aria-pressed="true"]{
  background:var(--accent-soft); border-color:var(--accent); color:var(--accent-2); box-shadow:none;
}
.rev{margin-top:var(--s4)}
.revq{border-radius:var(--r-lg)}
@media (max-width:640px){
  .result-head{padding:var(--s5)}
}

/* ---------- the quiet back control, outside the exam bar ----------
   `.iconbtn` is the player's own control class and had styling only inside
   .exambar, so the same button on the results and mock-start screens rendered
   as a bare user-agent button - stretched to the full grid column and, in dark
   mode, a white bar with black text. The bar's own rules are more specific and
   still win there. */
.iconbtn{
  display:inline-flex; align-items:center; gap:8px; justify-self:start; align-self:start;
  width:auto; min-height:44px; padding:0 var(--s4);
  border:1px solid var(--line); border-radius:var(--pill);
  background:var(--ground); color:var(--text-2);
  font-family:var(--ui); font-size:13.5px; font-weight:600; cursor:pointer;
}
.iconbtn:hover{background:var(--surface-2); color:var(--text)}

/* ===========================================================================
 * Canvas C Recall, Progress and backup (Phase 6) - STRUCTURE.
 * What exists on screen. Appearance is in the block after this one.
 * ======================================================================== */

/* The Recall page states itself from #recall-app. */

/* .player-foot declares display:flex, which out-specifies the UA's [hidden]
   rule - so the judgement pills stayed on screen before the card was turned
   over, offering a grade for an answer nobody had seen. */
.player-foot[hidden]{display:none}

/* The account panel started life as a dropdown - position:absolute, a fixed
   244px, its own border and shadow. Adopted into the backup sheet it stayed
   out of flow, so the sheet body measured 0px high and the sheet showed its
   title and nothing else. It has been that way since the sheet was
   introduced; the panel becomes an ordinary block inside it. */
#backup-sheet .account-panel{
  position:static; width:auto; padding:0; border:0; box-shadow:none; background:none;
}

#local-backup-fold > summary > h2{display:inline}

/* ===========================================================================
 * Canvas C Recall, Progress and backup (Phase 6) - APPEARANCE.
 *
 * Everything below applies unconditionally, reusing the
 * Phase 1 token/component set (.card, .row, .rowset, .blob, .badge, .meter,
 * .seg.quiet, .eyebrow, .evidence, .lede, .rows-head, .player-foot, .sheet)
 * and the Phase 5 player block's tone rather than inventing a second one.
 * These rules override the earlier unscoped .recall-*/.m-sec/.account-panel
 * base rules by source order.
 * ======================================================================== */

/* ---------- Recall : top line + queue pips ---------- */
#recall-app{max-width:620px; margin:0 auto}
.recall-top{
  display:flex; align-items:baseline; justify-content:space-between; gap:var(--s4);
  flex-wrap:wrap; margin-bottom:var(--s3);
}
.recall-top h1{margin:var(--s1) 0 0}
/* #cardbar reuses the exam bar's own .pbar look (Phase 5) rather than a
   second progress-pip component; it just is not INSIDE .exambar here, so it
   needs its own (identical) rule rather than inheriting that one. */
#cardbar.pbar{display:flex; gap:4px; margin-bottom:var(--s5)}
#cardbar.pbar i{
  height:8px; flex:1 1 0; min-width:3px; background:var(--surface-2); border-radius:var(--pill);
}
#cardbar.pbar i.now{background:var(--accent)}
#cardbar.pbar i.done{background:var(--ok)}
/* Past CARDBAR_PIPS the bar is three proportional segments rather than one pip
   per card, because a per-card minimum width overflows a phone once a deck
   passes about fifty cards. The current segment keeps a fixed width so it stays
   visible against a long remainder. */
#cardbar.pbar.summed i{flex-grow:0}
#cardbar.pbar.summed i.now{flex:0 0 10px}

/* ---------- Recall : the card, a real <button> rebuilt as the prototype's
   oversized tappable .flash-card ---------- */
.flash-card{
  display:grid; place-items:center; width:100%; min-height:260px; text-align:center;
  padding:var(--s7) var(--s5); background:var(--ground); border:1px solid var(--line);
  border-radius:var(--r-lg); box-shadow:var(--shadow); cursor:pointer; font:inherit; color:inherit;
  transition:box-shadow .15s, border-color .15s;
}
.flash-card:hover{box-shadow:var(--shadow-lift); border-color:var(--k-cards)}
.flash-card:focus-visible{outline:2px solid var(--accent); outline-offset:3px}
.flash-card .eyebrow{display:block}
.flash-q{
  display:block; margin-top:var(--s3); font-family:var(--body); font-size:26px; font-weight:700; line-height:1.35;
}
/* A missed question's options, read-only, on the front. Spans only (the card
   is a <button>); centred like the stem, wrapping at any width and reading
   size, with no ch or px cap. */
.flash-opts{display:block; width:100%; margin-top:var(--s4); font-family:var(--body); font-size:16px; line-height:1.5; color:var(--text-2)}
.flash-opts-group{display:block}
.flash-opts-group + .flash-opts-group{margin-top:var(--s3); padding-top:var(--s3); border-top:1px solid var(--line)}
.flash-opt{display:block; margin-top:var(--s2); overflow-wrap:anywhere; min-width:0}
.flash-opt:first-child{margin-top:0}
.flash-opt-k{font-family:var(--mono); font-weight:700; color:var(--text-3); margin-right:.5em}
.flash-a{display:block; margin-top:var(--s4); width:100%}
.flash-a-text{
  display:block; font-family:var(--body); font-size:17px; line-height:1.6; color:var(--text-2);
}
.flash-cue{
  display:block; margin-top:var(--s3); font-family:var(--ui); font-size:13.5px; color:var(--text-3);
}
.flash-cue strong{color:var(--text-2)}
.flash-tap{
  display:block; margin-top:var(--s5); font-family:var(--ui); font-size:13px; font-weight:700; color:var(--text-3);
}
/* .flash-a and .flash-tap declare display:block, which out-specifies the UA's
   [hidden] rule just like .player-foot[hidden] above - so the answer and the
   "tap to reveal" hint both rendered at once instead of one replacing the
   other on flip. */
.flash-a[hidden],
.flash-tap[hidden]{display:none}

/* the two judgement pills, centred - "Show again later" stays the quiet
   .btn.ghost; "I knew it" is the affirmative action, so it earns the filled
   accent treatment .btn.primary carries elsewhere, without the markup
   growing a class this phase's contract does not give it. */
.flash-judge{justify-content:center; margin-top:var(--s5)}
.flash-judge .btn{border-radius:var(--pill)}
.flash-judge .btn:not(.ghost){
  background:var(--accent); border-color:var(--accent); color:var(--brand-text);
}
.flash-judge .btn:not(.ghost):hover{filter:brightness(1.07)}
.flash-judge .btn:not(.ghost) .evidence{color:inherit}
.flash-judge .btn .evidence{margin-left:var(--s2); font-weight:700}
.recall-live > .hint{
  display:block; margin-top:var(--s4); text-align:center; font-family:var(--mono);
  font-size:12.5px; color:var(--text-2);
}
@media (max-width:480px){
  .flash-judge{flex-direction:column; align-items:stretch}
  .flash-judge .btn{justify-content:center}
}

/* ---------- Recall : the queue-cleared state, and nothing-in-set ---------- */
.recall-done{max-width:620px; margin:0 auto; display:grid; gap:var(--s5)}
.recall-done-card{padding:var(--s7) var(--s5); text-align:center}
.recall-done-card .blob{margin:0 auto var(--s4)}
.recall-done-card h1{margin:0}
.recall-done-card .lede{max-width:44ch; margin:var(--s2) auto 0}
.recall-done-card .player-foot{justify-content:center; margin-top:var(--s5)}
.recall-done-card .player-foot .btn{border-radius:var(--pill)}
.recall-done-card .player-foot .btn:not(.ghost){
  background:var(--accent); border-color:var(--accent); color:var(--brand-text);
}
.recall-done .save-prompt{border-radius:var(--r-lg); margin:0}

/* ---------- Recall : the one disclosure - a quiet link when closed, never a
   control panel. "Choose a different set" plus its evidence line is the
   whole closed affordance; opening it reveals the domain focus and the five
   filter chips, which borrow .seg.quiet as-is. ---------- */
.recall-sets{max-width:620px; margin:var(--s6) auto 0}
.recall-sets summary{
  display:flex; align-items:baseline; gap:var(--s2); flex-wrap:wrap; cursor:pointer;
  list-style:none; font-family:var(--ui); font-weight:700; font-size:14px; color:var(--accent-2);
  padding:var(--s2) 0;
}
.recall-sets summary::-webkit-details-marker{display:none}
.recall-sets summary::before{
  content:""; width:0; height:0; flex:0 0 auto; margin-top:5px;
  border-left:5px solid currentColor; border-top:4px solid transparent; border-bottom:4px solid transparent;
  transition:transform .15s;
}
.recall-sets[open] summary::before{transform:rotate(90deg)}
.recall-sets summary:hover{text-decoration:underline}
.recall-sets summary .evidence{color:var(--text-3); font-weight:600}
.recall-sets-body{margin-top:var(--s4); display:grid; gap:var(--s4)}
.recall-sets .focus-row{display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap}
.recall-sets .focus-row .eyebrow{margin:0}
/* One wrapping row of pills; a 44px target, and the count is small text so
   five of them stay about a third of the height the old .row cards took. */
.recall-sets .recall-chips button{min-height:44px; max-width:100%}
.recall-sets .recall-chips .cnt{font-weight:600; font-size:12px; color:var(--text-3)}
.recall-sets .recall-chips button[aria-pressed="true"] .cnt{color:inherit}
.recall-sets > .recall-sets-body > .evidence{display:block; margin-top:var(--s1)}
@media (prefers-reduced-motion:reduce){
  .recall-sets summary::before{transition:none}
}

/* ---------- Progress : the readiness verdict, then evidence cards ---------- */
#mastery-app{max-width:760px; margin:0 auto; display:grid; gap:var(--s6)}
#mastery-app > .lede{max-width:60ch}
.progress-verdict{display:grid; gap:0}
.progress-verdict .verdict-word{
  margin:var(--s1) 0 var(--s2); font-size:2rem; letter-spacing:-.02em;
}
.progress-verdict .meter{max-width:420px; margin-top:var(--s1)}
.progress-verdict .evidence{display:block; margin-top:var(--s3)}
.progress-verdict .progress-stats{
  margin-top:var(--s4); padding-top:var(--s4); border-top:1px solid var(--line);
}
.progress-domains .rowset{margin-top:var(--s2)}
.progress-trend .trend-list,
.progress-weak .spot-list{border-radius:var(--r-md)}
.progress-trend .trend-row,
.progress-weak .spot-row{font-family:var(--ui)}
.progress-weak .startbar{margin-top:var(--s4)}
.progress-weak .startbar .btn{border-radius:var(--pill)}

/* ---------- Backup sheet : the adopted account panel ---------- */
#backup-sheet{width:min(480px, calc(100vw - 32px))}
#backup-sheet .account-panel{display:grid; gap:0}
#backup-sheet .account-backup-note,
#backup-sheet .account-fineprint{font-size:13.5px; line-height:1.55}

/* Closed, the fold reads as a summary line with a state on the right, not a
   control - the studio backup sheet collapses it once an account is saving
   (renderAccountMenu decides open/closed; this only styles either state). */
#local-backup-fold{
  border:1px solid var(--line); border-radius:var(--r-lg); padding:var(--s4) var(--s5); background:var(--ground);
}
/* Wraps rather than shrinks: with the reading size raised the state does not
   fit beside the heading, and it drops under it instead of pushing the sheet
   wider than the window. space-between puts a lone line back at the start. */
#local-backup-fold > summary{
  display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:var(--s1) var(--s3);
  min-height:44px; cursor:pointer; list-style:none; font-family:var(--ui);
}
#local-backup-fold > summary::-webkit-details-marker{display:none}
#local-backup-fold > summary h2{margin:0; font-size:15px}
#local-backup-fold .account-fold-state{
  font-family:var(--ui); font-size:13px; font-weight:600; color:var(--text-3); min-width:0; overflow-wrap:anywhere;
}
#local-backup-fold[open] > summary{margin-bottom:var(--s3)}
#local-backup-fold .account-backup-note{margin-top:0}

#backup-sheet #account-sync{
  margin-top:var(--s5); padding-top:var(--s5); border-top:1px solid var(--line);
}
#backup-sheet #account-sync h2{font-size:15px; margin:0 0 var(--s2)}

/* The account actions/sync buttons ship at 38px (.account-panel .btn) for
   the classic dropdown, which stays untouched for its own tests; the studio
   sheet is this phase's own surface and gets the 44px floor the guidelines
   set for a touch destination. */
#backup-sheet .account-actions{gap:var(--s3); margin-bottom:var(--s4)}
#backup-sheet .account-panel .btn{
  min-height:44px; border-radius:var(--pill); font-size:14px; padding:0 var(--s4);
}
/* .btn.ghost (0,3,0) otherwise beats .account-danger (0,2,0) regardless of
   source order, so the reset button's classic parent rule wins and the
   danger colour never shows - not a studio regression, but a gap this
   contract explicitly asks to close for the sheet. Scoped to #backup-sheet so
   the classic dropdown, and its own tests, are untouched. */
#backup-sheet .account-panel .btn.account-danger{color:var(--bad)}
#backup-sheet .account-panel .btn.account-danger:hover:not(:disabled){
  background:var(--bad-soft);
}
#backup-sheet .account-state{font-size:14px; margin-bottom:var(--s3)}
#backup-sheet .account-status{
  border-radius:var(--r-sm); padding:var(--s3) var(--s4); background:var(--surface); margin-bottom:var(--s3);
}
/* This is a live region: it exists from first paint and is empty until an
   export, import or reset has something to say. Padded and filled, an empty
   one painted as a blank grey bar under "Reset study data" that looked like a
   component that had failed to load. It stays in the DOM - removing it would
   break the announcement - and simply takes no space until it has text. */
#backup-sheet .account-status:empty{display:none}
#backup-sheet .account-status.is-warn{background:var(--bad-soft)}
#backup-sheet .account-fineprint{margin-top:var(--s4)}
/* Leaves room for the real 300x65 Turnstile widget renderTurnstileGate()
   creates - this reserves the space, it does not create the widget. */
#backup-sheet .account-turnstile{min-height:65px}

/* @feedback-widget:begin */
/* The feedback pill and dialog (js/feedback/widget.js).
   This block is shared with the apex: landing/public/styles.css carries the same
   text between the same two markers, and scripts/verify-shared-assets.mjs
   compares them. Edit it in both or in neither. It therefore names only tokens
   both sheets define - --ground --surface --line --text --text-2 --text-3
   --accent --brand-text --focus --shadow-lift --s1..--s5 --r-sm --r-lg --pill
   --ui --bad --bad-soft - and sets no colour, size or display on anything it
   does not own.
   A <dialog> is never given a display value here: `dialog:not([open])` is what
   keeps a closed one out of the page, and any author display beats it. Layout
   lives on the inner .fb-in wrapper. */
.fb-fab{
  position:fixed; right:16px; bottom:16px; z-index:45;
  display:inline-flex; align-items:center; justify-content:center; gap:var(--s2);
  min-height:44px; padding:0 var(--s4); border:0; border-radius:var(--pill);
  background:var(--accent); color:var(--brand-text);
  font-family:var(--ui); font-size:14px; font-weight:700; line-height:1; cursor:pointer;
  box-shadow:var(--shadow-lift);
  transition:filter .15s, transform .1s;
}
.fb-fab:hover{filter:brightness(1.07)}
.fb-fab:active{transform:translateY(1px)}
.fb-fab:focus-visible{outline:3px solid var(--focus); outline-offset:3px; border-radius:var(--pill)}
.fb-fab .fb-ico{flex:none}
/* Up to 960px the bottom bar is on screen (57px: a 56px button and its 1px top
   border), so the pill sits above it, plus the home-indicator inset the bar
   pads itself with. It is a shell element, not reading copy, so the reading
   scale skips it (applyReadingScale). Scoped to a page that has the bar: the
   apex has none, and raising the pill there would float it over nothing. (A
   room in the exam player hides both the bar and the pill.) */
@media (max-width:960px){
  body:has(.studio-bottom) .fb-fab{bottom:calc(57px + env(safe-area-inset-bottom) + 12px)}
}
/* A question in progress must not compete with a feedback button. */
body.exam-active .fb-fab{display:none}
@media print{
  .fb-fab{display:none}
}
@media (prefers-reduced-motion:reduce){
  .fb-fab{transition:none}
}

.fb-dialog{
  border:0; border-radius:var(--r-lg); padding:0;
  width:min(480px, calc(100vw - 32px));
  max-height:calc(100vh - 32px); max-height:calc(100dvh - 32px);
  overflow:auto;
  background:var(--ground); color:var(--text); box-shadow:var(--shadow-lift);
  font-family:var(--ui);
}
.fb-dialog::backdrop{background:rgba(12,18,23,.45)}
.fb-dialog [hidden]{display:none}
.fb-in{padding:var(--s5)}
.fb-head{display:flex; align-items:center; justify-content:space-between; gap:var(--s3)}
.fb-head h2{margin:0; font-family:var(--ui); font-size:19px; line-height:1.2}
.fb-form{display:grid; gap:var(--s4); margin-top:var(--s4)}
.fb-faq{margin:0; font-size:14px}
.fb-faq a{color:var(--text-2)}
.fb-field{display:grid; gap:var(--s1); min-width:0}
.fb-field label{font-size:14px; font-weight:700}
.fb-dialog select,
.fb-dialog input[type="text"],
.fb-dialog input[type="email"],
.fb-dialog textarea{
  width:100%; min-width:0; min-height:44px; padding:var(--s2) var(--s3);
  border:1px solid var(--line); border-radius:var(--r-sm);
  background:var(--surface); color:var(--text);
  /* 16px: anything smaller makes iOS Safari zoom the page when a field takes focus. */
  font-family:var(--ui); font-size:16px; font-weight:500; line-height:1.4;
}
.fb-dialog textarea{min-height:120px; resize:vertical}
.fb-dialog select:focus-visible,
.fb-dialog input:focus-visible,
.fb-dialog textarea:focus-visible{outline:2px solid var(--focus); outline-offset:1px}
.fb-dialog [aria-invalid="true"]{border-color:var(--bad)}
.fb-field-error{margin:0; font-size:13px; font-weight:600; color:var(--bad)}
.fb-count-row{display:flex; justify-content:flex-end}
.fb-count{font-size:12px; font-weight:600; color:var(--text-3); font-variant-numeric:tabular-nums}
.fb-count.is-near{color:var(--text)}
.fb-count.is-over{color:var(--bad); font-weight:800}
.fb-check{display:flex; align-items:center; gap:var(--s3); min-height:44px; font-weight:600; cursor:pointer}
.fb-check input{width:20px; height:20px; flex:none; margin:0; accent-color:var(--accent)}
.fb-note,
.fb-disclosure{margin:0; font-size:13px; line-height:1.5; color:var(--text-2)}
.fb-disclosure a{color:var(--text-2)}
/* Off-screen rather than display:none: a bot that reads layout to skip hidden
   fields would skip this one too, which is the only thing it is for. */
.fb-hp{position:absolute; left:-10000px; top:auto; width:1px; height:1px; overflow:hidden}
.fb-error{
  margin:0; padding:var(--s3) var(--s4); border:1px solid var(--bad); border-radius:var(--r-sm);
  background:var(--bad-soft); color:var(--text); font-size:14px; line-height:1.5;
}
.fb-error a{color:var(--text); font-weight:700}
.fb-actions{display:flex; gap:var(--s3); justify-content:flex-end; flex-wrap:wrap}
.fb-btn{
  min-height:44px; padding:0 var(--s4); border:1px solid var(--line); border-radius:var(--pill);
  background:var(--surface); color:var(--text);
  font-family:var(--ui); font-size:14px; font-weight:700; cursor:pointer;
}
.fb-btn:hover{border-color:var(--text-3)}
.fb-btn.is-primary{background:var(--accent); border-color:var(--accent); color:var(--brand-text)}
.fb-btn.is-primary:hover{filter:brightness(1.07)}
.fb-btn.is-busy{opacity:.6; cursor:progress}
.fb-btn:focus-visible{outline:3px solid var(--focus); outline-offset:2px; border-radius:var(--pill)}
/* Margin, not grid gap, so this live region can exist empty from the start
   (a region added with its text already in it is not announced) and still take
   no space until it has something to say. */
.fb-sent{margin-top:var(--s4); font-size:16px; font-weight:700}
.fb-sent:empty{margin-top:0}
.fb-sent + .fb-actions{margin-top:var(--s4)}
.fb-sr{position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0}
/* The quiet "Report a problem with this question" button under an explanation. */
.fb-quiet{
  display:inline-flex; align-items:center; min-height:44px; margin-top:var(--s2); padding:0;
  border:0; background:none; color:var(--text-3);
  font-family:var(--ui); font-size:13px; font-weight:600; text-decoration:underline; cursor:pointer;
}
.fb-quiet:hover{color:var(--text)}
/* @feedback-widget:end */

/* ---------------------------------------------------------------------------
 * AIP-C01 palette, appended to styles.css when this room's Pages bundle is staged.
 * Only these five colour tokens differ from the baseline; layout, type scale
 * and behaviour are shared. Three blocks, because the baseline defines its
 * palette three times (light, dark by preference, dark by choice) and each
 * override repeats the baseline's selector exactly.
 *
 * Bronze-amber, clear of AIF-C01's navy, SAA-C03's violet and the green, red and violet semantic colours.
 *
 * --ok, --bad, --flag and --focus carry meaning and are untouched. Contrast
 * and hue separation are asserted by tests/unit/palette-contrast.test.js.
 * ------------------------------------------------------------------------- */
:root{
  --accent:#9A5B00; --accent-2:#7F4A00; --accent-soft:#FBEFD2;
  --brand:#7F4A00; --brand-text:#FFFFFF;
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --accent:#E8B04A; --accent-2:#F0C677; --accent-soft:#382A10;
    --brand:#E8B04A; --brand-text:#2A1B00;
  }
}
:root[data-theme="dark"]{
  --accent:#E8B04A; --accent-2:#F0C677; --accent-soft:#382A10;
  --brand:#E8B04A; --brand-text:#2A1B00;
}
