/* ===========================================================================
   RollMyLibrary — shared stylesheet
   Direction: Daylight Utility. Tokens are verbatim from /DESIGN.md §2.

   This file is the ONLY place site colour, type and spacing are defined. It
   used to be copy-pasted inline into all seven pages, which meant a token
   change was a seven-file edit and they had already drifted apart.

   Loading this externally is allowed by the CSP: style-src is 'self'.
   The FONTS are the part that would fail — font-src is 'self' data:, so
   Google Fonts is blocked and fails SILENTLY, falling back to a system face
   with no console error. Both faces below are self-hosted and SIL OFL 1.1;
   their licence texts sit beside them in assets/fonts/.
   =========================================================================== */

/* ── Faces ───────────────────────────────────────────────────────────────── */
@font-face{
  font-family:"Bricolage Grotesque";
  src:url("/assets/fonts/bricolage-grotesque.woff2") format("woff2");
  font-weight:400 800;          /* variable */
  font-style:normal;
  font-display:swap;
}
@font-face{
  font-family:"Instrument Sans";
  src:url("/assets/fonts/instrument-sans.woff2") format("woff2");
  font-weight:400 700;          /* variable */
  font-style:normal;
  font-display:swap;
}

/* ── Tokens ──────────────────────────────────────────────────────────────── */
:root{
  --ground:#F2F3EF;
  --surface:#FBFBF9;
  --surface-alt:#E8EAE4;
  --accent-soft:#E4EDE9;

  --ink:#16191A;
  --ink-strong:#0B0D0E;
  /* #5C6360 was measured on --surface and recorded as if it were on --ground.
     On the actual page background it is Lc 73.3, under the 75 body threshold,
     and it carries every .lead and .micro on the site. Darkened to clear 75 on
     the GROUND, which is the harder of the two. */
  --muted:#484F4B;

  /* Same error: #00695A is Lc 74.8 on --ground, not the 79.7 that was recorded
     from the on-surface measurement. */
  --accent:#005544;
  --accent-hover:#003D31;
  --on-accent:#F2F3EF;
  /* Legacy alias, not for new code. store.html's checkout script writes
     style="color:var(--teal)" into the PayPal amount line via innerHTML.
     That script is SHA-256 hashed into the CSP, so editing it would couple
     store.html to .htaccess on every manual upload; an alias keeps the
     emphasis without touching its bytes. Follows --accent into dark mode. */
  --teal:var(--accent);

  --line-subtle:#DDE0DA;
  --line-control:#9BA299;

  /* Two tiers on purpose. --error/--success are for fills, rails and icons,
     where the non-text threshold applies. Body-size TEXT needs Lc 75 against
     the page, which these saturated values miss badly, so status text uses the
     -text variants instead. */
  --error:#C22F5E;
  --error-text:#8E1B3B;
  --success:#2F7D33;
  --success-text:#1D5619;
  --warning:#B86A04;

  --shadow:0 1px 2px rgba(11,13,14,.05), 0 28px 60px -34px rgba(11,13,14,.42);

  /* 8pt grid, 4 as half-step */
  --s1:4px;  --s2:8px;  --s3:16px; --s4:24px;
  --s5:32px; --s6:48px; --s7:64px; --s8:96px; --s9:128px;

  --radius:3px;
  --ease:cubic-bezier(.2,0,0,1);
  --measure:68ch;
}

@media (prefers-color-scheme:dark){
  :root{
    --ground:#101314;
    --surface:#171B1D;
    --surface-alt:#222A2C;
    --accent-soft:#0E3A32;

    --ink:#E6E9E7;
    --ink-strong:#F4F7F5;
    --muted:#D8DFDB;

    /* Body-size links need #4FEBCC (Lc 78.1). #00D4AA is Lc 64 — fine for
       large text and UI, not for running text. */
    --accent:#6FF0D8;
    --accent-hover:#00D4AA;
    /* In dark mode the primary fill is cream, NOT teal: even pure black on
       #00d4aa reaches only Lc 67.8, short of the 75 body threshold. */
    --on-accent:#101314;

    --line-subtle:#262C2E;
    --line-control:#7A8388;

    /* #FF3366 is the brand pink and stays for fills and rails, but as body text
       on the dark ground it is only Lc 37.2 — less than half the threshold. The
       download page shows "that code is not valid" in this colour to someone who
       has just paid, so it has to be readable. */
    --error:#FF3366;
    --error-text:#FFCBD6;
    --success:#37D67A;
    --success-text:#8CEDB2;
    --warning:#F5A524;

    --shadow:0 1px 2px rgba(0,0,0,.5), 0 30px 70px -34px rgba(0,0,0,.9);
  }
}

/* ── Reset ───────────────────────────────────────────────────────────────── */
*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){ html{scroll-behavior:auto} }

body{
  margin:0;
  background:var(--ground);
  color:var(--ink);
  font-family:"Instrument Sans",ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
  font-size:16px;
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
img{max-width:100%;height:auto}
a{color:var(--accent);text-decoration-thickness:1px;text-underline-offset:.2em}
a:hover{color:var(--accent-hover)}

:focus-visible{
  outline:2px solid var(--accent);
  outline-offset:3px;
  border-radius:2px;
}

/* Visually hidden, still announced. Not display:none, which would remove it
   from the accessibility tree entirely. */
.sr{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
}
.skip{
  position:absolute;left:var(--s3);top:-64px;z-index:100;
  background:var(--accent);color:var(--on-accent);
  padding:var(--s2) var(--s3);border-radius:var(--radius);
  font-weight:600;text-decoration:none;transition:top .16s var(--ease);
}
.skip:focus{top:var(--s3);color:var(--on-accent)}

/* ── Type ────────────────────────────────────────────────────────────────── */
h1,h2,h3,h4{
  font-family:"Bricolage Grotesque",ui-sans-serif,system-ui,sans-serif;
  font-weight:800;
  color:var(--ink-strong);
  margin:0;
  line-height:1.05;
  letter-spacing:-.035em;
  text-wrap:balance;
}
/* Scale x1.25 from 16px: 20 / 25 / 31 / 39 / 49 / 61 */
h1{font-size:clamp(34px,4.6vw,56px)}
h2{font-size:clamp(27px,3.4vw,39px);letter-spacing:-.03em}
h3{font-size:22px;letter-spacing:-.02em;line-height:1.2}
h4{font-size:17px;font-weight:600;letter-spacing:-.01em}

p{margin:0}
.lead{color:var(--muted);font-size:18px;line-height:1.6;max-width:52ch}
.prose{max-width:var(--measure)}
.prose p+p{margin-top:var(--s3)}
.micro{font-size:13.5px;color:var(--muted)}

/* ── Layout ──────────────────────────────────────────────────────────────── */
.wrap{max-width:1180px;margin:0 auto;padding:0 var(--s6)}
.wrap-narrow{max-width:860px;margin:0 auto;padding:0 var(--s6)}

/* Section padding VARIES by role — a single constant reads as a template.
   Proximity encodes meaning: these are the between-section values. */
.band{padding:var(--s8) 0}
.band-tight{padding:var(--s7) 0}
.band-loose{padding:var(--s9) 0}
.band-alt{background:var(--surface-alt)}

.rule{border:0;border-top:1px solid var(--line-subtle);margin:0}

/* ── Nav ─────────────────────────────────────────────────────────────────── */
.nav{
  display:flex;align-items:center;gap:var(--s5);height:72px;
  border-bottom:1px solid var(--line-subtle);
}
.nav .brand{
  display:flex;align-items:center;gap:10px;
  font-family:"Bricolage Grotesque",sans-serif;font-weight:800;
  font-size:17px;letter-spacing:-.02em;color:var(--ink-strong);
  text-decoration:none;
}
.nav .brand img{width:24px;height:24px;border-radius:var(--radius)}
.nav a{color:var(--muted);text-decoration:none;font-size:14.5px}
.nav a:hover{color:var(--ink)}
.nav .sp{margin-left:auto}
.nav .cta{
  background:var(--accent);color:var(--on-accent);
  padding:9px 17px;border-radius:var(--radius);font-weight:600;font-size:14px;
}
.nav .cta:hover{background:var(--accent-hover);color:var(--on-accent)}
@media (prefers-color-scheme:dark){
  .nav .cta{background:var(--ink);color:var(--ground)}
  .nav .cta:hover{background:#B9F0E2;color:var(--ground)}
}

/* ── Buttons ─────────────────────────────────────────────────────────────── */
.btn{
  display:inline-block;padding:14px 26px;border-radius:var(--radius);
  font-size:15px;font-weight:600;text-decoration:none;border:0;cursor:pointer;
  background:var(--accent);color:var(--on-accent);
  font-family:inherit;
  transition:background .16s var(--ease), transform .16s var(--ease);
}
.btn:hover{background:var(--accent-hover);color:var(--on-accent);transform:translateY(-1px)}
.btn:active{transform:translateY(0)}
.btn-ghost{
  background:transparent;color:var(--ink);
  box-shadow:inset 0 0 0 1px var(--line-control);
}
.btn-ghost:hover{
  background:transparent;color:var(--accent);
  box-shadow:inset 0 0 0 1px var(--accent);
}
/* The checkout button ships with [disabled] and is enabled once the cart has a
   line in it. Without this it looked identical to an active button, so the only
   feedback for a click was nothing happening. */
.btn:disabled,.btn[disabled]{
  opacity:.45;cursor:not-allowed;pointer-events:none;
}
@media (prefers-color-scheme:dark){
  .btn{background:var(--ink);color:var(--ground)}
  .btn:hover{background:#B9F0E2;color:var(--ground)}
  .btn-ghost{background:transparent;color:var(--ink)}
}
@media (prefers-reduced-motion:reduce){
  .btn{transition:none}
  .btn:hover,.btn:active{transform:none}
}
.cta-row{display:flex;gap:var(--s2) 12px;align-items:center;flex-wrap:wrap}
.btn-block{display:block;width:100%;text-align:center}
/* PayPal and Revolut get equal weight: the page says "pick whichever you
   prefer", so neither is styled as the default. Stacks when too narrow. */
.pay-choice{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:12px}
.pay-choice .btn{text-align:center}

/* ── Screenshot frames ───────────────────────────────────────────────────── */
.shot{
  display:block;width:100%;height:auto;
  border-radius:var(--radius);box-shadow:var(--shadow);
}

/* ── Capability index — a contents page, not a card grid ─────────────────── */
.index-list{
  list-style:none;margin:0;padding:0;
  display:grid;grid-template-columns:repeat(auto-fit,minmax(340px,1fr));
  gap:0 var(--s7);
}
.index-list li{
  padding:var(--s4) 0;
  border-bottom:1px solid var(--line-subtle);
  display:grid;gap:var(--s1);
}
.index-list h3{font-size:17px;font-weight:600;letter-spacing:-.01em}
.index-list p{color:var(--muted);font-size:14.5px;line-height:1.55}

/* ── Split rows ──────────────────────────────────────────────────────────── */
.split{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:var(--s7);align-items:center;
}
.split-copy{display:grid;gap:var(--s4);align-content:start}
.split ul{list-style:none;margin:0;padding:0;display:grid;gap:10px;color:var(--muted)}
.split li{padding-left:var(--s4);position:relative;font-size:15px;line-height:1.5}
.split li::before{
  content:"";position:absolute;left:0;top:.62em;
  width:9px;height:1px;background:var(--accent);
}

/* A main column with a narrower aside that stays in view — the store's product
   and its cart. Distinct from .split, which balances two equal halves. */
.split-aside{
  display:grid;grid-template-columns:minmax(0,1.45fr) minmax(300px,1fr);
  gap:var(--s7);align-items:start;
}
.split-aside > .cart{position:sticky;top:var(--s4)}

/* ── Steps — numbered ONLY because the Organiser genuinely is a sequence ─── */
.steps{list-style:none;margin:0;padding:0;display:grid;gap:var(--s4);counter-reset:step}
.steps li{
  counter-increment:step;display:grid;grid-template-columns:auto 1fr;
  gap:var(--s3);align-items:baseline;
}
.steps li::before{
  content:counter(step);
  font-family:"Bricolage Grotesque",sans-serif;font-weight:800;
  font-size:14px;color:var(--accent);
  font-variant-numeric:tabular-nums;
}
.steps b{display:block;color:var(--ink);font-weight:600;font-size:15.5px}
.steps span{color:var(--muted);font-size:14.5px;line-height:1.55}

/* ── Price ───────────────────────────────────────────────────────────────── */
.price{display:grid;gap:var(--s4);justify-items:start;max-width:46ch}
.price .amount{
  font-family:"Bricolage Grotesque",sans-serif;font-weight:800;
  font-size:clamp(48px,7vw,76px);line-height:.9;letter-spacing:-.045em;
  color:var(--ink-strong);
}
.price .amount span{
  font-family:"Instrument Sans",sans-serif;font-size:17px;font-weight:400;
  color:var(--muted);letter-spacing:0;
}
.price ul{list-style:none;margin:0;padding:0;display:grid;gap:10px;color:var(--muted)}
.price li{padding-left:var(--s4);position:relative;font-size:15px}
.price li::before{
  content:"";position:absolute;left:0;top:.55em;width:9px;height:1px;
  background:var(--accent);
}

/* ── Plans ───────────────────────────────────────────────────────────────── */
.plans{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:var(--s6);align-items:start;
}
.plan{display:grid;gap:var(--s3);align-content:start}
.plan.best{
  background:var(--surface);padding:var(--s5);border-radius:var(--radius);
}
.plan .who{color:var(--muted);font-size:14px}
.plan .cost{
  font-family:"Bricolage Grotesque",sans-serif;font-weight:800;
  font-size:34px;letter-spacing:-.035em;color:var(--ink-strong);line-height:1;
}
.plan ul{list-style:none;margin:0;padding:0;display:grid;gap:9px;
  font-size:14.5px;color:var(--muted)}
.plan li{display:grid;grid-template-columns:18px 1fr;gap:8px;align-items:baseline}
.plan li i{font-style:normal;color:var(--success)}
.plan li.off{color:var(--muted);opacity:.72}
.plan li.off i{color:var(--muted)}

/* ── Comparison table ────────────────────────────────────────────────────────
   Kept as a real <table>. A screen reader then announces "Playlists, Free: no,
   Full: yes", which two side-by-side lists cannot do. That reasoning came from
   the original site and is worth preserving. What changed is only the styling:
   the shell border and rounded corners are gone in favour of rules.
   ------------------------------------------------------------------------- */
.cmp-scroll{overflow-x:auto}
.cmp{width:100%;border-collapse:collapse;font-size:15px;min-width:520px}
.cmp th,.cmp td{
  padding:13px var(--s3);text-align:left;font-weight:400;
  border-bottom:1px solid var(--line-subtle);
}
.cmp thead th{
  font-family:"Bricolage Grotesque",sans-serif;font-weight:800;
  font-size:15px;letter-spacing:-.01em;color:var(--ink-strong);
  border-bottom:1px solid var(--line-control);
}
.cmp th:nth-child(2),.cmp th:nth-child(3),
.cmp td:nth-child(2),.cmp td:nth-child(3){text-align:center;width:132px}
/* The paid column is tinted all the way down so the eye can follow it without
   counting across. */
.cmp td:nth-child(3),.cmp thead th:nth-child(3){background:var(--accent-soft)}
.cmp tr.grp th{
  font-family:"Instrument Sans",sans-serif;
  font-size:13px;font-weight:600;color:var(--muted);
  padding-top:var(--s5);border-bottom:1px solid var(--line-subtle);
}
.cmp .y{color:var(--success);font-weight:600}
.cmp .n{color:var(--muted);opacity:.6}

/* ── FAQ ─────────────────────────────────────────────────────────────────── */
.faq{display:grid}
.faq details{border-bottom:1px solid var(--line-subtle)}
.faq summary{
  cursor:pointer;list-style:none;padding:var(--s4) var(--s5) var(--s4) 0;
  font-weight:600;font-size:16.5px;position:relative;color:var(--ink);
}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{
  content:"";position:absolute;right:var(--s2);top:50%;
  width:9px;height:9px;margin-top:-6px;
  border-right:1.5px solid var(--accent);border-bottom:1.5px solid var(--accent);
  transform:rotate(45deg);transition:transform .18s var(--ease);
}
.faq details[open] summary::after{transform:rotate(-135deg);margin-top:-2px}
.faq summary:hover{color:var(--accent)}
.faq .answer{padding:0 var(--s5) var(--s4) 0;color:var(--muted);max-width:var(--measure)}
@media (prefers-reduced-motion:reduce){
  .faq summary::after{transition:none}
}

/* ── Notice ──────────────────────────────────────────────────────────────── */
.notice{
  background:var(--surface-alt);padding:var(--s4);border-radius:var(--radius);
  font-size:14px;color:var(--muted);max-width:var(--measure);
}

/* ── Footer ──────────────────────────────────────────────────────────────── */
.foot{
  border-top:1px solid var(--line-subtle);
  padding:var(--s7) 0 var(--s6);
  font-size:14px;color:var(--muted);
}
.foot .cols{
  display:grid;grid-template-columns:1.4fr repeat(3,minmax(0,1fr));
  gap:var(--s6);align-items:start;
}
.foot h4{color:var(--ink);margin-bottom:var(--s3)}
.foot ul{list-style:none;margin:0;padding:0;display:grid;gap:9px}
.foot a{color:var(--muted);text-decoration:none}
.foot a:hover{color:var(--accent)}
.foot .base{
  margin-top:var(--s6);padding-top:var(--s4);
  border-top:1px solid var(--line-subtle);
  display:flex;justify-content:space-between;gap:var(--s4);flex-wrap:wrap;
  font-size:13px;
}

/* ── Long-form document flow ─────────────────────────────────────────────────
   The reset above sets p{margin:0} and h1..h4{margin:0}, which is right for the
   marketing pages where layout supplies every gap. It is wrong for the legal
   pages, which are genuinely long-form prose: without this, privacy.html and
   terms.html collapse into one unreadable slab.

   Scope it to .doc so the marketing pages keep their layout-driven spacing.
   -------------------------------------------------------------------------- */
.doc{max-width:var(--measure);color:var(--muted)}
.doc > :first-child{margin-top:0}
.doc h2{font-size:27px;margin:var(--s6) 0 var(--s3)}
.doc h3{font-size:19px;margin:var(--s5) 0 var(--s2)}
.doc h4{margin:var(--s4) 0 var(--s2)}
.doc p{margin:0 0 var(--s3);line-height:1.65}
.doc ul,.doc ol{margin:0 0 var(--s3);padding-left:var(--s4);display:grid;gap:10px}
.doc li{line-height:1.6}
.doc strong,.doc b{color:var(--ink);font-weight:600}
.doc a{color:var(--accent)}
.doc dl{margin:0 0 var(--s3)}
.doc dt{color:var(--ink);font-weight:600;margin-top:var(--s3)}
.doc dd{margin:var(--s1) 0 0}
.doc table{width:100%;border-collapse:collapse;margin:0 0 var(--s4);font-size:15px}
.doc th,.doc td{text-align:left;padding:10px var(--s3);border-bottom:1px solid var(--line-subtle);vertical-align:top}
.doc th{color:var(--ink);font-weight:600}

/* Statutory identity block — trader name, address, email, VAT.
   Regulation 6 of the E-Commerce Regulations requires this to be "easily,
   directly and permanently accessible", so it is given weight, not tucked away. */
.doc .trader{
  background:var(--surface);border-radius:var(--radius);
  padding:var(--s4);margin:0 0 var(--s4);color:var(--ink);line-height:1.9;
}

/* ── Callout — a notice that holds more than one block ───────────────────────
   .notice is a single muted paragraph. This is for the multi-paragraph boxes in
   terms.html (the cancellation-rights box is four paragraphs and a list), where
   the global p{margin:0} would otherwise weld them together.
   -------------------------------------------------------------------------- */
.callout{
  background:var(--surface-alt);border-radius:var(--radius);
  padding:var(--s4);margin:0 0 var(--s4);display:grid;gap:var(--s3);
  max-width:var(--measure);
}
.callout > *{margin:0}
.callout h3,.callout h4{margin:0;color:var(--ink)}
/* A left rail is banned as decoration, and allowed for true semantic state.
   These two carry real meaning: a statutory right, and a legal warning. */
.callout.is-key{box-shadow:inset 3px 0 0 var(--accent);background:var(--accent-soft)}
.callout.is-warn{box-shadow:inset 3px 0 0 var(--warning)}

/* ── Demos — video, not GIF ──────────────────────────────────────────────────
   The GIFs these replace were 5-6 fps, 256-colour and up to 230 KB. WebM/MP4 is
   full colour at a tenth of the weight. media-src is not declared in the CSP, so
   these load under default-src 'self' — same-origin only, which is what we serve.

   width/height attributes on the <video> are REQUIRED: they give the box an
   intrinsic ratio so the page does not reflow when the video loads.

   Autoplay cannot be made conditional in CSS. site.js pauses these and reveals
   controls when prefers-reduced-motion is set; the poster still shows, so the
   section never renders empty.
   -------------------------------------------------------------------------- */
.demo{display:grid;gap:var(--s3);margin:0}
.demo video{
  display:block;width:100%;height:auto;
  border-radius:var(--radius);box-shadow:var(--shadow);
  background:var(--surface-alt);
}
.demo figcaption{font-size:14px;color:var(--muted);max-width:56ch;line-height:1.55}
.demo figcaption b{color:var(--ink);font-weight:600}

/* ── Form controls ───────────────────────────────────────────────────────────
   store.html's checkout is the only form on the site, and it collects a name, an
   email and the two Consumer Contracts Regulations consents. Controls get a real
   border because a control boundary IS semantic — --line-control is the token
   that clears APCA Lc 30 for non-text UI, unlike the decorative --line-subtle.
   -------------------------------------------------------------------------- */
.field{display:grid;gap:6px;margin:0 0 var(--s3)}
.field label{font-size:14px;font-weight:600;color:var(--ink)}
.field .hint{font-size:13px;color:var(--muted)}
.field input,.field textarea,.field select{
  font:inherit;font-size:15px;color:var(--ink);
  background:var(--surface);
  border:1px solid var(--line-control);border-radius:var(--radius);
  padding:11px 13px;width:100%;
  transition:border-color .16s var(--ease),box-shadow .16s var(--ease);
}
.field input:hover,.field textarea:hover{border-color:var(--ink)}
.field input:focus-visible,.field textarea:focus-visible,.field select:focus-visible{
  outline:none;border-color:var(--accent);
  box-shadow:0 0 0 3px var(--accent-soft);
}
.field input[aria-invalid="true"]{border-color:var(--error)}
.field .err{font-size:13px;color:var(--error-text);font-weight:600}
.field .err:empty{display:none}

.check{
  display:grid;grid-template-columns:auto 1fr;gap:var(--s2);
  align-items:start;font-size:14.5px;color:var(--muted);
  line-height:1.5;margin:0 0 var(--s3);cursor:pointer;
}
.check input{width:18px;height:18px;margin:1px 0 0;accent-color:var(--accent);flex:none}
.check b{color:var(--ink);font-weight:600}

/* ── Status messages ─────────────────────────────────────────────────────────
   download.html writes class="msg", "msg ok" or "msg err" onto two <p
   role="status"> elements as the licence code is checked. Without these rules
   all three states rendered as identical plain body text, so "Checking…",
   "that code is not valid" and "here is your download" were indistinguishable —
   on the page a customer reaches immediately after paying.

   Colour alone never carries the meaning: weight changes too, and the elements
   are role="status" so a screen reader announces the text either way.
   -------------------------------------------------------------------------- */
.msg{margin:0;font-size:14.5px;line-height:1.5;color:var(--muted)}
.msg:empty{display:none}
.msg.ok{color:var(--success-text);font-weight:600}
.msg.err{color:var(--error-text);font-weight:600}

/* ── Quantity stepper ────────────────────────────────────────────────────── */
.qty{
  display:inline-flex;align-items:stretch;
  border:1px solid var(--line-control);border-radius:var(--radius);overflow:hidden;
}
.qty button{
  font:inherit;font-size:16px;line-height:1;cursor:pointer;
  background:var(--surface);color:var(--ink);border:0;
  padding:0 14px;transition:background .16s var(--ease);
}
.qty button:hover{background:var(--surface-alt)}
.qty button:disabled{opacity:.4;cursor:not-allowed}
.qty input{
  font:inherit;font-size:15px;font-variant-numeric:tabular-nums;
  width:46px;text-align:center;border:0;border-left:1px solid var(--line-control);
  border-right:1px solid var(--line-control);
  background:var(--ground);color:var(--ink);padding:9px 0;
}

/* A labelled quantity control: the label sits above the stepper. */
.qty-pick{display:grid;gap:6px}
.qty-pick label{font-size:14px;font-weight:600;color:var(--ink)}

/* ── Cart / order summary ────────────────────────────────────────────────────
   store.html's checkout script builds each line with innerHTML, so these class
   names are ITS contract, not a free choice: .cart-item > .info > .name /
   .meta / .rm, then .price. The script is CSP-hashed and left byte-identical,
   so the stylesheet follows the markup it writes.

   .cart-item .price is scoped deliberately. A bare .price is the pricing block
   on the home page — a grid with a 46ch measure — and would otherwise turn a
   single line total into a column.
   -------------------------------------------------------------------------- */
.cart{
  display:grid;gap:0;align-content:start;
  background:var(--surface);padding:var(--s5);border-radius:var(--radius);
}
.cart h3{
  margin:0 0 var(--s3);display:flex;justify-content:space-between;
  align-items:baseline;gap:var(--s3);
}
.cart h3 .count{
  font-family:"Instrument Sans",sans-serif;font-size:13.5px;font-weight:400;
  letter-spacing:0;color:var(--muted);font-variant-numeric:tabular-nums;
}
.cart-empty{color:var(--muted);font-size:14.5px;padding:var(--s2) 0 var(--s3)}
.cart-item{
  display:flex;justify-content:space-between;align-items:flex-start;gap:var(--s3);
  padding:var(--s3) 0;border-bottom:1px solid var(--line-subtle);
}
.cart-item .info{display:grid;gap:2px;justify-items:start}
.cart-item .name{color:var(--ink);font-weight:600;font-size:15px}
.cart-item .meta{font-size:13.5px;color:var(--muted);font-variant-numeric:tabular-nums}
.cart-item .price{
  display:block;max-width:none;color:var(--ink);font-weight:600;
  font-variant-numeric:tabular-nums;white-space:nowrap;
}
.cart .rm{
  font:inherit;font-size:13px;background:none;border:0;cursor:pointer;
  color:var(--muted);text-decoration:underline;padding:0;margin-top:4px;
}
.cart .rm:hover{color:var(--error-text)}
.cart-row{
  display:flex;justify-content:space-between;gap:var(--s3);
  padding:var(--s2) 0;font-size:15px;color:var(--muted);
  font-variant-numeric:tabular-nums;
}
.cart-row.grand{
  color:var(--ink);font-weight:600;margin-top:var(--s2);
  padding-top:var(--s3);border-top:1px solid var(--line-subtle);
}
.cart-row .amt{
  font-family:"Bricolage Grotesque",sans-serif;font-weight:800;
  font-size:22px;letter-spacing:-.02em;color:var(--ink-strong);
}
.cart .btn-block{margin-top:var(--s4)}
.cart .micro{margin-top:var(--s3)}

/* ── Modal ───────────────────────────────────────────────────────────────────
   Opened by adding .show to .modal-back, because that is what store.html's
   checkout script does (classList.add/remove("show")) and that script is
   CSP-hashed and kept byte-identical — so the stylesheet follows it. Closed,
   the backdrop is display:none, which also takes it out of the accessibility
   tree. Focus and Escape handling live in site.js for the same reason.
   -------------------------------------------------------------------------- */
.modal-back{
  position:fixed;inset:0;z-index:100;
  background:rgba(11,13,14,.55);
  display:none;place-items:center;padding:var(--s4);
}
.modal-back.show{display:grid}
.modal-back[hidden]{display:none}
.modal{
  background:var(--surface);border-radius:var(--radius);
  padding:var(--s5);width:100%;max-width:540px;
  max-height:88vh;overflow-y:auto;box-shadow:var(--shadow);
}
.modal h2{font-size:24px;margin:0 0 var(--s2)}
.modal .lead{font-size:15.5px;margin:0 0 var(--s4)}
.modal .modal-actions{
  display:flex;gap:12px;flex-wrap:wrap;align-items:center;
  margin-top:var(--s4);padding-top:var(--s4);
  border-top:1px solid var(--line-subtle);
}
.modal-close{
  font:inherit;font-size:14px;background:none;border:0;cursor:pointer;
  color:var(--muted);text-decoration:underline;padding:0;
}
.modal-close:hover{color:var(--ink)}
.modal p{margin:0 0 var(--s3)}
.modal .callout p{margin:0}
.modal .modal-step{
  font-size:16px;letter-spacing:-.01em;margin:var(--s4) 0 var(--s2);
}
.modal .msg{margin:0 0 var(--s3)}
.modal .btn-block{margin-top:var(--s2)}
.callout .check{margin:0}

/* ── Responsive ──────────────────────────────────────────────────────────── */
@media (max-width:900px){
  .wrap,.wrap-narrow{padding:0 var(--s4)}
  .band{padding:var(--s7) 0}
  .band-loose{padding:var(--s8) 0}
  .split,.plans,.split-aside{grid-template-columns:1fr;gap:var(--s5)}
  .split-aside > .cart{position:static}
  .index-list{grid-template-columns:1fr;gap:0}
  .foot .cols{grid-template-columns:1fr 1fr;gap:var(--s5)}
  .nav{gap:var(--s3)}
  /* :not(.brand) matters — the brand lockup IS an <a>, so without it the site
     logo vanished on every page below 900px and the header showed a lone buy
     button floating on the right. */
  .nav a:not(.cta):not(.brand){display:none}
}
@media (max-width:560px){
  .foot .cols{grid-template-columns:1fr}
}
