/* The app pages — exam, paper, result, account, admin.
   Every colour here resolves to css/tokens.css, which is the brand: cream
   paper, one lavender accent, generous radii. Nothing invents a colour.
   The design is light by intent — "cream paper" is the whole idea — so there
   is no dark variant to drift out of step with the landing page. */
:root{
  /* Only names tokens.css does NOT already define. Aliasing a token to itself
     (--ink: var(--ink)) is a cycle, which makes the property invalid and takes
     the colour with it. */
  --bg:var(--cream);
  --card:var(--paper);
  --mut:var(--muted);
  --accent-ink:#fff;
  --ok:var(--lime-ink);
  --warn:var(--peach-ink);
  --bad:#C0566A;
  --s1:var(--accent); --s2:#E8833A; --s3:var(--lime-ink); --s4:var(--sky-ink);
  --ph-bg:var(--peach-soft); --ph-ink:var(--peach-ink);
  --shadow:var(--shadow-sm);
  --r:var(--r-sm);
}

*{box-sizing:border-box}
html,body{margin:0}
body{background:var(--bg);color:var(--ink-2);
  font-family:var(--f-body),var(--dev);font-size:15px;line-height:1.6;
  -webkit-text-size-adjust:100%}
a{color:var(--accent);text-decoration:none} a:hover{text-decoration:underline}

/* The nav, the brand, the language switch and the footer are chrome.css —
   one implementation, shared with the landing page. */

/* The same measure the landing uses, so the brand, the language switch and the
   footer sit at exactly the same place on every page — leaving the front page
   used to move the whole shell 220px inward.

   Width is not the same as reading width: `.page` is the stage, and the things
   that are actually read carry their own measure — see the reading-measure
   block under the headings below. Nothing narrows the stage itself, because a
   stage that changes size between pages is the thing that reads as broken. */
.page{width:100%;max-width:1600px;margin:0 auto;padding:28px var(--gut)}

/* cards */
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r);padding:18px;box-shadow:var(--shadow)}
/* `minmax(0,1fr)`, not the implicit `auto`.
 *
 * A grid item's default minimum is `min-content`, so a column left to size
 * itself refuses to go narrower than the widest unbreakable thing inside it —
 * and on a 320px phone that made the exam page's subject cards 346px wide and
 * pushed the whole page sideways. `minmax(0, …)` is what gives a grid child
 * permission to be smaller than its contents and wrap them instead. The wider
 * layouts below all set their own columns, so this only governs the stacked
 * single-column case, which is every phone. */
.grid{display:grid;gap:14px;grid-template-columns:minmax(0,1fr)}
@media(min-width:720px){.grid.two{grid-template-columns:1fr 1fr}}
/* A list of cards fills the stage instead of queueing down the middle of it. */
@media(min-width:900px){.grid.cards{grid-template-columns:repeat(auto-fill,minmax(330px,1fr))}}

h1,h2,h3,.brand{font-family:var(--f),var(--dev)}
h1{font-size:clamp(26px,5vw,38px);line-height:1.15;letter-spacing:-.02em;margin:0 0 12px;
  font-weight:900;color:var(--ink)}
h2{font-size:clamp(19px,3vw,23px);letter-spacing:-.01em;margin:30px 0 12px;font-weight:800;color:var(--ink)}
h3{font-size:16px;margin:0 0 8px;font-weight:800;color:var(--ink)}
p{margin:0 0 12px}
.lede{font-size:17px;color:var(--mut);max-width:60ch}

/* Reading measure, now that `.page` is a stage rather than a column. A line of
   prose is easier to read short than wide however big the window is; a grid of
   cards is not, so this deliberately names the prose and leaves the rest. */
.page > p:not(.err):not(.ok):not(.note),
.page > h1,
.page > h2,
.fmd > p,
.fmd > ul,
.fmd > ol,
.fmd > blockquote,
.fmd .verdict{max-width:68ch}
.fmd .sec > p,
.fmd .sec > ul,
.fmd .sec > ol,
.fmd .sec > blockquote{max-width:68ch}
/* ── AN EYEBROW IS A LABEL THAT HAPPENS TO BE A HEADING ──────────────────
 *
 * The sitting hall and the report card name themselves with a small uppercase
 * line — "CG SHIKSHAK (VARG-2) 2026 · COMPLETENESS MOCK". It is marked up as
 * an `h1` on purpose: it is what that screen IS, and without it the one screen
 * in the product that matters most would have no heading for a reader arriving
 * by keyboard or screen reader.
 *
 * WHICH MEANS IT MUST NEVER FALL BACK TO LOOKING LIKE ONE. It lived in
 * paper.css, one sheet among six; a stale cache or a page that forgot the link
 * left `h1{font-size:clamp(26px,5vw,38px);font-weight:900}` in charge, and a
 * mock's name filled a phone screen five lines deep before the score. So the
 * rule lives here, in the stylesheet every page loads, and it names the tag as
 * well as the class so nothing plainer can outrank it. */
h1.eyebrow,.eyebrow{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  margin:0 0 14px;
  font-family:var(--f);font-size:11px;font-weight:800;line-height:1.5;
  letter-spacing:.14em;text-transform:uppercase;color:var(--muted);
}
.eyebrow .modetag{
  flex:none;
  border:1.5px solid var(--lav-line);background:var(--lav-soft);color:var(--lav-ink);
  border-radius:var(--pill);padding:2px 10px;letter-spacing:.09em;font-size:10px;
}
.mut{color:var(--mut)} .sm{font-size:13px} .xs{font-size:12px}

/* pills */
.tag{display:inline-flex;align-items:center;gap:5px;border-radius:999px;padding:3px 10px;font-size:11px;
  font-weight:800;letter-spacing:.04em;text-transform:uppercase;border:1px solid var(--line);color:var(--mut)}
.tag.free{background:color-mix(in srgb,var(--ok) 14%,transparent);border-color:var(--ok);color:var(--ok)}
.tag.paid{background:color-mix(in srgb,var(--accent) 12%,transparent);border-color:var(--accent);color:var(--accent)}
.tag.owned{background:color-mix(in srgb,var(--ok) 14%,transparent);border-color:var(--ok);color:var(--ok)}
.tag.soon{opacity:.75}

/* mock rows */
.mock{display:flex;align-items:center;gap:12px;padding:12px 0;border-bottom:1px solid var(--line)}
.mock:last-child{border-bottom:0}
.mock .m-main{flex:1;min-width:0}
.mock .m-name{font-weight:700}
.mock .m-ans{color:var(--mut);font-size:13px}
.mock.locked{opacity:.62}

/* ── THE MOCK ROW, WHEN THERE IS NO ROOM FOR A ROW ───────────
 *
 * Name, then a badge, then a button, all on one line. It holds
 * down to about 380px and then the badge and the button — both
 * fixed width — take everything, leaving the name ninety pixels
 * to live in. "Full Real Exam Like Mock" then sets as five
 * lines of one word each, which is not a layout, it is a
 * column of words.
 *
 * So on the narrowest phones it stops being a row: the name
 * takes the full width and the badge and the button sit under
 * it on their own line, where they have room to be read and
 * pressed. */
@media(max-width:400px){
  .mock{flex-wrap:wrap;row-gap:9px}
  .mock .m-main{flex:1 1 100%}
  .mock .tag{order:2}
  .mock .btn{order:3;margin-left:auto}
}

/* forms */
label{display:block;font-size:13px;font-weight:700;margin:0 0 5px}
/* `search` was missing, so every search box on the site fell back to the
   browser's own 170px-wide unstyled control. */
input[type=text],input[type=email],input[type=password],input[type=number],
input[type=search],input[type=tel],input:not([type]),textarea,select{
  width:100%;padding:10px 12px;border:1px solid var(--line);border-radius:9px;background:var(--bg);
  color:var(--ink);font:inherit;font-size:15px}
input:focus,select:focus{outline:2px solid var(--accent);outline-offset:1px;border-color:var(--accent)}
/* A field is as wide as what goes in it. `width:100%` inside a stage that is
   now 1400px wide gives you a text box a metre long for an exam id. */
.field{margin-bottom:14px;max-width:44ch}
.field.wide{max-width:none}
.fields .field{max-width:none}
.err{background:color-mix(in srgb,var(--bad) 10%,transparent);border:1px solid var(--bad);color:var(--bad);
  border-radius:9px;padding:10px 12px;font-size:14px;margin:0 0 14px}
.ok{background:color-mix(in srgb,var(--ok) 10%,transparent);border:1px solid var(--ok);color:var(--ok);
  border-radius:9px;padding:10px 12px;font-size:14px;margin:0 0 14px}
/* ONE STEP UP, ON THE PAGE THAT LISTS WHAT YOU OWN.
 *
 * Peach rather than the accent: this is an offer, not the thing the page is
 * for. A candidate came here to open a paper, and the upgrade must read as
 * available rather than as the page nagging them. It is one tap target, the
 * price is the one they will actually be charged, and it never appears at all
 * once they hold everything. */
.up{display:flex;align-items:center;gap:12px;margin:12px 0 0;padding:12px 14px;
  border:1px solid var(--peach-line);background:var(--peach-soft);
  border-radius:var(--r-sm);text-decoration:none;color:inherit}
.up:hover{border-color:var(--peach-ink)}
.up-t{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}
.up-t b{font-size:14px;color:var(--peach-ink)}
.up-t small{font-size:12px;color:var(--muted);line-height:1.45}
.up-p{flex:none;font-family:var(--f);font-weight:900;font-size:17px;color:var(--peach-ink);
  font-variant-numeric:tabular-nums}
@media (max-width:520px){ .up{align-items:flex-start;flex-wrap:wrap} .up-p{margin-left:auto} }

/* A LABEL FOR THE EAR, NOT THE EYE.
 *
 * Some controls are obvious to look at and nameless to a screen reader — the
 * search box on /exams was one, carrying only a placeholder, which is not a
 * name and disappears the moment somebody types. This gives it a real <label>
 * without putting a second word on the screen beside it. */
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;border:0}

/* Neither a failure nor a success: "this cannot be taken back, read it again".
   Peach, because the other two already mean wrong and right. */
.warn{background:var(--peach-soft);border:1px solid var(--peach-line);color:var(--peach-ink);
  border-radius:9px;padding:10px 12px;font-size:14px;margin:0 0 14px}

/* sitting a paper */
/* The stage is wide; a question is not. A stem stretched across a metre of
   glass is harder to read than the same stem in a column, and the palette
   belongs next to it rather than a hand's width away. */
/* Capped, but not centred: the eyebrow above it starts at the page gutter, so
   the paper starts there too. A centred column under a left-aligned line is
   the same misalignment the shell used to have, one level down. */
/* THE SITTING HALL AND THE REPORT CARD LIVE IN css/paper.css.
   They were here, and they are the two screens the whole product exists to
   reach — a question card sharing a rule with a marketing panel is how the
   one screen that matters most ends up being the one nobody designed. What
   stays here is only what other pages also use. */

/* rendered FMD */
.fmd .tw{overflow-x:auto;border:1px solid var(--line);border-radius:10px;margin:0 0 16px;background:var(--card)}
.fmd table{border-collapse:collapse;width:100%;font-size:14px}
.fmd th,.fmd td{padding:9px 12px;text-align:left;border-bottom:1px solid var(--line);vertical-align:top}
.fmd thead th{background:color-mix(in srgb,var(--line) 35%,transparent);font-weight:700}
.fmd tbody tr:last-child td{border-bottom:0}
.fmd .verdict{background:var(--card);border:1px solid var(--line);border-left:3px solid var(--accent);
  border-radius:10px;padding:14px 16px;margin:14px 0}
.fmd .ph{background:var(--ph-bg);color:var(--ph-ink);border-radius:4px;padding:0 4px}
.fmd .viz{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:14px;margin:0 0 16px}
.fmd .viz svg{width:100%;height:auto}
.fmd pre.mermaid{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:14px;
  overflow-x:auto;font-size:13px;color:var(--mut)}
.fmd .kind{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--mut);font-weight:800}
.fmd blockquote{margin:0 0 14px;padding:8px 14px;border-left:3px solid var(--line);color:var(--mut)}

.hide{display:none!important}
.spin{display:inline-block;width:15px;height:15px;border:2px solid var(--line);border-top-color:var(--accent);
  border-radius:50%;animation:sp .7s linear infinite}
@keyframes sp{to{transform:rotate(360deg)}}
@media(max-width:520px){.wrap{padding-block:18px}}

/* The two ways to sit a paper. Asked once, before anything loads, because it
   changes what a paper is: a measurement or a lesson. */
.modepick{max-width:700px;margin:0 auto}
.modepick h2{margin:0 0 16px}
.modes{display:grid;gap:12px;grid-template-columns:minmax(0,1fr)}
@media(min-width:640px){.modes{grid-template-columns:1fr 1fr}}
.mode{display:grid;gap:6px;text-align:left;background:var(--card);border:1.5px solid var(--line);
  border-radius:var(--r);padding:20px;cursor:pointer;font:inherit;color:var(--ink);
  transition:border-color .12s,box-shadow .12s,transform .12s}
.mode:hover,.mode:focus-visible{border-color:var(--accent);box-shadow:var(--shadow);transform:translateY(-1px)}
.mode .mg{font-size:20px;color:var(--accent);line-height:1}
.mode b{font-size:16px;letter-spacing:-.01em}
.mode .md{font-size:13.5px;color:var(--mut);line-height:1.5}
.modetag{margin-left:8px;padding:2px 9px;border-radius:var(--pill);background:var(--lav-soft);
  color:var(--lav-ink);font-size:10.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase}
/* A one-line aside inside a page — "this paper was resumed". Called .banner
   because .note further down this file is the revision-note card, and two
   rules with the same name meant the card's display:grid quietly won: every
   one of these was being drawn as a bordered grid box instead of a compact
   lavender line. */
.banner{background:var(--lav-soft);color:var(--lav-ink);border-radius:9px;padding:9px 13px;margin:0 0 14px}


/* The reveal, in an open sitting. It opens the moment an answer is committed
   and never before, which is the whole difference between the two modes. */
.reveal{margin-top:16px;padding:14px 16px;border:1.5px solid var(--line);border-radius:12px;
  animation:rv .18s ease-out}
@keyframes rv{from{opacity:0;transform:translateY(-3px)}to{opacity:1;transform:none}}
@media(prefers-reduced-motion:reduce){.reveal{animation:none}.mode:hover{transform:none}}
.rv-v{margin:0 0 10px;font-weight:800;font-size:15px}
/* Not .ok / .no: app.css already uses .ok for a banner, and a class that means
   two things is a class that breaks when either one changes. */
.reveal.hit{border-color:var(--lime-line);background:color-mix(in srgb,var(--ok) 6%,transparent)}
.reveal.miss{border-color:color-mix(in srgb,var(--bad) 45%,var(--line));background:color-mix(in srgb,var(--bad) 5%,transparent)}
.reveal.hit .rv-v{color:var(--ok)}
.reveal.miss .rv-v{color:var(--bad)}
.rv-t{color:var(--mut);font-weight:700;font-size:12.5px}
.rv-x{font-size:14.5px}
.rv-x>*:last-child{margin-bottom:0}

/* Three labels for a hit and three for a miss — never the same three, because
   until the answer is committed nobody knows which set applies. */
.howsure{display:flex;align-items:center;gap:7px;flex-wrap:wrap;margin:14px 0 0;
  padding-top:13px;border-top:1px dashed var(--line)}
.hs-q{font-size:12.5px;font-weight:700;color:var(--mut);margin-right:2px;font-family:var(--f)}
.hs{border:1.5px solid var(--line);background:var(--bg);border-radius:var(--pill);
  padding:6px 13px;font:inherit;font-size:12.5px;font-weight:700;color:var(--mut);cursor:pointer;
  font-family:var(--f);transition:background .12s,border-color .12s,color .12s}
.hs .hs-c{font-variant-numeric:tabular-nums;opacity:.65;margin-right:3px}
.hs:hover{border-color:var(--lav-line);color:var(--ink)}
.hs-C:hover{border-color:var(--ok)}
.hs-W:hover{border-color:var(--bad)}
.hs.on .hs-c{opacity:1}
.hs-C.on{background:color-mix(in srgb,var(--ok) 13%,transparent);border-color:var(--ok);color:var(--ok)}
.hs-W.on{background:color-mix(in srgb,var(--bad) 13%,transparent);border-color:var(--bad);color:var(--bad)}
.lab-C{color:var(--ok);font-weight:800}
.lab-W{color:var(--bad);font-weight:800}

/* THE TWO BUTTONS THAT ASK "ARE YOU SURE".
 *
 * This panel is appended into the palette column, which is about 260px wide —
 * six 34px squares and their gaps. Two buttons in a row do not fit there, and
 * with no wrap the second one ("Keep working") was laid out past the card's
 * right edge: the way out of a submit confirmation, hanging outside the box
 * that was asking the question.
 *
 * They wrap, and each takes the full width when it does, so the answer you
 * want is never the one that fell off. Confirm first, and not full-bleed on a
 * wide screen, so it stays a pair rather than becoming a stack of slabs. */
.confirm-do{display:flex;gap:8px;flex-wrap:wrap}
.confirm-do .btn{flex:1 1 auto;min-width:0}


/* ============================================================
   REVISION NOTES, AND THE STICKIES WALL

   A sticky is a note that is pinned, so there is one card here, not two —
   .sticky only changes how it is laid out, never what it is. The five colours
   are the five pastels the whole product is built from; a candidate's own
   colour system is only useful if the colours are distinguishable, so they are
   the tokens, not tints of one.
   ============================================================ */
.note{border:1.5px solid var(--line);border-radius:14px;padding:14px 16px;
  background:var(--card);display:grid;gap:8px;align-content:start}
.note-lime {background:var(--lime-soft); border-color:var(--lime-line)}
.note-lav  {background:var(--lav-soft);  border-color:var(--lav-line)}
.note-peach{background:var(--peach-soft);border-color:var(--peach-line)}
.note-sky  {background:var(--sky-soft);  border-color:color-mix(in srgb,var(--sky-ink) 30%,var(--line))}
.note-rose {background:var(--rose-soft); border-color:color-mix(in srgb,var(--rose-ink) 30%,var(--line))}
.ntitle{font-family:var(--f);font-size:13px;font-weight:800;color:var(--ink);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.nbody{font-size:14.5px;line-height:1.6;color:var(--ink-2);white-space:normal;overflow-wrap:anywhere}
.note textarea{width:100%;border:1px solid var(--line);border-radius:10px;padding:10px 12px;
  background:var(--card);font:inherit;font-size:14.5px;resize:vertical}

.nhead{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:0 0 14px}
.nhead>:first-child{flex:1}
.nlist{display:grid;gap:12px;grid-template-columns:minmax(0,1fr)}
@media(min-width:760px){.nlist{grid-template-columns:1fr 1fr}}

/* The wall. Wider than the notes list is narrow: a sticky is a glance, so more
   of them fit across, and each one stays short enough to be one. */
.wall{display:grid;gap:12px;grid-template-columns:repeat(auto-fill,minmax(210px,1fr))}
.sticky .nbody{font-size:14px;display:-webkit-box;-webkit-line-clamp:9;-webkit-box-orient:vertical;overflow:hidden}
.sticky{min-height:150px}

.nbar{display:flex;gap:6px;align-items:center;flex-wrap:wrap;
  border-top:1px dashed color-mix(in srgb,var(--ink) 12%,transparent);padding-top:9px;margin-top:2px}
.nb{border:1px solid transparent;background:transparent;border-radius:var(--pill);
  padding:4px 9px;font:inherit;font-family:var(--f);font-size:11.5px;font-weight:800;
  color:var(--ink-2);cursor:pointer;opacity:.72}
.nb:hover{opacity:1;border-color:color-mix(in srgb,var(--ink) 18%,transparent)}
.nb.on{opacity:1;background:var(--card)}
.nb.del:hover{color:var(--bad);border-color:var(--bad)}
.nb:disabled{opacity:.3;cursor:default}

.swatch{display:flex;gap:5px}
.sw{width:16px;height:16px;border-radius:50%;border:1.5px solid rgba(0,0,0,.12);cursor:pointer;padding:0}
.sw.on{outline:2px solid var(--ink);outline-offset:2px}
.sw-lime{background:var(--lime)} .sw-lav{background:var(--lav)} .sw-peach{background:var(--peach)}
.sw-sky{background:var(--sky)}   .sw-rose{background:var(--rose)}

/* Keeping something, from wherever it is being read. Deliberately quiet: it
   sits under an explanation and must not compete with it. */
.keep{border:1.5px dashed var(--line-2);background:transparent;border-radius:var(--pill);
  padding:5px 12px;font:inherit;font-family:var(--f);font-size:12px;font-weight:800;
  color:var(--mut);cursor:pointer;margin-top:10px}
.keep:hover{border-style:solid;border-color:var(--accent);color:var(--accent)}
.keep.on{border-style:solid;border-color:var(--lime-line);background:var(--lime-soft);color:var(--lime-ink)}
.keep:disabled{cursor:default}
.keep.pin{margin-left:2px}

/* The weakness mock, before there is enough to build it from. Progress, not an
   error: nothing is broken, it simply has not been earned yet. */
.wkrow{display:grid;grid-template-columns:74px 1fr;gap:4px 12px;align-items:center;margin:0 0 14px}
.wkn{font-size:19px;font-weight:800;font-family:var(--f);letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;text-align:right}
.wkn .mut{font-size:13px;font-weight:700}
.wkb{height:8px;border-radius:var(--pill);background:var(--line);overflow:hidden}
.wkb i{display:block;height:100%;background:var(--accent);border-radius:var(--pill);
  transition:width .3s ease-out}
.wkl{grid-column:2;font-size:11.5px;font-weight:800;letter-spacing:.07em;
  text-transform:uppercase;color:var(--mut)}

/* ============================================================
   THE FMD PRIMITIVES

   engine/fmd/render.mjs turns every ```stats / ```cards / ```map / ```flow /
   ```chart / ```matrix / ```leak / ```label block into markup. None of that
   markup had any styling, so a forensics document — the exam's shop window and
   the whole reading half of the product — rendered its structured blocks as
   run-together prose: "15 Years / 15 वर्षForensic Timeline Analysed".
   ============================================================ */

/* The document shell */
.fmd .doc-h{margin:0 0 26px;padding-bottom:18px;border-bottom:1px solid var(--line)}
.fmd .doc-h h1{margin:4px 0 0}
.fmd .sec{margin:0 0 30px;scroll-margin-top:80px}
.fmd .sec > h2{margin:0 0 14px}
.fmd .sec.detail > details{border:1px solid var(--line);border-radius:var(--r);
  background:var(--card);padding:14px 16px}
.fmd .sec.detail > details > summary{cursor:pointer;font-family:var(--f);font-weight:800;
  font-size:16px;color:var(--ink);list-style:none}
.fmd .sec.detail > details > summary::-webkit-details-marker{display:none}
.fmd .sec.detail > details > summary::before{content:"▸ ";color:var(--accent)}
.fmd .sec.detail > details[open] > summary::before{content:"▾ "}
.fmd .sec.detail > details[open] > summary{margin-bottom:12px}

/* ```stats — a row of figures, each with its caption underneath */
.fmd .stats-row{display:grid;gap:10px;margin:0 0 18px;
  grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
.fmd .stat-fig{background:var(--card);border:1px solid var(--line);border-radius:var(--r);
  padding:14px 16px;display:grid;gap:3px;align-content:start}
.fmd .stat-fig b{font-family:var(--f);font-size:21px;font-weight:900;letter-spacing:-.02em;
  color:var(--ink);line-height:1.2}
.fmd .stat-fig span{font-size:12.5px;line-height:1.45;color:var(--mut)}

/* ```cards — a title, a kicker, and a line of explanation */
.fmd .card-grid{display:grid;gap:10px;margin:0 0 18px;
  grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}
.fmd .card-item{background:var(--card);border:1px solid var(--line);border-radius:var(--r);
  padding:14px 16px;display:grid;gap:4px;align-content:start}
.fmd .card-item b{font-family:var(--f);font-size:14.5px;font-weight:800;color:var(--ink)}
.fmd .card-item .ci-sub{font-size:11.5px;font-weight:800;letter-spacing:.07em;
  text-transform:uppercase;color:var(--accent)}
.fmd .card-item p{margin:2px 0 0;font-size:13.5px;line-height:1.55;color:var(--ink-2)}

/* ```map — what you already know → what the paper actually asks */
.fmd .map-list{list-style:none;margin:0 0 18px;padding:0;display:grid;gap:8px}
.fmd .map-list li{display:grid;gap:2px 10px;align-items:baseline;
  grid-template-columns:1fr;background:var(--lav-soft);border:1px solid var(--lav-line);
  border-radius:12px;padding:11px 14px}
@media(min-width:600px){.fmd .map-list li{grid-template-columns:1fr auto 1fr}}
.fmd .m-from{color:var(--mut);font-size:14px}
.fmd .m-arrow{color:var(--lav-ink);font-weight:800}
.fmd .m-to{font-weight:700;color:var(--lav-ink);font-size:14px}

/* ```flow — the steps, in order, because the order is the point */
.fmd .flow-list{margin:0 0 18px;padding:0;list-style:none;counter-reset:f;display:grid;gap:8px}
.fmd .flow-list li{counter-increment:f;display:grid;grid-template-columns:24px 1fr;
  gap:11px;align-items:baseline;font-size:14.5px;line-height:1.55}
.fmd .flow-list li > span{min-width:0}
.fmd .flow-list li::before{content:counter(f);display:grid;place-items:center;
  width:24px;height:24px;border-radius:50%;background:var(--accent);color:#fff;
  font-family:var(--f);font-size:12px;font-weight:800;align-self:start}

/* ```text — kept as written */
.fmd pre.plain{background:var(--card);border:1px solid var(--line);border-radius:12px;
  padding:13px 15px;margin:0 0 16px;overflow-x:auto;font-size:13.5px;line-height:1.6;
  white-space:pre-wrap;font-family:var(--f-body),var(--dev);color:var(--ink-2)}

/* ```chart — the SVG, its axis labels and its key */
.fmd .viz figcaption{font-family:var(--f);font-weight:800;font-size:13px;color:var(--ink);
  margin:0 0 10px}
.fmd .viz text.ax{font-size:10px;fill:var(--mut);font-family:var(--f-body),sans-serif}
.fmd .legend{display:flex;gap:14px;flex-wrap:wrap;margin-top:10px}
.fmd .lg{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--mut);font-weight:700}
.fmd .lg i{width:10px;height:10px;border-radius:3px;display:inline-block}

/* ```matrix — heat, not just numbers */
.fmd table.grid td{text-align:center;font-variant-numeric:tabular-nums;font-size:13px}
.fmd table.grid td.on{background:color-mix(in srgb,var(--accent) calc(var(--a) * 55%),transparent)}
.fmd table.grid th{font-family:var(--f);font-weight:800;font-size:12.5px}

/* ```leak — ranked, because the order is the advice */
.fmd .leaks{list-style:none;margin:0 0 18px;padding:0;display:grid;gap:9px}
.fmd .leak{display:grid;grid-template-columns:30px 1fr;gap:12px;background:var(--card);
  border:1px solid var(--line);border-left:3px solid var(--bad);border-radius:12px;padding:13px 15px}
.fmd .leak .rk{font-family:var(--f);font-size:17px;font-weight:900;color:var(--bad);
  font-variant-numeric:tabular-nums;line-height:1.3}
.fmd .leak b{font-family:var(--f);font-size:14.5px;color:var(--ink)}
.fmd .leak p{margin:5px 0 3px;font-size:13.5px;line-height:1.55}
.fmd .leak small{font-size:11.5px;color:var(--mut)}

/* ```label — the six ASA labels, in their own colours */
.fmd .labels{display:flex;gap:7px;flex-wrap:wrap;margin:0 0 18px}
.fmd .lab{display:inline-flex;align-items:baseline;gap:6px;border:1.5px solid var(--line);
  border-radius:var(--pill);padding:5px 13px;font-size:12.5px;font-weight:700;color:var(--ink-2);
  background:var(--card);font-family:var(--f)}
.fmd .lab b{font-variant-numeric:tabular-nums;font-weight:900}
.fmd .lab.green,.fmd .lab.ok{border-color:var(--lime-line);background:var(--lime-soft);color:var(--lime-ink)}
.fmd .lab.red,.fmd .lab.bad{border-color:color-mix(in srgb,var(--bad) 45%,var(--line));
  background:var(--rose-soft);color:var(--rose-ink)}
.fmd .lab.amber,.fmd .lab.warn{border-color:var(--peach-line);background:var(--peach-soft);color:var(--peach-ink)}
.fmd .lab.blue{border-color:color-mix(in srgb,var(--sky-ink) 35%,var(--line));
  background:var(--sky-soft);color:var(--sky-ink)}

/* A block we could not read, and one meant for the operator. Neither is shown
   to a student; when they are shown they should look like a note, not a bug. */
.fmd details.broken,.fmd details.machine{border:1px dashed var(--line-2);border-radius:10px;
  padding:9px 13px;margin:0 0 16px;background:var(--cream-2)}
.fmd details.broken summary,.fmd details.machine summary{cursor:pointer;font-size:12px;
  font-weight:800;color:var(--mut);font-family:var(--f)}
.fmd details.broken pre,.fmd details.machine pre{margin:10px 0 0;font-size:12px;
  overflow-x:auto;color:var(--ink-2)}

/* A visual whose numbers are not written yet. It says so plainly rather than
   drawing an empty chart, because an empty chart reads as a broken one. */
.fmd .fallback{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;
  background:var(--peach-soft);border:1px solid var(--peach-line);border-radius:12px;
  padding:10px 14px;margin:0 0 16px;font-size:13.5px;color:var(--peach-ink)}
.fmd .fallback .chip{font-family:var(--f);font-size:10.5px;font-weight:800;letter-spacing:.07em;
  text-transform:uppercase;background:var(--peach);color:var(--peach-ink);
  border-radius:var(--pill);padding:2px 9px;flex:none}
.fmd .viz.mx figcaption{margin-bottom:8px}

/* The two tiers, side by side, in the order a candidate meets them. */
/* One column on a phone, which is where most of this is read — the rungs then
   arrive in price order, cheapest first, which is the order somebody decides
   in. Two up when there is room, three once all three fit without squeezing. */
/* Same reason as `.grid` above: an implicit column will not go narrower than
   the widest unbreakable thing in the card, which on a 320px phone left the
   three price cards 318px wide inside a 284px page. */
.tiers{display:grid;gap:12px;margin-top:24px;grid-template-columns:minmax(0,1fr)}
@media(min-width:700px){.tiers{grid-template-columns:1fr 1fr;align-items:start}}
@media(min-width:1040px){.tiers{grid-template-columns:repeat(3,1fr)}}
.tier h3{margin:0 0 6px}
.tier .btn{width:100%}

/* Six subjects, each a stack of six drills: two columns on a wide screen, so
   the page is a board to scan rather than a mile of scrolling. */
@media(min-width:1100px){.grid.subjects{grid-template-columns:1fr 1fr;align-items:start}}

/* ============================================================
   REGISTRATION
   Its own page, not a tab hidden behind Sign in. Asking which exam
   here rather than after they are in is what decides where they land:
   an exam we have opens; one we do not gets written down.
   ============================================================ */
.reg{max-width:560px;margin:0 auto}
.reg h1{font-size:clamp(24px,4vw,32px)}
.reg .lede{margin:0 0 22px}
.reg-card{padding:22px}
.reg-card .field{max-width:none}
.reg-exam{border:0;padding:0;margin:18px 0 16px;min-width:0}
.reg-exam legend{padding:0;font-size:13px;font-weight:700;margin:0 0 8px;color:var(--ink-2)}
.reg-picks{display:grid;gap:8px}
@media(min-width:460px){.reg-picks{grid-template-columns:1fr 1fr}}
.reg-pick{display:grid;gap:2px;text-align:left;border:1.5px solid var(--line);border-radius:12px;
  padding:11px 13px;background:var(--bg);font:inherit;cursor:pointer;color:var(--ink-2);
  transition:border-color .12s,background .12s}
.reg-pick b{font-family:var(--f);font-size:13.5px;color:var(--ink)}
.reg-pick span{font-size:11.5px;color:var(--mut);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.reg-pick:hover{border-color:var(--accent)}
.reg-pick.on{border-color:var(--accent);background:var(--lav-soft)}
.reg-pick.on b{color:var(--lav-ink)}
.reg-pick.alt{border-style:dashed}

.reg-notes{list-style:none;margin:20px 0 0;padding:0;display:grid;gap:9px}
.reg-notes li{position:relative;padding-left:22px;font-size:13.5px;color:var(--mut);line-height:1.55}
.reg-notes li::before{content:"✓";position:absolute;left:0;top:0;color:var(--lime-ink);font-weight:900}

/* ============================================================
   THE CATALOGUE  ·  /exams

   Every exam we serve or intend to, in three states. A list of only
   the finished ones answers a question nobody asked, and makes the
   one they did ask look like a no.
   ============================================================ */
/* ── THE CATALOGUE ────────────────────────────────────────────────────────
   One question: is my exam here, and can I start? The search answers it
   fastest, so it is the biggest thing on the page; what can be opened today
   comes next as cards; everything else is one line each, because a name and a
   tap is all a row that cannot be sat yet has to offer. */

.ex-find{margin:0 0 14px}
.ex-find input{font-size:17px;padding:15px 18px;border-radius:var(--r-sm);
  border-width:2px;background:var(--paper)}
.ex-find input:focus{border-color:var(--accent)}
@media(min-width:700px){.ex-find input{font-size:19px;padding:17px 20px}}

/* Category as a filter, not five sections to scroll past. */
.ex-chips{display:flex;flex-wrap:wrap;gap:7px;margin:0 0 22px}
/* Five chips wrap to three rows on a phone — ninety pixels of filter above the
   thing being filtered. One row that scrolls sideways instead, the same rail
   the landing page uses, so what is below stays where the thumb expects it. */
@media(max-width:560px){
  .ex-chips{flex-wrap:nowrap;overflow-x:auto;overscroll-behavior-x:contain;
    scroll-snap-type:x proximity;padding-bottom:4px;margin-inline:calc(var(--gut) * -1);
    padding-inline:var(--gut);scrollbar-width:none}
  .ex-chips::-webkit-scrollbar{display:none}
  .ex-chips button{flex:none;scroll-snap-align:start}
}
.ex-chips button{display:inline-flex;align-items:baseline;gap:6px;cursor:pointer;
  border:1.5px solid var(--line-2);background:var(--paper);color:var(--ink-2);
  border-radius:var(--pill);padding:7px 13px;font:inherit;font-size:13px;font-weight:700}
.ex-chips button span{font-size:11px;font-weight:800;color:var(--muted);font-variant-numeric:tabular-nums}
.ex-chips button:hover{border-color:var(--accent)}
.ex-chips button.on{background:var(--ink);border-color:var(--ink);color:var(--cream)}
.ex-chips button.on span{color:var(--cream-2);opacity:.75}

.ex-h{display:flex;align-items:baseline;gap:9px;margin:22px 0 12px;font-size:19px}
.ex-h span{font-family:var(--f-body);font-size:13px;font-weight:700;color:var(--muted);
  font-variant-numeric:tabular-nums}
.ex-h-quiet{color:var(--ink-2);font-size:16px;margin-top:30px}

.ex-grid{display:grid;gap:12px}
@media(min-width:560px){.ex-grid{grid-template-columns:repeat(auto-fill,minmax(240px,1fr))}}

.ex-card{display:grid;gap:4px;align-content:start;background:var(--card);
  border:1.5px solid var(--line-2);border-radius:var(--r-sm);padding:16px 18px;
  transition:border-color .15s,box-shadow .15s,transform .15s}
.ex-card:hover{text-decoration:none;border-color:var(--accent);box-shadow:var(--shadow);transform:translateY(-1px)}
.ex-card b{font-family:var(--f);font-size:17px;letter-spacing:-.01em;color:var(--ink)}
.ex-card .ex-body{font-size:12.5px;color:var(--mut);line-height:1.45}
.ex-card .ex-state{justify-self:start;font-size:10.5px;font-weight:800;letter-spacing:.07em;
  text-transform:uppercase;border-radius:999px;padding:3px 9px;margin-bottom:4px}
.ex-card.ready   .ex-state{background:var(--lime-soft);color:var(--lime-ink)}
.ex-card.open    .ex-state{background:var(--sky-soft);color:var(--sky-ink)}
.ex-card.reading .ex-state{background:var(--peach-soft);color:var(--peach-ink)}
.ex-card.topmate .ex-state{background:var(--lav-soft);color:var(--lav-ink)}
.ex-card.topmate .ex-go{color:var(--lav-ink)}
.ex-card .ex-go{margin-top:8px;font-family:var(--f);font-size:13px;font-weight:800;color:var(--accent)}
/* Ready is the one thing on this page anybody can start today. It reads like it. */
.ex-card.ready{border-color:var(--lime-line);background:var(--lime-soft)}
.ex-card.ready:hover{border-color:var(--lime-ink)}
.ex-card.ready .ex-go{color:var(--lime-ink)}

/* One line each. Seventeen of these is a list you can read; seventeen cards is
   a page you have to leave. */
.ex-rows{display:grid;border:1.5px solid var(--line);border-radius:var(--r-sm);
  overflow:hidden;background:var(--paper)}
.ex-row{display:flex;align-items:center;gap:12px;justify-content:space-between;
  padding:13px 16px;border-bottom:1px solid var(--line);color:var(--ink)}
.ex-row:last-child{border-bottom:0}
.ex-row:hover{text-decoration:none;background:var(--cream-2)}
.ex-row .ex-n{display:grid;min-width:0}
.ex-row .ex-n b{font-family:var(--f);font-size:14.5px;font-weight:800;color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ex-row .ex-n i{font-style:normal;font-size:12px;color:var(--mut);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ex-row .ex-go{flex:none;font-family:var(--f);font-size:12px;font-weight:800;color:var(--mut)}
.ex-row:hover .ex-go{color:var(--accent)}
.ex-row.topmate .ex-go{color:var(--lav-ink)}
@media(max-width:430px){
  .ex-row{padding:12px 13px}
  /* The action is the whole row on a phone — the words beside it are not worth
     a second line, and the tap target is already the row. */
  .ex-row .ex-go{font-size:0}
  .ex-row .ex-go::after{content:"→";font-size:15px}
}

/* Nothing by that name is the most useful thing a visitor can tell us, and it
   is one tap from here rather than a link to another page. */
.ex-empty{border:1.5px dashed var(--line-2);border-radius:var(--r-sm);
  padding:26px 22px;text-align:center;background:var(--card)}
.ex-empty p{margin:0 0 6px}
.ex-empty .btn{margin-top:12px}
@media(prefers-reduced-motion:reduce){.ex-card:hover{transform:none}}

/* THE STANDING ASK. The same offer as the empty state, at the foot of the
   page, where somebody who scrolled the whole list and found nothing ends up.
   It used to appear only when a search matched nothing — which put the one
   thing a visitor can tell us behind typing a name we do not have. */
.ex-ask{display:flex;align-items:center;gap:16px;flex-wrap:wrap;
  margin:34px 0 0;padding:20px 22px;border-radius:var(--r-sm);
  border:1.5px solid var(--line);background:var(--card)}
.ex-ask .g{flex:1;min-width:230px;display:flex;flex-direction:column;gap:4px}
.ex-ask .g b{font-family:var(--f);font-size:16px}
.ex-ask .btn{flex:none}
.ex-askform{display:flex;gap:10px;flex-wrap:wrap;align-items:center;
  margin:10px 0 0;padding:16px 22px;border-radius:var(--r-sm);
  border:1.5px solid var(--line-2);background:var(--paper)}
.ex-askform input{flex:1;min-width:210px;margin:0}
.ex-askform .btn{flex:none}
.ex-askform .sm{flex-basis:100%;margin:0}

/* An exam somebody asked for that is not written yet. Not an error page — the
   person who typed its name is the one we most want to hear from. */
.soon-card{max-width:640px;border-left-width:3px}
.soon-card.making{border-left-color:var(--peach-line)}
.soon-card.topmate{border-left-color:var(--lav-line)}
.soon-card.open{border-left-color:color-mix(in srgb,var(--sky-ink) 30%,var(--line))}
.soon-card.reading{border-left-color:var(--peach-line)}
.soon-card .lede{font-size:16px}

/* A button that has to read as a link — "sign out to use another account" is
   an action, so it must be a <button> for the keyboard and for screen readers,
   but it sits in a sentence and would shout as a button. */
.linky{background:none;border:0;padding:0;margin:0;font:inherit;color:var(--accent);
  font-weight:700;cursor:pointer;text-decoration:none}
.linky:hover{text-decoration:underline}

/* A document's own bar: back on the left, and — for whoever may use it — the
   switch into the author's eye on the right. */
.docbar{display:flex;gap:12px;align-items:center;justify-content:space-between;
  flex-wrap:wrap;margin:0 0 14px}

/* ============================================================
   BY LABEL — sitting your own labels back

   The six labels a candidate puts on their own answers, as a list of
   doors. A miss reads rose and a hit reads lime, the same as they do
   on the question itself, so the pile is recognisable before it is
   read.
   ============================================================ */
.lab-h{font-family:var(--f);font-size:12px;font-weight:800;letter-spacing:.06em;
  text-transform:uppercase;color:var(--mut);margin:22px 0 8px}
.lab-h:first-child{margin-top:4px}
.lab-row{display:flex;align-items:center;gap:12px;background:var(--card);
  border:1.5px solid var(--line);border-radius:var(--r);padding:13px 16px;margin:0 0 9px}
/* .none, not .empty: desk.css already owns `.empty` as the full-height
   centred empty-state block, and a row carrying it was drawn as one. */
.lab-row.none{opacity:.6}
.lab-chip{flex:none;font-family:var(--f);font-size:12px;font-weight:900;
  border-radius:8px;padding:4px 8px;letter-spacing:.02em}
.lab-row .lab-tx{flex:1;min-width:0;display:grid;gap:1px}
.lab-row .lab-tx b{font-size:14.5px;color:var(--ink)}
.lab-row .btn{flex:none}
@media(max-width:520px){
  .lab-row{flex-wrap:wrap}
  .lab-row .btn{width:100%;margin-top:4px}
}
/* .lab-C / .lab-W already colour the label where it appears beside a question
   number. As a chip it needs a ground as well as an ink. */
.lab-chip.lab-C{background:var(--lime-soft);color:var(--lime-ink)}
.lab-chip.lab-W{background:var(--rose-soft);color:var(--rose-ink)}

/* ============================================================
   THE CART — several exams, one payment
   ============================================================ */
.cart-list{padding:4px 18px}
.cart-row{display:grid;grid-template-columns:1fr auto auto;align-items:center;gap:14px;
  padding:14px 0;border-bottom:1px solid var(--line)}
.cart-row:last-child{border-bottom:0}
.cart-what{display:grid;gap:2px;min-width:0}
.cart-what b{font-size:14.5px;color:var(--ink)}
.cart-amt{font-family:var(--f);font-weight:800;font-size:15px;color:var(--ink);
  font-variant-numeric:tabular-nums;white-space:nowrap}
/* Something that cannot be paid for is shown, not silently dropped — a cart
   that empties itself is a cart nobody trusts. */
.cart-row.stale{opacity:.6}
.cart-row.stale .cart-amt{text-decoration:line-through}
.err-t{color:var(--bad)}
.cart-row.total{border-top:1.5px solid var(--line-2);border-bottom:0;margin-top:4px}
.cart-row.total .cart-amt{font-size:18px}
@media(max-width:520px){
  .cart-row{grid-template-columns:1fr auto;row-gap:6px}
  .cart-row .linky{grid-column:2;justify-self:end}
}

/* A business fact nobody has filled in yet. Loud on purpose: a placeholder
   that looks like an answer is how a wrong address goes live. */
.unset{background:var(--rose-soft);color:var(--rose-ink);border:1px dashed var(--rose-ink);
  border-radius:6px;padding:1px 7px;font-size:.92em;font-weight:700}

/* The written pages — about, contact, policies. One measure, generous line
   height: these are read, not scanned. */
/* The measure belongs to the text, not to the page. Setting it on .page
   itself collapsed the whole container to 68ch and centred it, so these pages
   started three hundred pixels in from where every other page starts. The
   gutter stays the shell's; only the lines are short. */
.page.prose{max-width:none}
.prose > *{max-width:68ch}
.prose > h1{max-width:20ch}
.prose h2{font-size:20px;margin:30px 0 10px}
.prose h3{font-size:16px;margin:22px 0 6px}
.prose p,.prose li{font-size:15.5px;line-height:1.72;color:var(--ink-2)}
.prose ul,.prose ol{padding-left:22px;margin:10px 0}
.prose li{margin:5px 0}
.prose .lede{font-size:17.5px;line-height:1.65;color:var(--ink-2)}
.prose dl{margin:14px 0;display:grid;grid-template-columns:auto 1fr;gap:8px 18px}
.prose dt{font-weight:800;color:var(--ink);font-family:var(--f);font-size:14px}
.prose dd{margin:0;font-size:15px}
@media(max-width:560px){ .prose dl{grid-template-columns:1fr;gap:2px 0} .prose dd{margin-bottom:10px} }
.prose .updated{font-size:13px;color:var(--muted);margin-top:34px;
  border-top:1px solid var(--line);padding-top:14px}

/* What is in a part-written course today, and what is not. A two-column
   definition list rather than two sentences: the reader is comparing, and
   comparing is what a table is for. */
.haves{display:grid;grid-template-columns:auto 1fr;gap:4px 12px;margin:0 0 10px;
  font-size:13px;line-height:1.5}
.haves dt{color:var(--muted);font-weight:700;white-space:nowrap}
.haves dd{margin:0;color:var(--ink-2)}
@media (max-width:420px){
  .haves{grid-template-columns:1fr;gap:2px}
  .haves dd{margin:0 0 6px}
}

/* PASSWORD FIELDS — the eye, the meter and the second box.
   See web/js/pw.js; the shapes here exist because a password you cannot read
   is a password you can mistype into a lockout. */
.pw-wrap{position:relative;display:block}
.pw-wrap input{width:100%;padding-right:72px}
.pw-eye{position:absolute;right:6px;top:50%;transform:translateY(-50%);
  background:none;border:0;padding:6px 10px;border-radius:9px;cursor:pointer;
  font:inherit;font-size:12px;font-weight:700;color:var(--muted)}
.pw-eye:hover{color:var(--lav-ink);background:var(--lav-soft)}
.pw-eye:focus-visible{outline:2px solid var(--lav-line);outline-offset:1px}
/* Inside a password field, and the only way to check what you have typed on a
   phone keyboard that hides it. 31px is under a thumb; the field is 48 tall,
   so the target can grow without moving anything. */
@media (hover:none),(max-width:640px){
  .pw-eye{padding:11px 12px;font-size:12.5px}
}

.pw-meter{margin:7px 0 0}
.pw-bar{height:4px;border-radius:4px;background:var(--line);overflow:hidden}
.pw-bar i{display:block;height:100%;width:0;border-radius:4px;
  transition:width .18s ease, background-color .18s ease;background:var(--muted)}
.pw-meter[data-band="1"] .pw-bar i{background:var(--bad)}
.pw-meter[data-band="2"] .pw-bar i{background:var(--peach-ink)}
.pw-meter[data-band="3"] .pw-bar i{background:var(--sky-ink)}
.pw-meter[data-band="4"] .pw-bar i{background:var(--ok)}
.pw-say{margin:5px 0 0;font-size:12px;line-height:1.5;color:var(--muted)}
.pw-meter[data-band="1"] .pw-say{color:var(--bad)}

input.nomatch{border-color:var(--bad)}

/* What an account is proved by — shown, not editable. See me.html. */
.idents{display:grid;grid-template-columns:auto 1fr;gap:6px 16px;margin:18px 0 0;font-size:14px}
.idents dt{color:var(--muted);font-weight:700;white-space:nowrap}
.idents dd{margin:0;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
@media (max-width:420px){ .idents{grid-template-columns:1fr;gap:2px} .idents dd{margin:0 0 8px} }
.okline{color:var(--ok);font-weight:700}
/* Its opposite, for a refusal said next to the field it is about rather than
   in a banner at the top of the card. */
.errline{color:var(--bad);font-weight:700}

/* A field whose action sits beside it rather than at the foot of the form.
   "Send me a code" belongs next to the address it sends to, and the answer —
   sent, or refused, and why — belongs there too. A button at the bottom of a
   long form is a button whose result you cannot see. */
.withbtn{display:flex;gap:8px;align-items:stretch}
.withbtn input{flex:1;min-width:0;margin:0}
.withbtn .btn{flex:none;white-space:nowrap;align-self:stretch}
@media(max-width:430px){.withbtn{flex-wrap:wrap}.withbtn .btn{width:100%}}
/* A field that is waiting on a step before it. */
input:disabled{background:var(--cream-2);color:var(--muted);cursor:not-allowed}

/* ============================================================
   WHAT YOUR CODE HAS EARNED

   This page is about money we owe somebody, so it is built to be
   checked rather than admired: one figure at the top, the four
   numbers it is made of under it, then every purchase that made
   it. Somebody who does not trust the total can add the rows up.

   Its own pieces live here rather than borrowed from paper.css
   or doc.css, because this is neither a paper nor a document and
   a page that loads two other stylesheets for one table is how
   the report card ended up half-styled.
   ============================================================ */
.earn-hero{
  position:relative;overflow:hidden;
  margin:0 0 4px;padding:24px 26px;border-radius:var(--r-lg);
  background:linear-gradient(158deg,var(--lime-soft),var(--paper) 62%,var(--lav-soft));
  box-shadow:var(--clay);
}
.earn-hero::after{
  content:"";position:absolute;inset:0;border-radius:inherit;
  pointer-events:none;background:var(--clay-sheen);
}
.earn-hero > *{position:relative;z-index:1}
/* Set as large as it can be: this is the number the page is for. */
.earn-big{
  margin:2px 0 0;font-family:var(--f);
  font-size:clamp(38px,9vw,58px);font-weight:900;line-height:1.02;
  letter-spacing:-.035em;color:var(--ink);font-variant-numeric:tabular-nums;
}
.earn-sub{margin:8px 0 0;font-size:14.5px;line-height:1.55;color:var(--ink-2);max-width:44ch}

/* The four numbers under it. Same moulded tile as everywhere else. */
.earn-hero + .score{
  display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(118px,1fr));
}
.earn-hero + .score .stat{
  position:relative;overflow:hidden;
  padding:14px 16px;border-radius:16px;
  background:linear-gradient(158deg,var(--paper),var(--cream-2));
  box-shadow:var(--clay-sm);
}
.earn-hero + .score .stat::after{
  content:"";position:absolute;inset:0;border-radius:inherit;
  pointer-events:none;background:var(--clay-sheen);
}
.earn-hero + .score .stat b,.earn-hero + .score .stat span{position:relative;z-index:1;display:block}
.earn-hero + .score .stat b{
  font-family:var(--f);font-size:20px;font-weight:900;letter-spacing:-.02em;
  color:var(--ink);font-variant-numeric:tabular-nums;
}
.earn-hero + .score .stat span{margin-top:3px;font-size:12.5px;line-height:1.45;color:var(--mut)}

/* ── the codes themselves ────────────────────────────────────
 * A code is a thing somebody says out loud, so it is set like
 * one: large, monospaced, spaced out, and pressable-looking even
 * though there is nothing to press. */
.codes{display:grid;gap:12px;grid-template-columns:1fr;margin:0 0 6px}
@media(min-width:680px){ .codes{grid-template-columns:1fr 1fr} }
.code-card{
  position:relative;overflow:hidden;
  padding:18px 20px;border-radius:18px;
  background:linear-gradient(158deg,var(--paper),var(--cream-2));
  box-shadow:var(--clay-sm);
}
.code-card::after{
  content:"";position:absolute;inset:0;border-radius:inherit;
  pointer-events:none;background:var(--clay-sheen);
}
.code-card > *{position:relative;z-index:1}
/* A code that has been switched off still shows what it made, but
   stops looking like something anybody can use. */
.code-card.off{opacity:.62;box-shadow:var(--clay-in)}
.code-top{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0 0 10px}
.code-chip{
  font-family:var(--f);font-size:17px;font-weight:900;letter-spacing:.12em;
  padding:7px 13px;border-radius:12px;
  background:var(--clay-sheen),linear-gradient(158deg,var(--lav-soft),var(--lav));
  color:var(--lav-ink);box-shadow:var(--clay-sm);
}
.code-terms{margin:0 0 10px;font-size:14px;line-height:1.55;color:var(--ink-2)}
.code-nums{display:flex;gap:18px;flex-wrap:wrap;font-size:13.5px;color:var(--mut)}
.code-nums b{font-family:var(--f);font-size:16px;font-weight:900;color:var(--ink);
  font-variant-numeric:tabular-nums}

/* ── the rows the total is made of ───────────────────────────
 * Lifted as one object, like every other table in the product.
 * On a phone each row becomes its own card, because five columns
 * of money read sideways is five columns nobody reads. */
/* `overflow-x:auto` rather than `hidden`, as a floor under every table in the
   product: one too wide to fit scrolls inside its own box instead of pushing
   the whole page sideways, and a table that is clipped with no way to reach
   the rest of it is worse than one that scrolls. The radius still clips. */
.tw{margin:0 0 20px;border-radius:16px;overflow-x:auto;box-shadow:var(--clay-sm);
  background:var(--card)}
.tw table{border-collapse:collapse;width:100%;font-size:14.5px}
.tw thead th{
  background:var(--cream-2);
  font-family:var(--f);font-size:11px;font-weight:800;
  letter-spacing:.09em;text-transform:uppercase;color:var(--mut);
  padding:11px 14px;text-align:left;white-space:nowrap;
  border-bottom:1.5px solid var(--line);
}
.tw th.num,.tw td.num{text-align:right;font-variant-numeric:tabular-nums}
.tw td{padding:11px 14px;vertical-align:top;border-bottom:1px solid var(--line);line-height:1.55}
.tw tbody tr:last-child td{border-bottom:0}
.tw tbody tr:nth-child(even){background:color-mix(in srgb,var(--cream-2) 45%,transparent)}
.tw code{font-family:var(--f);font-weight:800;letter-spacing:.08em;font-size:13px;color:var(--lav-ink)}
@media(max-width:640px){
  .tw{box-shadow:none;background:none;border-radius:0;overflow:visible}
  /* `th` was missing from this list, and that is what pushed the page.
     A table whose cells become blocks still has table-cell HEADER cells, and
     a header row of nowrap cells is as wide as its longest heading — 516px in
     a 390px phone. The thead is taken out of the flow just below, but an
     absolutely positioned box still lays its children out, so the row was
     measured and the page grew to fit it. */
  .tw table,.tw thead,.tw tbody,.tw tr,.tw th,.tw td{display:block;width:100%}
  /* Out of the flow AND out of the way. `left:-9999px` rather than only
     `clip`, because clip leaves the element where it is for layout purposes
     in some engines — and it is the layout, not the paint, that was the
     problem here. */
  .tw thead{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
  .tw tbody tr{
    background:var(--card);border-radius:14px;box-shadow:var(--clay-sm);
    padding:4px 2px;margin:0 0 10px;
  }
  .tw td{border-bottom:1px solid var(--line);padding:9px 14px;text-align:left}
  .tw tr td:last-child{border-bottom:0}
  .tw td[data-h]::before{
    content:attr(data-h) " ";
    font-family:var(--f);font-size:10.5px;font-weight:800;letter-spacing:.09em;
    text-transform:uppercase;color:var(--mut);display:block;margin-bottom:2px;
  }
  .tw td.num{text-align:left}
}

/* ── A CODE SOMEBODY WAS GIVEN ───────────────────────────────
 * Sits between the total and the pay button, because that is the
 * order the decision happens in: this is what it costs, here is
 * the code I was given, now I pay. Quiet until it is used —
 * a discount field shouting at somebody who has no code is a
 * page telling them they are paying too much. */
.coupon{margin:18px 0 0;max-width:420px}
.coupon label{
  display:block;margin:0 0 7px;
  font-family:var(--f);font-size:11px;font-weight:800;
  letter-spacing:.12em;text-transform:uppercase;color:var(--mut);
}
.coupon-row{display:flex;gap:8px;align-items:stretch}
.coupon-row input{
  flex:1;min-width:0;
  font-family:var(--f);font-weight:800;letter-spacing:.1em;text-transform:uppercase;
}
.coupon-row input::placeholder{letter-spacing:normal;text-transform:none;font-weight:600}
/* Accepted: the field itself says so, so the confirmation is where
   the eye already is rather than only in a line underneath. */
.coupon-row input.on{
  border-color:var(--lime-line);
  background:color-mix(in srgb,var(--lime-soft) 70%,var(--paper));
  color:var(--lime-ink);
}
.coupon-row .btn{flex:none}
.coupon .ok-t,.coupon .err-t{display:block;margin:8px 0 0;font-size:13.5px;line-height:1.5}
.ok-t{color:var(--lime-ink);font-weight:700}

/* ============================================================
   THE TWELVE FACES

   A preference, offered where preferences live and nowhere
   else: inside the account drawer on your own desk. Never at
   signup, never a step anybody has to get past, and "none" is
   one of the choices rather than the absence of one.
   ============================================================ */
.me-head{display:flex;align-items:center;gap:16px;flex-wrap:wrap}
.me-who{min-width:0}
.me-who h1{margin:0 0 6px}
/* Large enough to be a face rather than an icon — this is the one place in
   the product where it is worth the room. */
.me-pic{
  flex:none;width:72px;height:72px;border-radius:50%;object-fit:cover;display:block;
  background:var(--lav-soft);box-shadow:var(--clay-sm);
}
@media(max-width:520px){ .me-pic{width:58px;height:58px} }

/* Capped, so the twelve wrap into rows of six the way the sheet draws them.
   Across a full-width drawer they came out as one line of thirteen, which is
   a strip to scan rather than a set to choose from. */
.pics{margin:0 0 22px;max-width:468px}
.pics-h{
  margin:0 0 4px;font-family:var(--f);font-size:11px;font-weight:800;
  letter-spacing:.12em;text-transform:uppercase;color:var(--mut);
}
.pics-grid{
  display:grid;gap:10px;
  grid-template-columns:repeat(6,1fr);
  margin:0 0 4px;
}
@media(max-width:420px){ .pics-grid{grid-template-columns:repeat(4,1fr)} }

.pic{
  position:relative;aspect-ratio:1;padding:0;border:0;cursor:pointer;
  border-radius:16px;overflow:hidden;background:var(--lav-soft);
  box-shadow:var(--clay-sm);
  transition:transform .14s ease,box-shadow .16s ease;
}
.pic img{width:100%;height:100%;object-fit:cover;display:block}
.pic:hover{transform:translateY(-2px);box-shadow:var(--clay)}
/* Chosen: a ring the colour of every other "this one" in the product, and it
   sits inside the tile so the grid does not shift when you pick. */
.pic.on{box-shadow:var(--clay),inset 0 0 0 3px var(--accent)}
.pic.on::after{
  content:"";position:absolute;right:5px;bottom:5px;width:16px;height:16px;
  border-radius:50%;background:var(--accent);
  box-shadow:0 0 0 2px var(--paper);
}
.pic:active{transform:translateY(0);box-shadow:var(--clay-in)}
/* A drawing whose file is not there is not offered. Better a picker with
   eleven in it than a row of broken-image icons. */
.pic.gone{display:none}

/* "None" is a choice, so it is a tile like the others rather than a link
   underneath them. */
.pic.none{
  display:grid;place-items:center;background:linear-gradient(158deg,var(--paper),var(--cream-2));
  font-family:var(--f);font-size:11px;font-weight:800;letter-spacing:.06em;
  text-transform:uppercase;color:var(--mut);
}

/* ============================================================
   ACCOUNT AND PASSWORD

   Four things that are not the same kind of thing, and the
   drawer they live in had no rules at all — a bare <details>
   with a browser triangle and one long run of fields, so the
   password form looked as casual as the name box above it.

   Each is its own panel now. The heading over each is small and
   quiet because these are labels, not an argument: somebody who
   came here to change one thing should find it without reading
   the other three.
   ============================================================ */
.acct{
  margin:34px 0 0;border-radius:var(--r-lg);overflow:hidden;
  background:linear-gradient(158deg,var(--paper),var(--cream-2));
  box-shadow:var(--clay-sm);
}
/* The whole bar is the control, so it is the whole bar that is pressable and
   the whole bar that reacts — a triangle four pixels wide is not a target. */
.acct > summary{
  list-style:none;cursor:pointer;
  display:flex;align-items:center;gap:12px;
  padding:18px 22px;
  font-family:var(--f);font-size:15px;font-weight:800;letter-spacing:-.01em;
  color:var(--ink);
}
.acct > summary::-webkit-details-marker{display:none}
.acct > summary::after{
  content:"";flex:none;margin-left:auto;
  width:9px;height:9px;border-right:2.5px solid var(--muted);border-bottom:2.5px solid var(--muted);
  transform:rotate(45deg) translate(-2px,-2px);
  transition:transform .18s ease;
}
.acct[open] > summary::after{transform:rotate(-135deg) translate(-2px,-2px)}
.acct > summary:hover{background:color-mix(in srgb,var(--lav-soft) 55%,transparent)}
.acct[open] > summary{border-bottom:1px solid var(--line)}

.acct-in{padding:4px 22px 22px}
.acct-s{padding:22px 0 0}
.acct-s + .acct-s{border-top:1px solid var(--line);margin-top:4px}
.acct-h{
  margin:0 0 14px;
  font-family:var(--f);font-size:11px;font-weight:800;
  letter-spacing:.13em;text-transform:uppercase;color:var(--mut);
}
.acct-f{margin:0}
.acct-f .field:last-of-type{margin-bottom:16px}
@media(max-width:520px){ .acct > summary{padding:16px 18px} .acct-in{padding:4px 18px 18px} }

/* ── the code, where its owner will look for it ──────────────
 * Read off a screen and said out loud, so it is set the way the
 * sign-up code is: large, spaced, and with something that copies
 * it rather than leaving somebody to select it with a thumb. */
.mycode{
  position:relative;overflow:hidden;
  /* Capped like the forms beside it. Run full width it stopped reading as a
     card and became a band across the drawer, and the four facts inside it
     stretched until they were four separate things rather than one set. */
  max-width:560px;
  padding:18px 20px;border-radius:18px;
  background:var(--clay-sheen),linear-gradient(158deg,var(--lime-soft),var(--paper) 70%,var(--lav-soft));
  box-shadow:var(--clay-sm);
}
.mycode + .mycode{margin-top:12px}
.mycode.off{background:linear-gradient(158deg,var(--paper),var(--cream-2));opacity:.72}
.mycode-top{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0 0 16px}
.mycode-top .cp{flex:none}
.mycode-top .cp.done{
  border-color:var(--lime-line);
  background:color-mix(in srgb,var(--lime-soft) 80%,var(--paper));
  color:var(--lime-ink);
}
/* The terms, as four facts rather than a sentence with numbers in it — these
   get quoted to somebody else, and a number you have to extract from a clause
   is a number that gets quoted wrong. */
.mycode-terms{
  display:grid;gap:8px;
  grid-template-columns:repeat(2,1fr);
}
@media(min-width:520px){ .mycode-terms{grid-template-columns:repeat(4,1fr)} }
.mycode-terms > div{
  background:color-mix(in srgb,var(--paper) 78%,transparent);
  border-radius:13px;padding:11px 13px;
}
.mycode-terms b{
  display:block;font-family:var(--f);font-size:19px;font-weight:900;
  letter-spacing:-.02em;color:var(--ink);font-variant-numeric:tabular-nums;
}
.mycode-terms span{display:block;margin-top:2px;font-size:11.5px;line-height:1.4;color:var(--mut)}

/* ── HOW MUCH WORK IS BEHIND YOU ─────────────────────────────
 *
 * Two figures under your own name on the desk. They had no rules
 * at all, so `<b>8</b><span>papers sat</span>` came out as the
 * words "8papers sat" — a number welded to its label, which
 * reads as a fault in the data rather than a gap in a stylesheet.
 *
 * Tiles rather than a sentence, and the same moulded ones the
 * report card and the earnings page use, because a figure about
 * your own work should look the same wherever the product shows
 * it to you.
 */
.stats{
  display:grid;gap:10px;margin:20px 0 0;
  grid-template-columns:repeat(auto-fit,minmax(132px,1fr));
  max-width:468px;
}
.stats .stat{
  position:relative;overflow:hidden;
  padding:13px 16px;border-radius:15px;
  background:linear-gradient(158deg,var(--paper),var(--cream-2));
  box-shadow:var(--clay-sm);
}
.stats .stat::after{
  content:"";position:absolute;inset:0;border-radius:inherit;
  pointer-events:none;background:var(--clay-sheen);
}
.stats .stat b,.stats .stat span{position:relative;z-index:1;display:block}
.stats .stat b{
  font-family:var(--f);font-size:22px;font-weight:900;letter-spacing:-.02em;
  line-height:1.05;color:var(--ink);font-variant-numeric:tabular-nums;
}
.stats .stat span{margin-top:3px;font-size:12.5px;line-height:1.45;color:var(--mut)}
@media(max-width:420px){ .stats{grid-template-columns:1fr 1fr} .stats .stat{padding:11px 13px} }

/* ============================================================
   THE DESK — /me

   These rules lived in a <style> block inside me.html, which is
   the one page in the product that had one. Two things went
   wrong because of it, and both are the same thing:

   A page's own block loads after every stylesheet, so it wins
   ties. `.stats` was declared here as a flex row AND in this
   file as a grid, and the page's copy quietly won — the grid,
   the gap and the cap did nothing. And `details.acct` is
   specificity (0,1,2), which outranks `.acct > summary` at
   (0,1,1), so the account drawer was being half-styled by a rule
   nobody editing app.css could see.

   Neither was visible while reading either file. So the page
   keeps no rules of its own and everything is here, where the
   cascade is one list in one order.
   ============================================================ */
/* NOTHING HERE YET, SAID PROPERLY.
 *
 * Used on the desk, the cart, the earnings page and /me — and styled only by
 * `#desk .empty`, so on three of those four it was unstyled text floating in
 * the page. The desk's own rule is more specific and still wins inside it; this
 * is what the others have always needed. */
.empty{
  background:var(--card);border:1.5px dashed var(--line-2);border-radius:var(--r-lg);
  padding:26px 22px;text-align:center;
}
.empty p{margin:0 0 6px}
.empty p:last-child{margin:0}
.empty .sm{max-width:46ch;margin-left:auto;margin-right:auto}

.resume{
  background:var(--lav-soft);border:1.5px solid var(--lav-line);border-radius:var(--r-lg);
  padding:20px 22px;margin:0 0 22px;
}
.resume h2{margin:0 0 4px;font-size:18px}
.prog{height:7px;background:var(--paper);border-radius:99px;overflow:hidden;margin:12px 0 14px}
.prog i{display:block;height:100%;background:var(--accent)}

.ex{background:var(--card);border:1.5px solid var(--line);border-radius:var(--r-lg);
  padding:20px 22px;margin:0 0 14px}
/* Paid for and not written yet. Marked, because a row with nothing in it
   next to a "Yours" tag has to explain itself at a glance. */
.ex.waiting{background:var(--sky-soft);border-color:color-mix(in srgb,var(--sky-ink) 26%,var(--line))}
/* SCOPED, BECAUSE THE NAME IS ALREADY TAKEN. `.ex-h` on /exams is a section
   heading — an h2 standing on its own — and this one is the header inside an
   exam card. They were only ever kept apart by living in different files, and
   the moment this rule moved into the shared sheet the catalogue's headings
   started using the card's margins. */
.ex .ex-h{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin:0 0 4px}
.ex .ex-h h3{margin:0;font-size:18px;flex:1;min-width:160px}

.next{display:flex;align-items:center;gap:12px;flex-wrap:wrap;background:var(--lime-soft);
  border:1.5px solid var(--lime-line);border-radius:var(--r-sm);padding:12px 14px;margin:14px 0 0}
.next .g{flex:1;min-width:160px}
.next b{display:block}
.next small{color:var(--lime-ink)}

/* Scoped for the same reason, and this one would have been worse: `.tag.owned`
   is the small green "verified" / "yours" badge on four pages, and a bare
   `.owned` laying out a row would have turned every one of them into a flex
   box with a top margin. */
.ex .owned{display:flex;gap:6px;flex-wrap:wrap;margin:8px 0 0}

/* ── ASKING FOR A CODE ───────────────────────────────────────
 * What came of it, said back to them — an ask that is waiting,
 * or one that was turned down. Quiet: this is a state, not an
 * argument, and somebody reading it has already had the answer. */
.ask-said{
  background:linear-gradient(158deg,var(--lav-soft),var(--paper));
  box-shadow:var(--clay-sm);border-radius:15px;padding:14px 16px;max-width:520px;
}
.ask-said.quiet{background:linear-gradient(158deg,var(--paper),var(--cream-2))}
.ask-said b{display:block;font-size:14.5px;color:var(--ink)}
.ask-said + p{margin-top:12px}
#askcode textarea{
  width:100%;box-sizing:border-box;resize:vertical;min-height:76px;
  font:inherit;font-size:14.5px;line-height:1.55;
}

/* ============================================================
   THE MARKETPLACE

   Somebody else's material, sold beside ours — so it is drawn in
   the same clay as everything else rather than looking like a
   bolted-on shop. A card is an object you can press; a rating is
   quiet until there is one; the price is the last thing read,
   which is why it sits at the end of the line rather than the
   start of it.
   ============================================================ */
/* ONE CONTROL, NOT A BOX AND A BUTTON.
 *
 * It was an input with a button parked beside it, which is how a search looks
 * before anybody has designed it. Held in one moulded well with the button
 * inside, so it reads as a thing you type into and press. */
.mfind{
  display:flex;align-items:center;gap:6px;margin:0 0 26px;max-width:540px;
  padding:5px 5px 5px 6px;border-radius:var(--pill);
  background:linear-gradient(158deg,var(--paper),var(--cream-2));
  box-shadow:var(--clay-sm);
  transition:box-shadow .16s ease;
}
.mfind:focus-within{box-shadow:var(--clay),0 0 0 2.5px var(--lav-soft)}
.mfind input{
  flex:1;min-width:0;border:0;background:none;box-shadow:none;
  padding:0 12px;height:38px;font-size:14.5px;
}
.mfind input:focus{outline:none;box-shadow:none}
.mfind .btn{flex:none;height:38px;padding:0 18px;display:inline-flex;align-items:center}

.pgrid{display:grid;gap:12px;grid-template-columns:1fr}
@media(min-width:620px){ .pgrid{grid-template-columns:1fr 1fr} }
@media(min-width:980px){ .pgrid{grid-template-columns:1fr 1fr 1fr} }

/* A CARD FOR A THING SOMEBODY WROTE.
 *
 * It was a row: three across, all text, all the same colour, with the price
 * jammed against the stars — which reads as a table that has been given
 * rounded corners. A card has to hold a shape.
 *
 * So: a tinted head that says what KIND of thing this is, the name with room
 * to breathe, the seller as a person rather than a URL, and the price on its
 * own line at the foot where a price belongs. The tint is picked off the
 * subject, so the same subject is the same colour every time — variety that
 * means something rather than decoration. */
.pcard{
  position:relative;overflow:hidden;display:flex;flex-direction:column;
  min-height:212px;padding:0;border-radius:20px;
  text-decoration:none;color:var(--ink);
  background:linear-gradient(158deg,var(--paper),var(--cream-2));
  box-shadow:var(--clay-sm);
  transition:transform .16s ease,box-shadow .18s ease;
}
.pcard::after{content:"";position:absolute;inset:0;border-radius:inherit;
  pointer-events:none;background:var(--clay-sheen)}
.pcard > *{position:relative;z-index:1}
.pcard:hover{transform:translateY(-3px);box-shadow:var(--clay)}

/* The head. Its colour is the subject's, and it is the only loud thing here. */
.pcard-k{
  display:block;padding:11px 20px 10px;
  font-family:var(--f);font-size:10.5px;font-weight:800;
  letter-spacing:.12em;text-transform:uppercase;
  background:var(--lav-soft);color:var(--lav-ink);
}
.pcard[data-band="1"] .pcard-k{background:var(--lime-soft);color:var(--lime-ink)}
.pcard[data-band="2"] .pcard-k{background:var(--peach-soft);color:var(--peach-ink)}
.pcard[data-band="3"] .pcard-k{background:var(--sky-soft);color:var(--sky-ink)}
.pcard[data-band="4"] .pcard-k{background:var(--rose-soft);color:var(--rose-ink)}
.pcard[data-band="5"] .pcard-k{background:var(--cream-2);color:var(--mut)}

.pcard-n{
  display:block;margin:15px 20px 0;
  font-family:var(--f);font-size:16.5px;font-weight:800;line-height:1.3;
  letter-spacing:-.015em;text-wrap:balance;
}
.pcard-say{
  margin:8px 20px 0;font-size:13.5px;line-height:1.55;color:var(--ink-2);
  /* Two lines, because a third makes every card a different height and the
     grid stops reading as a grid. */
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}

.pcard-f{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  margin:auto 20px 0;padding:14px 0 0;
}
/* The seller is a person, so they get a face — the initial, in the same round
   tile the nav uses, rather than a line of grey text. */
.pcard-by{
  display:inline-flex;align-items:center;gap:8px;min-width:0;flex:1;
  font-size:12.5px;color:var(--ink-2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.pcard-by i{
  flex:none;display:grid;place-items:center;width:24px;height:24px;border-radius:50%;
  background:var(--lav);color:var(--lav-ink);
  font-style:normal;font-family:var(--f);font-size:11px;font-weight:900;
}
.pcard-r{display:inline-flex;align-items:center;gap:6px;flex:none}

.pcard-buy{
  display:flex;align-items:baseline;gap:10px;
  margin:12px 0 0;padding:12px 20px 16px;
  border-top:1px solid var(--line);
}
.pcard-p{font-family:var(--f);font-size:19px;font-weight:900;letter-spacing:-.02em;
  color:var(--ink);flex:1}

/* A rating is somebody's word, so it is set as a word rather than a graphic. */
.stars{color:var(--peach-ink);font-size:13px;letter-spacing:.04em;white-space:nowrap}

/* ── one product ─────────────────────────────────────────────
 * Who wrote it on the left, what it costs on the right, because
 * those are the two questions in that order. */
.pbuy{
  display:flex;gap:20px;flex-wrap:wrap;align-items:flex-start;
  margin:22px 0 26px;padding:20px 22px;border-radius:var(--r-lg);
  background:var(--clay-sheen),linear-gradient(158deg,var(--lav-soft),var(--paper) 70%,var(--lime-soft));
  box-shadow:var(--clay);
}
.pbuy-who{flex:1;min-width:200px}
.pbuy-who b{font-size:16px}
.pbuy-who p{max-width:46ch}
.pbuy-do{flex:none;text-align:right;min-width:150px}
.pbuy-p{display:block;margin:0 0 10px;font-family:var(--f);
  font-size:clamp(26px,5vw,34px);font-weight:900;letter-spacing:-.03em;color:var(--ink)}
@media(max-width:520px){ .pbuy-do{text-align:left} }

/* The seller's own words, in the document voice the rest of the
   product reads in. */
.psay{margin:0 0 26px}
.psay h3{font-size:16px;margin:22px 0 8px}

.psays{display:grid;gap:10px;margin:0 0 24px}
.psay-one{
  padding:14px 16px;border-radius:15px;
  background:linear-gradient(158deg,var(--paper),var(--cream-2));
  box-shadow:var(--clay-sm);
}
.psay-h{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin:0 0 6px}
.psay-one p{margin:0;font-size:14px;line-height:1.6;color:var(--ink-2)}

.prate{max-width:520px}
.prate-s{display:flex;gap:4px;margin:0 0 10px}
.prate-s button{
  border:0;background:none;cursor:pointer;padding:2px 4px;
  font-size:26px;line-height:1;color:var(--peach-ink);
}
.prate textarea{width:100%;box-sizing:border-box;resize:vertical;font:inherit;font-size:14.5px}

/* ── a seller's own row ──────────────────────────────────────
 * The link is the loudest thing after the name, because handing
 * it to somebody is the entire job. */
/* A SELLER'S OWN ROW.
 *
 * Capped, because it was running the full width of a wide screen and a row
 * that wide is a row with a lot of nothing in the middle of it — the link sat
 * in a 1,200px bar with its text at one end. Held to a measure, the three
 * things it has to say (what it is, where it lives, how it is doing) sit close
 * enough to be read as one row. */
.mprod{
  max-width:720px;
  padding:16px 18px;border-radius:16px;margin:0 0 10px;
  background:linear-gradient(158deg,var(--paper),var(--cream-2));
  box-shadow:var(--clay-sm);
}
.mprod.on{background:var(--clay-sheen),linear-gradient(158deg,var(--lime-soft),var(--paper) 72%)}
.mprod-h{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin:0 0 6px}
.mprod-h b{font-size:15.5px;flex:1;min-width:140px}
.mprod-p{font-family:var(--f);font-weight:900;font-size:15px}
/* The link is the thing a seller came here for, so it looks like something to
   pick up rather than a field to read. */
.mprod-l{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:0 0 12px}
.mprod-l code{
  flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-family:var(--f);font-size:12.5px;font-weight:700;color:var(--lav-ink);
  background:var(--lav-soft);border:1.5px dashed var(--lav-line);
  border-radius:10px;padding:8px 12px;
}
.mprod-n{
  display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  padding-top:12px;border-top:1px solid var(--line);
  font-size:12.5px;color:var(--mut);
}
.mprod-n b{font-family:var(--f);font-size:14px;color:var(--ink)}
/* Numbers on the left, the two things you can do pushed to the right. */
.mprod-n > a,.mprod-n > button{margin-left:auto}
.mprod-n > a + button{margin-left:0}
.mprod-n > span + span{margin-left:8px}
.mprod-err{margin:8px 0 0;font-size:13px}

/* The seller, as a person. A handle is how a link is spelled; a name with a
   face is who wrote the thing somebody is deciding whether to pay for. */
.pbuy-me{display:flex;align-items:center;gap:12px}
.pbuy-face,.psay-face{
  flex:none;display:grid;place-items:center;width:42px;height:42px;border-radius:50%;
  background:var(--clay-sheen),linear-gradient(158deg,var(--lav-soft),var(--lav));
  color:var(--lav-ink);font-family:var(--f);font-size:17px;font-weight:900;
  box-shadow:var(--clay-sm);
}
.psay-face{width:28px;height:28px;font-size:12px}
.pbuy-who b{font-size:16.5px}

/* ── TARGETS BIG ENOUGH FOR A THUMB ──────────────────────────
 *
 * A link is as tall as its text — 20 or 21 pixels — and on a
 * pointer device that is exactly right. On a phone it is half
 * what a thumb lands on, and these are not decorative links:
 * they are the row of things to do with an exam, the control
 * that pauses a product, and the way out of a sign-in page.
 *
 * Padding, not a bigger font: the type stays where it is and
 * the hit area grows underneath it. On touch and at phone
 * widths only, because on a laptop a 38px link in a line of
 * text is a hole in the paragraph.
 */
@media (hover:none),(max-width:640px){
  /* The row under an exam: What next · Report card · Revision
     notes · Stickies wall · By label. Five targets side by side
     is where a mis-tap is likeliest. */
  .exlinks a,
  .exlinks button{display:inline-flex;align-items:center;min-height:38px}
  /* A button drawn as a link — "Pause it", "Sign out to use
     another account". It reads as text and is pressed as a
     button, so it needs a button's target. */
  .linky{min-height:38px;display:inline-flex;align-items:center}
  /* The seller's own product rows: See it → · Pause it. */
  .mprod-n > a,.mprod-n > button{min-height:38px;display:inline-flex;align-items:center}
}
