/* ============================================================================
   Sitonce components — Material 3.
   Every colour, radius, duration and type step comes from sitonce.css. There
   is no hex literal in this file and there must never be one: the compliance
   test in DESIGN_SYSTEM.md is grep-able for exactly that reason.

   Desktop first. The layout assumes a wide viewport and narrows gracefully;
   it is not a phone layout that grew.
   ========================================================================== */

/* ---------------------------------------------------------------- state layer
   M3's one genuinely load-bearing interaction primitive: an overlay of the
   element's own content colour, at a fixed opacity per state. Applied by class
   so a link, a button and a label can all share it. */
.m3{position:relative; isolation:isolate; overflow:hidden;
    transition:background-color var(--dur-medium) var(--ease-standard),
               box-shadow var(--dur-medium) var(--ease-standard),
               color var(--dur-medium) var(--ease-standard)}
.m3::after{content:""; position:absolute; inset:0; background:currentColor;
           opacity:0; pointer-events:none;
           transition:opacity var(--dur-short) var(--ease-standard)}
.m3:hover::after{opacity:var(--state-hover)}
.m3:focus-visible::after{opacity:var(--state-focus)}
.m3:active::after{opacity:var(--state-press)}
.m3>*{position:relative; z-index:1}
/* Rows whose children overflow their box (badges, tooltips) cannot be clipped. */
.m3-flat{overflow:visible}
.m3[disabled],.m3[aria-disabled="true"]{pointer-events:none}

.skip{position:absolute; left:-9999px; top:0; z-index:99;
      background:var(--primary); color:var(--on-primary);
      padding:var(--s3) var(--s5); border-radius:0 0 var(--r-md) 0}
.skip:focus{left:0}

/* ============================================================================
   SHELL — navigation rail + top app bar.
   The rail is the whole primary navigation: four destinations, always visible,
   no hamburger and no hidden state. At 96px it costs almost nothing on a
   desktop and removes a click from every journey.
   ========================================================================== */

/* The ground the whole product sits on. It used to live on .shell, which was
   fine while every page had one - the public pages draw a top bar instead and
   came out on the browser's default grey. */
body{min-height:100vh; background:var(--surface); color:var(--on-surface)}

.shell{display:flex; min-height:100vh; background:var(--surface)}

.navtoggle,.burger,.scrim{display:none}
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
/* Seven entries can outgrow a short viewport, so the rail scrolls itself
   rather than pushing the last one off the screen. */
.rail{overflow-y:auto;
      width:var(--rail-w); flex:0 0 var(--rail-w);
      background:var(--surface); border-right:1px solid var(--outline-variant);
      display:flex; flex-direction:column; align-items:center;
      padding:var(--s5) 0 var(--s6); gap:var(--s3);
      position:sticky; top:0; height:100vh}
.rail a{width:80px; display:flex; flex-direction:column; align-items:center;
        gap:var(--s1); padding:var(--s1) 0; border-radius:var(--r-md);
        text-decoration:none; color:var(--on-surface-variant)}
.rail a:hover{text-decoration:none}
.rail .pip{width:56px; height:32px; border-radius:var(--r-lg);
           display:flex; align-items:center; justify-content:center;
           background:transparent; color:inherit;
           transition:background-color var(--dur-medium) var(--ease-standard)}
.rail .lbl{font:var(--label-md); letter-spacing:var(--label-md-track);font-variant-numeric:tabular-nums}
.rail a[aria-current="page"]{color:var(--on-secondary-container)}
.rail a[aria-current="page"] .pip{background:var(--secondary-container)}
.rail svg{width:24px; height:24px}

.appbar{height:var(--appbar-h); flex:0 0 var(--appbar-h);
        display:flex; align-items:center; gap:var(--s4);
        padding:0 var(--s6); border-bottom:1px solid var(--outline-variant);
        background:var(--surface)}
/* inline-flex with a floor, so the tap target is 40px rather than the 24-28px
   the type alone gives it — the home link is the most-tapped thing on a phone. */
.appbar .brand{font:var(--title-lg); letter-spacing:-0.2px;
               font-weight:500; color:var(--primary); text-decoration:none;
               display:inline-flex; align-items:center; min-height:40px;font-variant-numeric:tabular-nums}
.appbar .brand:hover{text-decoration:none}
/* The brand is now the drawn wordmark rather than styled text. The plain
   cut-free setting, because at 21px the 42deg slice measures 1.4px and
   reads as a printing fault rather than a detail. */
.appbar .brand img{display:block; height:21px; width:auto}
.appbar .spacer{flex:1 1 auto}
.appbar .who{font:var(--body-md); letter-spacing:var(--body-md-track);
             color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.appbar form.inline{display:inline}

/* The one persistent piece of urgency in the product: how long until the exam.
   Tertiary rather than error, because a date approaching is not a fault. */
.urgency{display:inline-flex; align-items:center; gap:var(--s2);
         height:32px; padding:0 var(--s3); border-radius:var(--r-sm);
         background:var(--tertiary-container); color:var(--on-tertiary-container);
         font:var(--label-lg); letter-spacing:var(--label-lg-track);
         text-decoration:none; white-space:nowrap;font-variant-numeric:tabular-nums}
.urgency:hover{color:var(--on-tertiary-container)}
.urgency.soon{background:var(--error-container); color:var(--on-error-container)}
.urgency svg{width:18px; height:18px}

.col{flex:1 1 auto; min-width:0; display:flex; flex-direction:column}

/* The content column. 40px gutters, generous bottom padding so the last
   section never sits on the fold. */
.page{padding:var(--s7) var(--s8) var(--s9);
      display:flex; flex-direction:column; gap:var(--s6)}
.page.wide{padding-left:var(--s8); padding-right:var(--s8)}
.page.prose{max-width:calc(var(--measure) + var(--s8) * 2)}

/* Long-form and one-decision pages: a single centred column, no rail. */
.narrow{max-width:640px; margin:0 auto; padding:var(--s11) var(--s6) var(--s9);
        display:flex; flex-direction:column; gap:var(--s5)}
.wrap{display:flex; flex-direction:column; gap:var(--s6)}

.pagehead{display:flex; align-items:flex-end; justify-content:space-between;
          gap:var(--s4); flex-wrap:wrap}
.pagehead .ph-main{display:flex; flex-direction:column; gap:var(--s1); min-width:0}
.pagehead .crumb{font:var(--label-lg); letter-spacing:var(--label-lg-track);font-variant-numeric:tabular-nums}
/* Which exam you are inside. Below the heading and at subtitle size, because
   above it in label type it was the smallest thing on a page whose whole
   meaning depends on it. */
/* A link, so it has to be a target: 32px is the audit's floor and a 28px line
   box was one pixel-hunt short of it. align-self keeps the box the width of the
   text rather than the width of the column. */
.examline{display:inline-flex; align-items:center; align-self:flex-start; min-height:32px;
          font:var(--title-lg); letter-spacing:var(--title-lg-track);
          color:var(--on-surface-variant); text-decoration:none;font-variant-numeric:tabular-nums}
.examline:hover{color:var(--primary); text-decoration:underline}
.pagehead .aside{font:var(--body-md); letter-spacing:var(--body-md-track);
                 color:var(--on-surface-variant);font-variant-numeric:tabular-nums}

.foot{border-top:1px solid var(--outline-variant); background:var(--surface);
      padding:var(--s6) var(--s8); margin-top:auto}
.foot-links{font:var(--body-sm); letter-spacing:var(--body-sm-track);
            color:var(--on-surface-variant);font-variant-numeric:tabular-nums}

/* ============================================================================
   TYPE HELPERS
   ========================================================================== */

.display{font:var(--display-sm); letter-spacing:var(--display-sm-track);font-variant-numeric:tabular-nums}
.eyebrow{font:var(--label-sm); letter-spacing:var(--label-sm-track);
         text-transform:uppercase; color:var(--primary);font-variant-numeric:tabular-nums}
.lede{font:var(--body-lg); letter-spacing:var(--body-lg-track);
      color:var(--on-surface-variant); max-width:var(--measure); text-wrap:pretty;font-variant-numeric:tabular-nums}
.lede-sm{font:var(--body-md); letter-spacing:var(--body-md-track);
         color:var(--on-surface-variant); max-width:var(--measure);font-variant-numeric:tabular-nums}
.microcopy,.hint,.src,.anchor-note,.guarantee,.consent{
  font:var(--body-sm); letter-spacing:var(--body-sm-track);
  color:var(--on-surface-variant); max-width:var(--measure);font-variant-numeric:tabular-nums}
/* A bare <b> or <strong> renders at 700 in every browser, and the system has no
   700 in it - the compliance test in DESIGN_SYSTEM.md says so in as many words.
   Emphasis is the next step up from body, which is 500. Components that set
   their own weight on a nested <b> still win on specificity. */
b, strong{font-weight:500}

.label{font:var(--label-sm); letter-spacing:var(--label-sm-track);
       text-transform:uppercase; color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.empty{color:var(--on-surface-variant); font:var(--body-md);
       letter-spacing:var(--body-md-track);font-variant-numeric:tabular-nums}
.alt{font:var(--body-md); letter-spacing:var(--body-md-track);
     color:var(--on-surface-variant);font-variant-numeric:tabular-nums}

/* ============================================================================
   BUTTONS
   M3 common buttons: filled, tonal, outlined, text. All full-pill. Height is
   40 by default, 56 for the one action a screen is actually about.
   ========================================================================== */

.btn{display:inline-flex; align-items:center; justify-content:center;
     gap:var(--s2); height:40px; padding:0 var(--s6);
     border:none; border-radius:var(--r-full); background:var(--primary);
     color:var(--on-primary); cursor:pointer; text-decoration:none;
     white-space:nowrap;
     font:var(--label-lg); letter-spacing:var(--label-lg-track);
     position:relative; isolation:isolate; overflow:hidden;
     transition:box-shadow var(--dur-medium) var(--ease-standard);font-variant-numeric:tabular-nums}
.btn::after{content:""; position:absolute; inset:0; background:currentColor;
            opacity:0; pointer-events:none;
            transition:opacity var(--dur-short) var(--ease-standard)}
.btn:hover::after{opacity:var(--state-hover)}
.btn:focus-visible::after{opacity:var(--state-focus)}
.btn:active::after{opacity:var(--state-press)}
.btn>*{position:relative; z-index:1}
.btn:hover{color:var(--on-primary); text-decoration:none}
.btn:hover{box-shadow:var(--elev-1)}
.btn svg{width:20px; height:20px}

.btn.tonal{background:var(--secondary-container); color:var(--on-secondary-container)}
.btn.tonal:hover{color:var(--on-secondary-container)}

/* Outlined and text buttons carry the action colour as their content colour,
   which is also what the state layer tints with — so hover reads as a faint
   purple wash rather than a grey one. */
.btn.ghost{background:transparent; color:var(--primary);
           border:1px solid var(--outline); box-shadow:none}
.btn.ghost:hover{color:var(--primary); box-shadow:none}
/* On a tinted surface the ghost button has to take that surface's on-colour.
   --primary on --primary-container measures 4.36:1, under the 4.5:1 floor, and
   the button carrying it says "Download the study kit" to somebody who has
   already paid for it. Same pattern as .countdown and .weakest below. */
.buybox.owned .btn.ghost{color:var(--on-primary-container); border-color:currentColor}
.btn.text{background:transparent; color:var(--primary); border:none;
          padding:0 var(--s3); box-shadow:none}
.btn.text:hover{box-shadow:none; color:var(--primary)}
.btn.danger{background:transparent; color:var(--error); border:1px solid var(--error)}
.btn.danger:hover{color:var(--error)}

.btn.lg{height:56px; padding:0 var(--s7);
        font:var(--title-md); letter-spacing:var(--title-md-track);font-variant-numeric:tabular-nums}
.btn.sm{height:32px; padding:0 var(--s4)}
.btn.block{display:flex; width:100%}

/* 56px is sized against a full desktop column. Put the same button in a
   half-width window and it becomes the largest object on the screen, which
   reads as shouting rather than as emphasis. Step it back toward the default
   height; it is still the only filled button in view, so it is still obviously
   the action. Not the 40px default, because the one thing a screen is about
   should stay a touch bigger than the buttons beside it. */
@media (max-width:820px){
  .btn.lg{height:44px; padding:0 var(--s6);
          font:var(--label-lg); letter-spacing:var(--label-lg-track)}
}

/* ---------------------------------------------------------------- admin */

/* The owner dashboard is its own workspace. It deliberately does not inherit
   the study rail: an operator scans the whole business, rather than moving
   through one candidate's course. */
.adminbody{background:var(--surface-container-low); color:var(--on-surface)}
.adminshell{min-height:100vh}
.adminbar{height:64px; padding:0 clamp(var(--s5),4vw,var(--s10)); display:flex;
          align-items:center; justify-content:space-between; gap:var(--s5);
          background:var(--surface-container-lowest); border-bottom:1px solid var(--outline-variant)}
.adminbrand,.adminwho{display:flex; align-items:center; gap:var(--s4)}
.adminbrand span{padding-left:var(--s4); border-left:1px solid var(--outline-variant);
                 font:var(--title-sm); letter-spacing:var(--title-sm-track)}
.adminwho>a{font:var(--label-lg); color:var(--on-surface-variant)}
.adminwho .who{max-width:24ch; overflow:hidden; text-overflow:ellipsis}
.adminnav{height:52px; padding:0 clamp(var(--s5),4vw,var(--s10)); display:flex;
          align-items:end; gap:var(--s7); overflow-x:auto; background:var(--surface-container-lowest);
          border-bottom:1px solid var(--outline-variant)}
.adminnav a{height:52px; display:flex; align-items:center; position:relative; white-space:nowrap;
            color:var(--on-surface-variant); font:var(--label-lg); letter-spacing:var(--label-lg-track)}
.adminnav a[aria-current="page"]{color:var(--primary)}
.adminnav a[aria-current="page"]::after{content:""; position:absolute; left:0; right:0; bottom:0;
  height:3px; border-radius:3px 3px 0 0; background:var(--primary)}
.adminmain{width:min(1480px,calc(100% - clamp(32px,8vw,128px))); margin:0 auto; padding:var(--s9) 0 var(--s12)}
.admintitle{display:flex; justify-content:space-between; align-items:end; gap:var(--s5); margin-bottom:var(--s7)}
.admintitle h1{margin:0; font:var(--headline-lg); letter-spacing:var(--headline-lg-track)}
.admintitle p{margin:var(--s2) 0 0; color:var(--on-surface-variant); font:var(--body-md)}
.adminrefresh{padding:var(--s2) var(--s4); border:1px solid var(--outline); border-radius:var(--r-full);
              color:var(--on-surface); font:var(--label-lg)}
.adminkpis{display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:var(--s4)}
.adminkpi,.adminpanel,.adminmoney article{background:var(--surface-container-lowest);
  border:1px solid var(--outline-variant); border-radius:var(--r-lg)}
.adminkpi{padding:var(--s5); min-width:0}
.adminkpi>span,.adminmoney span{display:block; color:var(--on-surface-variant); font:var(--label-lg)}
.adminkpi>strong,.adminmoney strong{display:block; margin-top:var(--s2); font:var(--display-sm);
  letter-spacing:var(--display-sm-track); font-variant-numeric:tabular-nums}
.adminkpi small{display:block; margin-top:var(--s1); color:var(--on-surface-variant)}
.adminkpi em{display:block; margin-top:var(--s2); color:var(--on-surface-variant); font:var(--body-sm); font-style:normal}
.spark{display:block; width:100%; height:42px; margin-top:var(--s3)}
.spark polyline{fill:none; stroke:var(--primary); stroke-width:2; vector-effect:non-scaling-stroke}
.adminrange{display:flex; align-items:center; gap:var(--s2); margin:var(--s6) 0 var(--s4); color:var(--on-surface-variant)}
.adminrange>a{padding:var(--s2) var(--s4); border-radius:var(--r-full); color:var(--on-surface-variant); font:var(--label-md)}
.adminrange>a[aria-current="true"]{background:var(--primary-container); color:var(--on-primary-container)}
.adminrange>i{margin-left:auto; font:var(--body-sm); font-style:normal}
.admincols{display:grid; grid-template-columns:minmax(0,1.65fr) minmax(320px,1fr); gap:var(--s4)}
.admincols.lower{margin-top:var(--s4); grid-template-columns:1.2fr 1fr}
.adminpanel{padding:var(--s5); overflow:hidden}
.adminpanelhead{display:flex; justify-content:space-between; align-items:start; gap:var(--s5); margin-bottom:var(--s5)}
.adminpanelhead h2{margin:0; font:var(--title-lg); letter-spacing:var(--title-lg-track)}
.adminpanelhead p{margin:var(--s1) 0 0; color:var(--on-surface-variant); font:var(--body-sm)}
.adminlegend{display:flex; flex-wrap:wrap; gap:var(--s4); color:var(--on-surface-variant); font:var(--label-sm)}
.adminlegend span{display:flex; align-items:center; gap:var(--s2)}
.adminlegend i{display:block; width:18px; height:3px; border-radius:2px; background:var(--primary)}
.adminlegend .l1 i{background:var(--secondary)} .adminlegend .l2 i{background:var(--tertiary)}
.adminlegend .l3 i{background:var(--error)}
.adminchart{width:100%; height:auto; margin-top:var(--s4); overflow:visible}
.adminchart .gridline{fill:none; stroke:var(--outline-variant); stroke-width:1}
.adminline{fill:none; stroke:var(--primary); stroke-width:3; vector-effect:non-scaling-stroke}
.adminline.l1{stroke:var(--secondary)} .adminline.l2{stroke:var(--tertiary)} .adminline.l3{stroke:var(--error)}
.chartdates{display:flex; justify-content:space-between; color:var(--on-surface-variant); font:var(--label-sm)}
.adminfunnel{display:grid; gap:var(--s5)}
.adminfunnel>div>span{display:flex; justify-content:space-between; gap:var(--s3); margin-bottom:var(--s2)}
.adminfunnel i{font-style:normal; color:var(--on-surface-variant); font-variant-numeric:tabular-nums}
.adminfunnel progress{display:block; width:100%; height:10px; border:0; border-radius:var(--r-full); overflow:hidden}
.adminfunnel progress::-webkit-progress-bar{background:var(--surface-container)}
.adminfunnel progress::-webkit-progress-value{background:var(--primary); border-radius:var(--r-full)}
.adminfunnel progress::-moz-progress-bar{background:var(--primary); border-radius:var(--r-full)}
.adminfeed>div,.admincontent>div{display:flex; justify-content:space-between; align-items:center; gap:var(--s5);
  padding:var(--s3) 0; border-bottom:1px solid var(--outline-variant)}
.adminfeed>div:last-child,.admincontent>div:last-child{border-bottom:0}
.adminfeed span,.admincontent span{display:flex; flex-direction:column; min-width:0}
.adminfeed small,.admincontent small{color:var(--on-surface-variant); overflow:hidden; text-overflow:ellipsis}
.adminfeed time{color:var(--on-surface-variant); font:var(--label-sm); white-space:nowrap}
.adminfeed.compact span{display:block; text-transform:capitalize}
.adminfeed.compact b{font-variant-numeric:tabular-nums}
.admintable{overflow:auto; margin:0 calc(var(--s5) * -1) calc(var(--s5) * -1)}
.admintable table{width:100%; border-collapse:collapse; min-width:720px; font:var(--body-sm)}
.admintable th,.admintable td{padding:var(--s3) var(--s4); text-align:left; vertical-align:top;
  border-bottom:1px solid var(--outline-variant)}
.admintable th{position:sticky; top:0; background:var(--surface-container-low); color:var(--on-surface-variant);
  font:var(--label-sm); text-transform:uppercase; letter-spacing:.04em}
.admintable td:first-child,.admintable th:first-child{padding-left:var(--s5)}
.admintable td:last-child,.admintable th:last-child{padding-right:var(--s5)}
.admintable td small{display:block; margin-top:var(--s1); color:var(--on-surface-variant)}
.admintable .num{text-align:right; font-variant-numeric:tabular-nums}
.admintable code{font-size:.85em; white-space:pre-wrap; overflow-wrap:anywhere}
.adminsearch{display:flex; gap:var(--s3); max-width:680px; margin-bottom:var(--s5)}
.adminsearch input{flex:1; min-width:0; padding:var(--s3) var(--s4); border:1px solid var(--outline);
  border-radius:var(--r-md); background:var(--surface-container-lowest); color:var(--on-surface); font:var(--body-md)}
.adminsearch button{padding:var(--s3) var(--s5); border:0; border-radius:var(--r-md); background:var(--primary);
  color:var(--on-primary); font:var(--label-lg); cursor:pointer}
.adminmoney{display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:var(--s4); margin-bottom:var(--s4)}
.adminmoney article{padding:var(--s5)}
.adminstatus{display:inline-flex; padding:var(--s1) var(--s3); border-radius:var(--r-full);
  background:var(--surface-container); color:var(--on-surface-variant); font:var(--label-sm); white-space:nowrap}
.adminstatus.paid{background:var(--primary-container); color:var(--on-primary-container)}
.adminstatus.failed,.adminstatus.refunded{background:var(--error-container); color:var(--on-error-container)}
.admincontent span:first-child{gap:var(--s1)}
.admincomments{margin-top:var(--s5); display:grid; gap:var(--s3)}
.admincomments article{padding:var(--s4); border-radius:var(--r-md); background:var(--surface-container-low)}
.admincomments p{margin:0 0 var(--s2)} .admincomments span{color:var(--on-surface-variant); font:var(--body-sm)}
.adminempty{color:var(--on-surface-variant)}
.admindatachips{display:flex; flex-wrap:wrap; gap:var(--s2); margin-bottom:var(--s5)}
.admindatachips .chip{display:inline-flex; gap:var(--s2); align-items:center; padding:var(--s2) var(--s3);
  border:1px solid var(--outline-variant); border-radius:var(--r-full); color:var(--on-surface-variant); font:var(--label-sm)}
.admindatachips .chip.on{background:var(--primary-container); border-color:transparent; color:var(--on-primary-container)}
.admindatanote{margin:var(--s5) 0 0; color:var(--on-surface-variant); font:var(--body-sm)}
.admintable .null{color:var(--on-surface-variant); font-style:italic}

@media (max-width:1050px){
  .adminkpis{grid-template-columns:1fr 1fr}
  .admincols,.admincols.lower{grid-template-columns:1fr}
}
@media (max-width:700px){
  .adminbar{padding:0 var(--s4)} .adminwho>a,.adminwho .who{display:none}
  .adminnav{padding:0 var(--s4); gap:var(--s5)}
  .adminmain{width:calc(100% - var(--s6)); padding:var(--s6) 0 var(--s9)}
  .admintitle{align-items:start} .admintitle p{max-width:36ch}
  .adminkpis{grid-template-columns:1fr}
  .adminrange{flex-wrap:wrap} .adminrange>i{width:100%; margin:var(--s1) 0 0}
}

.btn[disabled],.btn:disabled{background:var(--on-surface-12);
  color:var(--on-surface-38); cursor:not-allowed; box-shadow:none; pointer-events:none}
.btn.ghost[disabled],.btn.text[disabled]{background:transparent;
  border-color:var(--on-surface-12); color:var(--on-surface-38)}

/* A submit button that has to read as a link — the exam-date skip. Real
   button, real focus ring, deliberately not competing with the primary. */
.linkish{background:none; border:none; padding:0; cursor:pointer;
         color:var(--on-surface-variant); text-decoration:underline;
         text-underline-offset:3px;
         font:var(--body-sm); letter-spacing:var(--body-sm-track);font-variant-numeric:tabular-nums}
.linkish:hover{color:var(--on-surface)}

/* ============================================================================
   CHIPS
   ========================================================================== */

.chip{display:inline-flex; align-items:center; gap:var(--s2);
      height:32px; padding:0 var(--s4); border-radius:var(--r-sm);
      border:1px solid var(--outline); background:transparent;
      color:var(--on-surface-variant); cursor:pointer; text-decoration:none;
      white-space:nowrap;
      font:var(--label-lg); letter-spacing:var(--label-lg-track);font-variant-numeric:tabular-nums}
.chip:hover{color:var(--on-surface-variant); text-decoration:none}
.chip.on{background:var(--secondary-container); color:var(--on-secondary-container);
         border-color:var(--secondary-container)}
.chip svg{width:18px; height:18px}

/* Small non-interactive state badges. */
.badge{display:inline-flex; align-items:center; height:24px; padding:0 var(--s2);
       border-radius:var(--r-sm); background:var(--surface-container-high);
       color:var(--on-surface-variant);
       font:var(--label-sm); letter-spacing:var(--label-sm-track);
       text-transform:uppercase; white-space:nowrap;font-variant-numeric:tabular-nums}
.badge.ok{background:var(--primary); color:var(--on-primary)}
.badge.bad{background:var(--error); color:var(--on-error)}
.badge.warn{background:var(--tertiary-container); color:var(--on-tertiary-container)}
.badge.calm{background:var(--secondary-container); color:var(--on-secondary-container)}
.badge.plain{background:var(--surface-container-lowest); color:var(--on-surface-variant);
             border:1px solid var(--outline-variant)}

/* ============================================================================
   SURFACES
   ========================================================================== */

.card{background:var(--surface-container-lowest);
      border:1px solid var(--outline-variant); border-radius:var(--r-md);
      overflow:hidden}
.panel{background:var(--surface-container-low);
       border:1px solid var(--outline-variant); border-radius:var(--r-md);
       padding:var(--s5) var(--s6); display:flex; flex-direction:column; gap:var(--s3)}
.tinted{background:var(--primary-container); color:var(--on-primary-container);
        border-radius:var(--r-md); padding:var(--s6) var(--s7);
        display:flex; flex-direction:column; gap:var(--s5)}
.tinted h2,.tinted h1{color:inherit}

.cardhead{display:flex; align-items:baseline; justify-content:space-between;
          gap:var(--s4); padding:var(--s5) var(--s6) var(--s3)}
.cardhead h2{font:var(--title-md); letter-spacing:var(--title-md-track);font-variant-numeric:tabular-nums}
/* The "see all" link beside a card title. inline-flex with a floor so it is a
   32px target rather than the 24px its type gives it. */
.cardhead>a{display:inline-flex; align-items:center; min-height:32px}

/* The same header, but for a section that is not inside a card. .cardhead's
   padding is the card's inset; used on a bare section it indents the heading
   out of line with the tiles above it. */
.bandhead{display:flex; align-items:baseline; justify-content:space-between;
          gap:var(--s4)}
.bandhead h2{font:var(--title-md); letter-spacing:var(--title-md-track);font-variant-numeric:tabular-nums}
.bandhead>a{display:inline-flex; align-items:center; min-height:32px}
.bandhead h2>a{display:inline-flex; align-items:center; gap:var(--s2);
               min-height:32px; color:inherit}
.bandhead h2>a svg{width:18px; height:18px}
.cardnote{padding:var(--s3) var(--s6) var(--s4);
          border-top:1px solid var(--outline-variant);
          font:var(--body-sm); letter-spacing:var(--body-sm-track);
          color:var(--on-surface-variant);font-variant-numeric:tabular-nums}

/* A card whose whole body is a stack of rows separated by a rule. */
.rows>.row{display:flex; align-items:center; gap:var(--s3);
           padding:var(--s3) var(--s6); border-top:1px solid var(--outline-variant)}
.rows>.row .grow{flex:1 1 auto; min-width:0}
.rows>.row .t{font:var(--body-md); letter-spacing:var(--body-md-track);
              color:var(--on-surface);font-variant-numeric:tabular-nums}
.rows>.row .m{font:var(--body-sm); letter-spacing:var(--body-sm-track);
              color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.rows>.row .v{font:var(--title-md); letter-spacing:var(--title-md-track);font-variant-numeric:tabular-nums}
.rows>.row .v.bad{color:var(--error)}
.rows>.row .v.mute{color:var(--on-surface-variant)}

/* Grid helpers the screens share. */
.split{display:grid; grid-template-columns:1fr 380px; gap:var(--s6);
       align-items:stretch}
.split.rev{grid-template-columns:380px 1fr; align-items:start}
/* Stretch, not start. The two panels sit side by side and a short one leaves a
   ragged step down the middle of the page — on Final review the left card ended
   a third of the way up the right one. Equal height, and the content inside
   each keeps its own alignment. */
.split.wideaside{grid-template-columns:1fr 460px; align-items:stretch}
.stack{display:flex; flex-direction:column; gap:var(--s6)}
.split.wideaside>.card,.split.wideaside>.panel,.split.wideaside>.weakest{height:100%}
/* With the panel taller than its text, the buttons go to the foot rather than
   floating in the middle of an expanse of colour. */
.split.wideaside>.weakest{display:flex; flex-direction:column}
.split.wideaside>.weakest .acts{margin-top:auto; padding-top:var(--s4)}
/* One exception to the stretch above, and it is the reverse case the comment
   describes. On a product page with no pass-rate table the LEFT column is the
   long one - five paragraphs of prose against a card of four figures - and
   stretching the card paints a grey panel three hundred pixels past its last
   row. A short sidebar beside a long column is the normal shape for a sidebar;
   a short sidebar inflated to match is just a large empty box. */
.split.pdpfacts{align-items:start}
.three{display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s6)}
.four{display:grid; grid-template-columns:repeat(4,1fr); gap:var(--s4)}
.band{display:flex; flex-direction:column; gap:var(--s4)}

/* ============================================================================
   FEEDBACK
   ========================================================================== */

.flash{border-radius:var(--r-md); padding:var(--s4) var(--s5);
       background:var(--secondary-container); color:var(--on-secondary-container);
       font:var(--body-md); letter-spacing:var(--body-md-track);font-variant-numeric:tabular-nums}
.flash.warn{background:var(--tertiary-container); color:var(--on-tertiary-container)}
.flash.error{background:var(--error-container); color:var(--on-error-container)}

.callout{border-radius:var(--r-md); padding:var(--s5) var(--s6);
         background:var(--surface-container); color:var(--on-surface);
         display:flex; flex-direction:column; gap:var(--s2)}
.callout.warn{background:var(--tertiary-container); color:var(--on-tertiary-container)}
.callout b{font-weight:500}

/* ============================================================================
   FORMS
   ========================================================================== */

.check{display:flex; align-items:flex-start; gap:var(--s3); cursor:pointer;
       font:var(--body-md); letter-spacing:var(--body-md-track);font-variant-numeric:tabular-nums}
.check input[type=checkbox]{width:18px; height:18px; margin:var(--s1) 0 0;
                            accent-color:var(--primary); flex:0 0 18px}

.field{display:flex; align-items:center; gap:var(--s3); height:56px;
       padding:0 var(--s5); border-radius:var(--r-xl);
       background:var(--surface-container-high); color:var(--on-surface-variant)}
/* Stretch to the field's full 56px. Left at its natural 26px the text was
   centred but only the middle third of the box focused it, so a tap near the
   top or bottom edge of an apparently large search field did nothing. */
.field input{border:none; background:transparent; outline:none; width:100%; align-self:stretch;
             padding:0;
             color:var(--on-surface);
             font:var(--body-lg); letter-spacing:var(--body-lg-track);font-variant-numeric:tabular-nums}
.field svg{width:24px; height:24px; flex:0 0 24px}
/* The input inside sets outline:none, so without this the field has no focus
   indicator at all - a keyboard user tabbing through the form loses their
   place. The ring goes on the wrapper because that is the thing you can see. */
.field:focus-within{outline:3px solid var(--secondary); outline-offset:2px}

/* "Already booked?", under the choices on the exam-date screen. */
.ownwhen{display:flex; flex-direction:column; gap:var(--s2)}
.ownwhen label{font:var(--body-md); letter-spacing:var(--body-md-track);
               color:var(--on-surface-variant); text-wrap:pretty}

.skiprow{margin-top:calc(var(--s2) * -1)}
.prefs{display:flex; flex-direction:column; gap:var(--s3); align-items:flex-start}

/* ============================================================================
   CHOICE ROW — the most-used component in the product.
   Radio semantics with the input visually hidden: the label is the target, so
   the whole row is clickable and the focus ring lands on the row rather than
   on a 13px dot.
   ========================================================================== */

.choices{display:flex; flex-direction:column; gap:var(--s3)}

.choice{display:flex; align-items:flex-start; gap:var(--s4);
        padding:var(--s4) var(--s5); border-radius:var(--r-md); cursor:pointer;
        background:var(--surface-container-lowest);
        border:2px solid var(--outline-variant)}
.choice input{position:absolute; opacity:0; width:1px; height:1px}
.choice .letter{width:32px; height:32px; flex:0 0 32px; border-radius:var(--r-full);
                display:flex; align-items:center; justify-content:center;
                background:var(--surface-container-high);
                color:var(--on-surface-variant);
                font:var(--label-lg); letter-spacing:var(--label-lg-track);font-variant-numeric:tabular-nums}
.choice .text{font:var(--body-lg); letter-spacing:var(--body-lg-track);
              color:var(--on-surface); padding-top:var(--s1);font-variant-numeric:tabular-nums}
.choice .text b{font-weight:500; display:block}
.choice .text i{display:block; font-style:normal;
                font:var(--body-sm); letter-spacing:var(--body-sm-track);
                color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
/* The border is 2px and coloured rather than absent, so selecting a row shifts
   nothing. Everything below only changes the colour. */
.choice:hover{border-color:var(--outline)}
.choice:has(input:focus-visible){outline:3px solid var(--secondary); outline-offset:2px}
.choice.picked,.choice:has(input:checked){
  background:var(--primary-container); border-color:var(--primary)}
.choice.picked .letter,.choice:has(input:checked) .letter{
  background:var(--primary); color:var(--on-primary)}

/* ============================================================================
   PLAYER
   Full-bleed: no rail, no app bar, no footer. One question, one decision.
   ========================================================================== */

body.playing{background:var(--surface)}

.player{min-height:100vh; display:flex; flex-direction:column;
        background:var(--surface)}

.playbar{height:var(--appbar-h); flex:0 0 var(--appbar-h);
         background:var(--surface-container);
         border-bottom:1px solid var(--outline-variant);
         display:flex; align-items:center; gap:var(--s6); padding:0 var(--s7)}
.playbar .back{font:var(--label-lg); letter-spacing:var(--label-lg-track);
               color:var(--on-surface-variant); white-space:nowrap;font-variant-numeric:tabular-nums}
.playbar .prog{flex:1 1 auto; height:8px; border-radius:var(--r-xs);
               background:var(--surface-container-highest); overflow:hidden}
.playbar .prog-fill{height:8px; border-radius:var(--r-xs); background:var(--primary);
                    transition:width var(--dur-medium) var(--ease-standard)}
.playbar .count{font:var(--label-lg); letter-spacing:var(--label-lg-track);
                color:var(--on-surface-variant); white-space:nowrap;font-variant-numeric:tabular-nums}

.timer{display:inline-flex; align-items:center; gap:var(--s2); height:40px;
       padding:0 var(--s4); border-radius:var(--r-full);
       background:var(--secondary-container); color:var(--on-secondary-container);
       font-family:var(--mono); font-size:16px; font-weight:500;
       white-space:nowrap}
/* Under five minutes the timer goes to error — and takes an alarm glyph with
   it, because colour alone is not a signal. */
.timer.low{background:var(--error); color:var(--on-error)}
.timer .alarm{display:none}
.timer.low .alarm,.timer.over .alarm{display:block; width:18px; height:18px}
/* Past the published time the clock counts up, so it needs to look different
   from a clock running down — and the words beside it have to say which,
   because a "+" is not something to bet a candidate's reading of the screen on. */
.timer.over{background:var(--error-container); color:var(--on-error-container)}
.overnote{font:var(--body-sm); letter-spacing:var(--body-sm-track);
          color:var(--error); white-space:nowrap}

/* The question navigator.
 *
 * A CSS-only sheet over the paper: a checkbox in the player toggles it, the
 * same trick the mobile drawer uses, because the whole product has to work with
 * scripting off. Sixty questions is more than anyone holds in their head, and
 * the last ten minutes of a paper are spent going back to what you flagged. */
.qnavtoggle{position:absolute; opacity:0; width:0; height:0}
.navbtn{display:inline-flex; align-items:center; justify-content:center;
        width:40px; height:40px; border-radius:var(--r-full); cursor:pointer;
        color:var(--on-surface-variant)}
.navbtn:hover{background:var(--surface-container-high)}
.navbtn svg{width:24px; height:24px}
.qnavtoggle:focus-visible ~ .playbar .navbtn{outline:3px solid var(--on-secondary-container); outline-offset:2px}

.qnav{display:none; position:fixed; inset:var(--appbar-h) 0 0; z-index:40;
      background:rgba(0,0,0,.32)}
.qnavtoggle:checked ~ .qnav{display:block}
.qnav-in{max-height:100%; overflow-y:auto; background:var(--surface-container-low);
         border-bottom:1px solid var(--outline-variant);
         padding:var(--s6) var(--s8) var(--s7)}
.qnav-head{display:flex; align-items:center; gap:var(--s5); flex-wrap:wrap; margin-bottom:var(--s5)}
.qnav-head b{font:var(--title-md); letter-spacing:var(--title-md-track)}
.qnav-head .btn{margin-left:auto}
.qnav-key{display:flex; align-items:center; gap:var(--s2) var(--s4); flex-wrap:wrap;
          font:var(--body-sm); letter-spacing:var(--body-sm-track); color:var(--on-surface-variant)}
/* A swatch and its word travel together or the pair is meaningless. */
.qnav-key > span{display:inline-flex; align-items:center; gap:var(--s1); white-space:nowrap}
.qnav-key .k{display:inline-block; flex:0 0 14px; width:14px; height:14px;
             border-radius:var(--r-xs); border:1px solid var(--outline)}
.qnav-key .k.done{background:var(--primary); border-color:var(--primary)}
.qnav-key .k.flag{background:var(--tertiary-container); border-color:var(--tertiary)}
.qnav-key .k.todo{background:var(--surface-container-lowest)}

.qnav-grid{display:grid; grid-template-columns:repeat(auto-fill,minmax(48px,1fr)); gap:var(--s2)}
.qn{display:flex; align-items:center; justify-content:center; height:44px;
    border-radius:var(--r-sm); border:1px solid var(--outline);
    background:var(--surface-container-lowest); color:var(--on-surface);
    text-decoration:none; font:var(--title-md); letter-spacing:var(--title-md-track);
    font-variant-numeric:tabular-nums}
.qn:hover{text-decoration:none; border-color:var(--primary)}
.qn.done{background:var(--primary); color:var(--on-primary); border-color:var(--primary)}
/* Flagged wins over answered: the reason to open this panel is to find the ones
   you were unsure about, and they are useless if a correct-looking tile hides
   them. */
.qn.flag{background:var(--tertiary-container); color:var(--on-tertiary-container);
         border-color:var(--tertiary)}
.qn.on{outline:3px solid var(--on-secondary-container); outline-offset:2px}
.qnav-note{margin-top:var(--s4); font:var(--body-md); letter-spacing:var(--body-md-track);
           color:var(--on-surface-variant)}

@media (max-width:720px){
  .qnav-in{padding:var(--s5) var(--s4) var(--s6)}
  .qnav-grid{grid-template-columns:repeat(auto-fill,minmax(42px,1fr))}
  .qn{height:42px; font:var(--body-md); letter-spacing:var(--body-md-track)}
}

/* Time is up. A dialog rather than a banner, because it is the one moment in
   the paper that has to interrupt — but it closes on the primary button, on the
   scrim, and on Escape, and closing it changes nothing about the paper. */
.timeup{position:fixed; inset:0; z-index:60; display:flex;
        align-items:center; justify-content:center; padding:var(--s6);
        background:rgba(0,0,0,.32)}
.timeup[hidden]{display:none}
.timeup-card{width:100%; max-width:480px; border-radius:var(--r-xl);
             background:var(--surface-container-high); color:var(--on-surface);
             padding:var(--s7); display:flex; flex-direction:column; gap:var(--s4)}
.timeup-card h2{font:var(--headline-sm); letter-spacing:var(--headline-sm-track)}
.timeup-card p{font:var(--body-lg); letter-spacing:var(--body-lg-track);
               color:var(--on-surface-variant); text-wrap:pretty}
.timeup-card .acts{display:flex; gap:var(--s3); flex-wrap:wrap; margin-top:var(--s2)}
.timeup-card .acts .btn{flex:1 1 auto; justify-content:center}

.qform{flex:1 1 auto; display:flex; justify-content:center;
       padding:var(--s9) var(--s7)}

/* Untimed sets keep the navigation rail, so the player is a page inside the
   shell rather than the whole viewport.
 *
 * The question column stays CENTRED, matching the timed player. Left-aligning
 * it to agree with the rest of the app was tried and reverted: a question is
 * one decision on an otherwise empty screen, and centring it is what makes the
 * page feel like sitting a paper rather than reading a dashboard. The
 * consistency worth having here is practice with mock, not practice with
 * My study. */
.player.railed{min-height:0; flex:1 1 auto; background:transparent}
.player.railed .playbar{background:transparent; padding:0 var(--s8)}
.player.railed .qform{padding:var(--s7) var(--s8) var(--s9)}
.qbody{width:var(--reading-col); max-width:100%;
       display:flex; flex-direction:column; gap:var(--s6)}

/* min-height, not height, and it may wrap.
   Every exam until FTCE had subtopic names that fitted on one line. FTCE's are
   the competencies of a state rule and read "Knowledge of key ideas and details
   based on text selections" — on a phone that wraps to three lines, and against
   a fixed 32px the text simply left the rounded background it was sitting on. */
.qmeta{align-self:flex-start; display:inline-flex; align-items:center; min-height:32px;
       padding:var(--s1) var(--s3); border-radius:var(--r-sm); max-width:100%;
       background:var(--surface-container-high); color:var(--on-surface-variant);
       font:var(--label-md); letter-spacing:var(--label-md-track);
       text-transform:uppercase; text-wrap:pretty;font-variant-numeric:tabular-nums}
.stem{font:var(--headline-md); letter-spacing:var(--headline-md-track);
      color:var(--on-surface); text-wrap:pretty;font-variant-numeric:tabular-nums}
.qhint{margin:calc(var(--s3) * -1) 0 0; color:var(--on-surface-variant);
       font:var(--body-md); letter-spacing:var(--body-md-track)}
.hotspot{position:relative; display:block; width:100%; padding:0; overflow:hidden;
         border:2px solid var(--outline-variant); border-radius:var(--r-md);
         background:var(--surface-container-lowest); cursor:crosshair; line-height:0}
.hotspot:hover{border-color:var(--outline)}
.hotspot:focus-visible{outline:3px solid var(--secondary); outline-offset:2px}
.hotspot img{display:block; width:100%; height:auto; user-select:none}
.hotspot-mark{position:absolute; z-index:2; width:20px; height:20px;
              border:3px solid var(--on-primary); border-radius:var(--r-full);
              background:var(--primary); box-shadow:0 0 0 2px var(--primary);
              transform:translate(-50%,-50%); pointer-events:none}
.review-hotspot{cursor:default; margin:var(--s4) 0; border-width:1px}
.hotspot-key{position:absolute; z-index:1; border:3px solid var(--tertiary);
             background:color-mix(in srgb,var(--tertiary) 18%,transparent);
             pointer-events:none}

/* A shared passage, table or scenario the question is answered against.
   Reading measure and body type, because it is prose to be read rather than a
   question to be scanned — the stem above it carries the display size and the
   contrast between the two is what says which is which. Its own surface, so it
   reads as source material rather than as more of our voice.
   Capped and scrollable: a passage taller than the viewport pushes the options
   off the screen, and a candidate who cannot see the options while reading has
   a worse paper than the real one. */
.stimulus{margin:0; padding:var(--s5) var(--s5) var(--s4);
          border-radius:var(--r-md); border:1px solid var(--outline-variant);
          background:var(--surface-container-low); color:var(--on-surface);
          max-height:42vh; overflow-y:auto; overscroll-behavior:contain}
.stimulus figcaption{font:var(--label-lg); letter-spacing:var(--label-lg-track);
                     color:var(--on-surface-variant); text-transform:uppercase;
                     margin-bottom:var(--s3)}
.stimulus p{font:var(--body-lg); letter-spacing:var(--body-lg-track);
            margin:0 0 var(--s3); text-wrap:pretty}
.stimulus p:last-child{margin-bottom:0}
.stimulus-resource{margin-top:var(--s4); padding-top:var(--s3); border-top:1px solid var(--outline-variant)}
.stimulus-resource h4{margin:0 0 var(--s2); font:var(--title-sm)}
.stimulus-resource img{display:block; max-width:100%; height:auto; margin:0 0 var(--s3); border:1px solid var(--outline-variant)}
.rcard .stimulus{max-height:24vh; margin-bottom:var(--s4)}

.unsure{align-items:center; color:var(--on-surface-variant)}

.navrow{display:flex; align-items:center; gap:var(--s4);
        padding-top:var(--s2); border-top:1px solid var(--outline-variant)}
.navrow .answered{flex:1 1 auto; margin:0;
                  font:var(--body-md); letter-spacing:var(--body-md-track);
                  color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.navrow .btn.lg{padding:0 var(--s8)}

/* Offered once enough questions are answered for the score to mean something.
   Deliberately below the fold of the decision, not beside Next. */
.earlyout{display:flex; align-items:center; gap:var(--s5);
          padding:var(--s4) var(--s5); border-radius:var(--r-md);
          background:var(--surface-container)}
.earlyout .grow{flex:1 1 auto}
.earlyout .t{font:var(--body-md); letter-spacing:var(--body-md-track);
             color:var(--on-surface);font-variant-numeric:tabular-nums}
.earlyout .m{font:var(--body-sm); letter-spacing:var(--body-sm-track);
             color:var(--on-surface-variant);font-variant-numeric:tabular-nums}

/* ============================================================================
   RESULT
   ========================================================================== */

.scorehead{padding:var(--s9) var(--s8); display:flex; align-items:flex-end;
           gap:var(--s10); flex-wrap:wrap;
           background:var(--secondary-container); color:var(--on-secondary-container)}
.scorehead.pass{background:var(--primary-container); color:var(--on-primary-container)}
.scorehead.fail{background:var(--error-container); color:var(--on-error-container)}
.scorehead .who{display:flex; flex-direction:column; gap:var(--s2)}
.scorehead .eyebrow{color:inherit}
.scorehead .bignum{font-size:96px; line-height:96px; font-weight:400;
                   letter-spacing:-2px; font-variant-numeric:tabular-nums}
.scorehead .bignum span{font-size:.5em; letter-spacing:0}
.scorehead .said{display:flex; flex-direction:column; gap:var(--s3);
                 padding-bottom:var(--s3); max-width:720px}
.scorehead .sub{font:var(--title-lg); letter-spacing:var(--title-lg-track);
                color:inherit;font-variant-numeric:tabular-nums}
.scorehead .tail{font:var(--body-lg); letter-spacing:var(--body-lg-track);
                 color:inherit;font-variant-numeric:tabular-nums}

/* The verdict is the one loud element on the page, because it carries the
   learning moment. It is a filled block rather than tinted text so it cannot
   be skimmed past. */
.verdict{display:inline-flex; align-self:flex-start; align-items:center;
         height:40px; padding:0 var(--s5); border-radius:var(--r-sm);
         background:var(--secondary); color:var(--on-secondary);
         font:var(--title-md); letter-spacing:var(--title-md-track);font-variant-numeric:tabular-nums}
.verdict.ok{background:var(--primary); color:var(--on-primary)}
.verdict.bad{background:var(--error); color:var(--on-error)}

.topicrow{display:grid; grid-template-columns:1fr 64px 220px 110px;
          align-items:center; gap:var(--s4);
          padding:var(--s3) 0; border-top:1px solid var(--outline-variant)}
.topicrow .name{font:var(--body-lg); letter-spacing:var(--body-lg-track);font-variant-numeric:tabular-nums}
.topicrow .score{font:var(--body-md); letter-spacing:var(--body-md-track);
                 color:var(--on-surface-variant); text-align:right;font-variant-numeric:tabular-nums}
.topicrow .pct{text-align:right; font:var(--title-md);
               letter-spacing:var(--title-md-track);font-variant-numeric:tabular-nums}
.topicrow .pct.bad{color:var(--error)}
.topicrow .pct small{display:block; font:var(--label-sm);
                     letter-spacing:var(--label-sm-track); font-weight:400;
                     color:var(--on-surface-variant);font-variant-numeric:tabular-nums}

.bar{height:10px; border-radius:5px; background:var(--surface-container-highest);
     overflow:hidden}
.bar-fill{height:10px; border-radius:5px; background:var(--error);
          transition:width var(--dur-long) var(--ease-decelerate)}
.bar-fill.ok{background:var(--primary)}

/* ---- the paywall ---------------------------------------------------------
   Names the count and the topic of every question they missed, and redacts
   only the answer. A blurred wall that hides the shape of what is behind it
   sells nothing. */
.paywall{background:var(--surface-container-lowest);
         border:1px solid var(--outline-variant); border-radius:var(--r-md);
         padding:var(--s7); display:flex; flex-direction:column; gap:var(--s5)}
.paywall h2{font:var(--headline-md); letter-spacing:var(--headline-md-track);
            text-wrap:pretty;font-variant-numeric:tabular-nums}
.redacted{display:flex; flex-direction:column; gap:var(--s2);
          padding:var(--s4); border-radius:var(--r-md);
          background:var(--surface-container-low)}
.redacted .r{display:flex; align-items:center; gap:var(--s3)}
.redacted .n{width:32px; font:var(--label-md); letter-spacing:var(--label-md-track);
             color:var(--on-error-container);font-variant-numeric:tabular-nums}
.redacted .sub{flex:0 0 auto; font:var(--body-sm);
               letter-spacing:var(--body-sm-track); color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.redacted .rule{flex:1 1 auto; height:10px; border-radius:5px;
                background:repeating-linear-gradient(90deg,
                  var(--surface-container-highest) 0 10px,
                  var(--surface-container-low) 10px 14px)}
.redacted .more{font:var(--body-sm); letter-spacing:var(--body-sm-track);
                color:var(--on-surface-variant); padding-top:var(--s1);font-variant-numeric:tabular-nums}

.ticks{list-style:none; margin:0; padding:0;
       display:flex; flex-direction:column; gap:var(--s2)}
/* The marker is positioned, NOT a flex item.
   As a flex row, every child of the <li> became its own flex item - so
   "<b>Downloadable study guide and 5 mock papers as PDFs</b>, to print and
   mark up on paper" rendered as two columns, the bold phrase wrapping in one
   and the trailing clause floating beside it halfway down. Inline content has
   to stay one flow; only the tick is taken out of it. */
.ticks li{position:relative; padding-left:var(--s6); /* 24: the 18px tick plus 6 */
          font:var(--body-md); letter-spacing:var(--body-md-track);font-variant-numeric:tabular-nums}
.ticks li::before{content:""; position:absolute; left:0;
  top:0.15em; /* optical: aligns the dot to cap height, not to the line box */
  width:18px; height:18px;
  background:var(--primary);
  -webkit-mask:var(--tick) center/18px no-repeat; mask:var(--tick) center/18px no-repeat}
:root{--tick:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M9.55 17.6 4 12.05l1.4-1.4 4.15 4.15 9.05-9.05L20 7.15Z'/%3E%3C/svg%3E")}
.ticks.lg li{font:var(--body-lg); letter-spacing:var(--body-lg-track);font-variant-numeric:tabular-nums}

/* ---- paid review ---------------------------------------------------------- */

.review{display:flex; flex-direction:column; gap:var(--s5)}
.rcard{border:1px solid var(--outline-variant); border-left:4px solid var(--primary);
       border-radius:var(--r-md); background:var(--surface-container-lowest);
       padding:var(--s6) var(--s7);
       display:flex; flex-direction:column; gap:var(--s5)}
.rcard.wrong{border-left-color:var(--error)}
.rcard>header{display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap}
.rcard .rn{font:var(--label-lg); letter-spacing:var(--label-lg-track);
           color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.rcard .rsub{font:var(--body-md); letter-spacing:var(--body-md-track);
             color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.rcard .flag{margin-left:auto; font:var(--body-sm);
             letter-spacing:var(--body-sm-track); color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.rcard .stem{font:var(--title-lg); letter-spacing:var(--title-lg-track);font-variant-numeric:tabular-nums}

.ropts{list-style:none; margin:0; padding:0;
       display:flex; flex-direction:column; gap:var(--s2)}
.ropts li{display:flex; gap:var(--s4); padding:var(--s3) var(--s5);
          border-radius:var(--r-md); background:var(--surface-container-low);
          border:2px solid transparent}
.ropts .letter{width:28px; height:28px; flex:0 0 28px; border-radius:var(--r-full);
               display:flex; align-items:center; justify-content:center;
               background:var(--surface-container-high); color:var(--on-surface-variant);
               font:var(--label-lg); letter-spacing:var(--label-lg-track);font-variant-numeric:tabular-nums}
.ropts .body{flex:1 1 auto}
.ropts .text{display:block; font:var(--body-lg);
             letter-spacing:var(--body-lg-track); color:var(--on-surface);font-variant-numeric:tabular-nums}
.ropts .mark{display:block; font:var(--label-sm);
             letter-spacing:var(--label-sm-track); text-transform:uppercase;font-variant-numeric:tabular-nums}
.ropts .mis{display:block; margin-top:var(--s1);
            font:var(--body-sm); letter-spacing:var(--body-sm-track);
            color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.ropts .mis b{font-weight:500}

.ropts li.key{background:var(--primary-container); border-color:var(--primary)}
.ropts li.key .letter{background:var(--primary); color:var(--on-primary)}
.ropts li.key .mark{color:var(--on-primary-container)}
.ropts li.chose{background:var(--error-container); border-color:var(--error)}
.ropts li.chose .letter{background:var(--error); color:var(--on-error)}
.ropts li.chose .mark,.ropts li.chose .mis{color:var(--on-error-container)}

/* The explanation. This is the thing the course is sold for; it gets its own
   surface so it never reads as a footnote to the options above it. */
.why{background:var(--surface-container); border-radius:var(--r-md);
     padding:var(--s5) var(--s6); display:flex; flex-direction:column; gap:var(--s2)}
.why>b{font:var(--label-sm); letter-spacing:var(--label-sm-track);
       text-transform:uppercase; color:var(--on-surface-variant); font-weight:500;font-variant-numeric:tabular-nums}
.why p{font:var(--body-lg); letter-spacing:var(--body-lg-track);
       color:var(--on-surface); text-wrap:pretty;font-variant-numeric:tabular-nums}
/* Citations sit inline, immediately after the sentence they support. */
.cite{display:inline-flex; align-items:center; height:24px; padding:0 var(--s2);
      margin-left:var(--s2); border-radius:var(--r-sm);
      border:1px solid var(--outline); background:var(--surface-container-lowest);
      color:var(--on-surface);
      font:var(--label-md); letter-spacing:var(--label-md-track);
      vertical-align:middle; white-space:nowrap;font-variant-numeric:tabular-nums}
/* Said quietly, on the few items written from the syllabus rather than from
   published source text. It is a caveat, not a warning — loud styling here
   would read as "this answer is doubtful", which is not the claim. */
.why p.unverified{font:var(--body-md); letter-spacing:var(--body-md-track);
      color:var(--on-surface-variant); padding-top:var(--s2);
      border-top:1px solid var(--outline-variant);font-variant-numeric:tabular-nums}

/* ============================================================================
   DASHBOARD
   ========================================================================== */

/* The countdown owns the top of My study. Everything else on the page is
   subordinate to the number of days left. */
.countdown{background:var(--primary-container); color:var(--on-primary-container);
           border-radius:var(--r-md); padding:var(--s6) var(--s7);
           display:flex; flex-direction:column; gap:var(--s5)}
.countdown .days{display:flex; align-items:flex-end; gap:var(--s4)}
.countdown .n{font-size:96px; line-height:88px; letter-spacing:-2px;
              font-variant-numeric:tabular-nums}
.countdown .u{font:var(--headline-sm); letter-spacing:var(--headline-sm-track);
              padding-bottom:var(--s2);font-variant-numeric:tabular-nums}
.countdown .eyebrow{color:inherit}
.countdown p{font:var(--body-lg); letter-spacing:var(--body-lg-track);font-variant-numeric:tabular-nums}
.countdown p b{font-weight:500}
.countdown .acts{display:flex; gap:var(--s3); margin-top:var(--s1); flex-wrap:wrap}
.countdown .btn.ghost{border-color:var(--outline); color:var(--on-primary-container)}

/* Readiness against the pass mark. The pass line is a hard tick on the bar,
   not a colour change — the distance to it is the whole message. */
.ready{display:flex; flex-direction:column; gap:var(--s2)}
.ready .top{display:flex; align-items:baseline; justify-content:space-between;
            gap:var(--s2)}
.ready .track{position:relative; height:16px; border-radius:var(--r-sm);
              background:var(--surface-container-lowest)}
.ready .fill{position:absolute; inset:0 auto 0 0; border-radius:var(--r-sm);
             background:var(--primary);
             transition:width var(--dur-long) var(--ease-decelerate)}
.ready .line{position:absolute; top:-6px; bottom:-6px; width:2px;
             background:var(--on-surface)}
/* .cap, not .foot — .foot is the PAGE FOOTER, and this caption row was
   inheriting its background, top border, 40px side padding and margin-top:auto.
   Second instance of the same collision; see the note on .excard. */
.ready .cap{display:flex; justify-content:space-between; gap:var(--s4);
             font:var(--body-sm); letter-spacing:var(--body-sm-track);font-variant-numeric:tabular-nums}
.ready .cap b{font-weight:500}

/* The readiness summary under the three destination tiles on My Study and the
   course home. It was a bare <section>: a heading and a bar floating on the
   page background beside three filled tiles, which read as unfinished. .panel
   is the system's surface for this; .bandhead brings its own spacing, so the
   header padding is zeroed rather than doubled. */
.readypanel{gap:var(--s3)}
.readypanel .bandhead{padding:0}

/* Next sitting: a calendar tile and the two facts that matter. */
.sitting{display:flex; align-items:center; gap:var(--s4)}
.sitting .tile{width:64px; height:64px; flex:0 0 64px; border-radius:var(--r-md);
               background:var(--tertiary-container); color:var(--on-tertiary-container);
               display:flex; flex-direction:column; align-items:center;
               justify-content:center}
.sitting .tile .d{font:var(--label-sm); letter-spacing:var(--label-sm-track);font-variant-numeric:tabular-nums}
.sitting .tile .n{font:var(--title-lg); letter-spacing:var(--title-lg-track);font-variant-numeric:tabular-nums}
.sitting .when{font:var(--body-lg); letter-spacing:var(--body-lg-track);font-variant-numeric:tabular-nums}
.sitting .where{font:var(--body-md); letter-spacing:var(--body-md-track);
                color:var(--on-surface-variant);font-variant-numeric:tabular-nums}

.queue{display:flex; flex-direction:column; gap:var(--s2); text-decoration:none;
       color:inherit}
.queue:hover{text-decoration:none; color:inherit}
.queue .n{font:var(--display-sm); letter-spacing:var(--display-sm-track);font-variant-numeric:tabular-nums}
.queue .cta{font:var(--label-lg); letter-spacing:var(--label-lg-track);
            color:var(--primary);font-variant-numeric:tabular-nums}

/* The single weakest topic, called out by name. A dashboard that shows eight
   bars and no instruction leaves the reader to do the ranking themselves. */
.weakest{background:var(--error-container); color:var(--on-error-container);
         border-radius:var(--r-md); padding:var(--s6);
         display:flex; flex-direction:column; gap:var(--s3)}
.weakest .eyebrow{color:inherit}
.weakest .t{font:var(--title-lg); letter-spacing:var(--title-lg-track);font-variant-numeric:tabular-nums}
.weakest p{font:var(--body-md); letter-spacing:var(--body-md-track);font-variant-numeric:tabular-nums}
.weakest .acts{display:flex; gap:var(--s3); margin-top:var(--s1); flex-wrap:wrap}
.weakest .btn.ghost{border-color:currentColor; color:inherit}
/* The final-review panel is the same shape in the tertiary tone: what to read
   the day before, rather than what is failing now. */
.weakest.final{background:var(--tertiary-container); color:var(--on-tertiary-container)}

/* Three destinations, each a filled tile in its own tone. */
/* Was fixed at three columns; four tiles now (Learn, Practice, Mock, Print
   kit) and the grid flexes to fit rather than stretch its rows. On desktop
   this lands 2x2 or 4x1 depending on width; on mobile it stacks. */
.dests{display:grid; grid-template-columns:repeat(auto-fit, minmax(240px,1fr)); gap:var(--s6)}
.dest{display:flex; flex-direction:column; gap:var(--s3);
      padding:var(--s6) var(--s7); border-radius:var(--r-md);
      min-height:200px; text-decoration:none;
      background:var(--primary-container); color:var(--on-primary-container)}
.dest:hover{text-decoration:none; color:var(--on-primary-container)}
.dest.b{background:var(--secondary-container); color:var(--on-secondary-container)}
.dest.b:hover{color:var(--on-secondary-container)}
.dest.c{background:var(--tertiary-container); color:var(--on-tertiary-container)}
.dest.c:hover{color:var(--on-tertiary-container)}
/* Fourth tile - the paper-edition downloads. Uses a neutral surface tone so
   it does not compete with the three action tiles for the eye. */
.dest.d{background:var(--surface-container-high); color:var(--on-surface)}
.dest.d:hover{color:var(--on-surface)}
.dest .h{font:var(--headline-sm); letter-spacing:var(--headline-sm-track);font-variant-numeric:tabular-nums}
.dest .d{font:var(--body-md); letter-spacing:var(--body-md-track);font-variant-numeric:tabular-nums}
.dest .f{margin-top:auto; font:var(--body-md);
         letter-spacing:var(--body-md-track);font-variant-numeric:tabular-nums}
.dest svg{width:20px; height:20px}

/* ============================================================================
   LEARN
   The teaching half. Two screens: an index of the syllabus, and one topic.
   ========================================================================== */

/* The index is a list, not a grid of tiles. Nine topics in a fixed order that a
   candidate works down is a sequence, and a grid says "pick any" — the wrong
   invitation for a syllabus where topic 1 sets up topic 3. */
.lessons{display:flex; flex-direction:column;
         border:1px solid var(--outline-variant); border-radius:var(--r-md);
         overflow:hidden; background:var(--surface-container-lowest)}
.lesson{display:grid; grid-template-columns:96px 1fr 120px 32px; align-items:center;
        gap:var(--s5); padding:var(--s5) var(--s6); text-decoration:none;
        color:var(--on-surface); border-top:1px solid var(--outline-variant)}
.lesson:first-child{border-top:none}
.lesson:hover{text-decoration:none; color:var(--on-surface);
              background:var(--surface-container-low)}
/* A pill reading "Topic 4", not a bare numbered circle. The circle looked like
   a step counter for the list itself; the number a candidate cares about is the
   syllabus topic number, which is what every other source they own refers to. */
.lesson .no{display:inline-flex; align-items:center; justify-content:center;
            height:32px; padding:0 var(--s3); border-radius:var(--r-full);
            background:var(--surface-container-high); color:var(--on-surface-variant);
            font:var(--label-lg); letter-spacing:var(--label-lg-track);
            white-space:nowrap;font-variant-numeric:tabular-nums}
.lesson.done .no{background:var(--primary); color:var(--on-primary)}
.lesson.part .no{background:var(--secondary-container); color:var(--on-secondary-container)}
.lesson .mid{display:flex; flex-direction:column; gap:var(--s1); min-width:0}
.lesson .h{font:var(--title-md); letter-spacing:var(--title-md-track);font-variant-numeric:tabular-nums}
.lesson .d{font:var(--body-md); letter-spacing:var(--body-md-track);
           color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.lesson .f{font:var(--body-sm); letter-spacing:var(--body-sm-track);
           color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
/* What the topic is worth, drawn rather than stated. "8 of 60 marks" made the
   reader do the division before they could compare two topics; a bar the width
   of the topic's share does it for them at a glance. The raw marks stay in the
   title attribute for anyone who wants the number. */
.lesson .weight{display:flex; align-items:center; gap:var(--s3); margin-top:var(--s1)}
.lesson .wtrack{width:120px; height:6px; border-radius:var(--r-xs);
                background:var(--surface-container-highest); overflow:hidden; flex:0 0 auto}
.lesson .wfill{display:block; height:6px; border-radius:var(--r-xs); background:var(--tertiary)}
.lesson .wtxt{font:var(--body-sm); letter-spacing:var(--body-sm-track);
              color:var(--on-surface-variant);font-variant-numeric:tabular-nums}

.lesson .prog{display:flex; flex-direction:column; align-items:flex-end; gap:var(--s1); text-align:right}
.lesson .pnum{font:var(--title-lg); letter-spacing:var(--title-lg-track);
              color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.lesson.part .pnum{color:var(--primary)}
.lesson.done .pnum{color:var(--primary)}
.lesson .psub{font:var(--body-sm); letter-spacing:var(--body-sm-track);
              color:var(--on-surface-variant);font-variant-numeric:tabular-nums}

.bignum-sm{font:var(--title-lg); letter-spacing:var(--title-lg-track);
           color:var(--primary);font-variant-numeric:tabular-nums}

.lesson .go{display:flex; justify-content:flex-end; color:var(--on-surface-variant)}
.lesson .go svg{width:20px; height:20px}
/* A topic in the blueprint with nothing written yet still appears, greyed. It
   is the honest state: hiding it would make the syllabus look complete. */
.lesson.soon{color:var(--on-surface-variant)}
.lesson.soon .no{background:var(--surface-container)}
.lesson .pending{font:var(--label-sm); letter-spacing:var(--label-sm-track);
                 color:var(--on-surface-variant); white-space:nowrap;font-variant-numeric:tabular-nums}

/* One step of a topic: the block on the left, the outline of the topic on the
   right. The page fills its width — a measure-limited column against a
   left-hand rail reads as a page that failed to load the rest of itself — while
   the prose inside the card stays at a readable measure. */
.steplayout{display:grid; grid-template-columns:minmax(0,1fr) 320px; gap:var(--s7); align-items:start}
.stepmain{display:flex; flex-direction:column; gap:var(--s6); min-width:0}
/* The cards fill the column and the PROSE inside them is what gets measured.
   Capping the card instead left a narrow box floating in the middle of the
   page, which is the thing that made the old lesson page look broken. */
.stepmain .lcard{margin-bottom:0}
.stepmain .lcard .eyebrow{display:block; margin-bottom:var(--s2)}
.stepmain .lcard p,.stepmain .lcard li,.stepmain .lcard h2{max-width:none}

.stepnav{position:sticky; top:var(--s5); display:flex; flex-direction:column; gap:var(--s4);
         padding:var(--s5); border:1px solid var(--outline-variant);
         border-radius:var(--r-md); background:var(--surface-container-lowest)}
.stepnav .sn-head{display:flex; flex-direction:column; gap:var(--s2)}
.stepnav .crumb{display:inline-flex; align-items:center; gap:var(--s1);
                font:var(--label-lg); letter-spacing:var(--label-lg-track)}
.stepnav .crumb svg{width:18px; height:18px; transform:rotate(180deg)}
.stepnav .sn-t{font:var(--title-md); letter-spacing:var(--title-md-track)}
.stepnav .sn-bar{height:6px; border-radius:var(--r-xs);
                 background:var(--surface-container-highest); overflow:hidden}
.stepnav .sn-bar span{display:block; height:6px; background:var(--primary)}
.stepnav .sn-m{font:var(--body-sm); letter-spacing:var(--body-sm-track);
               color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.stepnav .sn-list{list-style:none; display:flex; flex-direction:column; gap:var(--s1);
                  margin:0; padding:0}
.stepnav .sn-list a{display:flex; align-items:flex-start; gap:var(--s3);
                    padding:var(--s2) var(--s3); border-radius:var(--r-sm);
                    color:var(--on-surface-variant); text-decoration:none;
                    font:var(--body-md); letter-spacing:var(--body-md-track)}
.stepnav .sn-list a:hover{background:var(--surface-container); text-decoration:none}
.stepnav .sn-list .on a{background:var(--secondary-container);
                        color:var(--on-secondary-container); font-weight:500}
.stepnav .sn-list .tick{display:inline-flex; align-items:center; justify-content:center;
                        width:22px; height:22px; flex:0 0 22px; border-radius:var(--r-full);
                        background:var(--surface-container-high); color:var(--on-surface-variant);
                        font:var(--label-sm); letter-spacing:var(--label-sm-track);font-variant-numeric:tabular-nums}
.stepnav .sn-list .done .tick{background:var(--primary); color:var(--on-primary)}
.stepnav .sn-list .done .tick svg{width:14px; height:14px}

/* The check. Same choice control as the player, so answering a question inside
   a lesson feels like answering one anywhere else in the product.
 *
 * NOT .check — the player's "I'm not sure about this one" is a `.check` and
 * this rule turned it into a centred column with a card around it. Third time
 * a bare word claimed by another component has leaked; see .stat and .foot. */
.stepcheck{display:flex; flex-direction:column; gap:var(--s5);
       padding:var(--s6) var(--s7); border-radius:var(--r-md);
       border:1px solid var(--outline-variant); background:var(--surface-container-lowest)}
.stepcheck h2{font:var(--title-lg); letter-spacing:var(--title-lg-track)}
.stepcheck .checkhead{display:flex; align-items:baseline; justify-content:space-between; gap:var(--s4)}
.stepcheck .score{font:var(--title-md); letter-spacing:var(--title-md-track); color:var(--error)}
.stepcheck .score.ok{color:var(--primary)}
.stepcheck .acts{display:flex; gap:var(--s4); flex-wrap:wrap}
/* Air between questions. Two question blocks with only the option gap between
   them read as one eight-option question. */
.cq{border:none; padding:0; margin:0; display:flex; flex-direction:column; gap:var(--s3)}
/* Space, not a rule. A border-top on a fieldset is interrupted by its own
   legend, so the line came out as a stub floating to the right of the question
   text. Margin does the whole job here. */
.cq + .cq{margin-top:var(--s9)}
.cq legend{display:flex; gap:var(--s3); padding:0; margin-bottom:var(--s3);
           max-width:var(--measure);
           font:var(--title-md); letter-spacing:var(--title-md-track); text-wrap:pretty}
.stepcheck p{max-width:var(--measure)}
.cq .cqn{display:inline-flex; align-items:center; justify-content:center;
         width:24px; height:24px; flex:0 0 24px; border-radius:var(--r-full);
         background:var(--surface-container-high); color:var(--on-surface-variant);
         font:var(--label-sm); letter-spacing:var(--label-sm-track);font-variant-numeric:tabular-nums}
.stepcheck .rcard{margin:0}

@media (max-width:1100px){
  .steplayout{grid-template-columns:1fr}
  .stepnav{position:static; order:-1}
  .stepnav .sn-list{display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr))}
}
@media (max-width:820px){
  .lesson{grid-template-columns:1fr auto; gap:var(--s3)}
  .lesson .no{grid-column:1; justify-self:start}
  .lesson .prog{grid-column:2; grid-row:1; align-items:flex-end}
  .lesson .mid{grid-column:1 / -1}
  .lesson .go{display:none}
}
.covers{display:flex; flex-wrap:wrap; gap:var(--s2)}
.chip.static{pointer-events:none}

.lcard{background:var(--surface-container-lowest);
       border:1px solid var(--outline-variant); border-radius:var(--r-md);
       padding:var(--s6) var(--s7); margin-bottom:var(--s5)}
.lcard h2{font:var(--title-lg); letter-spacing:var(--title-lg-track);
          margin-bottom:var(--s3);font-variant-numeric:tabular-nums}
.lcard p{font:var(--body-lg); letter-spacing:var(--body-lg-track);
         color:var(--on-surface-variant); margin-bottom:var(--s3);font-variant-numeric:tabular-nums}
.lcard ul{list-style:none; display:flex; flex-direction:column; gap:var(--s3);
          margin-bottom:var(--s3)}
/* The marker is drawn rather than a list-style bullet so it aligns with the
   first line's cap height instead of its baseline. */
.lcard li{position:relative; padding-left:var(--s5);
          font:var(--body-lg); letter-spacing:var(--body-lg-track);
          color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.lcard li::before{content:""; position:absolute; left:0; top:0.6em;
                  width:6px; height:6px; border-radius:var(--r-full);
                  background:var(--primary)}
.lcard b{color:var(--on-surface); font-weight:500}
.lcard>*:last-child{margin-bottom:0}
.lcard .cites{display:flex; flex-wrap:wrap; gap:var(--s2);
              padding-top:var(--s3); border-top:1px solid var(--outline-variant)}
.lesson-figure{margin:var(--s5) 0 var(--s3); padding:var(--s4);
               border:1px solid var(--outline-variant); border-radius:var(--r-sm);
               background:var(--surface-container-low)}
.lesson-figure img{display:block; width:100%; max-height:520px; object-fit:contain}
.lesson-figure figcaption{margin-top:var(--s3); font:var(--body-sm);
                          color:var(--on-surface-variant)}

/* The practice call sits at the foot of every topic, in the primary tone, so
   the page ends on the thing the product is arguing for: read it, then test it
   while it is still in your head. */
.lessonfoot{background:var(--primary-container); color:var(--on-primary-container);
            border-radius:var(--r-md); padding:var(--s6) var(--s7);
            display:flex; flex-direction:column; gap:var(--s3);
            margin-top:var(--s7)}
.lessonfoot h2{font:var(--title-lg); letter-spacing:var(--title-lg-track); color:inherit;font-variant-numeric:tabular-nums}
.lessonfoot p{font:var(--body-md); letter-spacing:var(--body-md-track); color:inherit;font-variant-numeric:tabular-nums}
.lessonfoot .acts{display:flex; flex-wrap:wrap; align-items:center; gap:var(--s3);
                  margin-top:var(--s2)}
.lessonfoot .btn.ghost{border-color:currentColor; color:inherit}

.lessonnav{display:flex; justify-content:space-between; gap:var(--s4);
           margin-top:var(--s6)}

/* A button whose label is content, not a fixed string. "Continue — Licensing,
   registration and subsidiary legislation" is 380px of nowrap text and pushed
   a 390px phone into sideways scroll; it now ellipses instead. */
.btn.trunc{max-width:100%}
.btn.trunc>span{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}

/* ============================================================================
   READINESS
   ========================================================================== */

.mastery{display:grid; grid-template-columns:1fr 140px 200px 72px 96px;
         align-items:center; gap:var(--s4);
         padding:var(--s3) var(--s6); border-top:1px solid var(--outline-variant)}
.mastery .name{font:var(--body-md); letter-spacing:var(--body-md-track);font-variant-numeric:tabular-nums}
.mastery .pct{text-align:right; font:var(--title-sm);
              letter-spacing:var(--title-sm-track);font-variant-numeric:tabular-nums}
.mastery .pct.bad{color:var(--error)}
.mastery .bar,.mastery .bar-fill{height:8px; border-radius:var(--r-xs)}
.mastery .btn{justify-self:end}
.lvl{display:inline-flex; align-items:center; justify-content:center; height:24px;
     padding:0 var(--s2); border-radius:var(--r-sm);
     font:var(--label-sm); letter-spacing:var(--label-sm-track);
     text-transform:uppercase; border:1px solid transparent;font-variant-numeric:tabular-nums}
.lvl.untouched{background:var(--surface-container-lowest);
               color:var(--on-surface-variant); border-color:var(--outline-variant)}
.lvl.learning{background:var(--error-container); color:var(--on-error-container)}
.lvl.solid{background:var(--secondary-container); color:var(--on-secondary-container)}
.lvl.mastered{background:var(--primary); color:var(--on-primary)}

.nbrow{display:grid; grid-template-columns:1fr 230px 176px 96px;
       align-items:center; gap:var(--s4);
       padding:var(--s3) var(--s6); border-top:1px solid var(--outline-variant)}
.nbrow .stem{font:var(--body-md); letter-spacing:var(--body-md-track);
             color:var(--on-surface);
             overflow:hidden; text-overflow:ellipsis; white-space:nowrap;font-variant-numeric:tabular-nums}
.nbrow .m,.nbrow .topic{font:var(--body-sm); letter-spacing:var(--body-sm-track);
                        color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.nbrow .btn{justify-self:end}
.nbfilters{display:flex; gap:var(--s2); padding:0 var(--s6) var(--s4)}

/* ============================================================================
   SETTINGS
   ========================================================================== */

.who-card{display:flex; align-items:center; gap:var(--s5); padding:var(--s6)}
.avatar{width:64px; height:64px; flex:0 0 64px; border-radius:var(--r-full);
        background:var(--primary-container); color:var(--on-primary-container);
        display:flex; align-items:center; justify-content:center;
        font:var(--title-lg); letter-spacing:var(--title-lg-track); font-weight:500;font-variant-numeric:tabular-nums}
.who-card .nm{font:var(--title-lg); letter-spacing:var(--title-lg-track);font-variant-numeric:tabular-nums}
.who-card .em{font:var(--body-md); letter-spacing:var(--body-md-track);
              color:var(--on-surface-variant);font-variant-numeric:tabular-nums}

.kv{display:flex; align-items:center; gap:var(--s4);
    padding:var(--s3) var(--s6); border-top:1px solid var(--outline-variant)}
.kv .k{width:200px; flex:0 0 200px;
       font:var(--label-sm); letter-spacing:var(--label-sm-track);
       text-transform:uppercase; color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.kv .v{flex:1 1 auto; font:var(--body-md); letter-spacing:var(--body-md-track);font-variant-numeric:tabular-nums}
/* The row action ("Change", "Manage"). A 24px target in a 56px row — give it
   the row height so the whole line is comfortably tappable. */
.kv>a{display:inline-flex; align-items:center; min-height:32px}

.buyrow{display:grid; grid-template-columns:160px 1fr 220px 110px;
        align-items:center; gap:var(--s4);
        padding:var(--s3) var(--s6); border-top:1px solid var(--outline-variant);
        font:var(--body-md); letter-spacing:var(--body-md-track);font-variant-numeric:tabular-nums}
.buyrow .date,.buyrow .access{color:var(--on-surface-variant)}
.buyrow .amt{text-align:right; font-weight:500}
.buyrow.head{border-top:none; padding-top:0; padding-bottom:var(--s3);
             font:var(--label-sm); letter-spacing:var(--label-sm-track);
             text-transform:uppercase; color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.buyrow.head .amt{font-weight:500}

/* ============================================================================
   PRODUCT PAGE
   ========================================================================== */

.pdp{display:flex; flex-direction:column; gap:var(--s10);
     padding:var(--s9) var(--s8) var(--s13)}
.hero-pdp{display:grid; grid-template-columns:1fr 400px; gap:var(--s9);
          align-items:start}
.hero-copy{display:flex; flex-direction:column; gap:var(--s5); padding-top:var(--s2)}
.hero-copy h1{font:var(--display-lg); letter-spacing:var(--display-lg-track);
              text-wrap:balance;font-variant-numeric:tabular-nums}
.hero-cta{display:flex; gap:var(--s3); margin-top:var(--s2); flex-wrap:wrap}

/* Three numbers, stated plainly. Not four boxes in a grid — the figures do the
   work and a card around each would only dilute them. */
.figures{display:flex; gap:var(--s8); padding-top:var(--s1); flex-wrap:wrap}
.figures div{display:flex; flex-direction:column; gap:var(--s1)}
.figures .n{font:var(--display-sm); letter-spacing:var(--display-sm-track);
            color:var(--primary);font-variant-numeric:tabular-nums}
.figures .l{font:var(--body-md); letter-spacing:var(--body-md-track);
            color:var(--on-surface-variant);font-variant-numeric:tabular-nums}

/* Follows the page. The product page is long — pass-rate table, blueprint,
   trial steps — and the decision the reader is being asked to make should not
   scroll off the top of it. Sticks below the app bar, and stops being sticky
   the moment the column is too short to be worth it. */
.buybox{position:sticky; top:calc(var(--appbar-h) + var(--s6));
        background:var(--surface-container-lowest);
        border:1px solid var(--outline-variant); border-radius:var(--r-md);
        padding:var(--s6); display:flex; flex-direction:column; gap:var(--s5);
        max-height:calc(100vh - var(--appbar-h) - var(--s9)); overflow-y:auto}
.buybox .price{display:flex; align-items:baseline; gap:var(--s2)}
.buybox .amt{font:var(--display-md); letter-spacing:var(--display-md-track);font-variant-numeric:tabular-nums}
.buybox .per{font:var(--body-md); letter-spacing:var(--body-md-track);
             color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.buybox.owned{background:var(--primary-container); color:var(--on-primary-container);
              border-color:transparent}

/* Label left, value right, each pair on its own row with a rule between.
   The values were right-aligned in a two-column grid, which is fine until one
   of them wraps — "Multiple choice, computer-based, closed book" then breaks
   with a single orphaned word hard against the right edge. Wrapping values get
   left alignment; short ones still sit right, because the row is a flex pair
   rather than a rigid grid. */
.facts{margin:0; background:var(--surface-container); border-radius:var(--r-md);
       padding:var(--s2) var(--s6); display:flex; flex-direction:column}
.facts dt{flex:0 0 auto; font:var(--label-md); letter-spacing:var(--label-md-track);
          text-transform:uppercase; color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.facts dd{margin:0; flex:1 1 auto; min-width:0; text-align:right;
          font:var(--body-lg); letter-spacing:var(--body-lg-track);
          text-wrap:pretty;font-variant-numeric:tabular-nums}
.facts .r{display:flex; align-items:baseline; justify-content:space-between;
          gap:var(--s6); padding:var(--s3) 0;
          border-top:1px solid var(--outline-variant)}
.facts .r:first-child{border-top:none}
/* The qualification under a fact, for the rows where the number alone would
   mislead - a pass mark quoted against a form that carries unscored questions.
   Second text level, so it reads as the explanation and not as another fact. */
/* Left-aligned inside a block pushed to the right. Three ragged-left lines under
   a right-aligned value are hard to read; this keeps the note under its number
   and still gives every line the same starting edge. */
.facts .factnote{display:block; margin-top:var(--s1); max-width:44ch; margin-left:auto;
                 font:var(--body-sm); letter-spacing:var(--body-sm-track);
                 color:var(--on-surface-variant); text-wrap:pretty; text-align:left}
/* A row carrying one stops sharing a baseline with its label - the note makes
   the value two or three lines tall and the term would float in the middle. */
.facts .r-note{align-items:start}

/* The sitting calendar on the product page. Reuses the tile and the two text
   lines from .sitting on the exam-date screen; the wrapper is what differs,
   because there it is one next sitting and here it is the next eight. */
/* 260 rather than 240: at 240 the grid fits four across 1280 and "15 September
   2026" wraps, which sets the whole row taller for one long month name. */
.sitlist{display:grid; grid-template-columns:repeat(auto-fill,minmax(260px,1fr)); gap:var(--s4)}
.sitrow{display:flex; align-items:center; gap:var(--s4);
        background:var(--surface-container); border-radius:var(--r-md); padding:var(--s3) var(--s4)}
.sitrow .tile{width:56px; height:56px; flex:0 0 56px; border-radius:var(--r-md);
              display:flex; flex-direction:column; align-items:center; justify-content:center;
              background:var(--secondary-container); color:var(--on-secondary-container)}
.sitrow .tile .d{font:var(--label-sm); letter-spacing:var(--label-sm-track);font-variant-numeric:tabular-nums}
.sitrow .tile .n{font:var(--title-lg); letter-spacing:var(--title-lg-track);font-variant-numeric:tabular-nums}
.sitrow .when{font:var(--body-lg); letter-spacing:var(--body-lg-track);font-variant-numeric:tabular-nums}
.sitrow .where{font:var(--body-sm); letter-spacing:var(--body-sm-track);
               color:var(--on-surface-variant);font-variant-numeric:tabular-nums; text-wrap:pretty}
/* text-wrap:pretty so the caveat does not end on a one-word line - the
   authority names are long enough to leave an orphan without it. */
#sittings .lede-sm{margin-top:var(--s4); text-wrap:pretty}

.trial{display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s5)}
.trial-step{background:var(--surface-container-lowest);
            border:1px solid var(--outline-variant); border-radius:var(--r-md);
            padding:var(--s6); display:flex; flex-direction:column; gap:var(--s2)}
.trial-step .n{width:32px; height:32px; border-radius:var(--r-full);
               background:var(--primary-container); color:var(--on-primary-container);
               display:flex; align-items:center; justify-content:center;
               font:var(--label-lg); letter-spacing:var(--label-lg-track);font-variant-numeric:tabular-nums}
.trial-step.paid .n{background:var(--primary); color:var(--on-primary)}
.trial-step h3{font:var(--title-md); letter-spacing:var(--title-md-track);font-variant-numeric:tabular-nums}
.trial-step p{font:var(--body-md); letter-spacing:var(--body-md-track);
              color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.trial-step .btn{align-self:flex-start; margin-top:auto}

/* The same three-destination pattern as .dest and .hpstep: a fixed sequence the
   reader moves along, so it takes the same three grounds in the same order. The
   third is the paid step and already carries .paid, so it gets the slate and the
   weight that goes with being the thing the page is asking for. */
.trial .trial-step{border-color:transparent}
.trial .trial-step:nth-child(1){background:var(--primary-container); color:var(--on-primary-container)}
.trial .trial-step:nth-child(2){background:var(--secondary-container); color:var(--on-secondary-container)}
.trial .trial-step.paid{background:var(--tertiary-container); color:var(--on-tertiary-container)}
.trial .trial-step p{color:inherit; opacity:.82}
/* Solid, not a white alpha — see the note on .hpstep. */
.trial .trial-step .n{background:var(--surface-container-lowest); color:inherit}
.trial .trial-step.paid .n{background:var(--tertiary-container-high); color:inherit}
.trial .trial-step .btn.ghost{border-color:currentColor; color:inherit}
.trial .trial-step.paid .btn{background:var(--surface-container-lowest);
                             color:var(--tertiary-container)}

/* Sticky purchase bar. Anchored to the content column, not the viewport edge,
   so it never runs under the rail. */
.stickybuy{position:fixed; left:var(--rail-w); right:0; bottom:0; height:72px;
           background:var(--surface-container-high);
           border-top:1px solid var(--outline-variant);
           display:flex; align-items:center; gap:var(--s6); padding:0 var(--s8);
           z-index:5}
.stickybuy .grow{flex:1 1 auto}
/* The bar sits on a tinted surface, where --primary lands at 4.34:1 - just under
   AA. The darker container tone is the system's answer for exactly this. */
.stickybuy .btn.ghost{color:var(--on-primary-container)}
.stickybuy .t{font:var(--title-md); letter-spacing:var(--title-md-track);font-variant-numeric:tabular-nums}
.stickybuy .m{font:var(--body-sm); letter-spacing:var(--body-sm-track);
              color:var(--on-surface-variant);font-variant-numeric:tabular-nums}

/* ============================================================================
   EXAM LIST
   ========================================================================== */

.filters{display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap}
.filters .field{flex:0 0 420px}

.cards{display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s5)}
/* The inner spans are named .spec / .btm, not .stat / .foot, because those two
   names were already taken by the admin stat tile and the PAGE FOOTER. Both
   leaked in: .stat gave every card's one-line summary a bordered grey box, and
   .foot gave the bottom row the footer's background, its 40px side padding and
   its margin-top:auto — which is where the empty gap in the second card came
   from. Neither was visible in .excard's own rules, which is what made it hard
   to see: the more specific selector set font and colour, so the generic rule
   still won everything it did not mention.

   The lesson is in the naming, not the reset. A component's internals must not
   use a bare word that another component has claimed globally. */
.excard{display:flex; flex-direction:column; gap:var(--s2); padding:var(--s6);
        border-radius:var(--r-md); background:var(--surface-container-lowest);
        border:1px solid var(--outline-variant); text-decoration:none;
        color:inherit}
.excard:hover{text-decoration:none; color:inherit}
.excard .nm{font:var(--title-lg); letter-spacing:var(--title-lg-track);font-variant-numeric:tabular-nums}
.excard .spec{font:var(--body-md); letter-spacing:var(--body-md-track);
              color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
/* Pinned to the bottom so the bottom rows line up across cards whose titles
   and summaries wrap to different heights. */
.excard .rule{height:1px; background:var(--outline-variant);
              margin:var(--s4) 0 var(--s3); margin-top:auto}
.excard .btm{display:flex; align-items:baseline; justify-content:space-between;
             gap:var(--s4)}
.excard .rate{font:var(--body-md); letter-spacing:var(--body-md-track);
              color:var(--on-surface-variant); min-width:0;font-variant-numeric:tabular-nums}
/* The price is two short words and must never be the thing that wraps. */
.excard .price{font:var(--label-lg); letter-spacing:var(--label-lg-track);
               color:var(--primary); white-space:nowrap; flex:0 0 auto;font-variant-numeric:tabular-nums}
/* An exam still being built is a state, not a statistic, and it was set in the
   same grey and weight as "600 questions" on the card beside it — directly next
   to a price. The chip says it once, and the price steps back because there is
   nothing to buy yet. */
.excard.prep .rate{display:inline-flex; align-items:center; height:22px; padding:0 var(--s3);
                   border-radius:var(--r-full); background:var(--secondary-container);
                   color:var(--on-secondary-container); font:var(--label-sm);
                   letter-spacing:var(--label-sm-track); text-transform:uppercase}
.excard.prep .price{color:var(--on-surface-variant); font-weight:400}

/* ============================================================================
   TABLES
   ========================================================================== */

.data{font:var(--body-md); letter-spacing:var(--body-md-track);font-variant-numeric:tabular-nums}
.data caption{font:var(--body-sm); letter-spacing:var(--body-sm-track);
              color:var(--on-surface-variant); padding-bottom:var(--s2);font-variant-numeric:tabular-nums}
.data th{text-align:left; padding:var(--s2) 0;
         border-bottom:1px solid var(--outline);
         font:var(--title-sm); letter-spacing:var(--title-sm-track);
         color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.data td{padding:var(--s3) 0; border-bottom:1px solid var(--outline-variant)}
.data .num,.data th.num{text-align:right}
.data td.num{color:var(--on-surface-variant)}
.data tr.tot td{border-bottom:none; font-weight:500; color:var(--on-surface)}

/* The data browser: many narrow columns, so every cell gets a vertical rule and
   nothing wraps. Reading a database is a scanning task, not a reading one. */
.data.grid{font:var(--body-sm); letter-spacing:var(--body-sm-track);
           font-variant-numeric:tabular-nums; white-space:nowrap}
.data.grid th,.data.grid td{padding:var(--s2) var(--s3); border-right:1px solid var(--outline-variant)}
.data.grid th:last-child,.data.grid td:last-child{border-right:none}
.data.grid tbody tr:hover{background:var(--surface-container-low)}
.data.grid .null{color:var(--on-surface-38)}
.chip b{font-weight:500; opacity:.7}
.data .good{color:var(--on-surface)}
.data .bad{color:var(--error)}

/* A wide table inside a card scrolls itself rather than widening the page. */
.tablewrap{padding:0 var(--s6) var(--s5); overflow-x:auto}

.statrow{display:grid; grid-template-columns:repeat(auto-fit,minmax(160px,1fr));
         gap:var(--s4)}
.stat{background:var(--surface-container-low);
      border:1px solid var(--outline-variant); border-radius:var(--r-md);
      padding:var(--s5); display:flex; flex-direction:column; gap:var(--s1)}
.statnum{font:var(--display-sm); letter-spacing:var(--display-sm-track);
         font-variant-numeric:tabular-nums}
.stat span:last-child{font:var(--body-md); letter-spacing:var(--body-md-track);
                      color:var(--on-surface-variant);font-variant-numeric:tabular-nums}

/* ============================================================================
   SIGN IN
   ========================================================================== */

.signin{max-width:480px}
.email-login{margin:0}
.email-login summary{list-style:none; cursor:pointer}
.email-login summary::-webkit-details-marker{display:none}
.email-login[open] summary{background:var(--surface-container-low)}
.email-login form{margin-top:var(--s3); padding:var(--s4); border:1px solid var(--outline-variant); border-radius:var(--r-md); background:var(--surface-container-lowest)}
.email-login label{display:block; margin-bottom:var(--s2); font-weight:600}
.email-login-row{display:flex; gap:var(--s2)}
.email-login-row input{flex:1; min-width:0; border:1px solid var(--outline); border-radius:var(--r-sm); padding:0 var(--s3); font:inherit}
.email-login .hint{margin:var(--s2) 0 0}
.oauths{display:flex; flex-direction:column; gap:var(--s3)}
.oauth{display:flex; align-items:center; gap:var(--s3); height:56px;
       padding:0 var(--s6); border-radius:var(--r-xl);
       border:1px solid var(--outline); background:var(--surface-container-lowest);
       color:var(--on-surface); text-decoration:none;
       font:var(--title-md); letter-spacing:var(--title-md-track);font-variant-numeric:tabular-nums}
.oauth:hover{text-decoration:none; color:var(--on-surface)}
.oauth svg{flex:0 0 auto; width:18px; height:18px}
.oauth i{font-style:normal; color:var(--on-surface-variant)}

/* ============================================================================
   RESPONSIVE
   Desktop is the design target. These breakpoints keep a laptop and a tablet
   usable; they are a graceful degradation, not a second design.
   ========================================================================== */

/* 1279, not 1280. A MacBook Air and most 13" laptops report exactly 1280px, and
   max-width is inclusive — so the single most common laptop width was getting
   the tablet layout. Two columns fit at 1281 with room to spare, so they fit at
   1280 too. Measured, not assumed. */
@media (max-width:1279px){
  .split,.split.rev,.split.wideaside{grid-template-columns:1fr}
  .hero-pdp{grid-template-columns:1fr; gap:var(--s7)}
  .buybox{max-width:480px}
}

@media (max-width:1100px){
  .cards,.three,.dests,.trial{grid-template-columns:repeat(2,1fr)}
  .four{grid-template-columns:repeat(2,1fr)}
  .mastery{grid-template-columns:1fr 120px 100px 88px}
  .mastery .bar{display:none}
  .nbrow{grid-template-columns:1fr 140px 96px}
  .nbrow .topic{display:none}
}

@media (max-width:720px){
  /* The chevron column costs 32px that the title needs more. */
  .lesson{grid-template-columns:40px 1fr; gap:var(--s3); padding:var(--s4) var(--s5)}
  .lesson .go{display:none}
  .lesson .pending{display:none}
  .lcard{padding:var(--s5) var(--s5)}
  .lesson-figure{margin-left:calc(-1 * var(--s2)); margin-right:calc(-1 * var(--s2)); padding:var(--s2)}
  .lessonfoot{padding:var(--s5)}
  .lessonfoot .acts .btn{width:100%; justify-content:center}
  .lessonfoot .acts form.inline{display:block; width:100%}
  .lessonnav{flex-direction:column; gap:var(--s2)}
}

@media (max-width:820px){
  :root{--rail-w:72px}
  .rail a{width:64px}
  .rail .pip{width:48px}
  .page{padding:var(--s6) var(--s5) var(--s8)}
  .pdp{padding:var(--s7) var(--s5) var(--s13)}
  .cards,.three,.dests,.trial,.four{grid-template-columns:1fr}
  .hero-copy h1{font:var(--display-sm); letter-spacing:var(--display-sm-track);font-variant-numeric:tabular-nums}
  .scorehead{padding:var(--s7) var(--s5); gap:var(--s6)}
  .scorehead .bignum{font-size:72px; line-height:72px}
  .countdown .n{font-size:72px; line-height:68px}
  .topicrow{grid-template-columns:1fr 64px 88px}
  .topicrow .bar{display:none}
  .kv{flex-wrap:wrap}
  .kv .k{flex-basis:100%; width:auto}
  .buyrow{grid-template-columns:1fr auto; gap:var(--s2)}
  .buyrow.head{display:none}
  .qform{padding:var(--s6) var(--s5)}
  .playbar{padding:0 var(--s5); gap:var(--s4)}
  .stickybuy{left:0; padding:0 var(--s5)}
}

/* ---------------------------------------------------------------- compact
   Below 720 the rail becomes M3's navigation bar: the same four destinations,
   fixed to the bottom, horizontal. This is the system's own answer to a
   compact window, not an invention — and it keeps the promise the rail makes
   on desktop, that every destination is one tap away with nothing hidden.

   This is a graceful narrowing, not a phone design. Desktop remains the
   product we optimise; this exists so a candidate who opens a result on their
   phone can read it. */
@media (max-width:720px){
  :root{--rail-w:0px; --appbar-h:56px}

  .shell{flex-direction:column}

  .burger{display:inline-flex; align-items:center; justify-content:center;
          width:40px; height:40px; margin-left:calc(var(--s2) * -1);
          border-radius:var(--r-full); color:var(--on-surface-variant); cursor:pointer}
  .burger svg{width:24px; height:24px}
  .navtoggle:focus-visible ~ .col .burger{outline:3px solid var(--on-secondary-container); outline-offset:2px}

  .rail{position:fixed; top:0; bottom:0; left:0; right:auto; z-index:50;
        width:280px; max-width:82vw; flex:0 0 auto; height:100dvh;
        flex-direction:column; justify-content:flex-start; align-items:stretch;
        padding:var(--s4) var(--s3); gap:var(--s1);
        background:var(--surface-container); border-right:1px solid var(--outline-variant);
        transform:translateX(-100%); transition:transform .18s ease-out}
  .navtoggle:checked ~ .rail{transform:none}
  @media (prefers-reduced-motion:reduce){.rail,.scrim{transition:none}}
  .rail .mark{display:none}
  /* A drawer row reads left to right, like every other list in the product. */
  .rail a{width:auto; flex:0 0 auto; flex-direction:row; justify-content:flex-start;
          gap:var(--s4); padding:0 var(--s4); height:48px; border-radius:var(--r-full)}
  .rail .pip{width:24px; height:24px; background:none}
  .rail a[aria-current="page"]{background:var(--secondary-container)}
  .rail a[aria-current="page"] .pip{background:none}
  .rail .lbl{font:var(--body-md); letter-spacing:var(--body-md-track); white-space:nowrap}

  .scrim{display:block; position:fixed; inset:0; z-index:45;
         background:rgba(0,0,0,.32); opacity:0; pointer-events:none;
         transition:opacity .18s ease-out}
  .navtoggle:checked ~ .scrim{opacity:1; pointer-events:auto}

  /* Nothing is pinned to the foot of the screen any more, so the clearance the
     bottom bar needed goes with it. */
  .stickybuy{bottom:0}

  /* The app bar keeps the brand, the countdown and the way out. The email
     address is the first thing to go: it identifies an account the person is
     already inside, which is the least useful thing on a 390px row. */
  .appbar{padding:0 var(--s4); gap:var(--s2); flex-wrap:nowrap}
  .appbar .who{display:none}
  .appbar .brand{font:var(--title-md); letter-spacing:var(--title-md-track);font-variant-numeric:tabular-nums}
  .urgency{min-width:0; overflow:hidden; text-overflow:ellipsis}

  .split,.split.rev,.split.wideaside{gap:var(--s4)}
  .page{gap:var(--s5)}
  .filters .field{flex:1 1 100%}
  .buybox{max-width:none}
  .figures{gap:var(--s5)}
  .countdown,.tinted{padding:var(--s5)}
  .countdown .n{font-size:64px; line-height:60px}
  .countdown .acts .btn,.weakest .acts .btn{flex:1 1 auto}
  .scorehead{padding:var(--s6) var(--s4); gap:var(--s4)}
  .scorehead .bignum{font-size:64px; line-height:64px}
  .verdict{height:auto; min-height:40px; padding:var(--s2) var(--s4)}

  /* Tables and wide grids drop their least-load-bearing column rather than
     shrinking every column into unreadability. */
  .mastery{grid-template-columns:1fr 72px 88px; gap:var(--s3); padding:var(--s3) var(--s4)}
  .mastery .lvl{display:none}
  .nbrow{grid-template-columns:1fr 88px; gap:var(--s3); padding:var(--s3) var(--s4)}
  .nbrow .badge{display:none}
  .nbfilters{padding:0 var(--s4) var(--s3); overflow-x:auto; flex-wrap:nowrap}
  .cardhead,.rows>.row,.kv,.buyrow{padding-left:var(--s4); padding-right:var(--s4)}
  .cardhead{flex-wrap:wrap}
  .tablewrap{padding-left:var(--s4); padding-right:var(--s4)}
  .topicrow{grid-template-columns:1fr 72px; gap:var(--s3)}
  .topicrow .score{display:none}
  .paywall{padding:var(--s5)}
  .rcard{padding:var(--s5)}
  .rcard .stem{font:var(--title-md); letter-spacing:var(--title-md-track);font-variant-numeric:tabular-nums}

  /* The player keeps its full width and loses only the chrome it can spare.
     A question stem is the one thing that must never be cramped. */
  .player .rail{display:none}
  .qform{padding:var(--s5) var(--s4)}
  .stem{font:var(--headline-sm); letter-spacing:var(--headline-sm-track);font-variant-numeric:tabular-nums}
  .playbar{padding:0 var(--s4); gap:var(--s3)}
  .playbar .back{font:var(--label-md); letter-spacing:var(--label-md-track);font-variant-numeric:tabular-nums}
  .navrow{flex-wrap:wrap; gap:var(--s3)}
  .navrow .answered{order:3; flex-basis:100%; text-align:center}
  .navrow .btn.lg{flex:1 1 auto; padding:0 var(--s5)}
  .earlyout{flex-direction:column; align-items:stretch; gap:var(--s3)}
  .earlyout .btn{width:100%}
  .choice{padding:var(--s3) var(--s4); gap:var(--s3)}

  .stickybuy{height:auto; padding:var(--s3) var(--s4); flex-wrap:wrap; gap:var(--s3)}
  .stickybuy .grow{flex-basis:100%}
  .stickybuy .btn,.stickybuy form{flex:1 1 auto}
  .stickybuy form .btn{width:100%}
  /* A sign-in provider button is a form control and fills its form. A pair of
     hero pills is not: stretched edge to edge at 56px they became the loudest
     thing on the screen, which is the opposite of what a hero CTA is for. Let
     them size to their words and stack. */
  .oauth{width:100%; justify-content:center}
  .hero-cta{flex-direction:column; align-items:flex-start; gap:var(--s3)}
}

/* ------------------------------------------------------------------- home */

/* /exams/ is where the domain lands, so it has to do a homepage's job: say what
   this is, then get the visitor to pick an exam. Everything below the hero is
   in service of that one act — the competitors we looked at (Pocket Prep,
   UWorld) both structure their front page the same way, and neither shows a
   dashboard to someone who has not chosen yet. */
.home{gap:var(--s11)}

.hero-home{display:flex; flex-direction:column; gap:var(--s5);
           max-width:760px; padding:var(--s7) 0 var(--s2)}
.hero-home h1{font:var(--display-lg); letter-spacing:var(--display-lg-track);
              text-wrap:balance; margin:0}
.hero-home .lede{max-width:64ch}
.hero-home .hero-cta{margin-top:var(--s2)}
.hero-home .hero-note{font:var(--body-md); letter-spacing:var(--body-md-track);
                      color:var(--on-surface-variant); margin:0}

/* Four claims in two columns, filling the band above them.
   As one 80ch stack it was the only block on the home page that did not run the
   width of the page, which made a deliberate summary look like a leftover
   paragraph somebody forgot to lay out. */
/* NOT .why — that name belongs to the explanation block on a reviewed question,
   and reusing it here gave every paid explanation this band's grey panel and
   32px of padding. Same trap as .stat and .foot, recorded above. */
.claims{background:var(--surface-container-low);
        border-radius:var(--r-md); padding:var(--s6) var(--s7)}
.claims .ticks.lg{display:grid; grid-template-columns:repeat(2,1fr);
                  gap:var(--s4) var(--s8)}
.claims .ticks li b{font-weight:500}
@media (max-width:1100px){ .claims .ticks.lg{grid-template-columns:1fr} }

/* The purpose line is the reason someone sits the exam, so it gets the weight
   the pass rate used to have. Clamped to two lines: a third makes the cards in
   a row different heights and knocks their dividers out of alignment. */
.excard .purpose{font:var(--body-md); letter-spacing:var(--body-md-track);
                 color:var(--on-surface); display:-webkit-box; -webkit-line-clamp:2;
                 -webkit-box-orient:vertical; line-clamp:2; overflow:hidden}
/* No dotted underline. The cursor already says there is something to hover, and
   a dotted rule under two words in the middle of a card is noise on the one
   surface that has to look calm. */
.excard abbr,.excard .hint{text-decoration:none; cursor:help}
.excard.soon{opacity:.72}

@media (max-width:820px){
  .home{gap:var(--s9)}
  .hero-home{padding-top:var(--s5)}
  .hero-home h1{font:var(--display-sm); letter-spacing:var(--display-sm-track)}
}

/* Which exam you are in, and the way out of it. Sitting two papers at once is
   ordinary, so this is on every screen rather than only where the URL says so. */
.examchip{display:inline-flex; align-items:center; gap:var(--s1);
          height:32px; padding:0 var(--s2) 0 var(--s3);
          border-radius:var(--r-full); background:var(--surface-container-high);
          color:var(--on-surface-variant); text-decoration:none;
          font:var(--label-lg); letter-spacing:var(--label-lg-track); white-space:nowrap}
.examchip:hover{background:var(--secondary-container); color:var(--on-secondary-container);
                text-decoration:none}
.examchip svg{width:18px; height:18px}
.examchip.static{cursor:default}
.examchip.static:hover{background:var(--surface-container-high);color:var(--on-surface-variant)}
.examswitch{position:relative}
.examswitch>summary{list-style:none;cursor:pointer}
.examswitch>summary::-webkit-details-marker{display:none}
.examswitch[open]>summary svg{transform:rotate(90deg)}
.examswitch-menu{position:absolute;left:0;top:calc(100% + var(--s2));z-index:60;
  min-width:280px;padding:var(--s3);border:1px solid var(--outline-variant);
  border-radius:var(--r-md);background:var(--surface-container-lowest);box-shadow:var(--elev-2)}
.examswitch-menu .label{display:block;padding:var(--s1) var(--s3) var(--s2)}
.examswitch-menu a{display:block;padding:var(--s3);border-radius:var(--r-sm);
  color:var(--on-surface);text-decoration:none;white-space:nowrap}
.examswitch-menu a:hover{background:var(--surface-container);text-decoration:none}
.examswitch-menu a[aria-current]{background:var(--secondary-container);color:var(--on-secondary-container)}
.excard.on{border-color:var(--primary)}
@media (max-width:720px){ .examchip{display:none} }
.narrow-page{max-width:60ch; display:flex; flex-direction:column; gap:var(--s5)}

/* ============================================================================
   SURVEY
   A card at the top of the page, not a modal. It only ever appears at the end
   of a real piece of work, so there is nothing to interrupt.
   ========================================================================== */

/* Over the page, not in it. No scrim click and no Escape: the two buttons are
   the only ways out, so "skipped" is a decision we can count rather than a
   mis-click we cannot tell apart from one. */
.svwrap{position:fixed; inset:0; z-index:70; display:flex;
        align-items:flex-start; justify-content:center; overflow-y:auto;
        padding:var(--s7) var(--s5); background:rgba(0,0,0,.42)}
.survey{width:100%; max-width:640px; display:flex; flex-direction:column; gap:var(--s5);
        padding:var(--s7); border-radius:var(--r-xl);
        background:var(--secondary-container); color:var(--on-secondary-container)}
.survey .sv-head{display:flex; flex-direction:column; gap:var(--s2)}
.survey h2{font:var(--title-lg); letter-spacing:var(--title-lg-track)}
.survey .lede-sm{color:inherit; opacity:.85}
.survey .btn.text{color:inherit}
.sv-q{border:none; margin:0; padding:0; display:flex; flex-direction:column; gap:var(--s2)}
.sv-q legend{padding:0; margin-bottom:var(--s2); max-width:var(--measure);
             font:var(--title-md); letter-spacing:var(--title-md-track); text-wrap:pretty}
/* Ten radios, not a slider. A slider needs script to show its value, has no
   keyboard story worth the name, and starts at a default that becomes the modal
   answer whether the reader meant it or not. */
.sv-scale{display:flex; gap:var(--s2); flex-wrap:wrap}
.sv-n{position:relative}
.sv-n input{position:absolute; inset:0; opacity:0; margin:0; cursor:pointer}
.sv-n span{display:flex; align-items:center; justify-content:center;
           width:44px; height:44px; border-radius:var(--r-full);
           border:1px solid var(--outline); background:var(--surface-container-lowest);
           color:var(--on-surface);
           font:var(--title-md); letter-spacing:var(--title-md-track);
           font-variant-numeric:tabular-nums}
.sv-n input:checked + span{background:var(--primary); color:var(--on-primary); border-color:var(--primary)}
.sv-n input:focus-visible + span{outline:3px solid var(--on-secondary-container); outline-offset:2px}
.sv-ends{display:flex; justify-content:space-between; gap:var(--s4);
         font:var(--body-sm); letter-spacing:var(--body-sm-track); opacity:.85}
.sv-c{display:flex; flex-direction:column; gap:var(--s2);
      font:var(--body-md); letter-spacing:var(--body-md-track)}
.sv-c textarea{width:100%; max-width:var(--measure); resize:vertical;
               padding:var(--s3) var(--s4); border-radius:var(--r-sm);
               border:1px solid var(--outline); background:var(--surface-container-lowest);
               color:var(--on-surface); font:var(--body-lg); letter-spacing:var(--body-lg-track)}
.survey .acts{display:flex; gap:var(--s3); flex-wrap:wrap; margin-top:var(--s2)}
.survey .acts .btn{flex:1 1 auto; justify-content:center}
.survey .btn.ghost{border-color:currentColor; color:inherit}

@media (max-width:720px){
  .sv-n span{width:38px; height:38px; font:var(--body-md); letter-spacing:var(--body-md-track)}
  .sv-ends{flex-direction:column; gap:var(--s1)}
}

/* ============================================================================
   FINAL REVIEW
   ========================================================================== */

.plan{list-style:none; margin:0; padding:0; display:flex; flex-direction:column}
.plan-item{display:flex; align-items:center; gap:var(--s4);
           padding:var(--s5) var(--s6); border-top:1px solid var(--outline-variant)}
.plan-item:first-child{border-top:none}
.plan-item .pn{display:inline-flex; align-items:center; justify-content:center;
               width:28px; height:28px; flex:0 0 28px; border-radius:var(--r-full);
               background:var(--error-container); color:var(--on-error-container);
               font:var(--label-lg); letter-spacing:var(--label-lg-track);font-variant-numeric:tabular-nums}
.plan-item .grow{flex:1 1 auto; min-width:0}
.plan-item .t{font:var(--title-md); letter-spacing:var(--title-md-track);font-variant-numeric:tabular-nums}
.plan-item .m{font:var(--body-sm); letter-spacing:var(--body-sm-track);
              color:var(--on-surface-variant); text-wrap:pretty;font-variant-numeric:tabular-nums}

/* How many times this one has been missed. A number, because "you keep getting
   this wrong" is only actionable when the reader can see how many times. */
.wrongn{display:flex; flex-direction:column; align-items:center; gap:0;
        min-width:76px; padding:var(--s2) var(--s3); border-radius:var(--r-sm);
        background:var(--surface-container-high); color:var(--on-surface-variant)}
.wrongn b{font:var(--title-md); letter-spacing:var(--title-md-track);font-variant-numeric:tabular-nums}
.wrongn small{font:var(--body-sm); letter-spacing:var(--body-sm-track); white-space:nowrap}
.wrongn.warm{background:var(--secondary-container); color:var(--on-secondary-container)}
.wrongn.hot{background:var(--error-container); color:var(--on-error-container)}

/* The navigator, and the groups it jumps to. Eighty rows in one scroll is a
   list; four labelled groups with counts is something a candidate can work
   through in the week they have left. */
.rvnav{display:flex; gap:var(--s2); flex-wrap:wrap; padding:0 var(--s6) var(--s4)}
.rvnav .chip{display:inline-flex; align-items:center; gap:var(--s2)}
.rvnav .chip b{font-weight:500; font-variant-numeric:tabular-nums}
.rvgroup{scroll-margin-top:calc(var(--appbar-h) + var(--s4))}
.rvgh{display:flex; align-items:baseline; gap:var(--s4); flex-wrap:wrap;
      padding:var(--s5) var(--s6) var(--s3); border-top:1px solid var(--outline-variant);
      background:var(--surface-container-low)}
.rvgroup:first-child .rvgh{border-top:none}
.rvgh h3{display:flex; align-items:center; gap:var(--s2);
         font:var(--title-md); letter-spacing:var(--title-md-track)}
.rvgh .n{display:inline-flex; align-items:center; justify-content:center;
         min-width:24px; height:22px; padding:0 var(--s2); border-radius:var(--r-full);
         background:var(--surface-container-highest); color:var(--on-surface-variant);
         font:var(--label-sm); letter-spacing:var(--label-sm-track);font-variant-numeric:tabular-nums}

/* Its own grid rather than the notebook's. The two lists carry different
   columns, and sharing a grid definition is how the badge and the Redo button
   ended up hanging off the right edge of the page. */
.rvrow{display:grid; grid-template-columns:minmax(0,1fr) 92px 148px;
       align-items:center; gap:var(--s4);
       padding:var(--s3) var(--s6); border-top:1px solid var(--outline-variant)}
.rvrow .grow{min-width:0}
.rvrow .stem{font:var(--body-md); letter-spacing:var(--body-md-track);
             color:var(--on-surface);
             overflow:hidden; text-overflow:ellipsis; white-space:nowrap;font-variant-numeric:tabular-nums}
.rvrow .m{font:var(--body-sm); letter-spacing:var(--body-sm-track);
          color:var(--on-surface-variant);
          overflow:hidden; text-overflow:ellipsis; white-space:nowrap;font-variant-numeric:tabular-nums}
.rvrow .badge{justify-self:start}
.rvrow .btn{justify-self:end}
@media (max-width:1100px){
  .rvrow{grid-template-columns:minmax(0,1fr) 92px 148px}
}
/* The count stays on a phone. It is the one column this page exists for —
   dropping it leaves a list of stems in an order the reader cannot see the
   reason for — so it shrinks instead. */
@media (max-width:720px){
  .rvnav{padding:0 var(--s4) var(--s3); overflow-x:auto; flex-wrap:nowrap}
  .rvgh{padding:var(--s4) var(--s4) var(--s2)}
  .rvrow{grid-template-columns:minmax(0,1fr) 68px 96px; gap:var(--s2);
         padding:var(--s3) var(--s4)}
  .rvrow .wrongn{min-width:0; padding:var(--s1) var(--s2)}
  .rvrow .wrongn b{font:var(--body-md); letter-spacing:var(--body-md-track)}
  .rvrow .wrongn small{font-size:10px; line-height:14px}
}

/* ============================================================================
   LEGAL
   A document, not a screen: one column at reading measure, generous leading,
   and numbered headings that can be cited in an email.
   ========================================================================== */

.legal{max-width:var(--measure); display:flex; flex-direction:column; gap:var(--s3)}
.legal-date{font:var(--body-sm); letter-spacing:var(--body-sm-track);
            color:var(--on-surface-variant);font-variant-numeric:tabular-nums}
.legal .lede{margin-bottom:var(--s4)}
.legal-s{display:flex; flex-direction:column; gap:var(--s3); margin-top:var(--s5);
         scroll-margin-top:calc(var(--appbar-h) + var(--s4))}
.legal-s h2{font:var(--title-lg); letter-spacing:var(--title-lg-track)}
.legal-s p{font:var(--body-lg); letter-spacing:var(--body-lg-track);
           color:var(--on-surface-variant); text-wrap:pretty}
.legal-dl{margin:0; display:flex; flex-direction:column; gap:var(--s3)}
.legal-dl dt{font:var(--title-md); letter-spacing:var(--title-md-track); color:var(--on-surface)}
.legal-dl dd{margin:0 0 var(--s2); font:var(--body-lg); letter-spacing:var(--body-lg-track);
             color:var(--on-surface-variant); text-wrap:pretty}

/* The comparison cards carry no step number — they are not a sequence, they are
   the three things a candidate is choosing between today. */
.trial.compare .trial-step{background:var(--surface-container-low)}
.trial.compare h3{color:var(--on-surface-variant)}

/* ============================================================================
   PUBLIC CHROME - top bar, drawer, centred page
   ============================================================================
   The rail is the study app's navigation and it is right for that: four
   destinations, always visible, no hidden state. As the front door it was
   wrong. Signed out it drew two entries in 96px of chrome reserved for eight,
   so the first thing a stranger saw was an application they had no account
   for.

   So the public half of the site - the pages a person reads before they have
   bought anything - carries a 64px top bar instead, and the study app keeps
   the rail. Same brand in the same place in both, so nothing jumps; the rail
   simply appears once there is something to study. Selected per page with
   layout({ chrome: "top" }).
   ========================================================================== */

.topbar{position:sticky; top:0; z-index:30; height:64px;
        display:flex; align-items:center; gap:var(--s6);
        padding:0 var(--s7); background:var(--surface-container-lowest);
        border-bottom:1px solid var(--outline-variant)}
/* height, not just the logo's 21px - the anchor is a tap target. */
.topbar .brand{display:flex; align-items:center; height:40px; margin-right:var(--s2)}
.topbar .spacer{flex:1}
.topbar .who{font:var(--body-md); letter-spacing:var(--body-md-track);
             color:var(--on-surface-variant); margin-right:var(--s3)}

.topnav{display:flex; align-items:center; gap:var(--s1)}
.topnav a{font:var(--label-lg); letter-spacing:var(--label-lg-track);
          color:var(--on-surface-variant); padding:var(--s2) var(--s4);
          border-radius:var(--r-full); text-decoration:none}
.topnav a:hover{background:var(--surface-container); color:var(--on-surface);
                text-decoration:none}
.topnav a[aria-current]{color:var(--on-secondary-container);
                        background:var(--secondary-container)}

/* Everything below the bar is centred. Section 13 of the design system makes
   full width the default so cards do not float against the rail with dead
   space beside them - with no rail there is nothing to float against, and a
   page that sells reads better on a measure than at 1400px. */
.pubwrap{max-width:1120px; margin:0 auto; padding:0 var(--s7)}

/* The drawer behind the menu icon. Same mechanism as the rail's: a checkbox,
   a scrim and a transform, because every public page has to work with
   scripting off. */
.navmenu-toggle{position:absolute; opacity:0; width:0; height:0}
.navmenu-scrim{position:fixed; inset:0; z-index:40; background:var(--scrim);
               opacity:0; visibility:hidden; transition:opacity .18s ease-out}
.navmenu-toggle:checked ~ .navmenu-scrim{opacity:.32; visibility:visible}
.navmenu{position:fixed; top:0; right:0; z-index:50; width:300px; max-width:84vw;
         height:100dvh; display:flex; flex-direction:column; gap:var(--s1);
         padding:var(--s4) var(--s4) var(--s7);
         background:var(--surface-container);
         border-left:1px solid var(--outline-variant);
         transform:translateX(100%); transition:transform .18s ease-out}
.navmenu-toggle:checked ~ .navmenu{transform:none}
@media (prefers-reduced-motion:reduce){.navmenu,.navmenu-scrim{transition:none}}
.navmenu-head{display:flex; align-items:center; justify-content:space-between;
              height:48px; margin-bottom:var(--s2)}
.navmenu-head span{font:var(--title-md); letter-spacing:var(--title-md-track)}
.navmenu > a{display:flex; align-items:center; height:48px; padding:0 var(--s4);
             border-radius:var(--r-full); color:var(--on-surface);
             text-decoration:none;
             font:var(--label-lg); letter-spacing:var(--label-lg-track)}
.navmenu > a:hover{background:var(--surface-container-high); text-decoration:none}
.navmenu > a.btn{margin-top:var(--s3); height:40px; color:var(--on-primary)}
.navmenu > a.btn:hover{background:var(--primary); color:var(--on-primary)}
.navmenu hr{border:none; border-top:1px solid var(--outline-variant);
            margin:var(--s3) var(--s4); width:100%}
.navmenu form{margin-top:var(--s2)}
.navmenu-btn{display:none; align-items:center; justify-content:center;
             width:40px; height:40px; border:none; background:transparent;
             border-radius:var(--r-full); color:var(--on-surface-variant); cursor:pointer}
.navmenu-btn svg{width:24px; height:24px}
.navmenu .navmenu-btn{display:inline-flex}
.navmenu-toggle:focus-visible ~ .topbar .navmenu-btn{outline:3px solid var(--on-secondary-container);
                                                     outline-offset:2px}

/* ---------------------------------------------------------------- sections */

.pubsect{padding:var(--s12) 0; border-top:1px solid var(--outline-variant)}
.pubsecthead{text-align:center; margin-bottom:var(--s9)}
.pubsecthead .kicker{display:block; font:var(--label-md); letter-spacing:1.4px;
                     text-transform:uppercase; color:var(--primary); margin-bottom:var(--s3)}
.pubsecthead h2{font:var(--headline-md); letter-spacing:var(--headline-md-track);
                margin:0 0 var(--s3); text-wrap:balance}
.pubsecthead p{max-width:60ch; margin:0 auto; font:var(--body-lg);
               letter-spacing:var(--body-lg-track); color:var(--on-surface-variant)}
.guides-index .cards{margin-inline:auto}
.samplelesson{max-width:1040px}
.samplelesson>.lcard{margin:0}
.samplelesson .lcard p,.samplelesson .lcard li{max-width:none}
.sample-actions{display:flex;justify-content:center;gap:var(--s3);flex-wrap:wrap;margin-top:var(--s6)}
.pubfilters{display:flex; align-items:center; justify-content:center;
            flex-wrap:wrap; gap:var(--s2); margin-bottom:var(--s6)}
/* .cards is a three-up grid. Two exams in it leaves a third of the row empty,
   and one leaves two thirds - which reads as a page that failed to load
   rather than as a short catalogue. Match the columns to what there is. */
.cards.two{grid-template-columns:repeat(2,1fr)}
.cards.one{grid-template-columns:minmax(0,440px); justify-content:center}

/* The account block in the bar. A bare span would let the address and the
   sign-out button wrap onto two lines inside a 64px bar. */
.topbar-full{display:inline-flex; align-items:center; gap:var(--s3)}

/* ----------------------------------------------------------- market picker */

/* A <details> and not a script, because this bar is swapped into the statically
   built guide pages and the public half of the site works with JavaScript off.
   The summary is a two-letter country code: there is no globe in the kit subset
   and icons.js will not let one be drawn, so the control says what it means. */
.mktpick{position:relative}
.mktpick > summary{display:inline-flex; align-items:center; gap:var(--s2); height:36px;
                   padding:0 var(--s4) 0 var(--s3); border-radius:var(--r-full); cursor:pointer;
                   list-style:none; user-select:none;
                   border:1px solid var(--outline-variant);
                   font:var(--label-lg); letter-spacing:var(--label-lg-track);
                   color:var(--on-surface-variant)}
.mktpick > summary svg{flex:0 0 20px; width:20px; height:20px}
.mktpick > summary::-webkit-details-marker{display:none}
.mktpick > summary:hover{background:var(--surface-container-high); color:var(--on-surface)}
.mktpick > summary:focus-visible{outline:3px solid var(--primary); outline-offset:2px}
.mktpick-menu{position:absolute; top:calc(100% + var(--s2)); right:0; z-index:60;
              min-width:200px; padding:var(--s3);
              border-radius:var(--r-md); background:var(--surface-container-lowest);
              border:1px solid var(--outline-variant); box-shadow:var(--elev-2)}
.mktpick-h{display:flex; align-items:center; gap:var(--s2);
           padding:var(--s1) var(--s3) var(--s2);
           font:var(--label-md); letter-spacing:var(--label-md-track);
           color:var(--on-surface-variant)}
.mktpick-h svg{flex:0 0 18px; width:18px; height:18px}

/* The flag beside each country. A hairline, because both of these have white
   at the edge and would otherwise bleed into a light menu. */
.mktpick-menu .flag,.navmenu-mkt .flag{flex:0 0 20px; width:20px; height:15px;
  border-radius:var(--r-xs); object-fit:cover;
  box-shadow:0 0 0 1px var(--outline-variant)}
.mktpick-menu a{display:flex; align-items:center; gap:var(--s3); height:40px; padding:0 var(--s3);
                border-radius:var(--r-sm); text-decoration:none;
                font:var(--body-md); letter-spacing:var(--body-md-track);
                color:var(--on-surface)}
.mktpick-menu a:hover{background:var(--surface-container-high); text-decoration:none}
/* The one already in force. A tick would need a glyph beside a text label in a
   40px row; the filled container says the same thing and costs nothing. */
.mktpick-menu a[aria-current]{background:var(--secondary-container);
                              color:var(--on-secondary-container)}

/* The drawer's copy of the same choice. The bar is hidden below 820 and its
   picker with it, so the drawer carries plain links - it is already open, so
   nothing here needs to disclose. */
.navmenu-mkt{margin-top:var(--s4); padding-top:var(--s4);
             border-top:1px solid var(--outline-variant)}
.navmenu-mkt a{display:flex; align-items:center; gap:var(--s3); height:44px; padding:0 var(--s4);
               border-radius:var(--r-sm); text-decoration:none;
               font:var(--body-md); letter-spacing:var(--body-md-track);
               color:var(--on-surface)}
.navmenu-mkt a:hover{background:var(--surface-container-high); text-decoration:none}
.navmenu-mkt a[aria-current]{background:var(--secondary-container);
                             color:var(--on-secondary-container)}

/* ------------------------------------------------------- catalogue groups */

/* Cards under the family they belong to. Six flat cards is a list; thirty is a
   wall, and the catalogue is going one way. */
.catgroup{margin-top:var(--s8)}
.catgroup:first-child{margin-top:0}
.catgroup-head{display:flex; align-items:baseline; gap:var(--s4);
               margin-bottom:var(--s5); padding-bottom:var(--s3);
               border-bottom:1px solid var(--outline-variant)}
.catgroup-head h3{margin:0; font:var(--title-lg); letter-spacing:var(--title-lg-track)}
.catgroup-n{font:var(--body-sm); letter-spacing:var(--body-sm-track);
            color:var(--on-surface-variant); font-variant-numeric:tabular-nums}
/* The way out of the market filter, under the catalogue rather than above it:
   somebody who read the whole list and did not find their exam is exactly who
   this line is for. */
.crossmkt{margin:var(--s8) 0 0; text-align:center;
          font:var(--body-md); letter-spacing:var(--body-md-track);
          color:var(--on-surface-variant)}

.pubfoot{border-top:1px solid var(--outline-variant); padding:var(--s9) 0 var(--s8);
         background:var(--surface-container-low)}
.pubfoot-cols{display:grid; grid-template-columns:2fr 1fr 1fr 1fr; gap:var(--s7)}
.pubfoot-cols h2{font:var(--label-lg); letter-spacing:var(--label-lg-track); margin:0 0 var(--s4)}
.pubfoot-cols a{display:block; font:var(--body-md); letter-spacing:var(--body-md-track);
                color:var(--on-surface-variant); margin-bottom:var(--s2); text-decoration:none}
.pubfoot-cols a:hover{color:var(--primary)}
.pubfoot-cols p{font:var(--body-sm); letter-spacing:var(--body-sm-track);
                color:var(--on-surface-variant); max-width:38ch}

/* -------------------------------------------------------------- home hero */

.pubhero{position:relative; text-align:center; padding:var(--s13) 0 var(--s12); overflow:hidden}
.pubhero::before{content:""; position:absolute; inset:-40% 0 auto; height:150%;
                 background:radial-gradient(60% 55% at 50% 22%,
                   rgba(47,91,234,.10), rgba(47,91,234,0) 70%);
                 pointer-events:none}
.pubhero > *{position:relative}
.pubhero h1{font:var(--display-md); letter-spacing:var(--display-md-track);
            margin:0 auto var(--s5); max-width:26ch; text-wrap:balance}
.pubhero h1 em{font-style:normal; color:var(--primary)}
.pubhero .lede{max-width:64ch; margin:0 auto var(--s8); color:var(--on-surface-variant)}

/* The search field IS the call to action, so nothing under it may outrank it.
   The field is 64px and its submit 48px; the two links below are the 40px
   default and the field carries the only fill in the hero. Two blue pills
   would be two primary actions, which is none. */
.examhunt{display:flex; align-items:center; gap:var(--s3); height:64px;
          max-width:660px; margin:0 auto var(--s4);
          padding:0 var(--s2) 0 var(--s6); border-radius:var(--r-full);
          background:var(--surface-container-lowest);
          border:1px solid var(--outline-variant); box-shadow:var(--elev-1)}
.examhunt:focus-within{border-color:var(--primary); box-shadow:var(--elev-2)}
.examhunt > svg{width:24px; height:24px; flex:0 0 24px; color:var(--on-surface-variant)}
.examhunt input{flex:1; min-width:0; align-self:stretch; padding:0;
                border:none; background:transparent; outline:none;
                font:var(--body-lg); letter-spacing:var(--body-lg-track); color:var(--on-surface)}
.examhunt input::placeholder{color:var(--on-surface-variant)}
.examhunt .btn{height:48px; flex:0 0 auto}

.hunttags{display:flex; align-items:center; justify-content:center; flex-wrap:wrap;
          gap:var(--s2); margin-bottom:var(--s8)}
.hunttags .tag{font:var(--label-md); letter-spacing:var(--label-md-track);
               color:var(--on-surface-variant); margin-right:var(--s2)}
.pubhero .hero-cta{display:flex; gap:var(--s3); justify-content:center}
.pubhero .hero-cta .btn{height:40px; padding:0 var(--s6);
                        font:var(--label-lg); letter-spacing:var(--label-lg-track)}

/* ------------------------------------------------------------- home bands */

/* Numbered because the product genuinely is a sequence - each part hands the
   reader to the next. The numbers inside a card are that part's specifics,
   which is the thing a paragraph cannot say as plainly. */
.hpsteps{display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s5)}
.hpstep{display:flex; flex-direction:column; gap:var(--s3); padding:var(--s7);
        border-radius:var(--r-lg); background:var(--surface-container-lowest);
        border:1px solid var(--outline-variant)}
.hpstep .kicker{align-self:flex-start; font:var(--label-sm); letter-spacing:1px;
                text-transform:uppercase; padding:var(--s1) var(--s3);
                border-radius:var(--r-full);
                background:var(--primary-container); color:var(--on-primary-container)}
.hpstep h3{font:var(--title-lg); letter-spacing:var(--title-lg-track); margin:0}
.hpstep > p{font:var(--body-md); letter-spacing:var(--body-md-track);
            color:var(--on-surface-variant); margin:0}
.hpstep ol{list-style:none; margin:var(--s2) 0 auto; padding:0;
           display:flex; flex-direction:column; gap:var(--s3)}
.hpstep ol li{display:flex; gap:var(--s3); font:var(--body-md);
              letter-spacing:var(--body-md-track)}
.hpstep ol li i{flex:0 0 20px; height:20px; border-radius:var(--r-full); font-style:normal;
                background:var(--secondary-container); color:var(--on-secondary-container);
                font:var(--label-sm); display:inline-flex;
                align-items:center; justify-content:center; font-variant-numeric:tabular-nums}
.hpstep .go{margin-top:var(--s6)}

/* The three steps are the same three destinations as .dest on My study, and
   they take the same three grounds. Before this they were three identical white
   cards here and three tones inside the product, so the pattern taught one thing
   in the pitch and another in the app. */
.hpsteps .hpstep{border-color:transparent}
.hpsteps .hpstep:nth-child(1){background:var(--primary-container); color:var(--on-primary-container)}
.hpsteps .hpstep:nth-child(2){background:var(--secondary-container); color:var(--on-secondary-container)}
.hpsteps .hpstep:nth-child(3){background:var(--tertiary-container); color:var(--on-tertiary-container)}
/* Descendants that set their own colour have to follow the ground they are on. */
.hpsteps .hpstep > p{color:inherit; opacity:.82}
.hpsteps .hpstep ol li{color:inherit}
/* The kicker and the step numerals lift off their own card rather than
   borrowing another role's colour, which would read as a second meaning. Solid
   surfaces, not white alpha: ui-audit.mjs cannot resolve alpha over a parent and
   reported these at 1.00:1, and neither can a reader checking the value by eye. */
.hpsteps .hpstep .kicker{background:var(--surface-container-lowest); color:inherit}
.hpsteps .hpstep ol li i{background:var(--surface-container-lowest); color:inherit}
.hpsteps .hpstep:nth-child(3) .kicker{background:var(--tertiary-container-high)}
.hpsteps .hpstep:nth-child(3) ol li i{background:var(--tertiary-container-high)}
/* .btn.tonal is secondary-container: invisible on the grey card and 1.3:1 on
   the slate one. Each card gets a solid button on its own ground instead. */
.hpsteps .hpstep .btn.tonal{background:var(--surface-container-lowest)}
.hpsteps .hpstep:nth-child(1) .btn.tonal{color:var(--on-primary-container)}
.hpsteps .hpstep:nth-child(2) .btn.tonal{color:var(--on-surface)}
.hpsteps .hpstep:nth-child(3) .btn.tonal{color:var(--tertiary-container)}

/* No step number here - these are not a sequence, they are the three things a
   candidate is choosing between today. */
.hpleaves{display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s5)}
.hpleaf{padding:var(--s6); border-radius:var(--r-lg); background:var(--surface-container-low)}
.hpleaf h3{font:var(--title-md); letter-spacing:var(--title-md-track); margin:0 0 var(--s2)}
.hpleaf p{font:var(--body-md); letter-spacing:var(--body-md-track);
          color:var(--on-surface-variant); margin:0}


/* Pages that keep their own body markup but lose the rail. .page and .pdp were
   written to fill a column that started 96px in; with no rail they run the full
   1440 and the reading line goes with them. Centre them on the same measure the
   home page uses rather than rewriting four page bodies. .narrow already
   centres itself and needs nothing. */
.pubchrome .page,
.pubchrome .pdp{max-width:1120px; margin-inline:auto; width:100%}

/* The buy bar is fixed and was inset by the rail's width. Without a rail that
   is 96px of nothing on the left and the bar no longer lines up with the page
   above it. */
.pubchrome .stickybuy{left:0}

/* ------------------------------------------------------------ public compact
   One control, not four. Sign in and the diagnostic move inside the drawer;
   three competing actions in a 390px bar is how a top bar ends up worse than
   the rail it replaced. */
@media (max-width:820px){
  .topbar{gap:var(--s3); padding:0 var(--s2) 0 var(--s4)}
  .topnav,.topbar-full{display:none}
  .topbar .navmenu-btn{display:inline-flex}
  .pubwrap{padding:0 var(--s5)}

  .pubhero{padding:var(--s9) 0 var(--s10)}
  .pubhero h1{font:var(--headline-lg); letter-spacing:var(--headline-lg-track); max-width:none}
  .pubhero .lede{font:var(--body-md); letter-spacing:var(--body-md-track); margin-bottom:var(--s7)}
  /* The leading magnifier and the Search button say the same thing, and on a
     narrow field only the one that also submits can afford the room. */
  .examhunt{height:56px; padding:0 var(--s2) 0 var(--s4); gap:var(--s2)}
  .examhunt > svg{display:none}
  .examhunt input{font:var(--body-md); letter-spacing:var(--body-md-track)}
  .examhunt .btn{height:40px; padding:0 var(--s4)}
  .hunttags{margin-bottom:var(--s7)}
  .hunttags .chip{height:28px; padding:0 var(--s3);
                  font:var(--label-md); letter-spacing:var(--label-md-track)}
  .pubhero .hero-cta{flex-direction:column; align-items:center; gap:var(--s2)}

  .pubsect{padding:var(--s10) 0}
  .pubsecthead{margin-bottom:var(--s7)}
  .pubsecthead h2{font:var(--headline-sm); letter-spacing:var(--headline-sm-track)}
  .hpsteps,.hpleaves,.cards.two{grid-template-columns:1fr}
  .hpstep{padding:var(--s6)}
  .hpstep .go{margin-top:var(--s5)}
  .pubfoot-cols{grid-template-columns:1fr 1fr; gap:var(--s6)}
}


/* ============================================================================
   The paper edition, as a file list.
   Two surfaces, one component. On the public product page the rows are inert
   (`.locked`) and just say what is in the box; inside the app they are links
   that download the PDF.

   Deliberately NOT cards. An earlier draft explained each file in its own
   panel, which turned a download list into a second sales pitch on a page
   that had already made one. A file list is what a reader expects when the
   answer is "here are the files".

   No icon glyph: the Material subset in design-system/icons.json has no
   document or PDF symbol, and icons.js forbids drawing one by hand. A "PDF"
   text badge says the same thing without inventing a glyph, and reads at any
   size and in any locale.
   ==========================================================================*/

.pdflist{list-style:none; margin:var(--s5) 0 0; padding:0;
         border-top:1px solid var(--outline-variant)}
.pdflist li{border-bottom:1px solid var(--outline-variant)}

.pdfrow{display:flex; align-items:center; gap:var(--s4);
        padding:var(--s4) var(--s2);
        color:var(--on-surface); text-decoration:none}
a.pdfrow:hover{background:var(--surface-container-low); text-decoration:none;
               color:var(--on-surface)}

/* The badge. Small, primary-tinted, monospace-ish tracking so it reads as a
   file-type marker rather than a word in the sentence. */
/* Padding on the scale (4/8) rather than 3px, and no font-weight override: the
   label token is already 500, the filled primary-container is what makes this
   read as a badge, and the system carries no 700 anywhere. */
.pdfbadge{flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center;
          min-width:42px; padding:var(--s1) var(--s2); border-radius:var(--r-xs);
          background:var(--primary-container); color:var(--on-primary-container);
          font:var(--label-md); letter-spacing:0.06em}

.pdfname{flex:0 0 auto; font:var(--title-md); letter-spacing:var(--title-md-track);
         color:var(--on-surface)}
.pdfmeta{flex:1 1 auto; min-width:0; font:var(--body-md);
         letter-spacing:var(--body-md-track); color:var(--on-surface-variant);
         overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.pdfsize{flex:0 0 auto; font:var(--body-md); letter-spacing:var(--body-md-track);
         color:var(--on-surface-variant); font-variant-numeric:tabular-nums}
.pdfrow svg{flex:0 0 auto; width:18px; height:18px; color:var(--primary)}

/* Public page: rows are not links and carry no arrow. Slightly muted so the
   list reads as a manifest of what is included rather than a set of controls
   that do nothing when clicked. */
.pdflist.locked .pdfrow{cursor:default}
.pdflist.locked .pdfname{color:var(--on-surface)}

@media (max-width:720px){
  .pdfrow{flex-wrap:wrap; gap:var(--s2) var(--s3); padding:var(--s4) var(--s1)}
  .pdfname{flex:1 1 auto}
  .pdfmeta{flex:1 0 100%; white-space:normal; order:3}
  .pdfsize{order:2}
}

/* ============================================================================
   PRODUCT PAGE - what you get, the sample question, the soft copies, coverage
   ========================================================================== */

/* The facts that are sentences rather than figures.
 *
 * They fill the column a pass-rate table would have filled, on the exams that
 * publish no rate. Label above value and left-aligned, because that is the
 * shape prose wants - the same value right-aligned in the card beside it read
 * as a paragraph shoved against a wall. See the note over `examFacts`. */
 /* Two columns. One column of five short paragraphs ran 560px down a page
    whose sidebar had stopped at 230, and the answer to a tall thin column is
    not a taller thinner one. Paired up they finish level with the card and the
    band reads as a spread rather than as a list that outgrew its neighbour. */
.factprose{margin:var(--s7) 0 0; display:grid; grid-template-columns:1fr 1fr;
           gap:var(--s6) var(--s8);
           border-top:1px solid var(--outline-variant); padding-top:var(--s6)}
.factprose dt{font:var(--label-md); letter-spacing:var(--label-md-track);
              text-transform:uppercase; color:var(--on-surface-variant)}
.factprose dd{margin:var(--s1) 0 0;
              font:var(--body-md); letter-spacing:var(--body-md-track)}
.factprose .factnote{display:block; margin-top:var(--s1);
                     font:var(--body-sm); letter-spacing:var(--body-sm-track);
                     color:var(--on-surface-variant)}

/* ---- sitting the real exam ----------------------------------------------- */

/* Written narrow-first on purpose. `.sitwrap.even` outranks a bare `.sitwrap`
   by specificity no matter where it sits in the file, so a single-column
   override in a max-width block never reaches it - the two cards stayed side
   by side in a 390px viewport and the left one wrapped to one word a line.
   Columns are added at a min-width instead, where nothing has to be undone. */
.sitwrap{display:grid; grid-template-columns:1fr; gap:var(--s8); align-items:start}
.sitmain{min-width:0}
@media (min-width:1101px){
  .sitwrap{grid-template-columns:1fr 320px}
  /* No calendar to show: two cards of equal weight rather than a wide empty
     column beside a narrow aside. */
  .sitwrap.even{grid-template-columns:1fr 1fr}
}
.sitcard{padding:var(--s6); border-radius:var(--r-md);
         background:var(--surface-container); border:1px solid var(--outline-variant)}
.sitcard h3{margin:0 0 var(--s3); font:var(--title-md); letter-spacing:var(--title-md-track)}
.sitcard .lede{margin:0 0 var(--s3)}
.sitcard .lede-sm{margin:0}

/* We sell the preparation; the awarding body sells the seat. A sales page that
   does not say where the seat is bought is hiding the obvious. */
.regbox{padding:var(--s6); border-radius:var(--r-md);
        background:var(--surface-container); border:1px solid var(--outline-variant)}
.regbox h3{margin:0 0 var(--s3); font:var(--title-md); letter-spacing:var(--title-md-track)}
.regbox p{margin:0 0 var(--s5); font:var(--body-md); letter-spacing:var(--body-md-track);
          color:var(--on-surface-variant)}
.regbox .btn{margin:0}
/* An awarding body's page has a name, and the name is a phrase - "Enrolment of
   Examinations - HKSI Institute" does not fit on one line of a 320px pill and
   was being clipped mid-word. A button whose label is a sentence has to be
   allowed to be two lines high. */
/* A pill is a shape for one line of text. Two or three lines inside a
   border-radius of 999px is a circle with words in it. */
.btn.wrap{height:auto; min-height:44px; padding-top:var(--s3); padding-bottom:var(--s3);
          white-space:normal; text-align:center; line-height:1.35;
          border-radius:var(--r-md)}
.regbox .regnote{margin:var(--s4) 0 0; font:var(--body-sm); letter-spacing:var(--body-sm-track);
                 color:var(--on-surface-variant)}

/* ---- what you get -------------------------------------------------------- */

.insidegrid{display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s5); margin-top:var(--s7)}
.sellpoint{display:flex; flex-direction:column; gap:var(--s2); padding:var(--s7);
           border-radius:var(--r-lg); background:var(--surface-container-lowest);
           border:1px solid var(--outline-variant)}
/* The number first, and large. Every one of these is checkable against the
   product, which is the difference between a feature list and a claim. */
.sellpoint-n{font:var(--display-sm); letter-spacing:var(--display-sm-track);
             color:var(--primary); font-variant-numeric:tabular-nums}
.sellpoint h3{margin:0; font:var(--title-md); letter-spacing:var(--title-md-track)}
.sellpoint p{margin:0; font:var(--body-md); letter-spacing:var(--body-md-track);
             color:var(--on-surface-variant)}

/* ---- the sample question ------------------------------------------------- */

/* One real item, answer and all. Framed as the review screen frames it rather
   than as a marketing card, because the claim being made is "this is what you
   get", and dressing it up would make it a different thing. */
.qsample{border:1px solid var(--outline-variant); border-radius:var(--r-lg);
         background:var(--surface-container-lowest); padding:var(--s8);
         margin-top:var(--s6); max-width:82ch}
.qsample-meta{display:block; font:var(--label-md); letter-spacing:var(--label-md-track);
              color:var(--on-surface-variant); margin-bottom:var(--s3)}
.qsample-stem{margin:0 0 var(--s6); font:var(--title-lg); letter-spacing:var(--title-lg-track)}
.qsample-opts{list-style:none; margin:0; padding:0;
              display:flex; flex-direction:column; gap:var(--s3)}
.qsample-opts li{display:grid; grid-template-columns:32px 1fr; gap:var(--s2) var(--s4);
                 align-items:start; padding:var(--s4) var(--s5);
                 border:1px solid var(--outline-variant); border-radius:var(--r-md)}
.qsample-opts .ltr{display:inline-flex; align-items:center; justify-content:center;
                   width:28px; height:28px; border-radius:var(--r-full);
                   background:var(--surface-container-high); color:var(--on-surface-variant);
                   font:var(--label-lg); letter-spacing:var(--label-lg-track)}
.qsample-opts .txt{font:var(--body-lg); letter-spacing:var(--body-lg-track)}
.qsample-opts li.right{border-color:var(--primary); background:var(--primary-container)}
.qsample-opts li.right .ltr{background:var(--primary); color:var(--on-primary)}
.qsample-opts .mark{grid-column:2; font:var(--label-md); letter-spacing:var(--label-md-track);
                    color:var(--on-primary-container)}
/* Why the wrong answers tempt people. The part a plain question bank does not
   have, so it is on the page rather than described on it. */
.qsample-opts .whynot{grid-column:2; font:var(--body-sm); letter-spacing:var(--body-sm-track);
                      color:var(--on-surface-variant)}
.qsample-why{margin-top:var(--s7); padding-top:var(--s6);
             border-top:1px solid var(--outline-variant)}
.qsample-why h3{margin:0 0 var(--s3); font:var(--label-lg); letter-spacing:var(--label-lg-track);
                text-transform:uppercase; color:var(--on-surface-variant)}
.qsample-why p{margin:0 0 var(--s3); font:var(--body-md); letter-spacing:var(--body-md-track)}
.qsample-why .src{margin:var(--s4) 0 0; font:var(--body-sm); letter-spacing:var(--body-sm-track);
                  color:var(--primary)}

/* ---- soft copies --------------------------------------------------------- */

/* Cards, not rows. Six files is a set of objects you pick one out of, and the
   row list they used to be read as a table of contents nobody could click. */
.pdfcards{display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s5); margin-top:var(--s7)}
.pdfcard{display:flex; flex-direction:column; align-items:flex-start; gap:var(--s3);
         padding:var(--s6); border-radius:var(--r-lg);
         background:var(--surface-container-lowest); border:1px solid var(--outline-variant);
         color:var(--on-surface); text-decoration:none}
.pdfcard:hover{border-color:var(--primary); box-shadow:var(--elev-1);
               text-decoration:none; color:var(--on-surface)}
/* No document glyph exists in the kit subset and icons.js forbids drawing one,
   so the marker is the three letters it would have stood for. */
.pdfcard-ico{display:inline-flex; align-items:center; justify-content:center;
             width:48px; height:56px; border-radius:var(--r-xs);
             background:var(--primary-container); color:var(--on-primary-container);
             font:var(--label-lg); letter-spacing:0.08em}
.pdfcard-nm{font:var(--title-md); letter-spacing:var(--title-md-track)}
.pdfcard-act{display:inline-flex; align-items:center; gap:var(--s1);
             font:var(--label-lg); letter-spacing:var(--label-lg-track); color:var(--primary)}
.pdfcard-act svg{width:18px; height:18px}

/* ---- syllabus coverage --------------------------------------------------- */

/* Was a three-column table whose middle column of syllabus headings ran into
   both its neighbours. The weight is the thing a candidate actually reads off
   this, and a bar is read at a glance where a number has to be compared. */
.covlist{list-style:none; margin:var(--s7) 0 0; padding:0;
         border-top:1px solid var(--outline-variant)}
.covrow{display:grid; grid-template-columns:28px minmax(0,1fr) 140px 56px;
        gap:var(--s5); align-items:center; padding:var(--s5) 0;
        border-bottom:1px solid var(--outline-variant)}
.covn{font:var(--label-lg); letter-spacing:var(--label-lg-track);
      color:var(--on-surface-variant); font-variant-numeric:tabular-nums}
.covmain{min-width:0}
.covmain h3{margin:0; font:var(--title-sm); letter-spacing:var(--title-sm-track)}
.covsub{margin:var(--s1) 0 0; font:var(--body-sm); letter-spacing:var(--body-sm-track);
        color:var(--on-surface-variant)}
.covbar{height:8px; border-radius:var(--r-full); background:var(--surface-container-high);
        overflow:hidden}
.covbar span{display:block; height:100%; border-radius:var(--r-full); background:var(--primary)}
.covq{text-align:right; font:var(--label-lg); letter-spacing:var(--label-lg-track);
      color:var(--on-surface-variant); font-variant-numeric:tabular-nums}

@media (max-width:1100px){
  .insidegrid,.pdfcards{grid-template-columns:repeat(2,1fr)}
  .regbox{max-width:420px}
}
@media (max-width:820px){
  .factprose{grid-template-columns:1fr}
  .insidegrid,.pdfcards{grid-template-columns:1fr}
  .qsample{padding:var(--s6)}
  /* The bar is the first thing to go: at this width the question count carries
     the same information and the label needs the room. */
  .covrow{grid-template-columns:24px minmax(0,1fr) 48px; gap:var(--s3)}
  .covbar{display:none}
}

/* ============================================================================
   QUESTION NAVIGATOR, DOCKED
   ==========================================================================*/

/* Above 1200px the navigator stops being a sheet you summon and becomes a
   column that is simply there.
 *
 * The overlay was right when the only place it had to work was a phone. On a
 * desktop it costs a click to answer "how many have I not done" and another to
 * get rid of it again, and it covers the question while it answers - which is
 * the wrong trade in the last ten minutes of a paper, when the whole job is
 * jumping back to what you flagged.
 *
 * Below 1200 nothing changes: the sheet is still the right answer where a
 * 300px column would take a third of the screen. So this is one media query
 * and no new markup - the same checkbox, the same panel, laid out differently.
 * With scripting off it still works, because there was never any scripting. */
@media (min-width:1200px){
  .player{display:grid; grid-template-columns:minmax(0,1fr) 316px;
          grid-template-rows:auto 1fr; align-items:start}
  .playbar{grid-column:1 / -1}
  .qform{grid-column:1; grid-row:2}
  .timeup{grid-column:1 / -1}

  /* Neither bar above it is sticky - the document scrolls - so the panel sticks
     to the viewport, not below a bar that is no longer there. */
  .qnav{display:block; position:sticky; grid-column:2; grid-row:2;
        top:var(--s5); inset-inline:auto; z-index:1;
        background:transparent; padding:var(--s7) var(--s7) var(--s7) 0}
  /* The panel scrolls inside itself rather than running off the bottom of a
     sixty-question paper. */
  .qnav-in{max-height:calc(100vh - var(--s9));
           border:1px solid var(--outline-variant); border-radius:var(--r-lg);
           padding:var(--s5) var(--s5) var(--s6)}
  /* Two controls that only make sense on a sheet you opened. */
  .playbar .navbtn,
  .qnav-head .btn{display:none}
  .qnav-head{margin-bottom:var(--s4)}
  .qnav-head b{font:var(--title-sm); letter-spacing:var(--title-sm-track)}
  .qnav-key{font:var(--body-sm); letter-spacing:var(--body-sm-track); gap:var(--s2)}
  .qnav-grid{grid-template-columns:repeat(auto-fill,minmax(40px,1fr)); gap:var(--s2)}
  .qn{height:40px; font:var(--title-sm); letter-spacing:var(--title-sm-track)}

  /* Inside the rail shell the page is already ~316px narrower, so the question
     column would be squeezed twice. It keeps its reading measure and the
     padding gives way instead. */
  .player.railed .qform{padding-left:var(--s6); padding-right:var(--s6)}
  .player.railed .qnav{padding-top:var(--s6)}
}

/* The rail shell adds its own 96px, so the dock needs more room to appear in
   before it starts squeezing the question. */
@media (min-width:1200px) and (max-width:1420px){
  .player.railed{grid-template-columns:minmax(0,1fr) 280px}
}

/* ============================================================================
   PRODUCT PAGE - proofs, price anchor, FAQ, folded syllabus
   ==========================================================================*/

/* The catalogue card's category badge. A chip shape rather than the eyebrow it
   replaced, because it says the same word the filter chips above it say, and
   two things that filter the same way should look related. */
.excat{align-self:flex-start; display:inline-flex; align-items:center;
       padding:var(--s1) var(--s3); border-radius:var(--r-full);
       background:var(--secondary-container); color:var(--on-secondary-container);
       font:var(--label-sm); letter-spacing:var(--label-sm-track)}

.seeall{margin:var(--s8) 0 0; text-align:center}

/* The market heading on /exams/. */
.catgroup-head h2{display:flex; align-items:center; gap:var(--s3); margin:0;
                  font:var(--title-lg); letter-spacing:var(--title-lg-track)}
.catgroup-head .flag{width:20px; height:15px; border-radius:var(--r-xs);
                     object-fit:cover; box-shadow:0 0 0 1px var(--outline-variant)}

/* ---- why believe any of this -------------------------------------------- */

/* Three checkable claims, straight under the hero. We have no reviews and will
   not invent any; what goes in the slot where social proof would go is the
   things that are true about how the material was built. */
.proofs{list-style:none; display:grid; grid-template-columns:repeat(3,1fr);
        gap:var(--s6); margin:0 0 var(--s10); padding:var(--s7) 0 0;
        border-top:1px solid var(--outline-variant)}
.proofs li{display:flex; flex-direction:column; gap:var(--s2)}
.proofs h2{margin:0; font:var(--title-sm); letter-spacing:var(--title-sm-track)}
.proofs p{margin:0; font:var(--body-md); letter-spacing:var(--body-md-track);
          color:var(--on-surface-variant)}

/* ---- what failing costs -------------------------------------------------- */

.anchorband{background:var(--surface-container); border-radius:var(--r-lg);
            padding:var(--s9) var(--s8)}
.anchorgrid{display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s5);
            margin:var(--s7) 0 var(--s6)}
.anchorfig{padding:var(--s6); border-radius:var(--r-md);
           background:var(--surface-container-lowest);
           border:1px solid var(--outline-variant)}
/* The third figure is the one being argued for, so it is the one that is
   coloured. The other two are the awarding body's numbers and are stated
   flatly, which is the whole rhetorical move. */
.anchorfig.on{background:var(--primary-container); border-color:transparent;
              color:var(--on-primary-container)}
.anchorfig .n{display:block; font:var(--display-sm); letter-spacing:var(--display-sm-track);
              font-variant-numeric:tabular-nums}
.anchorfig .l{display:block; margin-top:var(--s2);
              font:var(--body-md); letter-spacing:var(--body-md-track);
              color:var(--on-surface-variant)}
.anchorfig.on .n,.anchorfig.on .l{color:var(--on-primary-container)}
.anchorline{margin:0 0 var(--s6); font:var(--body-sm); letter-spacing:var(--body-sm-track);
            color:var(--on-surface-variant); max-width:76ch}

/* ---- the questions that would otherwise be typed into Google ------------- */

/* Full width, no measure. An accordion whose rows stop two thirds of the way
   across reads as a column that failed to fill rather than as a deliberate
   text measure - the summary rows are short lines with a marker pinned to the
   right, and the right edge is the thing that has to line up with the rest of
   the page. */
.faq{margin-top:var(--s6); border-top:1px solid var(--outline-variant)}
.faqrow{border-bottom:1px solid var(--outline-variant)}
.faqrow > summary{display:flex; align-items:center; justify-content:space-between;
                  gap:var(--s5); padding:var(--s5) 0; cursor:pointer; list-style:none;
                  font:var(--title-sm); letter-spacing:var(--title-sm-track);
                  color:var(--on-surface)}
.faqrow > summary::-webkit-details-marker{display:none}
/* No chevron glyph is spent on this: a rule that rotates is the same signal in
   two pseudo-elements and no icon budget. */
.faqrow > summary::after{content:""; flex:0 0 12px; width:12px; height:12px;
  border-right:2px solid var(--on-surface-variant); border-bottom:2px solid var(--on-surface-variant);
  transform:rotate(45deg) translate(-2px,-2px);
  transition:transform var(--dur-short) var(--ease-standard)}
.faqrow[open] > summary::after{transform:rotate(-135deg) translate(-2px,-2px)}
.faqrow > summary:hover{color:var(--primary)}
.faqrow > p{margin:0 0 var(--s6);
            font:var(--body-md); letter-spacing:var(--body-md-track);
            color:var(--on-surface-variant)}

/* ---- the syllabus, folded ------------------------------------------------ */

.syllabus > summary{display:flex; flex-direction:column; gap:var(--s2);
                    cursor:pointer; list-style:none; padding-bottom:var(--s4)}
.syllabus > summary::-webkit-details-marker{display:none}
.syllabus > summary .h{font:var(--headline-sm); letter-spacing:var(--headline-sm-track);
                       color:var(--on-surface)}
.syllabus > summary .s{font:var(--body-md); letter-spacing:var(--body-md-track);
                       color:var(--primary)}
.syllabus > summary:hover .h{color:var(--primary)}
.syllabus > summary .s::after{content:" - show all"}
.syllabus[open] > summary .s::after{content:" - hide"}

/* ---- the registration row inside the facts card -------------------------- */

 /* The value in this row is two things stacked - a control and a caption -
    rather than one line of text, so centring the label against the pair put
    "Register" level with the gap between them. It belongs beside the button,
    which is the thing it labels; the caption underneath is sub-text and has
    nothing to align to. 8px is half the difference between the 32px button and
    the label's own 16px line box. */
.facts .r-act{align-items:flex-start}
.facts .r-act dt{padding-top:var(--s2)}
.facts .r-act dd{display:flex; flex-direction:column; align-items:flex-end; gap:var(--s1)}
/* One line, and it stays one line: the caption is a short sentence, not the
   wrapped qualification .factnote is built for. */
.facts .r-act .factnote{max-width:none; white-space:nowrap; margin-top:0}

/* ---- the pass-rate table, folded ---------------------------------------- */

/* A monthly series is thirteen rows against a facts card of eight. The older
   half is a second <tbody> - valid, and the only way to hide table rows
   without script or duplicated markup - revealed by a checkbox. */
.ratesmore-t{position:absolute; opacity:0; width:0; height:0}
.ratesmore{display:none}
.ratesmore-t:checked ~ .data .ratesmore{display:table-row-group}
.ratesmore-b{display:inline-block; margin-top:var(--s4); cursor:pointer;
             font:var(--label-lg); letter-spacing:var(--label-lg-track); color:var(--primary)}
.ratesmore-b:hover{text-decoration:underline}
.ratesmore-t:focus-visible ~ .ratesmore-b{outline:3px solid var(--primary); outline-offset:3px}
.ratesmore-b .b{display:none}
.ratesmore-t:checked ~ .ratesmore-b .a{display:none}
.ratesmore-t:checked ~ .ratesmore-b .b{display:inline}

@media (max-width:1100px){
  .proofs{grid-template-columns:1fr 1fr}
  .anchorgrid{grid-template-columns:1fr}
}
@media (max-width:820px){
  .proofs{grid-template-columns:1fr; gap:var(--s5); margin-bottom:var(--s8)}
  .anchorband{padding:var(--s7) var(--s6)}
  .facts .r-act dd{align-items:flex-start}
  .facts .r-act .btn{max-width:none}
}
