/* ===== shared foundation ===== */
/* Everything between these two markers is byte-identical in Qwizzd-site/shared/styles.css
 * and Qwizzd-app/ui/style.css. Change it in one and you must change it in the other;
 * Qwizzd-site/test.js compares the two blocks and fails when they drift.
 *
 * See design-system.md. Tokens, type scale, and the components both projects share.
 */

/* ---------- tokens ---------- */
:root {
  /* ground and surfaces */
  --bg: #fbfbfa;
  --surface: #ffffff;
  --surface-2: #f4f4f3;
  /* the alternate ground for a full-bleed band; a shade off --bg, never a card */
  --band: #f6f6f4;
  --line: rgba(0, 0, 0, 0.09);
  /* a divider that has to be seen: a sidebar edge, the rule under a table head */
  --line-strong: rgba(0, 0, 0, 0.16);

  /* text, in two levels, and one more that is not text.
     --muted is the second voice: captions, metadata, the words under a label. It clears
     4.5:1 on every ground in this system, --surface-2 included, which is why it is a
     shade darker than it looks like it needs to be.
     --text-3 is NOT a third text level. At 2.5:1 on the ground it cannot carry a word
     somebody has to read, and three levels that all clear 4.5:1 on near-white do not
     exist. It is for what is deliberately not content yet: a placeholder, a disabled
     control. Anything a person is meant to read is --muted. */
  --text: #18181b;
  --muted: #6e6e77;
  --text-3: #a1a1aa;

  /* meaning */
  --accent: #2563eb;
  --ok: #047f5b;
  --bad: #dc2626;
  --warn: #b45309;
  /* What goes *on* a filled semantic surface. White is right on all four in light and
     wrong on all four in dark, where the same colours are light: white on the dark accent
     is 2.4:1. One token per colour, so a filled pill is legible in both appearances
     without anything having to remember which. */
  --on-accent: #fff;
  --on-ok: #fff;
  --on-bad: #fff;
  --on-warn: #fff;

  /* space: one 4px scale, and nothing between the steps */
  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 24px;
  --s6: 32px;
  --s7: 48px;
  --s8: 72px;
  --s9: 120px;
  --s10: 180px;

  /* radius, by what the thing is */
  --r0: 0;
  --r1: 4px;   /* small marks: swatches, skeleton bars, thumbnails */
  --r2: 8px;   /* controls: buttons, inputs, menu items, tabs */
  --r3: 12px;  /* containers: cards, panels, dialogs, media */
  --rfull: 999px;

  /* elevation. There is no level 3, and level 0 is no shadow at all. */
  --e1: 0 1px 2px rgba(0, 0, 0, 0.05);
  --e2: 0 8px 28px -6px rgba(0, 0, 0, 0.14);

  /* motion */
  --t-fast: 120ms;
  --t: 180ms;
  --t-slow: 280ms;
  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  /* opacity- and colour-only changes: nothing is travelling, so nothing needs to settle */
  --ease-out: ease;

  /* type: display is fluid and light, interface is fixed and compact.
     d0 is the hero and nothing else; d1 the closing band and the statement in the accent
     band. At 88px the hero read as a heading; at 144px with 0.9 leading it reads as the
     page's opening scene, which is what the references do. */
  --fs-d0: clamp(56px, 10vw, 144px);
  --fs-d1: clamp(48px, 7.8vw, 112px);
  --fs-d2: clamp(34px, 4.4vw, 60px);
  --fs-d3: clamp(26px, 3vw, 40px);
  --fs-d4: 28px;
  --fs-t1: 24px;
  --fs-t2: 17px;
  --fs-t3: 15px;
  --fs-t4: 14px;
  --fs-t5: 13px;
  --fs-t6: 12px;
  --fs-t7: 11px;

  --sans: system-ui, -apple-system, "Segoe UI Variable Display", "Segoe UI", sans-serif;
  --mono: ui-monospace, "Cascadia Mono", Consolas, monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f0f11;
    --surface: #18181b;
    --surface-2: #212124;
    --band: #141416;
    --line: rgba(255, 255, 255, 0.1);
    --line-strong: rgba(255, 255, 255, 0.18);
    --text: #f2f2f3;
    --muted: #8e8e97;
    --text-3: #6b6b74;
    --accent: #6ea8fe;
    --ok: #34d399;
    --bad: #f87171;
    --warn: #fbbf24;
    /* Ink, not white: these four are light colours. 7.8:1 to 11.3:1. */
    --on-accent: #0f1115;
    --on-ok: #0f1115;
    --on-bad: #0f1115;
    --on-warn: #0f1115;
    --e1: 0 1px 2px rgba(0, 0, 0, 0.35);
    --e2: 0 8px 28px -6px rgba(0, 0, 0, 0.5);
  }
}

/* ---------- base ---------- */
* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: var(--fs-t4) / 1.5 var(--sans);
  -webkit-font-smoothing: antialiased;
}

/* Weight comes down as size goes up: a light 88px line reads as confident, a heavy one
 * reads as a shout. Tracking tightens with size for the same reason. */
h1, h2, h3, h4 { margin: 0; font-weight: 600; letter-spacing: -0.015em; }
p { margin: 0; }
svg { display: block; }
code { font: var(--fs-t5) / 1.6 var(--mono); }
::selection { background: color-mix(in srgb, var(--accent) 25%, transparent); }

.d0 { font-size: var(--fs-d0); font-weight: 300; line-height: 0.9; letter-spacing: -0.04em; }
.d1 { font-size: var(--fs-d1); font-weight: 300; line-height: 0.96; letter-spacing: -0.04em; }
.d2 { font-size: var(--fs-d2); font-weight: 300; line-height: 1.02; letter-spacing: -0.03em; }
.d3 { font-size: var(--fs-d3); font-weight: 400; line-height: 1.08; letter-spacing: -0.025em; }
.d4 { font-size: var(--fs-d4); font-weight: 500; line-height: 1.2; letter-spacing: -0.02em; }
.t1 { font-size: var(--fs-t1); font-weight: 600; line-height: 1.25; letter-spacing: -0.02em; }
.t2 { font-size: var(--fs-t2); font-weight: 600; line-height: 1.3; letter-spacing: -0.015em; }
/* The eyebrow: a label above or beside something, never a heading on its own. */
.eyebrow {
  font-size: var(--fs-t7); font-weight: 600; line-height: 1.3;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted);
}
.muted { color: var(--muted); }
.hint { color: var(--muted); font-size: var(--fs-t6); margin: var(--s1) 0 0; }
.num { font-variant-numeric: tabular-nums; }
.spacer { flex: 1; }
.hidden { display: none !important; }
/* For a screen reader and nobody else. */
.vh {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* Links are never underlined at rest and always underlined on hover. Anything that is
 * really a control says so by cancelling that. */
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* Everything clickable is reachable, and says so when it is reached. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r1); }

/* ---------- buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: 32px; padding: 0 14px;
  border: 1px solid var(--line); border-radius: var(--r2);
  background: var(--surface); color: var(--text);
  font: inherit; font-size: var(--fs-t5); font-weight: 500;
  white-space: nowrap; cursor: pointer;
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast),
    filter var(--t-fast);
}
.btn:hover { background: var(--surface-2); text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn.primary { background: var(--accent); border-color: transparent; color: var(--on-accent); }
.btn.primary:hover { background: var(--accent); filter: brightness(1.07); }
.btn.ghost { background: transparent; border-color: transparent; color: var(--muted); }
.btn.ghost:hover { background: var(--surface-2); color: var(--text); }
/* Tertiary: no fill, no border, no box. For a third action in a header. */
.btn.quiet {
  background: none; border-color: transparent; color: var(--muted); padding: 0 var(--s2);
}
.btn.quiet:hover { background: none; color: var(--text); }
/* Reversible destruction: normal at rest, red only once you are pointing at it. */
.btn.danger:hover {
  color: var(--bad);
  border-color: color-mix(in srgb, var(--bad) 40%, transparent);
  background: color-mix(in srgb, var(--bad) 8%, transparent);
}
/* Irreversible destruction, and only as the confirm inside a dialog. */
.btn.danger-solid { background: var(--bad); border-color: transparent; color: var(--on-bad); }
.btn.danger-solid:hover { background: var(--bad); filter: brightness(1.08); }
.btn[disabled] { opacity: 0.5; cursor: default; pointer-events: none; }
.btn.sm { min-height: 28px; padding: 0 10px; }
.btn.lg { min-height: 44px; padding: 0 22px; font-size: var(--fs-t3); }
.actions { display: flex; gap: var(--s2); flex-wrap: wrap; align-items: center; }
.actions.right { justify-content: flex-end; }
.actions.center { justify-content: center; }

/* ---------- form controls ---------- */
/* A field is somewhere you put something, so it reads as raised — and stays visible when it
 * is sitting on --surface-2. */
input, select, textarea {
  font: inherit; font-size: var(--fs-t4); color: var(--text);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r2);
  min-height: 36px; padding: 0 12px; width: 100%;
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
textarea { resize: vertical; min-height: 96px; padding: 10px 12px; line-height: 1.5; }
input:focus, select:focus, textarea:focus {
  outline: 0; border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}
input::placeholder, textarea::placeholder { color: var(--text-3); }
/* A label above a field. `[for]` rather than `label`, because the labels that wrap their
 * own control — a switch, a tick box — are laid out by their own rule and are not captions. */
label[for], label.field > span {
  display: block; font-size: var(--fs-t6); font-weight: 500;
  color: var(--muted); margin-bottom: var(--s2);
}
label.field { display: block; }
/* Stacked field groups in a plain form, and room before whatever submits it. */
form > div + div { margin-top: var(--s4); }
form > .actions, form > .hint + .actions { margin-top: var(--s5); }
form > .hint { margin-top: var(--s2); }

/* ---------- containers ---------- */
/* A card is a claim that something is a separate object. Most content is not one — reach
 * for a heading and a rule first. See design-system.md §3.3. */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r3);
}
.card.pad { padding: var(--s5); }
.card.pad > * + * { margin-top: var(--s3); }
/* A region that needs containing without claiming to be an object: a table, a long list. */
.panel { border: 1px solid var(--line); border-radius: var(--r3); overflow: hidden; }
.panel.scroll { overflow-x: auto; }

/* One row, one meaning: what it is on the left, what it is worth on the right. */
.rows { display: flex; flex-direction: column; }
.row {
  display: flex; align-items: center; gap: var(--s4);
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line);
}
.row:last-child, .row.last { border-bottom: 0; }
.row .q { flex: 1; min-width: 0; }
.row .q b { display: block; font-weight: 550; font-size: var(--fs-t4); }
/* A sub-caption states a consequence you cannot read off the label. If it repeats the
 * label, delete it rather than styling it. */
.row .q small, .row .q p { display: block; color: var(--muted); font-size: var(--fs-t5); }
.row .q small, .row .q b { overflow-wrap: anywhere; }
.card.pad .row, .row.flush { padding-left: 0; padding-right: 0; }
.card.pad .row:first-child { padding-top: 0; }

/* ---------- small pieces ---------- */
.pill {
  flex: none; padding: 3px 9px; border-radius: var(--rfull);
  font-size: var(--fs-t6); font-weight: 500;
  color: var(--muted); background: var(--surface-2); border: 1px solid var(--line);
}
.pill.on { color: var(--on-accent); background: var(--accent); border-color: transparent; }
.pill.warn { color: var(--on-warn); background: var(--warn); border-color: transparent; }
.pill.bad { color: var(--on-bad); background: var(--bad); border-color: transparent; }

.chips { display: flex; flex-wrap: wrap; gap: var(--s2); }
.chip {
  display: inline-flex; align-items: center; gap: var(--s1);
  padding: 5px 11px; border-radius: var(--rfull);
  font: inherit; font-size: var(--fs-t6); color: var(--muted); cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); text-decoration: none;
  transition: color var(--t-fast), background var(--t-fast), border-color var(--t-fast);
}
.chip:hover { color: var(--text); text-decoration: none; }
.chip.on { color: var(--on-accent); background: var(--accent); border-color: transparent; }
.chip .num { opacity: 0.7; }

/* The tab: switching between views of one thing. Never top-level navigation. */
.seg, .tabs {
  display: inline-flex; gap: 2px; flex: none;
  background: var(--surface-2); padding: 3px; border-radius: var(--r2);
}
.seg button, .tabs button {
  border: 0; background: transparent; color: var(--muted);
  padding: 5px 12px; border-radius: 6px;
  font: inherit; font-size: var(--fs-t5); font-weight: 500; cursor: pointer;
  transition: color var(--t-fast), background var(--t-fast);
}
.seg button:hover, .tabs button:hover { color: var(--text); }
.seg button.on, .tabs button.on { background: var(--surface); color: var(--text); box-shadow: var(--e1); }

/* Something the page has to say. Inline where a form failed; a banner for a standing
 * condition. Both tinted, neither a shout.
 *
 * They carry their own vertical margin. One of these is inserted between things that were
 * already laid out — a heading and a table, a form and its button — and a box with 11px of
 * padding and no margin lands against both of them, which reads as the space having been
 * forgotten rather than as a message having arrived. First in a padded card is the one place
 * it is already spaced, so there it takes none. */
.msg, .banner {
  margin: var(--s4) 0;
  padding: 11px 13px; border-radius: var(--r2); font-size: var(--fs-t5);
  border: 1px solid var(--line); background: var(--surface-2);
}
.card.pad > .msg:first-child, .card.pad > .banner:first-child { margin-top: 0; }
.banner { border-radius: var(--r3); padding: var(--s4); font-size: var(--fs-t4); }
.msg.ok, .banner.ok {
  color: var(--ok);
  border-color: color-mix(in srgb, var(--ok) 35%, var(--line));
  background: color-mix(in srgb, var(--ok) 6%, transparent);
}
.msg.bad, .banner.bad {
  color: var(--bad);
  border-color: color-mix(in srgb, var(--bad) 35%, var(--line));
  background: color-mix(in srgb, var(--bad) 6%, transparent);
}
.msg.warn, .banner.warn {
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 35%, var(--line));
  background: color-mix(in srgb, var(--warn) 7%, transparent);
}
.banner.info {
  border-color: color-mix(in srgb, var(--accent) 30%, var(--line));
  background: color-mix(in srgb, var(--accent) 5%, transparent);
}
.banner p { margin: 0; }
.banner .actions { margin-top: var(--s3); }

/* ---------- tables ---------- */
.tbl { width: 100%; border-collapse: collapse; font-size: var(--fs-t5); }
.tbl th, .tbl td {
  padding: 10px 12px; text-align: left; white-space: nowrap;
  border-bottom: 1px solid var(--line);
}
.tbl th {
  font-weight: 600; font-size: var(--fs-t7); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted); background: var(--surface-2);
  border-bottom: 1px solid var(--line-strong);
}
.tbl tbody tr:last-child td { border-bottom: 0; }
.tbl tbody tr:hover td { background: var(--surface-2); }
.tbl .key { font-weight: 550; max-width: 34ch; overflow: hidden; text-overflow: ellipsis; }
.tbl td.num, .tbl th.num { text-align: right; font-variant-numeric: tabular-nums; }
.tbl select { min-height: 26px; padding: 0 6px; font-size: var(--fs-t6); }

/* ---------- groups within a deck ---------- */
/* A run of questions folded under a name, with how many are in it. Native <details>, so it
 * folds without a script — the website has none on this page. The marker is drawn here so
 * both sides draw the same one. */
details.grp { border-top: 1px solid var(--line); }
details.grp > summary {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s3) var(--s4); cursor: pointer; list-style: none;
  font-weight: 600; font-size: var(--fs-t4);
}
details.grp > summary::-webkit-details-marker { display: none; }
details.grp > summary::before {
  content: '\25B8'; color: var(--muted); font-size: var(--fs-t5);
  transition: transform 0.15s;
}
details.grp[open] > summary::before { transform: rotate(90deg); }
details.grp > summary .count {
  margin-left: auto; color: var(--muted); font-weight: 500; font-size: var(--fs-t5);
  font-variant-numeric: tabular-nums;
}

/* ---------- empty and loading ---------- */
/* Nothing here yet: say what is missing, say it once, offer the one thing to do about it. */
.empty { text-align: center; color: var(--muted); padding: var(--s7) var(--s5); }
.empty h2 { font-size: var(--fs-t2); color: var(--text); }
.empty p { margin: var(--s2) auto var(--s5); max-width: 46ch; font-size: var(--fs-t5); }

/* The shape of what is coming, so the page does not jump when it lands. A block, not a
 * moving highlight: a shimmer is decoration laid over something already saying "wait". */
.skel span, .skelcard span {
  display: block; height: 10px; border-radius: var(--r1); background: var(--surface-2);
  animation: breathe 1.6s ease-in-out infinite;
}
.skelcard span + span { margin-top: var(--s3); }
.skelcard span:nth-child(1) { width: 45%; }
.skelcard span:nth-child(2) { width: 78%; }
.skelcard span:nth-child(3) { width: 60%; }
.tbl.skel td { padding: 11px 12px; }
.tbl.skel tr:nth-child(odd) td:first-child span { width: 80%; }
.tbl.skel tr:nth-child(even) td:first-child span { width: 62%; }
@keyframes breathe { 50% { opacity: 0.45; } }

/* ---------- motion ---------- */
/* Enter is animated, leave is instant, travel is never more than 16px. */
@keyframes rise { from { opacity: 0; transform: translateY(12px); } }
@keyframes fade { from { opacity: 0; } }

/* Somebody who has asked for less motion has asked once, for everything — so this is one
 * rule rather than a guard remembered per component, which is how five of thirty-one
 * transitions came to be covered and the rest, including the popup and the dialog that
 * arrive unasked, did not.
 *
 * Not `none`: a transition that never runs never fires transitionend, and a 1ms one both
 * looks instant and keeps anything waiting on it working. The skeleton's breathing is an
 * infinite animation, so it is stopped outright rather than run very fast. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-delay: 0ms !important;
    transition-duration: 1ms !important;
    transition-delay: 0ms !important;
  }
  .skel span, .skelcard span { animation: none !important; }
}
/* ===== end shared foundation ===== */

/* One choice above another, each the full width: a sign-in page has two doors and neither
   of them is a footnote to the other. */
.actions.stack { flex-direction: column; align-items: stretch; gap: var(--s3); }

/* Where Clerk draws its sign-in form. It brings its own styling and is told our colours, so
   this only has to give it the width and hold the space until it arrives — a panel that
   collapses to nothing and then jumps to full height is worse than one that waits. */
.clerkpanel { display: flex; justify-content: center; min-height: 380px; }
.clerkpanel > .skelcard { width: 100%; align-self: center; }

/* ===== the website ===== */
/* The marketing page, the account pages and the deck library. One file, copied to both
 * hostnames by `npm run styles`, because two stylesheets that had drifted apart is what
 * this replaces.
 *
 * Layout here is one of four modes, never a single centred column applied to everything:
 * a Canvas of full-bleed bands, a Shell with a sidebar, an asymmetric Split, or Reading
 * for prose. See design-system.md §2.
 */

:root { --gutter: 24px; }
@media (min-width: 720px)  { :root { --gutter: 40px; } }
@media (min-width: 1024px) { :root { --gutter: 56px; } }
@media (min-width: 1440px) { :root { --gutter: 72px; } }

/* The website's quietest text is set on tinted grounds and read at a distance: the third
   text colour is lifted to 4.5:1 on white. An override of the foundation, kept here until
   the app takes the same value. */
:root { --text-3: #75757e; }
@media (prefers-color-scheme: dark) { :root { --text-3: #8f8f98; } }
/* A destructive button shows what it is to the keyboard as well as to the pointer. */
.btn.danger:focus-visible {
  color: var(--bad);
  border-color: color-mix(in srgb, var(--bad) 40%, transparent);
  background: color-mix(in srgb, var(--bad) 8%, transparent);
}
/* Something is being asked of the server: the button that asked shows it. */
[aria-busy='true'] .btn { cursor: progress; }
[aria-busy='true'] .btn.primary, [aria-busy='true'] button[type='submit'] { opacity: 0.6; }
/* Tables lose their one-line cells on a phone, where a row of nowrap cells is a page that
   scrolls sideways. */
@media (max-width: 600px) { .tbl td, .tbl th { white-space: normal; } }
.tbl select { min-width: 96px; }
/* An anchor arriving under the sticky bar lands with its heading in view. */
[id] { scroll-margin-top: 80px; }
/* Skip to the content, for the keyboard. Visible only while focused. */
.skip {
  position: absolute; left: var(--gutter); top: -100px; z-index: 30;
  padding: 10px 14px; border-radius: var(--r2);
  background: var(--accent); color: var(--on-accent); font-weight: 600;
}
.skip:focus-visible { top: 12px; outline-offset: 0; text-decoration: none; }

/* ---------- Canvas: full-bleed bands ---------- */
/* Background colour is the separator. No rules between sections, no card wrapping one. */
.band { padding: var(--s8) 0; }
.band.alt { background: var(--band); }
/* The emphasis of last resort. One per page, at most.
 *
 * Its own colours rather than --text/--bg: those two swap with the theme, so an ink band
 * built out of them turned into a white slab in dark mode — a hard edge against a near-black
 * page, which is the one thing a closing band should not be. Ink is dark in both themes; in
 * dark it goes *below* the page ground rather than above it, and takes hairlines so it is
 * still a defined edge. */
.band.ink {
  background: #16161a; color: #f2f2f3;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
@media (prefers-color-scheme: dark) { .band.ink { background: #08080a; } }
.band.ink .muted, .band.ink .eyebrow, .band.ink small, .band.ink .lede {
  color: rgba(242, 242, 243, 0.64);
}
.band.ink .row { border-bottom-color: rgba(242, 242, 243, 0.16); }
.band.ink .btn { background: transparent; border-color: rgba(242, 242, 243, 0.3); color: #f2f2f3; }
.band.ink .btn:hover { background: rgba(242, 242, 243, 0.1); }
.band.ink .btn.primary { background: #f2f2f3; border-color: transparent; color: #16161a; }
.band.ink .btn.primary:hover { background: #f2f2f3; filter: brightness(0.92); }
@media (min-width: 1024px) {
  .band { padding: var(--s9) 0; }
  .band.tall { padding: var(--s10) 0; }
}
.inset { width: min(1240px, 100% - 2 * var(--gutter)); margin-inline: auto; }
.inset.tight { max-width: 900px; }

/* The opener of a page that is not the landing page. The same full-bleed band, shorter: a
   working page should start with something to look at rather than with a 24px heading
   pinned under the bar. What goes inside it differs page by page — the marketplace puts its
   search here, a deck its buttons and its numbers, a written page its date — which is what
   keeps five pages from opening identically. */
.band.thin { padding: var(--s7) 0 var(--s6); }
@media (min-width: 1024px) { .band.thin { padding: var(--s8) 0 var(--s7); } }
.masthead { display: flex; align-items: flex-end; gap: var(--s5); flex-wrap: wrap; }
.masthead .titleblock { flex: 1 1 420px; min-width: 0; }
.masthead h1 { font-size: var(--fs-d3); font-weight: 400; letter-spacing: -0.025em; line-height: 1.08; }
.masthead .lede { margin-top: var(--s4); font-size: var(--fs-t2); color: var(--muted); line-height: 1.5; max-width: 56ch; }
.masthead .by { margin-top: var(--s3); font-size: var(--fs-t4); }
/* The flex row-gap is already 24px under the row above; this is the rest of the way to 32. */
.masthead .figures { flex-basis: 100%; margin-top: var(--s2); }
.band.thin .filters { margin: var(--s6) 0 0; }
.band.thin .crumb { margin-bottom: var(--s4); }

/* The opener of a band: an eyebrow, one display line, at most one sentence. */
.bandhead { max-width: 34ch; margin-bottom: var(--s6); }
.bandhead .eyebrow { margin-bottom: var(--s3); }
.bandhead p { margin-top: var(--s4); font-size: var(--fs-t3); color: var(--muted); max-width: 52ch; }
@media (min-width: 1024px) {
  .bandhead { margin-bottom: var(--s7); }
  /* The heading left, the sentence right: a band opener that uses the width it has. */
  .bandhead.wide { display: grid; grid-template-columns: 1.35fr 1fr; gap: var(--s7); max-width: none; align-items: end; }
  .bandhead.wide p { margin-top: 0; padding-bottom: 6px; }
}

/* Figures on a line: the number large, its label small and quiet under it. Four boxes would
   say these were four objects. */
.figures { display: flex; flex-wrap: wrap; gap: var(--s6); }
.figures b { display: block; font-size: 28px; font-weight: 650; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.figures span { color: var(--muted); font-size: var(--fs-t7); font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; }
.figures.big { gap: var(--s7); }
.figures.big b { font-size: 48px; font-weight: 500; }

/* ---------- Split: asymmetric two columns ---------- */
/* Never two equal halves — that says the two sides matter equally, which they rarely do. */
.split { display: grid; gap: var(--s6); }
@media (min-width: 900px) {
  .split { grid-template-columns: 1.35fr 1fr; gap: var(--s7); align-items: start; }
  .split.wide { grid-template-columns: 2fr 1fr; }
  .split > .aside { position: sticky; top: calc(64px + var(--s5)); }
}

/* The heading stays while its list scrolls past. A band whose opener sits above a full-width
   block is the shape of every other band on the page; this one is not. */
@media (min-width: 1024px) {
  .hold { display: grid; grid-template-columns: 0.8fr 1.6fr; gap: var(--s8); align-items: start; }
  /* No max-width: `ch` here is the width of the 14px body character, not of the display
     line it is meant to hold, and 24 of those is a column too narrow to read. The grid
     already decides how wide this is. */
  .hold > .bandhead { position: sticky; top: calc(64px + var(--s6)); margin-bottom: 0; max-width: none; }
}

/* ---------- Grid: by content width, not column count ---------- */
.grid { display: grid; gap: var(--s4); grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
.grid.wide { grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
.grid.narrow { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
/* One item in a grid should look deliberate rather than stretched across the page. */
.grid > :only-child { max-width: 420px; }

/* ---------- Reading: prose, and only prose ---------- */
.reading { width: min(62ch, 100% - 2 * var(--gutter)); margin-inline: auto; }
/* Inside a shell the gutter is already paid; a form there is simply capped. */
.work .reading { width: min(62ch, 100%); margin-inline: 0; }

/* ---------- Shell: sidebar and content ---------- */
/* The account and admin pages. Not a centred column: navigation on the left, content
 * taking whatever is left over. */
.shell {
  display: grid; grid-template-columns: 1fr; grid-template-rows: auto 1fr;
  min-height: calc(100vh - 64px);
}
/* Below the breakpoint the sidebar lies down and becomes a strip above the content. One
 * nav either way: navigation that disappears on a narrow screen is navigation you lost.
 * min-width: 0, or the strip's seven buttons set the width of the whole page. */
.shell > .side { min-width: 0; padding: var(--s2) var(--gutter); border-bottom: 1px solid var(--line); }
.shell > .side .eyebrow { display: none; }
.shell > .work { min-width: 0; padding: var(--s5) var(--gutter) var(--s8); }
.side nav { display: flex; flex-direction: row; gap: var(--s1); overflow-x: auto; scrollbar-width: none; }
@media (min-width: 1024px) {
  .shell { grid-template-columns: 232px 1fr; grid-template-rows: none; }
  .shell > .side {
    padding: var(--s5) var(--s4); border-bottom: 0;
    border-right: 1px solid var(--line-strong);
    position: sticky; top: 64px; align-self: start; height: calc(100vh - 64px);
  }
  .shell > .side .eyebrow { display: block; padding: 0 12px; margin: 0 0 var(--s2); }
  .shell > .work { padding: var(--s6) var(--s6) var(--s8); }
  .side nav { flex-direction: column; gap: 2px; overflow: visible; }
}
.side nav a, .side nav button {
  position: relative; display: block; text-align: left; width: 100%;
  padding: 8px 12px; border: 0; border-radius: var(--r2);
  font: inherit; font-size: var(--fs-t5); font-weight: 500;
  background: transparent; color: var(--muted); cursor: pointer; text-decoration: none;
  transition: background var(--t-fast), color var(--t-fast);
}
.side nav a:hover, .side nav button:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
.side nav .on { background: var(--surface-2); color: var(--text); }
.side nav .on::before {
  content: ''; position: absolute; left: 8px; right: 8px; bottom: 0; height: 2px;
  border-radius: 2px; background: var(--accent);
}
@media (min-width: 1024px) {
  .side nav .on::before { left: 0; right: auto; top: 8px; bottom: 8px; width: 2px; height: auto; }
}

/* A second group of sections in the rail: the ones only an admin has. A label, because a
   list that runs Your decks / Submit / Submissions / Users reads as one list of unrelated
   things. Hidden when the rail is lying down as a strip, where there is no room for it. */
@media (max-width: 1023px) {
  /* In the strip each item is as wide as its words, in a row that scrolls. */
  .side nav > a, .side nav > button { flex: none; width: auto; white-space: nowrap; }
  .shell > .side nav .railgroup { display: block; flex: none; width: 1px; height: 24px; margin: 6px var(--s1); padding: 0; background: var(--line-strong); overflow: hidden; color: transparent; }
}
/* The one control the browser draws itself. Enough padding that it is the same height as
   everything else in the form. */
input[type='file'] { padding: 7px 10px; line-height: 1.5; cursor: pointer; }
/* Or hidden inside a button-shaped label, with the chosen file's name written beside it. */
.filepick { display: inline-flex; align-items: center; gap: var(--s3); }
.filepick .btn { position: relative; }
.filepick .btn:has(:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.filepick .name { font-size: var(--fs-t5); color: var(--muted); }
/* The update form, opened under a deck's own row: indented under it, with a rule to say
   which row it belongs to, and the same fields as the submit form. */
form.update { margin: var(--s2) 0 var(--s5); padding: var(--s4) var(--s4) var(--s2); border-left: 2px solid var(--line-strong); }
form.update > div + div { margin-top: var(--s4); }
form.update .actions { flex-wrap: wrap; align-items: center; }
form.update .actions .hint { width: 100%; margin: var(--s1) 0 0; }
.tbl tr.edit td { padding: 0 12px; white-space: normal; }
.tbl tr.edit form.update { margin-bottom: var(--s3); }

/* A page that is not a shell: the width of the band inset, with room above and below. */
.page { padding: var(--s6) 0 var(--s8); }
/* A form is one of the few things that genuinely wants a narrow column — and a sign-in
   form on an otherwise empty page belongs in the middle of the window, not pinned under the
   header with the rest of the screen blank below it. */
.authwrap { width: min(420px, 100%); margin-inline: auto; }
.authhead { margin-bottom: var(--s5); }
/* A one-field form inside a row, for choosing a username. */
.row form.inline { width: 100%; }
.row form.inline > div { margin-top: 0; }
.authhead h1 { font-size: var(--fs-t1); font-weight: 600; letter-spacing: -0.02em; }
.authhead p { margin-top: var(--s2); font-size: var(--fs-t4); line-height: 1.5; }
main.authpage {
  display: flex; flex-direction: column; justify-content: center;
  min-height: calc(100vh - 64px - var(--s8));
  padding-block: var(--s7);
}

/* The header of a working page: what it is, and the one thing to do with it. */
.pagehead {
  display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap;
  padding-bottom: var(--s4); margin-bottom: var(--s5);
  border-bottom: 1px solid var(--line);
}
.pagehead h1 { font-size: var(--fs-t1); font-weight: 600; letter-spacing: -0.02em; }
.pagehead .titleblock { flex: 1; min-width: 0; }
.pagehead > h1 { flex: 1; min-width: 0; }
.pagehead p { width: 100%; color: var(--muted); font-size: var(--fs-t5); }
/* Who made it, what it is, how much of it there is — one muted line under the name, in
   place of a row of coloured capsules. */
.by { color: var(--muted); font-size: var(--fs-t5); margin-top: 2px; }
/* A label with a rule under it, for a section that is not inside a card. */
.eyebrow.ruled { padding-bottom: var(--s2); border-bottom: 1px solid var(--line); margin-bottom: var(--s3); }
/* A group of rows that is not a card: a rule, a label, and the rows under it. */
.group + .group { margin-top: var(--s6); }
.group > .eyebrow { padding-bottom: var(--s2); border-bottom: 1px solid var(--line); margin-bottom: var(--s1); }

/* ---------- chrome ---------- */
.topbar {
  position: sticky; top: 0; z-index: 20;
  /* The same gap the links use. At 12px the Download button, which has a fill and a border,
     read as attached to the last link rather than as the separate thing it is. */
  display: flex; align-items: center; gap: var(--s5);
  min-height: 64px; padding: 0 var(--gutter);
  /* The page shows through the bar. Blurred and desaturated so text crossing under it stays
     text-coloured smudge rather than something you try to read. */
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  -webkit-backdrop-filter: saturate(1.6) blur(14px);
  backdrop-filter: saturate(1.6) blur(14px);
  border-bottom: 1px solid var(--line);
}
/* Without the blur, translucency is just low contrast. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .topbar { background: var(--bg); }
}
.brand {
  display: flex; align-items: center; gap: 9px;
  font-weight: 650; letter-spacing: -0.02em; font-size: var(--fs-t3); color: var(--text);
}
.brand:hover { text-decoration: none; }
/* The wordmark (logo/final): ink is the text colour, the card is the accent, in both
   appearances. 22px tall keeps it over the 72px minimum width. */
.wordmark { display: block; height: 22px; width: auto; }
.wordmark .card { fill: var(--accent); }
.navlinks { display: none; gap: var(--s5); }
.navlinks a, .topbar .dl { color: var(--muted); font-weight: 500; font-size: var(--fs-t5); }
.navlinks a:hover, .topbar .dl:hover { color: var(--text); text-decoration: none; }
/* Download is a link, not a filled button: the bar's one button is the account. */
.topbar .dl { display: none; }
/* Who you are, on the right: an icon and the plan. "Sign in" until there is one. The same
   button on every page of the product, in the same colours — which plan somebody is on is a
   fact about their account, not a state the chrome should be shouting about. */
.btn.who { gap: 7px; padding: 0 12px 0 10px; max-width: 200px; }
.btn.who svg { width: 16px; height: 16px; flex: none; }
.btn.who span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The signed-in person's picture, in the icon's place. */
.btn.who img.face { width: 20px; height: 20px; flex: none; margin-left: -2px; border-radius: 50%; object-fit: cover; background: var(--surface-2); }
.navlinks a[aria-current], .navpanel a[aria-current] { color: var(--text); }
.navlinks a[aria-current], .navlinks a[aria-current]:hover {
  text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 7px;
  text-decoration-color: var(--accent);
}
/* Hiding navigation is not responsive design. Below the breakpoint it becomes a panel. */
.navtoggle { display: inline-flex; padding: 0 var(--s2); }
.navpanel {
  display: none; flex-direction: column; gap: 2px;
  position: fixed; inset: 64px 0 auto; z-index: 19;
  padding: var(--s3) var(--gutter) var(--s5);
  background: var(--bg); border-bottom: 1px solid var(--line);
  animation: fade var(--t-fast) both;
}
.navmenu[open] .navpanel { display: flex; }
.navpanel a { padding: 10px 0; color: var(--text); font-size: var(--fs-t3); font-weight: 500; }
@media (min-width: 900px) { .navlinks, .topbar .dl { display: flex; } }

/* A short page should not leave its footer floating half way up the window. */
body { min-height: 100vh; display: flex; flex-direction: column; }
body > main, body > .shell { flex: 1 0 auto; }
footer.site { border-top: 1px solid var(--line); margin-top: auto; flex: none; }
footer.site div {
  width: min(1240px, 100% - 2 * var(--gutter)); margin-inline: auto;
  padding: var(--s5) 0 var(--s7);
  display: flex; gap: var(--s4); align-items: center;
  font-size: var(--fs-t5); color: var(--muted); flex-wrap: wrap;
}

/* ---------- pricing ---------- */
.plans { display: grid; gap: var(--s4); }
@media (min-width: 760px) { .plans { grid-template-columns: 1fr 1fr; gap: var(--s5); } }
/* Scoped to `.plans`, and it has to stay that way: `.plan` on its own also matched the word
   "Free" in a marketplace card's foot, which gave a one-word span 24px of padding on every
   side. That is what had been throwing the plan word out of line with the download count
   beside it — the same class doing two jobs, not the flex layout above it. */
.plans .plan { padding: var(--s5); display: flex; flex-direction: column; }
.plans .plan .tier { font-size: var(--fs-t7); font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
.plans .plan .price { font-size: 44px; font-weight: 500; letter-spacing: -0.03em; margin: var(--s2) 0 var(--s5); line-height: 1; }
.plans .plan .price span { font-size: var(--fs-t4); font-weight: 400; color: var(--muted); letter-spacing: 0; }
.plans .plan ul { list-style: none; margin: 0 0 var(--s5); padding: 0; flex: 1; }
.plans .plan li { position: relative; padding: 6px 0 6px 22px; color: var(--muted); font-size: var(--fs-t5); }
.plans .plan li::before { content: ''; position: absolute; left: 2px; top: 13px; width: 8px; height: 1px; background: var(--text-3); }
/* The one being recommended says so by looking like it, not by wearing a badge. */
.plans .plan.best { border-color: color-mix(in srgb, var(--accent) 40%, var(--line)); }
.plans .plan.best .tier { color: var(--accent); }

/* ---------- the marketplace front door (§4.7) ---------- */
/* Four shelves, --s9 apart, each opened by a d4 heading, one muted sentence beside it and
   one accent link on the right — and each a different shape, so the page reads as chosen
   rather than dumped. */
.shelf + .shelf { margin-top: var(--s9); }
@media (max-width: 719px) { .shelf + .shelf { margin-top: var(--s8); } }
.shelfhead { display: flex; align-items: baseline; gap: var(--s4); flex-wrap: wrap; margin-bottom: var(--s5); }
.shelfhead h2 { font-size: var(--fs-d4); font-weight: 600; letter-spacing: -0.02em; }
.shelfhead p { color: var(--muted); font-size: var(--fs-t4); flex: 1; min-width: 12ch; }
.shelfhead a { font-size: var(--fs-t4); font-weight: 500; white-space: nowrap; }
/* The twelve shelves, each with a colour. Blue is geography: the swatch on the tile and the
   tint of the card's field are the same twelve, which is how the colour is learnt. */
.mk[data-cat='Geography'], .tile[data-cat='Geography'] { --cat: #2563eb; }
.mk[data-cat='History'], .tile[data-cat='History'] { --cat: #7c3aed; }
.mk[data-cat='Science'], .tile[data-cat='Science'] { --cat: #059669; }
.mk[data-cat='Mathematics'], .tile[data-cat='Mathematics'] { --cat: #0891b2; }
.mk[data-cat='Languages'], .tile[data-cat='Languages'] { --cat: #64748b; }
.mk[data-cat='Music'], .tile[data-cat='Music'] { --cat: #d97706; }
.mk[data-cat='Arts'], .tile[data-cat='Arts'] { --cat: #db2777; }
.mk[data-cat='Technology'], .tile[data-cat='Technology'] { --cat: #4f46e5; }
.mk[data-cat='Medicine'], .tile[data-cat='Medicine'] { --cat: #dc2626; }
.mk[data-cat='Business'], .tile[data-cat='Business'] { --cat: #ca8a04; }
.mk[data-cat='Sport'], .tile[data-cat='Sport'] { --cat: #65a30d; }
.mk[data-cat='General'], .tile[data-cat='General'] { --cat: #71717a; }
/* Qwizzd picks: one featured card across two columns and two ordinary ones. */
.mkgrid.picks { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
@media (min-width: 1200px) { .mkgrid.picks { grid-template-columns: repeat(4, 1fr); } }
/* The featured card: the §3.9 card turned sideways, the field the left half at full height
   with the numeral at 220px, the words on the right with one sentence of opinion. */
.mk.feat { flex-direction: row; grid-column: span 2; min-height: 0; }
.mk.feat .field { width: 50%; height: auto; flex: none; }
/* The numeral is meant to be cropped by the bottom of the field — that is the design — but
   never by its right edge, which put a three-digit count through the words beside it. Sized
   against the field's own width, so it fits at any viewport, and stepped down by digit count
   because three digits need less each than two. The 220px cap is the original size, which
   one and two digits still reach. */
.mk.feat .n { --cap: 220px; top: auto; bottom: 10px; }
.mk.feat .side { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.mk.feat .body { padding: var(--s5) 20px 0; }
.mk.feat h3 { font-size: var(--fs-t1); }
.mk.feat .why { margin-top: var(--s4); font-size: var(--fs-t4); line-height: 1.55; color: var(--muted); }
@media (max-width: 719px) {
  .mk.feat { flex-direction: column; grid-column: auto; }
  .mk.feat .field { width: auto; height: 190px; }
  .mk.feat .n { --cap: 132px; top: 66px; bottom: auto; }
}
/* A tag saying it is new: solid --ok, top right, where Start here goes on the picks. */
.mk .tag.new { left: auto; right: 16px; background: var(--ok); border-color: transparent; color: var(--on-ok); }
/* Most downloaded: a ranked list, ten in two columns, no cards. */
.ranked { list-style: none; margin: 0; padding: 0; display: grid; gap: 0 var(--s7); }
@media (min-width: 900px) { .ranked { grid-template-columns: 1fr 1fr; } }
.ranked li {
  display: grid; grid-template-columns: 40px 1fr auto auto; align-items: center; gap: var(--s3);
  padding: 14px 0; border-bottom: 1px solid var(--line);
}
.ranked li:first-child { border-top: 1px solid var(--line-strong); }
@media (min-width: 900px) { .ranked li:nth-child(6) { border-top: 1px solid var(--line-strong); } }
.ranked .rank { font-size: 24px; font-weight: 300; color: var(--muted); font-variant-numeric: tabular-nums; }
.ranked .who { min-width: 0; display: flex; flex-direction: column; }
.ranked .who a { color: var(--text); font-size: var(--fs-t4); font-weight: 500; }
.ranked .who small { color: var(--muted); font-size: var(--fs-t6); margin-top: 2px; }
.ranked .tag {
  padding: 2px 8px; border-radius: var(--rfull); border: 1px solid var(--line); background: var(--surface);
  font-size: var(--fs-t7); font-weight: 500; color: var(--muted); white-space: nowrap;
}
.ranked .count { font-size: var(--fs-t5); color: var(--muted); font-variant-numeric: tabular-nums; min-width: 4ch; text-align: right; }
@media (max-width: 480px) { .ranked .tag { display: none; } }
/* By shelf: the twelve categories as objects — a swatch, the name, the count. */
.tiles { display: grid; gap: var(--s3); grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
@media (min-width: 1100px) { .tiles { grid-template-columns: repeat(6, 1fr); } }
.tile {
  --cat: var(--muted);
  display: flex; flex-direction: column; justify-content: flex-end; min-height: 120px; padding: 14px 16px;
  border-radius: var(--r3); border: 1px solid var(--line); background: var(--surface);
  color: var(--text); text-decoration: none; transition: border-color var(--t-fast);
}
.tile:hover { text-decoration: none; border-color: color-mix(in srgb, var(--accent) 40%, var(--line)); }
.tile i { display: block; width: 10px; height: 10px; border-radius: var(--r1); background: var(--cat); margin-bottom: auto; }
.tile b { font-size: var(--fs-t3); font-weight: 500; margin-top: var(--s4); }
.tile small { font-size: var(--fs-t6); color: var(--muted); margin-top: 2px; }
.tile.off { color: var(--text-3); }
.tile.off i { opacity: 0.4; }
/* The thin version of the front door (§4.7). The shelves keep their order and their words;
   what changes is the shape of each, so four shelves fit a phone's height: the heading
   with its link on one line and the sentence under them, the cards as rows (the field a
   96px square on the left, the words beside it, the featured one's sentence under its
   name), the ranked list in one column, the tiles three across. */
@media (max-width: 719px) {
  .shelfhead { display: grid; grid-template-columns: 1fr auto; column-gap: var(--s4); row-gap: var(--s1); align-items: baseline; }
  .shelfhead h2 { font-size: 26px; }
  .shelfhead p { grid-column: 1 / -1; font-size: var(--fs-t5); min-width: 0; }
  .shelfhead a { grid-column: 2; grid-row: 1; font-size: var(--fs-t5); }
  .shelves .mkgrid { gap: var(--s3); }
  .shelves .mk, .shelves .mk.feat {
    min-height: 0; display: grid; grid-template-columns: 96px 1fr; grid-template-rows: auto auto; grid-column: auto;
  }
  .shelves .mk .field { grid-row: 1 / 3; width: auto; height: auto; display: flex; flex-direction: column; justify-content: space-between; padding: 10px; }
  .shelves .mk .tag { position: static; align-self: flex-start; padding: 2px 6px; font-size: 10px; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* No room for two tags in a 96px field: the picked one has its border and the new shelf
     says new, so the category keeps the tag. */
  .shelves .mk .tag.rec, .shelves .mk .tag.new { display: none; }
  .shelves .mk .n { position: static; --cap: 40px; --inset: 20px; line-height: 1; letter-spacing: -0.03em; margin-top: var(--s2); bottom: auto; }
  .shelves .mk .side { display: contents; }
  .shelves .mk .body { padding: var(--s3) 14px 0; }
  .shelves .mk h3 { font-size: var(--fs-t3); }
  .shelves .mk .by { margin-top: 0; }
  .shelves .mk .why { margin-top: var(--s2); font-size: var(--fs-t5); line-height: 1.45; }
  .shelves .mk .meta { margin-top: var(--s1); }
  .shelves .mk .foot { margin-top: var(--s2); padding: 4px 12px 12px 14px; border-top: 0; grid-column: 2; gap: var(--s2); font-size: var(--fs-t6); }
  .shelves .mk .foot a, .shelves .mk .foot .plan { white-space: nowrap; padding: 8px 0; margin: -8px 0; }
  .ranked li { padding: 10px 0; }
  .tiles { grid-template-columns: repeat(3, 1fr); gap: var(--s2); }
  .tile { min-height: 84px; padding: 10px 12px; }
  .tile b { font-size: var(--fs-t5); margin-top: var(--s2); }
  .tile small { font-size: 11px; }
  .shelf + .shelf { margin-top: var(--s7); }
}

/* The band the shelves sit in arrives under the masthead's curve, so it starts sooner. */
.band.shelves { padding-top: var(--s7); }
@media (min-width: 1024px) { .band.shelves { padding-top: var(--s8); } }

/* ---------- the marketplace listing ---------- */
/* The card itself is the marketing card (`.mk`, below). What is left here is the page
   furniture around it. */
.libfind { display: flex; gap: var(--s2); flex: 0 1 360px; min-width: 240px; }
.libfind input { border-radius: var(--rfull); padding: 0 var(--s4); }
/* Search and filters read as one line of controls, not two stacked blocks. */
.filters { display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap; margin-bottom: var(--s5); }
/* The order the list is in, beside what it is filtered to. The label is what keeps two rows
   of chips from reading as one set of filters. */
.chips.sortby { align-items: center; gap: var(--s2); }
.chips.sortby .eyebrow { margin-right: var(--s1); }
/* The three dots beside a deck's two buttons: a <details> whose summary is the button and
   whose panel hangs off its right edge. Everybody gets a way to report the deck; an admin
   gets the review actions underneath, on the page where the questions are. */
.more { position: relative; }
.more > summary { list-style: none; }
.more > summary::-webkit-details-marker { display: none; }
.more[open] > summary { background: var(--surface-2); }
.btn.icon { padding: 0 12px; }
.btn.icon.lg { padding: 0 13px; }
.more .menu {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 15; min-width: 240px;
  display: flex; flex-direction: column; padding: var(--s2);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r3);
  box-shadow: var(--e2); animation: fade var(--t-fast) both;
}
.more .menu a, .more .menu button {
  display: block; width: 100%; padding: 8px 10px; border: 0; border-radius: var(--r2);
  background: none; color: var(--text); font: inherit; font-size: var(--fs-t5); font-weight: 500;
  text-align: left; cursor: pointer;
}
.more .menu a:hover, .more .menu button:hover { background: var(--surface-2); text-decoration: none; }
.more .menu button:disabled { opacity: 0.5; cursor: default; }
.more .menu .danger { color: var(--bad); }
.more .menu .sect { border-top: 1px solid var(--line); margin-top: var(--s2); padding-top: var(--s2); }
.more .menu .hint { padding: 4px 10px 6px; }
.more .menu .hint:empty { display: none; }
.crumb { margin: 0 0 var(--s3); font-size: var(--fs-t5); }
/* A flag beside a preview question. Bordered, because a white flag on a white ground is a
   gap where a picture should be. */
.thumb {
  display: inline-block; vertical-align: -4px; width: 30px; height: 20px;
  margin-right: var(--s2); object-fit: contain;
  border: 1px solid var(--line); border-radius: var(--r1); background: var(--surface-2);
}

/* ---------- the admin dashboard ---------- */
/* What the chosen file says about itself, read back before it is sent. */
.readback { margin: var(--s3) 0 var(--s2); }
/* The picks: four selects in a row and the sentence under them. */
.picks { display: flex; flex-direction: column; gap: var(--s4); padding-top: var(--s3); }
.pickslots { display: grid; gap: var(--s3); grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
.pickslot { display: flex; flex-direction: column; gap: var(--s1); font-size: var(--fs-t5); color: var(--muted); }
.pickslot select, .pickslot input { width: 100%; }
.pickslot .pickname { min-height: 1.2em; color: var(--text); font-size: var(--fs-t5); }
.pickslot .pickname.bad { color: var(--bad); }
.picks .actions { align-items: center; gap: var(--s4); }
.picks .actions .hint { margin: 0; }
/* The rail's second heading is the same eyebrow as its first — "Publishing" above the
   list, "Admin" part-way down it — and differs from it in nothing but the room above. One
   property, at the specificity `.shell > .side .eyebrow` sets the rest at. */
.shell > .side nav .railgroup { margin-top: var(--s6); }
/* A table, because a list of accounts is a table. One row per record, one format down each
   column, read the way a query result is read. */
.searchbar { display: flex; gap: var(--s2); margin-bottom: var(--s4); }
.searchbar input { flex: 1; min-width: 0; }
/* A row of buttons in a table cell stays a row. The panel scrolls sideways when the table
   is wider than the window; buttons stacking three-high inside a cell instead is how a
   table of ten decks becomes a page and a half. */
.tbl td .actions { flex-wrap: nowrap; }
/* The rarer actions, in a row under the account rather than three more empty columns. */
.tbl tr.under td { white-space: normal; background: var(--surface-2); }
.tbl tr.under p { margin: 0 0 9px; }
.tbl tr.under:hover td { background: var(--surface-2); }

/* ---------- the written pages ---------- */
/* The table on the privacy page: prose in three columns, not a comparison grid. */
.matrix { width: 100%; border-collapse: collapse; }
.matrix th, .matrix td { padding: 14px 16px; text-align: left; border-bottom: 1px solid var(--line); }
.matrix thead th {
  font-size: var(--fs-t7); font-weight: 600; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.06em;
  border-bottom: 1px solid var(--line-strong);
}
.matrix tbody tr:last-child th, .matrix tbody tr:last-child td { border-bottom: 0; }
.matrix tbody th { font-weight: 550; }
.matrix.wide td { color: var(--text); }

.prose h1 { font-size: var(--fs-d3); font-weight: 400; letter-spacing: -0.025em; line-height: 1.08; }
.prose h2 { font-size: var(--fs-t2); margin: var(--s6) 0 var(--s3); }
.prose p { margin: 0 0 var(--s3); font-size: var(--fs-t3); line-height: 1.65; color: var(--text); }
.prose p.muted, .prose p.hint { color: var(--muted); }
@media (max-width: 480px) { .matrix th, .matrix td { padding: 10px 8px; } .matrix.wide td { font-size: var(--fs-t5); } }
.prose p strong { color: var(--text); font-weight: 600; }
.prose code { font-size: var(--fs-t5); }
.prose ul { margin: 0 0 var(--s3); padding-left: 22px; }
/* The privacy label: four columns want more than the reading column, so it steps out to
   the width of the inset and back in again. */
.prose .glance { width: min(1000px, 100vw - 2 * var(--gutter)); position: relative; left: 50%; transform: translateX(-50%); }
.prose .glance th:first-child { white-space: nowrap; }
.prose .glance td:nth-child(3) { white-space: nowrap; }
@media (max-width: 719px) { .prose .glance { width: 100%; left: 0; transform: none; } .prose .glance th:first-child, .prose .glance td:nth-child(3) { white-space: normal; } }
.prose li { font-size: var(--fs-t3); line-height: 1.6; margin: 4px 0; }
.prose .matrix { margin: var(--s2) 0 var(--s5); }
.prose .hint { margin-top: var(--s6); padding-top: var(--s4); border-top: 1px solid var(--line); }

/* The pairing code is read off one screen and typed into another, so it is the one string
   here meant to be transcribed. */
.code-in {
  font: 600 20px/1.3 var(--mono);
  letter-spacing: 0.16em; text-align: center; text-transform: uppercase;
}

/* Sections arrive as they are scrolled to. Guarded twice: without the timeline the opacity
   would simply stay at zero, and nothing moves for anyone who asked for less movement. */
@supports (animation-timeline: view()) {
  @media screen and (prefers-reduced-motion: no-preference) {
    .band > .inset > * {
      animation: enter linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 40%;
    }
    @keyframes enter { from { opacity: 0; transform: translateY(14px); } }
  }
}

/* ---------- signing in with Google ---------- */
/* One full-width button above the form, Google's mark on it, and a rule saying "or". */
.btn.google { width: 100%; min-height: 40px; gap: 10px; margin-bottom: var(--s4); }
.btn.google .g { flex: none; }
.or { display: flex; align-items: center; gap: var(--s3); margin: 0 0 var(--s4); color: var(--muted); font-size: var(--fs-t6); }
.or::before, .or::after { content: ''; flex: 1; border-top: 1px solid var(--line); }

/* ---------- the disclosure menu ---------- */
/* <details> so the page keeps loading no JavaScript. The marker is removed; the summary is
   the button. */
.navmenu > summary { list-style: none; cursor: pointer; }
.navmenu > summary::-webkit-details-marker { display: none; }
.navmenu[open] > summary { background: var(--surface-2); color: var(--text); }
.navmenu > summary .close { display: none; }
.navmenu[open] > summary .close { display: block; }
.navmenu[open] > summary .open { display: none; }
.topbar:has(.navmenu[open]) { background: var(--bg); animation: none; }
@media (min-width: 900px) { .navmenu { display: none; } }

/* The bar is part of the hero until you have scrolled past it. Scroll-driven, so the page
   still needs no script; where the timeline is unsupported the bar is simply always solid. */
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    body.landing .topbar {
      animation: settle linear both;
      animation-timeline: scroll();
      animation-range: 0 80px;
    }
    @keyframes settle {
      from { background: color-mix(in srgb, var(--bg) 0%, transparent); border-bottom-color: transparent; }
      to { background: color-mix(in srgb, var(--bg) 72%, transparent); border-bottom-color: var(--line); }
    }
  }
}
.pager { margin-top: var(--s5); }
/* Buttons that are the point of a column, one under the other. */
.actions.stack { flex-direction: column; align-items: stretch; margin-top: var(--s4); }
.actions.stack .btn { width: 100%; }

/* ---------- the marketing page ---------- */
/* Six scenes, each composed for roughly a viewport at 1440 × 900 — the comps are in
 * design-refs/ and the values below are theirs. The composition changes on every band, which
 * is what stops the page reading as a template, while the type, the popup, the palette and
 * the spacing do not change at all. */

/* The page's calls to action are pills, and they are the only pill-shaped buttons in the
   product. The pill says "this is the page asking"; the rounded rectangle says "this is the
   product working" — which is why every control inside a popup below is still --r2. */
.pillbtn {
  display: inline-flex; align-items: center; gap: var(--s2); padding: 14px 24px;
  border-radius: var(--rfull); border: 1px solid var(--line);
  font-size: var(--fs-t3); font-weight: 500; white-space: nowrap;
  color: var(--text); background: transparent;
  transition: background var(--t-fast), border-color var(--t-fast), filter var(--t-fast);
}
.pillbtn:hover { text-decoration: none; background: var(--surface-2); }
.pillbtn.primary { background: var(--accent); border-color: transparent; color: var(--on-accent); }
.pillbtn.primary:hover { background: var(--accent); filter: brightness(1.07); }
.ctas { display: flex; gap: var(--s3); flex-wrap: wrap; }
.trust { font-size: var(--fs-t6); color: var(--muted); }

/* ---- the popup, as the component itself ---- */
/* Rendered in HTML from the anatomy the real one has (§4.5), never a picture of it. It is
   `.demo` rather than `.pop` because `.pop` is the real quiz window in the app. */
/* width: 100% with a cap, not 400px with a cap: as a grid item's content, 400px is the
   column's minimum and the accent band clipped on any phone. */
.demo {
  width: 100%; max-width: 400px; color: var(--text);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r3);
  padding: 18px 20px 16px; display: flex; flex-direction: column; gap: var(--s4);
  box-shadow: var(--e2); text-align: left;
}
.demo .hd { display: flex; justify-content: space-between; align-items: center; gap: var(--s3); }
.demo .hd .eyebrow { letter-spacing: 0.06em; }
.demo .hd .x { font-size: 16px; font-weight: 500; color: var(--text-3); line-height: 1; }
.demo .prompt { font-size: var(--fs-t2); font-weight: 600; line-height: 1.3; letter-spacing: -0.01em; }
/* The foot: Show answer on the left, the keyboard hint on the right, as the real one. */
.demo .ft { display: flex; justify-content: space-between; align-items: center; gap: var(--s3); font-size: var(--fs-t5); color: var(--muted); }
.demo .ft b { font-size: var(--fs-t6); font-weight: 500; color: var(--muted); white-space: nowrap; }
.demo .answers { display: flex; flex-direction: column; gap: 6px; }
.demo .answers div {
  display: flex; gap: 10px; align-items: center; padding: 10px 12px;
  border-radius: var(--r2); background: var(--surface-2); font-size: var(--fs-t4);
}
.demo .answers i { font-style: normal; font-size: var(--fs-t6); font-weight: 600; color: var(--muted); }
.demo .answers .sel {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  padding: 9px 11px; font-weight: 500;
}
.demo .answers .sel i { color: var(--accent); }
/* An answer picked and waiting to be confirmed: the app's own row, Change and Confirm. */
.demo .staged {
  display: flex; align-items: center; gap: var(--s2);
  padding: 7px var(--s2) 7px var(--s3); border-radius: var(--r2);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}
.demo .staged .pick { font-weight: 600; font-size: var(--fs-t5); }
.demo .staged .btn { min-height: 28px; padding: 0 10px; }
/* Typed: the answer field in the popup's footer, disabled once there is a verdict, and the
   verdict as the message the rest of the product uses with the bar draining under it. */
.demo .typed { display: flex; }
.demo .typed .in {
  flex: 1; min-height: 36px; display: flex; align-items: center; padding: 0 12px;
  border-radius: var(--r2); border: 1px solid var(--line); background: var(--surface-2);
  color: var(--muted); font-size: var(--fs-t4);
}
.demo .msg { margin: 0; padding: 9px 12px; line-height: 1.45; }
.demo .bar { height: 2px; margin: -8px 0 0; border-radius: 1px; background: var(--surface-2); overflow: hidden; }
.demo .bar i { display: block; height: 100%; width: 58%; background: var(--ok); }
.demo .hint { margin: 0; font-size: var(--fs-t6); color: var(--muted); }
/* The map: the app's own boundaries (Natural Earth, from its map data) in the app's own
   colours, with the picked country filled. */
.demo .map {
  aspect-ratio: 2 / 1; border-radius: var(--r2); overflow: hidden;
  border: 1px solid var(--line); background: #a9cfe8;
}
.demo .map svg { display: block; width: 100%; height: 100%; }
.demo .map path { fill: #f7f2e6; stroke: #7f8d9b; stroke-width: 0.07; stroke-linejoin: round; }
.demo .map path.sel { fill: var(--accent); stroke: var(--accent); stroke-width: 0.2; }
/* The piano: three octaves from C3, the geometry the app draws, one key marked. */
.demo .piano { width: 100%; }
.demo .piano svg { display: block; width: 100%; height: auto; }
.demo .piano .whites rect { fill: #fff; stroke: #9aa3ac; stroke-width: 0.6; }
.demo .piano .blacks rect { fill: #23262b; stroke: #9aa3ac; stroke-width: 0.6; }
.demo .piano rect.sel { fill: var(--accent); }
@media (prefers-color-scheme: dark) {
  .demo .map { background: #0d2839; }
  .demo .map path { fill: #cabfa8; stroke: #5b6875; }
  .demo .piano .whites rect { fill: #e6e2d9; }
}

/* ---- 1. the hero ---- */
/* Headline bottom-left with its baseline in the lower third; the popup top-right with its
   right edge past the viewport, rotated −5°. The rotation is a static composition device for
   this band alone — nothing else on the site is rotated — and it is a transform on the
   decorative copy, never on the real popup. */
/* The band clips: the popup's right edge is past the viewport on purpose, and a page must
   never scroll sideways because of it. And it takes no padding of its own: the hero is one
   viewport tall and lays itself out inside that. */
.band.lit { overflow: hidden; }
.band.lit:not(.thin) { padding: 0; }
/* A masthead has nothing past its edges to clip — its wash is a pseudo-element inside it —
   and it holds the deck page's three-dots menu, which hangs below the band. Clipped, that
   menu lost its bottom. */
.band.lit.thin { overflow: visible; }
.hero {
  /* One viewport, bar included: the bar is 64px of the same scene. */
  position: relative; min-height: min(100vh - 64px, 836px);
  display: flex; flex-direction: column; justify-content: flex-end;
  padding: var(--s8) 0 var(--s8);
}
.hero h1 { max-width: 12ch; }
.hero .lede { margin-top: var(--s7); font-size: 16px; line-height: 1.55; color: var(--muted); max-width: 460px; }
.hero .ctas { margin-top: var(--s6); }
.hero .trust { margin-top: var(--s4); }
.hero .demo { margin: var(--s7) 0 0; }
@media (min-width: 1100px) {
  .hero { padding-top: var(--s10); }
  .hero .demo {
    position: absolute; right: calc(-30px - var(--gutter)); top: var(--s9); margin: 0; width: 400px;
    transform: rotate(-5deg);
  }
  /* Its right edge is off the page on purpose; the words in its foot are not. */
  .hero .demo .ft { justify-content: flex-start; gap: var(--s5); }
}

/* ---- 2. how it works ---- */
/* Three columns, each opened by the structural numeral (§1.2): a number set at display
   scale, weight 300, in the accent at 16%, sitting behind and above the step title. Then the
   application itself at the inset width, cropped by the band's bottom edge. */
.band.how { padding-bottom: 0; overflow: hidden; }
.steps3 { display: grid; gap: var(--s7) var(--s6); margin: var(--s8) 0 0; padding: 0; list-style: none; }
@media (min-width: 900px) { .steps3 { grid-template-columns: repeat(3, 1fr); } }
.step { position: relative; padding-top: 150px; }
.step .n {
  position: absolute; left: -4px; top: 0;
  font-size: 180px; font-weight: 300; line-height: 0.8; letter-spacing: -0.06em;
  color: var(--accent); opacity: 0.16; font-variant-numeric: tabular-nums;
  pointer-events: none; user-select: none;
}
.step h3 { font-size: var(--fs-t2); font-weight: 600; letter-spacing: -0.015em; }
.step p { margin-top: var(--s2); color: var(--muted); max-width: 330px; }
.shot { margin: var(--s8) 0 0; height: min(300px, 32vw); overflow: hidden; }
.shot img {
  /* height: auto is load-bearing beside a width attribute: without it the attribute wins as
     a presentational hint. */
  display: block; width: 100%; height: auto;
  border: 1px solid var(--line); border-radius: var(--r3) var(--r3) 0 0;
}

/* ---- 3. the moment ---- */
/* The one accent band: a solid field is the loudest thing this system allows, so it appears
   once, in the middle, with the two calmest bands either side. The popup at 1.6×, with the
   page's one large shadow. */
.band.accent {
  /* One ground in both appearances, like .band.ink — this is a brand field, not a themed
     surface, and it carries white type and a white-on-accent figure. Following the theme
     would put white on the light dark-mode accent at 2.4:1. */
  background: #2563eb; color: #fff; overflow: hidden;
}
.band.accent .eyebrow { color: rgba(255, 255, 255, 0.7); }
.band.accent::before {
  /* One darker vignette of the same hue, bottom-left, so the field has depth without a
     second colour. */
  content: ''; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background: radial-gradient(70rem 46rem at 6% 110%, color-mix(in srgb, #2563eb 80%, #000) 0%, transparent 75%);
  opacity: 0.9;
}
.moment { display: grid; gap: var(--s8); align-items: end; min-height: min(100vh - 2 * var(--s9), 660px); }
.moment > * { min-width: 0; }
@media (min-width: 1000px) { .moment { grid-template-columns: 1fr 1fr; gap: var(--s8); } }
.moment h2 { font-size: clamp(40px, 5.8vw, 84px); margin-top: var(--s10); }
.moment .lede { margin-top: var(--s8); font-size: 16px; line-height: 1.55; color: rgba(255, 255, 255, 0.8); max-width: 560px; }
/* The popup high in its half, the figures along the floor. */
.moment .obj { display: flex; flex-direction: column; justify-content: space-between; gap: var(--s8); align-self: stretch; padding-top: var(--s6); }
.moment .figures { gap: var(--s7); }
.band.accent .demo .eyebrow { color: var(--muted); }
/* 1.6× of the 400: zoom rather than transform so the page lays out the size it draws. */
.moment .demo { zoom: 1.3; box-shadow: 0 24px 60px -10px rgba(0, 0, 0, 0.28); border-color: transparent; }
@media (max-width: 999px) { .moment .demo { zoom: 1; } }
.moment .figures b { color: #fff; font-size: 48px; font-weight: 500; letter-spacing: -0.02em; }
.moment .figures span { color: rgba(255, 255, 255, 0.7); }

/* ---- 4. four ways to ask ---- */
/* Four columns, the even ones dropped 40px, each a label, the popup at 320 showing that type
   in a real state, and one sentence. It has to read as an offer of four things at once. */
.band.four .lede { margin-top: var(--s4); color: var(--muted); max-width: 400px; }
@media (min-width: 1000px) {
  .fourhead { display: grid; grid-template-columns: 1fr 400px; gap: var(--s8); align-items: start; }
  .band.four .lede { margin-top: var(--s7); }
}
.cols4 { display: grid; gap: var(--s6) var(--s4); margin-top: var(--s8); }
@media (min-width: 720px) { .cols4 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1200px) {
  .cols4 { grid-template-columns: repeat(4, 1fr); }
  .cols4 .col:nth-child(even) { padding-top: 40px; }
}
.col { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
.col .label { display: flex; gap: 10px; align-items: center; font-size: var(--fs-t3); font-weight: 600; letter-spacing: -0.01em; }
.col .label i {
  width: 28px; height: 28px; border-radius: var(--r2); flex: none;
  background: color-mix(in srgb, var(--accent) 10%, transparent); color: var(--accent);
  font: 600 12px/28px var(--sans); text-align: center; font-style: normal;
}
.col .demo { width: 100%; padding: 16px 18px 14px; gap: 14px; }
.col .desc { font-size: var(--fs-t5); color: var(--muted); max-width: 290px; }

/* ---- 5. the marketplace card ---- */
/* The one card with a fixed anatomy (§3.9): a tinted visual field carrying the category tag
   and the structural numeral — the question count — cropped by the field's bottom edge; then
   the name, the byline, the meta line; then a foot of two actions.
   The category colour is content, not chrome, like the map and piano palettes: it tints the
   field and colours the numeral and appears nowhere else on the card. */
.mk {
  --cat: var(--muted);
  position: relative; display: flex; flex-direction: column; min-height: 400px;
  color: inherit; text-decoration: none; overflow: hidden;
  transition: border-color var(--t-fast);
}
.mk:hover { text-decoration: none; border-color: color-mix(in srgb, var(--accent) 40%, var(--line)); }
/* The category colours are the twelve set with the shelf tiles, above. */
.mk .field {
  position: relative; height: 190px; overflow: hidden; container-type: inline-size;
  background: color-mix(in srgb, var(--cat) 8%, transparent);
}
/* The structural numeral. Sized from the field it sits in, so it fits at every width and
 * every digit count: one formula, and a layout only ever sets the cap and where it is
 * anchored. A digit of this face is 0.465em wide (measured in the app; tabular figures,
 * -0.05em tracking); 0.52 leaves room for a platform whose light digits are wider. `--d`
 * is how many digits, from the d2..d5 class the renderer puts on it; `--inset` the left
 * offset plus a margin on the right. */
.mk .n {
  position: absolute; left: 22px; top: auto; bottom: -3px;
  --cap: 132px; --inset: 30px; --d: 1;
  font-size: min(var(--cap), calc((100cqw - var(--inset)) / (0.52 * var(--d))));
  font-weight: 300; line-height: 0.9; letter-spacing: -0.05em;
  color: var(--cat); opacity: 0.9; font-variant-numeric: tabular-nums; pointer-events: none;
}
.mk .n.d2 { --d: 2; }
.mk .n.d3 { --d: 3; }
.mk .n.d4 { --d: 4; }
.mk .n.d5 { --d: 5; }
.mk .tag {
  position: absolute; left: 16px; top: 16px; padding: 3px 9px; border-radius: var(--rfull);
  background: var(--surface); border: 1px solid var(--line);
  font-size: var(--fs-t7); font-weight: 500; color: var(--muted); line-height: 1.5;
}
.mk .tag.rec { left: auto; right: 16px; background: var(--accent); border-color: transparent; color: var(--on-accent); }
.mk .tag.have { left: auto; right: 16px; background: var(--ok); border-color: transparent; color: var(--on-ok); }
.mk.rec { border-color: color-mix(in srgb, var(--accent) 60%, transparent); }
.mk .body { padding: var(--s5) 20px 0; flex: 1; }
.mk h3 { font-size: var(--fs-t2); font-weight: 600; letter-spacing: -0.015em; }
.mk h3 a { color: inherit; }
/* Anywhere on the card opens the deck. The title's link is stretched over the whole card;
   the foot's links and the tags sit above it, so they still go where they say.
   The tags are only lifted, never repositioned: they are already absolute, in the corners of
   the tinted field, and giving them `position: relative` here is what knocked "Start here"
   and "New" out of the top-right and into the flow. */
.mk h3 a::after { content: ''; position: absolute; inset: 0; }
.mk .foot a, .mk .foot .plan { position: relative; z-index: 1; }
.mk .tag { z-index: 1; }
.mk .by { font-size: var(--fs-t5); color: var(--muted); margin-top: var(--s1); }
.mk .meta { font-size: var(--fs-t6); font-weight: 500; color: var(--muted); margin-top: var(--s4); }
.mk .foot {
  display: flex; justify-content: space-between; align-items: center; gap: var(--s3);
  margin-top: var(--s5); padding: 14px 20px; border-top: 1px solid var(--line);
  font-size: var(--fs-t5); font-weight: 500; color: var(--muted);
}
.mk .foot .go { color: var(--accent); }
.mk .foot .plan.pro { color: var(--accent); }
/* In the app the card is shorter (§4.3); on the listing page the site's is the marketing
   card exactly. Four across from 1200. */
.mkgrid { display: grid; gap: var(--s4); grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
@media (min-width: 640px) and (max-width: 1199px) { .mkgrid.four { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1200px) { .mkgrid.four { grid-template-columns: repeat(4, 1fr); } }
.band.market .lede { color: var(--muted); max-width: 400px; margin-top: var(--s4); }
@media (min-width: 1000px) {
  .markethead { display: grid; grid-template-columns: 1fr 400px; gap: var(--s8); align-items: end; }
  .band.market .lede { margin-top: 0; padding-bottom: 6px; }
}
.band.market .mkgrid { margin-top: var(--s8); }
.morelink { margin-top: var(--s7); text-align: right; font-size: var(--fs-t3); font-weight: 500; }

/* ---- 6. download ---- */
/* The ink band closes the page: everything centred and stacked, one wash from the bottom-left
   — the opposite corner to the hero's — and the footer along the floor. Nothing else. */
.band.ink.close {
  min-height: min(100vh, 900px); display: flex; flex-direction: column;
  padding: var(--s9) 0 var(--s6);
}
.close .stack {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s7); text-align: center; padding-bottom: var(--s7);
}
.band.ink .eyebrow { color: #8e8e97; }
.band.ink .pillbtn { border-color: rgba(255, 255, 255, 0.25); color: #f2f2f3; }
.band.ink .pillbtn:hover { background: rgba(255, 255, 255, 0.08); }
.band.ink .pillbtn.primary { background: #fbfbfa; border-color: transparent; color: #16161a; }
.band.ink .pillbtn.primary:hover { background: #fbfbfa; filter: brightness(0.94); }
.close .trust { color: #8e8e97; font-size: var(--fs-t5); font-weight: 500; margin-top: calc(-1 * var(--s4)); }
.close .trust a { color: #f2f2f3; }
.close footer {
  display: flex; gap: var(--s5); align-items: center; flex-wrap: wrap;
  font-size: var(--fs-t5); color: #8e8e97;
}
.close footer .brand { color: #f2f2f3; font-size: var(--fs-t4); }
.close footer .wordmark .card { fill: #6ea8fe; } /* the band is ink in both appearances */
.close footer a { color: #8e8e97; }
.close footer a:hover { color: #f2f2f3; text-decoration: none; }
.close footer .year { margin-left: auto; }
/* The landing page's footer is inside its closing band. */
body.landing > footer.site { display: none; }

/* ---------- atmosphere ---------- */
/* Gradients at one scale only: the size of a section. A band may carry one painted thing,
 * and it is a wash. No grid, no dot field, no pattern — the first closing-band comp had a
 * perspective grid rising from the floor and looked like every generated landing page.
 * Everything here is painted, not animated, and tinted low enough that the text keeps its
 * contrast. There is no atmosphere anywhere in the desktop app. */
.band { position: relative; }
.band > .inset { position: relative; z-index: 1; }

/* Behind the hero: the accent from the top-right, gone by 70% of its radius. */
.band.lit::before {
  content: ''; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background: radial-gradient(58rem 40rem at 82% -10%, color-mix(in srgb, var(--accent) 16%, transparent), transparent 70%);
}
/* A masthead is a short band, and the hero's wash is bigger than it is: pulled in, so a page
   title sits under a light rather than inside a blur. */
.band.lit.thin::before {
  background: radial-gradient(40rem 22rem at 86% -34%, color-mix(in srgb, var(--accent) 15%, transparent), transparent 70%);
}
/* The closing band, lit from the opposite corner in the dark theme's accent — the light one
   would vanish on ink — so the page opens and closes on one light from two sides. */
.band.ink { overflow: hidden; }
.band.ink::before {
  content: ''; position: absolute; pointer-events: none; z-index: 0;
  left: -380px; bottom: -520px; width: 1300px; height: 1100px; border-radius: 50%;
  background: radial-gradient(closest-side, rgba(110, 168, 254, 0.16), transparent 72%);
}

/* One curve, between the hero and the band under it, filled with the arriving band's own
   ground: the section rising, not a divider drawn between two of them. */
.curve {
  position: absolute; left: 0; right: 0; top: -58px;
  width: 100%; height: 59px; z-index: 0; pointer-events: none;
  fill: var(--band);
}

/* A hairline of light along the top of the plan being recommended: the Magic UI registry's
   shine-border, rebuilt as three lines because this project has no build step. */
.plans .plan.best { position: relative; overflow: hidden; }
.plans .plan.best::before {
  content: ''; position: absolute; inset: 0 0 auto; height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
}

@media (prefers-reduced-motion: reduce) { .band.lit::before, .band.ink::before { opacity: 0.7; } }

/* Last, so it wins: filled buttons and capsules in the dark theme take dark text. White on
   the dark theme's accent is 2.4:1. (The ink band's are already dark on light.) */
@media (prefers-color-scheme: dark) {
  .btn.primary, .btn.primary:hover, .pillbtn.primary, .pillbtn.primary:hover,
  .pill.on, .mk .tag.rec, .mk .tag.have, .skip { color: #0f0f11; }
}
