/* Gable Real Estate Prep - generated by make_site_css.py from style.prototype.css.
   DO NOT EDIT THIS FILE. Edit the prototype and regenerate, or the
   next regeneration silently reverts whatever was changed here.
   Brand #7A1F3D. Hue rotated from Evergreen; luminance preserved, so every
   contrast ratio measured on the approved design still holds here. */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap');
/* =============================================================================
   Gable Real Estate Prep - derived from the PROTOTYPE stylesheet (approved on Evergreen), 14 September 2026
   Drop-in replacement for assets/style.css. No markup changes anywhere.

   WHAT CHANGED AND WHY. Four things were wrong with the old look, and they
   were all the same mistake in different clothes: nothing had a hierarchy.

     1. A saturated green slab sat at the top of every page, so the brand
        colour was spent before the page said anything. Colour is now
        CONCENTRATED: one deep-green hero on the home page, green on the
        things you can act on, and a quiet ground everywhere else.
     2. Everything was a card - hero, modules, FAQ, samples, results - each
        with its own shadow, so nothing read as more important than anything
        else. Cards are now hairline-ruled and flat; lift is reserved for
        things that respond to a pointer.
     3. One typeface did every job. Three now, each with a job: Source Serif 4
        for headings and figures (this is a study product - it should look
        like one), IBM Plex Sans for interface text, IBM Plex Mono for
        anything a candidate reads as DATA - counts, timers, option letters,
        citations, scores.
     4. No dark mode at all. There is one now.

   THE CASCADE BUG THIS FILE IS BUILT TO PREVENT. In the published mockup a
   "Start exam" button rendered white-on-white, because a rule inside a
   theme guard outscored the component rule that set its colour. So:
   TEXT-ON-ACCENT IS ALWAYS A TOKEN (--on-accent, --on-deep). Never write a
   colour for text sitting on a brand surface anywhere but in the token
   block, and never let a media query be the only place a colour is defined.

   PER-SITE BRANDING is the first block below and nothing else. The other
   four sites are a five-line swap; their values are recorded there so no
   one has to go digging in a fork for them (DESIGN-PRINCIPLES Layer 2 #7:
   a fork inherits the donor's brand unless someone stops it).
   ============================================================================= */

:root{
  /* ---------- BRAND: the only block that differs between the five sites ----
     Evergreen    #15643a   deep #0D3A28   (this file)
     Hangar       #bf3f00   deep #7A2800
     Lantern      #0e7490   deep #08475A
     Manifest     #4e2059   deep #2F1236
     Wheelhouse   #0b4f8a   deep #073156
     --accent-ink is the accent darkened until it passes AA on --surface for
     small text; --on-accent is what sits ON the accent. Both are set here,
     for every site, so no component ever guesses.                          */
  --accent:#7A1F3D;
  --accent-bright:#97264B;
  --accent-ink:#7A1F3D;
  --accent-wash:#F6EEF1;
  --on-accent:#FFFFFF;
  --brand:#761E3B;          /* every brand SURFACE: masthead and hero alike.  */
  --on-brand:#FFFFFF;       /* Never --accent, which is tuned for small fills */
  --accent-deep:var(--brand);  /* and inverts in dark.                        */
  --brand-bar:var(--brand);    /* kept as aliases so no rule had to be hunted */
  --on-brand-bar:var(--on-brand);
  --on-deep:#EFE4E7;
  --on-deep-dim:rgba(239,228,231,.88);

  /* ---------- Neutrals ---------- */
  --ground:#F8F5F6;
  --surface:#FEFEFE;
  --sunken:#F4F0F1;
  --ink:#2C141C;
  --body:#554249;
  --muted:#77666C;
  --rule:#E5DEE0;
  --rule-soft:#F1EBED;

  /* ---------- Secondary accent: brass. Used for emphasis that must NOT
       read as "correct" - counts, freshness, flags, price.              ---- */
  --brass:#9A6B2E;          /* brass for SMALL accents: rules, counts, citations */
  --brass-ink:#7A5322;
  --brass-wash:#F4E8D6;
  --brass-on-deep:#D2A469;  /* 5.11:1 on --accent-deep; --brass is 2.73:1 there */
  /* A BUTTON is not a small accent. --brass as a fill measured 3.56:1 against its
     own dark label - below AA, and visibly muddy: a mid-tone is too dark to read
     as inviting and too light to carry white text. The buy button gets its own
     lighter fill, which clears AA at 7.4:1 and actually looks like amber. */
  --buy:#D9A441;
  --buy-hover:#C89334;
  --on-buy:#2A1C08;

  /* ---------- Semantic ---------- */
  --good:#176B48; --good-wash:#DCEDE3; --good-rule:#A9CFBA;
  --bad:#9B3124;  --bad-wash:#F7E2DE;  --bad-rule:#E0B4AB;
  --flag:#8A6212; --flag-wash:#F7ECD5;

  /* ---------- Form ---------- */
  --r-lg:16px; --r:12px; --r-sm:9px;
  --lift:0 1px 2px rgba(60,24,36,.05),0 2px 6px rgba(60,24,36,.05);
  --lift-md:0 6px 18px rgba(60,24,36,.08);
  --lift-hi:0 12px 28px -10px rgba(44,20,28,.20),0 3px 8px rgba(44,20,28,.08);
  --ring:0 0 0 3px var(--accent-wash);

  --sym:"Segoe UI Symbol","Apple Symbols","Noto Sans Symbols 2","DejaVu Sans";
  /* Inter for everything the reader reads, IBM Plex Mono for everything the
     reader SCANS. --display is kept as a separate token even though it points at
     the same family: every heading rule already references it, so trying another
     display face later is one line rather than a hunt. */
  --display:"Inter",system-ui,-apple-system,"Segoe UI",Roboto,Arial,var(--sym),sans-serif;
  --sans:"Inter",system-ui,-apple-system,"Segoe UI",Roboto,Arial,var(--sym),sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,"SF Mono",Menlo,Consolas,var(--sym),monospace;
  --serif:var(--display);  /* legacy alias - earlier rules referenced --serif */
  --font:var(--sans);   /* legacy aliases: still referenced from inline */
  --line:var(--rule);   /* styles in app.js and older generated markup.   */
  --bg2:var(--sunken);  /* Dropping one of these removes a border or a    */
  --card:var(--surface);/* background somewhere no CSS rule can reach.    */
  --bg:var(--ground);
  color-scheme:light dark;
}

/* Dark. Only tokens are redefined - never a component. The :not() guard lets
   an explicit light choice win over the OS preference. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --accent:#C97691; --accent-bright:#D496AB; --accent-ink:#E5BAC8;
    --accent-wash:#3C1F28;
    --brand:#761E3B; --on-brand:#FFFFFF;
    --accent-deep:var(--brand); --brand-bar:var(--brand); --on-brand-bar:var(--on-brand);
    --on-accent:#1E0A10; --on-deep:#F4EAEE; --on-deep-dim:rgba(244,234,238,.88);
    --ground:#180D11; --surface:#25181C; --sunken:#2E1E23;
    --ink:#F2ECEE; --body:#C8BDC1; --muted:#A08E94;
    --rule:#3B2C31; --rule-soft:#322328;
    --brass:#CB9C5D; --brass-ink:#DEB77F; --brass-wash:#2B2317;
    --brass-on-deep:#D2A469;
    --buy:#E0AE52; --buy-hover:#D19E43; --on-buy:#231704;
    --good:#4FAE80; --good-wash:#15301F; --good-rule:#2C5A40;
    --bad:#D97A66;  --bad-wash:#33191A;  --bad-rule:#5E2F28;
    --flag:#D2A85E; --flag-wash:#2E2614;
    --lift:0 1px 2px rgba(0,0,0,.4);
    --lift-md:0 6px 18px rgba(0,0,0,.5);
    --lift-hi:0 12px 28px -10px rgba(0,0,0,.65),0 3px 8px rgba(0,0,0,.45);
    --ring:0 0 0 3px rgba(201,118,145,.28);
  }
}
:root[data-theme="dark"]{
  --accent:#C97691; --accent-bright:#D496AB; --accent-ink:#E5BAC8;
  --accent-wash:#3C1F28;
  --brand:#761E3B; --on-brand:#FFFFFF;
  --accent-deep:var(--brand); --brand-bar:var(--brand); --on-brand-bar:var(--on-brand);
  --on-accent:#1E0A10; --on-deep:#F4EAEE; --on-deep-dim:rgba(244,234,238,.88);
  --ground:#180D11; --surface:#25181C; --sunken:#2E1E23;
  --ink:#F2ECEE; --body:#C8BDC1; --muted:#A08E94;
  --rule:#3B2C31; --rule-soft:#322328;
  --brass:#CB9C5D; --brass-ink:#DEB77F; --brass-wash:#2B2317;
  --brass-on-deep:#D2A469;
  --buy:#E0AE52; --buy-hover:#D19E43; --on-buy:#231704;
  --good:#4FAE80; --good-wash:#15301F; --good-rule:#2C5A40;
  --bad:#D97A66;  --bad-wash:#33191A;  --bad-rule:#5E2F28;
  --flag:#D2A85E; --flag-wash:#2E2614;
  --lift:0 1px 2px rgba(0,0,0,.4);
  --lift-md:0 6px 18px rgba(0,0,0,.5);
  --lift-hi:0 12px 28px -10px rgba(0,0,0,.65),0 3px 8px rgba(0,0,0,.45);
  --ring:0 0 0 3px rgba(201,118,145,.28);
}

/* ---------- reset / base ---------- */
*{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%}
body{font-family:var(--sans);background:var(--ground);color:var(--body);
  font-size:15px;line-height:1.55;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
.wrap{max-width:1060px;margin:0 auto;padding-inline:20px;padding-block:0 72px}
h1,h2,h3,h4{font-family:var(--display);color:var(--ink);font-weight:700;line-height:1.2;
  text-wrap:balance;letter-spacing:-.019em}
/* Inter is loose by default at large sizes and tight at small ones; the tracking
   has to move with the size or headings read flabby and captions read cramped. */
h1{letter-spacing:-.028em}
h3{letter-spacing:-.012em}
/* Figures line up in columns only if they are tabular. Inter has them; ask. */
.sicell b,.num,.timer,.score-hero .big,.herostat b,td{font-variant-numeric:tabular-nums}
a{color:var(--accent-ink);text-underline-offset:2px}
a:hover{color:var(--accent)}
b,strong{color:var(--ink);font-weight:600}
/* ...EXCEPT on a brand surface, where --ink is unreadable. This is the cascade
   bug from the header note in its third costume: a global rule painting text a
   fixed colour, meeting a surface that is not the colour that rule assumed.
   It was found by the render harness on Wheelhouse, whose hero copy contains a
   <strong> - and only there, because no other site's hero copy happens to use
   one. The same latent fault sat in all five stylesheets, and a fourth costume
   is presumably waiting; `inherit` is the answer that does not need to know
   which surface it is on, so it holds for whatever gets painted brand next.
   (.hero .price b overrides this deliberately, further down, to go pure white.) */
.hero b,.hero strong,header.app b,header.app strong,
.herostat b,.herostat strong,button b,button strong,
.btn-buy b,.btn-buy strong,.tabs button.on b,.tabs button.on strong{color:inherit}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:3px}
@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}

/* ---------- masthead ------------------------------------------------------
   Was a saturated green slab on every page. Now a quiet editorial masthead:
   the brand is the only coloured thing, the exam title is the loudest thing,
   and a 2px ink rule does the work the green block used to do badly.      */
header.app{display:flex;justify-content:space-between;align-items:flex-end;gap:16px 24px;
  flex-wrap:wrap;background:var(--brand-bar);color:var(--on-brand-bar);border-radius:var(--r-lg);
  padding:18px 26px;margin:18px 0 20px;box-shadow:var(--lift-md);border:none}
header.app h1{font-size:clamp(1.4rem,3vw,1.95rem);margin-top:10px;color:var(--on-brand-bar);letter-spacing:-.024em}
header.app .sub{color:var(--on-brand-bar);opacity:.86;font-size:.88rem;max-width:76ch;margin-top:8px;line-height:1.5}
header.app .brand{color:var(--on-brand-bar);font-size:1.16rem}
header.app .brand:hover{color:var(--on-brand-bar);opacity:.88}
header.app .brandmark{color:var(--on-brand-bar)}
.brand{display:inline-flex;align-items:center;gap:9px;font-family:var(--display);font-weight:700;
  font-size:1.1rem;color:var(--accent-ink);text-decoration:none;letter-spacing:-.018em}
.brand:hover{color:var(--accent)}
header.app .brand{font-size:1.12rem}
.brandmark{height:1.32em;width:1.32em;vertical-align:-.22em;display:inline-block;color:var(--accent);flex:none}
header.app .brandmark{height:1.32em;width:1.32em;margin-right:0}

/* ---------- freshness stamp ---------- */
.freshness{display:flex;flex-wrap:wrap;align-items:center;gap:8px;font-size:.82rem;
  color:var(--muted);background:var(--accent-wash);border:1px solid var(--rule);
  border-radius:999px;padding:7px 15px;margin:0 0 20px;font-weight:500}
.freshness b{color:var(--ink);font-weight:600}
.freshness .fdot{width:6px;height:6px;border-radius:50%;background:var(--good);flex:none}

/* ---------- cards: hairline, flat, no competing shadows ---------- */
.card{background:var(--surface);border:1px solid var(--rule);border-radius:var(--r-lg);
  padding:24px 26px;margin-bottom:18px;box-shadow:var(--lift)}
.card > p.muted,.card > p{max-width:80ch}
.card h2{font-size:1.24rem;padding-bottom:12px;border-bottom:1px solid var(--rule);margin-bottom:18px}
.card > .row:first-child{justify-content:space-between;align-items:baseline;
  border-bottom:1px solid var(--rule);padding-bottom:12px;margin-bottom:18px}
.card > .row:first-child h2{border-bottom:none;padding-bottom:0;margin-bottom:0}
h2{font-size:1.24rem;margin-bottom:12px}
h3{font-size:1rem;margin-bottom:8px;font-weight:600}
p{margin-bottom:10px}
p:last-child{margin-bottom:0}
.muted{color:var(--muted);font-size:.87rem}

/* ---------- hero: the one place the brand colour is spent ---------- */
.hero{background:var(--accent-deep);color:var(--on-deep);border:1px solid transparent;border-radius:var(--r-lg);
  padding:34px 36px 32px;margin-bottom:22px;box-shadow:none;position:relative;overflow:hidden}
.hero::after{content:"";position:absolute;inset:0 0 auto 0;height:3px;
  background:linear-gradient(90deg,var(--brass),transparent 62%)}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) .hero{border-color:rgba(255,255,255,.09)}}
:root[data-theme="dark"] .hero{border-color:rgba(255,255,255,.09)}
.hero .eyebrow{font-size:.76rem;font-weight:600;text-transform:uppercase;
  letter-spacing:.1em;color:var(--brass-on-deep);margin-bottom:14px;display:flex;align-items:center;gap:12px}
.hero .eyebrow::after{content:"";height:1px;flex:1;max-width:140px;background:rgba(255,255,255,.18)}
.hero h1{font-family:var(--display);font-size:clamp(1.85rem,3.6vw,2.5rem);line-height:1.06;
  font-weight:700;letter-spacing:-.034em;color:#fff;margin-bottom:14px;max-width:24ch;border-bottom:none;padding-bottom:0}
.hero p{color:var(--on-deep);max-width:64ch;font-size:.97rem;margin-bottom:0}
.hero .price{font-size:.92rem;margin-top:18px;color:var(--on-deep-dim)}
.hero .price b{color:#fff;font-family:var(--display);font-size:1.08rem;font-weight:700;letter-spacing:-.018em}
.herostats{display:grid;grid-template-columns:repeat(auto-fit,minmax(132px,1fr));gap:1px;margin-top:24px;
  background:rgba(255,255,255,.18);border:1px solid rgba(255,255,255,.18);border-radius:var(--r);overflow:hidden}
.herostat{background:var(--brand);padding:13px 16px}
.herostat b{display:block;font-family:var(--display);font-size:1.45rem;font-weight:700;color:#fff;
  letter-spacing:-.022em;
  line-height:1;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.herostat span{display:block;font-size:.78rem;color:var(--on-deep-dim);margin-top:7px}

/* ---------- section heads ---------- */
.grouphead{display:flex;align-items:baseline;gap:14px;margin:34px 0 16px;font-family:var(--display);
  font-size:1.16rem;font-weight:700;color:var(--ink);text-transform:none;letter-spacing:-.012em;
  border-bottom:2px solid var(--ink);padding-bottom:10px}
.grouphead::after{content:none}
.card > .grouphead:first-child{margin-top:0}

/* ---------- credential tiles: tiles + reactivity, as asked for ---------- */
.credgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(306px,1fr));gap:12px}
/* auto-fill lays down as many tracks as FIT, not as many as are needed, so a
   group with one credential (Manifest has exactly one) put a single tile in the
   first of three tracks and left two thirds of the row empty - it read as a
   missing pair rather than as a deliberate single. Counting the children in the
   selector is the honest fix; :has() can do it, so the markup does not have to
   carry a class saying how many of itself there are. */
.credgrid:has(> :first-child:last-child){grid-template-columns:minmax(0,1fr)}
.credgrid:has(> :nth-child(2):nth-last-child(1)){grid-template-columns:repeat(2,minmax(0,1fr))}
.credlink{position:relative;display:flex;align-items:center;gap:15px;padding:16px 18px;
  border:1px solid var(--rule);border-radius:var(--r);background:var(--surface);color:var(--body);
  text-decoration:none;box-shadow:var(--lift);overflow:hidden;
  transition:transform .16s ease,box-shadow .16s ease,border-color .16s ease}
.credlink::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--accent);
  transform:scaleY(0);transform-origin:50% 50%;transition:transform .18s ease}
.credlink:hover{transform:translateY(-2px);box-shadow:var(--lift-hi);border-color:var(--rule)}
.credlink:hover::before{transform:scaleY(1)}
.credlink:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.credtile{width:46px;height:46px;flex:none;border-radius:var(--r-sm);background:var(--accent-wash);
  color:var(--accent-ink);display:flex;align-items:center;justify-content:center;
  font-weight:700;font-size:.9rem;letter-spacing:.02em}
.credbody{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1}
.credname{font-family:var(--display);font-weight:600;font-size:1.02rem;color:var(--ink);letter-spacing:-.014em}
.credsub{font-size:.8rem;color:var(--muted)}
.credcount{font-size:.84rem;color:var(--ink);margin-top:6px;font-weight:500;
  display:flex;align-items:center;gap:7px;font-variant-numeric:tabular-nums}
.credcount::before{content:"";width:5px;height:5px;border-radius:50%;background:var(--brass);flex:none}
.credsplit{font-size:.78rem;color:var(--muted);margin-top:2px}
.credchev{margin-left:auto;color:var(--muted);font-size:1.25rem;align-self:center;transition:color .16s,transform .16s}
.credlink:hover .credchev{color:var(--accent);transform:translateX(2px)}
.credlink.soon{opacity:.5;pointer-events:none;box-shadow:none}
.credlink.soon .credtile{background:var(--sunken);color:var(--muted)}
.credlink.mini{padding:13px 15px}
.credlink.mini .credtile{width:38px;height:38px;font-size:.76rem}
.credlink.mini .credname{font-size:.97rem}

/* ---------- state index --------------------------------------------------
   One row per state, both lines on the row: 25 rows where there were 50 tiles.
   A table would be the honest element here, but the generator emits divs and
   this is a CSS-led change; grid with matching column tracks on head and rows
   gives the same alignment without touching more markup than necessary.      */
.sitop{display:flex;flex-wrap:wrap;align-items:baseline;gap:10px 18px;margin-bottom:14px}
.sitop .grouphead{margin:0;border:none;padding:0;flex:0 0 auto}
.sitools{display:flex;align-items:center;gap:12px;margin-left:auto;flex:1 1 260px;justify-content:flex-end}
.sisearch{flex:1 1 auto;max-width:280px;min-width:160px;font-size:.92rem;padding:8px 13px}
.sisearch::-webkit-search-cancel-button{cursor:pointer}
.sicount{font-size:.82rem;color:var(--muted);white-space:nowrap;font-variant-numeric:tabular-nums}
/* A full-width row per state left a dead zone between the name and the two
   controls, and stood 106px tall - so it read as a wide table row after all and
   gave back the compactness the index was for. Two columns of TILES instead:
   badge and name on top, the two lines as controls beneath. Same information,
   same one-row-per-state idea, half the height, and the shape the old site used. */
.siwrap{display:grid;grid-template-columns:repeat(auto-fill,minmax(424px,1fr));gap:12px;
  border:none;border-radius:0;overflow:visible}
.sihead{display:none}   /* column headers are a spreadsheet idiom; every value names itself now */
.sirow{display:grid;grid-template-columns:46px minmax(0,1fr) minmax(0,1fr);gap:8px 13px;
  align-items:center;padding:12px 15px;background:var(--surface);border:1px solid var(--rule);
  border-radius:var(--r);box-shadow:var(--lift);position:relative;overflow:hidden;
  transition:transform .16s ease,box-shadow .16s ease}
.siabbr{grid-column:1;grid-row:1/3}
.siname{grid-column:2/4;grid-row:1}
.sirow > .sicell:nth-child(3){grid-column:2;grid-row:2}
.sirow > .sicell:nth-child(4){grid-column:3;grid-row:2}
.sirow::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--accent);
  transform:scaleX(0);transform-origin:left;transition:transform .16s}
.sirow:hover{transform:translateY(-2px);box-shadow:var(--lift-hi)}
.sirow:hover::before{transform:scaleX(1)}
.sirow[hidden]{display:none}   /* .sirow sets display:grid, which would otherwise beat [hidden] */
.siabbr{width:46px;height:46px;display:flex;align-items:center;justify-content:center;
  background:var(--accent-wash);color:var(--accent-ink);border-radius:var(--r-sm);
  font-size:.86rem;font-weight:700;letter-spacing:.03em;align-self:center}
.siname{font-family:var(--display);font-size:1.02rem;font-weight:600;color:var(--ink);letter-spacing:-.014em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Each line is a control you can press, and it says which line it is - so the
   column headers are not needed and the row does not need to be read as a grid. */
.sicell{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 6px;text-decoration:none;
  color:var(--body);padding:7px 11px;border:1px solid var(--rule);border-radius:var(--r-sm);
  background:var(--surface);transition:background .13s,border-color .13s,box-shadow .13s}
.sicell::before{content:attr(data-line);flex:0 0 100%;font-size:.72rem;color:var(--muted);
  line-height:1.35;margin-bottom:1px}
.sicell b{font-weight:600;color:var(--accent-ink);font-variant-numeric:tabular-nums;font-size:.98rem;
  letter-spacing:-.012em}
.sicell .siq{color:var(--muted);font-size:.78rem}
.sicell .sigo{display:none}
.sicell:hover{background:var(--accent-wash);border-color:var(--accent);box-shadow:var(--lift)}
.sicell:hover::before{color:var(--accent-ink)}
.sicell.empty{display:flex;align-items:center;justify-content:center;color:var(--muted);opacity:.45;
  font-size:1rem;background:var(--sunken);border-style:dashed}
.sinone{padding:20px 16px;color:var(--muted);font-size:.9rem;margin:0}
.sinone[hidden]{display:none}
@media (max-width:720px){
  .siwrap{grid-template-columns:1fr}
  .sirow{grid-template-columns:44px minmax(0,1fr);grid-auto-rows:min-content;
    padding:13px 14px;gap:8px 12px}
  .siabbr{width:44px;height:44px;grid-column:1;grid-row:1}
  .siname{grid-column:2;grid-row:1}
  .sirow > .sicell:nth-child(3){grid-column:1/-1;grid-row:2}
  .sirow > .sicell:nth-child(4){grid-column:1/-1;grid-row:3}
  .sicell{margin:0;padding:9px 12px}
  .sicell.empty{display:none}
  .sitools{margin-left:0;justify-content:flex-start}
}

/* ---------- buttons ---------- */
button{font:inherit;font-family:var(--sans);font-weight:500;cursor:pointer;border-radius:var(--r-sm);
  border:1px solid var(--rule);background:var(--surface);padding:9px 17px;color:var(--ink);
  transition:background .14s,border-color .14s,color .14s,transform .14s,box-shadow .14s}
button:hover{border-color:var(--accent);background:var(--accent-wash);color:var(--accent-ink)}
button:active{transform:translateY(1px)}
button.primary{background:var(--accent);border-color:var(--accent);color:var(--on-accent);font-weight:600;box-shadow:var(--lift)}
button.primary:hover{background:var(--accent-bright);border-color:var(--accent-bright);color:var(--on-accent);box-shadow:var(--lift-hi)}
button.danger{color:var(--bad);border-color:transparent;background:none}
button.danger:hover{background:var(--bad-wash);border-color:var(--bad-rule);color:var(--bad)}
button:disabled{opacity:.42;cursor:default;transform:none;box-shadow:none}
button:disabled:hover{background:var(--surface);border-color:var(--rule);color:var(--ink)}
.btn-buy{display:inline-flex;align-items:center;gap:9px;background:var(--buy);color:var(--on-buy);
  font-weight:700;padding:12px 22px;border-radius:var(--r-sm);text-decoration:none;box-shadow:var(--lift);
  transition:background .14s,box-shadow .14s,transform .14s}
.btn-buy:hover{background:var(--buy-hover);color:var(--on-buy);box-shadow:var(--lift-hi);transform:translateY(-1px)}
.row{display:flex;gap:12px;flex-wrap:wrap;align-items:center}
.field{display:flex;flex-direction:column;gap:6px;font-size:.84rem;font-weight:500;color:var(--ink)}
.field > span:first-child{font-size:.78rem;color:var(--muted);font-weight:500}
select,input{font:inherit;font-family:var(--sans);padding:9px 12px;border:1px solid var(--rule);
  border-radius:var(--r-sm);background:var(--surface);color:var(--ink);transition:border-color .14s,box-shadow .14s}
select:focus,input:focus{outline:none;border-color:var(--accent);box-shadow:var(--ring)}

/* ---------- tables / pills ---------- */
table{border-collapse:collapse;width:100%;font-size:.89rem}
th,td{text-align:left;padding:10px 12px;border-bottom:1px solid var(--rule-soft)}
th{color:var(--muted);font-weight:600;font-size:.78rem;border-bottom-color:var(--rule)}
td{font-variant-numeric:tabular-nums}
tr.histrow:hover td{background:var(--sunken)}
.pill{display:inline-block;padding:4px 12px;border-radius:999px;font-size:.8rem;
  background:var(--accent-wash);color:var(--accent-ink);font-weight:500}
.pill.good{background:var(--good-wash);color:var(--good)}
.pill.bad{background:var(--bad-wash);color:var(--bad)}

/* ---------- module tiles ---------- */
.modgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(272px,1fr));gap:13px}
.modcard{border:1px solid var(--rule);border-radius:var(--r);padding:17px 18px;background:var(--surface);
  box-shadow:var(--lift);display:flex;flex-direction:column;gap:11px;
  transition:transform .16s ease,box-shadow .16s ease}
.modcard:hover{transform:translateY(-2px);box-shadow:var(--lift-hi)}
.modcard h3{margin:0;font-size:1rem;line-height:1.28}
.modcard .meta{color:var(--muted);font-size:.8rem;margin:0}
.modchips{display:flex;flex-wrap:wrap;gap:6px}
.chip{font-size:.76rem;font-weight:500;color:var(--accent-ink);
  background:var(--accent-wash);border-radius:999px;padding:4px 11px;white-space:nowrap}
.chip.gold{color:var(--brass-ink);background:var(--brass-wash)}
.mprog{height:4px;border-radius:999px;background:var(--sunken);overflow:hidden}
.mprog > i{display:block;height:100%;background:var(--accent);transition:width .3s ease}
/* Prose, not data - it was reading as a machine field in mono. */
.mprog-label{font-family:var(--sans);font-size:.78rem;color:var(--muted);margin-top:0}

/* The module GROUP headings used to be reached with six !important
   declarations, because app.js wrote their typography as an inline style and an
   inline style beats a stylesheet. patch_app_shared.py removes that inline
   style and gives the h3 a class instead, so the rule can now win by being
   right rather than by shouting. The rule itself lives at the foot of the
   generated per-site files (make_site_css.py, SHARED_EXTRA) next to the note
   explaining the dependency, and covers the un-patched inline form too, so a
   site whose app.js has not been patched yet still renders correctly - just
   without the ability to override it later. */
#modGrid > p.muted{margin-top:6px}
.modcard button.primary{margin-top:auto;width:100%}
.modrow{display:flex;gap:8px;flex-wrap:wrap;margin-top:4px}
.modrow button{flex:1;min-width:112px}
.note-soft{background:var(--brass-wash);color:var(--brass-ink);border-radius:var(--r-sm);
  padding:11px 15px;font-size:.86rem;font-weight:500;display:flex;align-items:center;gap:9px;
  border:1px solid transparent}

/* ---------- exam runner ---------- */
.testbar{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:16px}
.timer{font-family:var(--mono);font-variant-numeric:tabular-nums;font-weight:600;font-size:1.02rem;
  padding:8px 16px;border-radius:var(--r-sm);background:var(--surface);border:1px solid var(--rule);
  color:var(--ink);box-shadow:var(--lift)}
.timer.low{color:var(--bad);border-color:var(--bad);background:var(--bad-wash)}
.qmeta{color:var(--muted);font-size:.82rem;margin-bottom:12px;
  display:flex;gap:12px;flex-wrap:wrap;align-items:center}
.stem{font-family:var(--display);font-size:1.19rem;font-weight:500;color:var(--ink);
  margin-bottom:24px;white-space:pre-wrap;line-height:1.45;letter-spacing:-.011em;max-width:72ch}
.opt{display:flex;gap:14px;align-items:flex-start;border:1px solid var(--rule);border-radius:var(--r);
  padding:14px 16px;margin-bottom:9px;cursor:pointer;background:var(--surface);color:var(--body);
  font-size:.945rem;line-height:1.5;
  transition:border-color .13s,background .13s,box-shadow .13s,transform .13s}
.opt:hover{border-color:var(--accent);background:var(--accent-wash);color:var(--ink)}
.opt.sel{border-color:var(--accent);background:var(--accent-wash);color:var(--ink);box-shadow:inset 0 0 0 1px var(--accent)}
.opt .letter{font-family:var(--mono);font-weight:500;color:var(--muted);min-width:1.3em;padding-top:.08em}
.opt.sel .letter{color:var(--accent-ink);font-weight:600}
.opt.locked{cursor:default}
.opt.locked:hover{transform:none}
.opt.correct{border-color:var(--good);background:var(--good-wash);color:var(--ink);box-shadow:inset 0 0 0 1px var(--good)}
.opt.correct .letter{color:var(--good)}
.opt.wrong{border-color:var(--bad);background:var(--bad-wash);color:var(--ink);box-shadow:inset 0 0 0 1px var(--bad)}
.opt.wrong .letter{color:var(--bad)}
.palette{display:grid;grid-template-columns:repeat(auto-fill,minmax(40px,1fr));gap:6px}
.palette button{padding:9px 0;font-family:var(--mono);font-size:.78rem;font-variant-numeric:tabular-nums;position:relative}
.palette button.answered{background:var(--sunken);border-color:var(--rule);color:var(--ink);font-weight:600}
.palette button.right{background:var(--good-wash);border-color:var(--good-rule);color:var(--good)}
.palette button.wrong{background:var(--bad-wash);border-color:var(--bad-rule);color:var(--bad)}
.palette button.current{outline:2px solid var(--accent);outline-offset:1px}
.palette button.flagged::after{content:"";position:absolute;top:3px;right:3px;width:5px;height:5px;
  border-radius:50%;background:var(--flag)}
.navrow{display:flex;justify-content:space-between;gap:10px;margin-top:18px;flex-wrap:wrap}
.flagbtn.on{background:var(--flag-wash);border-color:var(--flag);color:var(--flag)}
.verdict{font-size:.86rem;font-weight:700;letter-spacing:.01em;margin-bottom:6px}
.verdict.good{color:var(--good)}
.verdict.bad{color:var(--bad)}

/* ---------- results ---------- */
.score-hero{text-align:center;padding:40px 20px}
.score-hero h2{border-bottom:none;padding-bottom:0}
.score-hero .big{font-family:var(--display);font-size:3.8rem;font-weight:700;letter-spacing:-.04em;
  line-height:1;color:var(--ink);font-variant-numeric:tabular-nums}
.score-hero .big.good{color:var(--good)}
.score-hero .big.bad{color:var(--bad)}
/* Mono caps suits a short data line and shouts on a sentence. Scope it to the
   two lines that ARE data; app.js also puts .muted on prose here. */
.score-hero #resMeta,.score-hero #resCount{font-size:.86rem;color:var(--muted)}
.score-hero #resPass{margin-top:16px;display:flex;flex-wrap:wrap;gap:10px 14px;
  align-items:center;justify-content:center}
.score-hero #resPass .muted{font-family:var(--sans);font-size:.87rem;text-transform:none;letter-spacing:0}
.rev{border:1px solid var(--rule);border-radius:var(--r);padding:18px;margin-bottom:14px;
  background:var(--surface);box-shadow:none}
.rev .ans{padding:10px 13px;border-radius:var(--r-sm);margin:6px 0;font-size:.92rem;display:flex;gap:11px;
  border:1px solid transparent}
.rev .ans .letter{font-family:var(--mono);font-weight:500;min-width:1.3em}
.rev .ans.correct{background:var(--good-wash);border-color:var(--good-rule);color:var(--ink)}
.rev .ans.wrong{background:var(--bad-wash);border-color:var(--bad-rule);color:var(--ink)}
.rev .ans.plain{border-color:var(--rule-soft)}
.rationale{background:var(--sunken);border-left:2px solid var(--accent);border-radius:0 var(--r-sm) var(--r-sm) 0;
  padding:13px 15px;font-size:.9rem;margin-top:11px;color:var(--body)}
.rationale:has(.verdict.good){border-left-color:var(--good)}
.rationale:has(.verdict.bad){border-left-color:var(--bad)}
.src{display:block;margin-top:9px;font-family:var(--mono);font-size:.72rem;color:var(--brass-ink);letter-spacing:.02em}
.tabs{display:flex;gap:0;margin-bottom:16px;flex-wrap:wrap;border:1px solid var(--rule);
  border-radius:var(--r-sm);overflow:hidden;width:fit-content}
.tabs button{border:none;border-radius:0;font-size:.84rem;font-weight:500;
  padding:10px 18px;color:var(--muted);background:var(--surface)}
.tabs button:hover{background:var(--sunken);color:var(--ink)}
.tabs button.on{background:var(--accent);color:var(--on-accent);font-weight:600}

/* ---------- scope pickers ---------- */
.scopechips,.scopechiprow{display:flex;flex-wrap:wrap;gap:7px;margin-top:5px}
.scopechip{font-size:.82rem;font-weight:500;padding:6px 14px;
  border:1px solid var(--rule);border-radius:999px;background:var(--surface);color:var(--muted);
  cursor:pointer;transition:.13s}
.scopechip:hover{border-color:var(--accent);color:var(--ink)}
.scopechip.on{border-color:var(--accent);color:var(--accent-ink);background:var(--accent-wash);font-weight:600}
.scopebar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;flex-basis:100%;margin-top:5px}
.scopesummary{font-size:.82rem;font-weight:500;
  color:var(--accent-ink);background:var(--accent-wash);border:1px solid var(--rule);border-radius:999px;padding:5px 13px}
.scopelink{font-family:var(--sans);font-size:.84rem;font-weight:500;color:var(--accent-ink);
  background:none;border:none;cursor:pointer;padding:4px}
.scopelink:hover{color:var(--accent);text-decoration:underline;background:none;border-color:transparent}
.scopepanel{flex-basis:100%;margin-top:10px;border:1px solid var(--rule);border-radius:var(--r);
  padding:14px 16px;background:var(--sunken)}
.scopegrouphead{display:flex;align-items:center;gap:9px;font-size:.78rem;
  font-weight:600;color:var(--muted);margin:14px 0 7px}
.scopegrouphead:first-of-type{margin-top:2px}
.scopegroupn{font-size:.72rem;font-weight:600;color:var(--accent-ink);
  background:var(--accent-wash);border-radius:999px;padding:1px 9px}
.scopebreakdown{margin-top:8px;font-size:.82rem;color:var(--muted)}
.scopebreakdown b{color:var(--accent-ink);font-weight:600}
/* This badge is how a candidate tells an official past-exam question from an
   authored one, which on Manifest is most of the reason to buy. It sits INSIDE
   the rationale block, whose background is already a pale wash (green when
   right, pink when wrong) - so a pale wash with no border vanished into it in
   light mode: contrast measured fine, because the TEXT was legible; the chip
   simply had no edges. It keeps its rule in both variants now. The weight does
   the rest of the work: the official badge is the one that reads as a stamp. */
.provbadge{display:inline-block;margin-top:10px;padding:3px 11px;border-radius:999px;
  font-size:.76rem;background:var(--surface);color:var(--muted);border:1px solid var(--rule)}
.provbadge.off{background:var(--accent-wash);color:var(--accent-ink);
  border:1px solid var(--accent-ink);font-weight:600}

/* ---------- SEO blocks: present, but clearly secondary to the exam ---------- */
.seo-block{background:none;border:none;border-top:1px solid var(--rule);border-radius:0;
  padding:30px 0 6px;margin-bottom:0}
.seo-block h2{margin-top:0;border-bottom:none;padding-bottom:0;margin-bottom:6px;font-size:1.18rem}
.seo-block > .muted{margin-bottom:20px}
.seo-faq{border-bottom:1px solid var(--rule-soft);padding:16px 0}
.seo-faq:last-child{border-bottom:none}
.seo-faq h3{font-size:.99rem;margin-bottom:6px}
.seo-faq p{margin:0;color:var(--muted);max-width:82ch}
.seo-q{border:1px solid var(--rule);border-radius:var(--r);padding:18px 20px;margin-bottom:14px;
  background:var(--surface);box-shadow:none}
.seo-stem{font-family:var(--display);font-size:1.04rem;font-weight:600;color:var(--ink);
  letter-spacing:-.012em;margin-bottom:13px;white-space:pre-wrap}
.seo-opts{list-style-position:inside;margin:0;padding:0}
.seo-opts li{padding:10px 13px;border:1px solid var(--rule-soft);border-radius:var(--r-sm);margin:7px 0;font-size:.92rem}
.seo-opts li::marker{font-family:var(--mono);font-weight:500;color:var(--muted)}
.seo-opts li.seo-correct{background:var(--good-wash);border-color:var(--good-rule);color:var(--ink);font-weight:500}
.seo-exp{background:var(--sunken);border-left:2px solid var(--accent);border-radius:0 var(--r-sm) var(--r-sm) 0;
  padding:12px 14px;font-size:.89rem;margin-top:13px}
.seo-more{margin-top:6px}
.seo-more > summary{cursor:pointer;font-size:.88rem;font-weight:600;
  color:var(--accent-ink);padding:12px 0 4px;list-style:none}
.seo-more > summary:hover{color:var(--accent)}
.seo-more > summary::-webkit-details-marker{display:none}
.seo-more > summary::before{content:"+ "}
.seo-more[open] > summary::before{content:"\2212 "}
.seo-more > .seo-q:first-of-type{margin-top:12px}

/* ---------- footer ---------- */
footer{margin-top:44px;border-top:1px solid var(--rule);padding-top:22px;font-size:.82rem;
  color:var(--muted);text-align:left;line-height:1.6}
footer a{color:var(--muted);text-decoration:underline}
footer a:hover{color:var(--accent-ink)}

/* A page someone is SITTING AN EXAM on does not need the landing-page
   masthead: title, note and stamp cost ~200px, so a laptop showed two
   options before scrolling. :has() degrades to no-rule where unsupported,
   which is exactly the old behaviour - there is no broken state. */
body:has(#view-test:not(.hidden)) header.app{padding:12px 20px;margin:14px 0 16px}
body:has(#view-test:not(.hidden)) header.app h1{font-size:1.06rem;margin-top:0}
body:has(#view-test:not(.hidden)) header.app .sub,
body:has(#view-test:not(.hidden)) .freshness{display:none}

.hidden{display:none}

/* ---------- narrow screens ---------- */
@media (max-width:640px){
  .wrap{padding-inline:16px}
  /* As a flex row each text node became its own wrapping column and the
     stamp tore into a three-column mess. As a block it is just a sentence. */
  .freshness{display:block}
  .freshness .fdot{display:inline-block;margin-right:7px;vertical-align:.12em}
  header.app{padding-top:20px;align-items:flex-start}
  .hero{padding:30px 22px 28px;border-radius:var(--r)}
  .card{padding:20px 18px;border-radius:var(--r)}
  .seo-block{padding-inline:0}
  .stem{font-size:1.1rem}
  .score-hero .big{font-size:3.1rem}
  .credgrid,.modgrid{grid-template-columns:1fr}
  .tabs{width:100%}
  .tabs button{flex:1}
}

/* ---------- print ---------- */
@media print{
  .noprint{display:none}
  body{background:#fff;color:#000}
  .wrap{max-width:none}
  header.app{background:none;border-bottom:2px solid #000}
  header.app h1,header.app .brand{color:#000}
  .hero{background:#fff;color:#000;border:1px solid #000}
  .hero h1,.hero p,.hero .price b,.herostat b{color:#000}
  .herostat{background:#fff}
  .card,.rev,.seo-q{box-shadow:none;border:1px solid #999}
}

/* ---------- module group headings -----------------------------------------
   app.js used to write these as an inline style, which meant the stylesheet
   could only reach them with !important - a rule that wins by shouting rather
   than by being right, and one that silently stops working the day someone
   edits the inline string. app.js now emits class="modgrouphead" and the
   geometry it genuinely needs (spanning the grid) lives here, where it can be
   seen. patch_app_shared.py makes that change; this rule assumes it. */
#modGrid > h3.modgrouphead,#modGrid > h3[style]{
  grid-column:1/-1;font-family:var(--display);font-size:.82rem;font-weight:600;
  letter-spacing:0;text-transform:none;color:var(--muted);margin:22px 0 2px}
#modGrid > h3.modgrouphead:first-child,#modGrid > h3[style]:first-child{margin-top:4px}
