/* ============================================================================
   AppleVersus — design tokens. THE single source of visual truth.
   ----------------------------------------------------------------------------
   Every colour, typeface, size, space and radius used anywhere on the site is
   declared here and nowhere else. components.css and the generated markup may
   only reference these via var(). `python3 site/build.py` FAILS the build if a
   raw colour literal appears outside this file, so this rule is enforced rather
   than merely intended.

   To restyle the whole site: change values here and re-run the build. Every
   page in every language updates, because every page is composed from the same
   tokens and the same chrome.

   Documented in docs/wiki/concepts/brand-system.md
   ========================================================================== */

:root{
  /* -- Brand ---------------------------------------------------------------
     Signal blue. The subject is radio spectrum and cellular support, so the
     accent is the blue of a signal indicator, not a decorative brand colour. */
  --accent:#0057D9;
  --accent-soft:#E8F0FE;

  /* -- Neutrals -------------------------------------------------------------
     Biased slightly cool/blue toward the accent rather than pure grey, so the
     page reads as one palette. Never introduce a neutral outside this ramp. */
  --ground:#FBFCFD;
  --surface:#FFFFFF;
  --raised:#F3F6F9;
  --ink:#0B1117;
  --ink-2:#39454F;
  --muted:#5A6672;
  --line:#E3E8EE;
  --line-2:#CFD8E1;

  /* -- Semantic -------------------------------------------------------------
     Meaning, not decoration, and deliberately separate from the accent.
     `no` is neutral grey, never red: a market lacking mmWave is an absence,
     not an error, and colouring it red would editorialise the data.

     `diff` marks "these two markets are not the same here" — the payload of the
     whole site. It is deliberately the accent hue: blue is the familiar colour
     for "look here", it already marks rare bands, and one highlight colour
     across the site beats two that compete. An earlier draft used a dark orange
     for this; it read as brown, i.e. as an error rather than a highlight. */
  --yes:#0B7A55;      --yes-soft:#E4F4EC;
  --no:#8A96A3;       --no-soft:#EFF2F5;
  --diff:#0057D9;     --diff-soft:#F1F6FE;

  --shadow-1:0 1px 2px rgba(0,0,0,.08);
  --scrim:rgba(0,0,0,.08);

  /* -- Type ----------------------------------------------------------------- */
  --display:"Manrope",system-ui,-apple-system,"Segoe UI",sans-serif;
  --body:"IBM Plex Sans",system-ui,-apple-system,"Segoe UI",sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace;

  --fs-micro:11.5px;
  --fs-label:12px;
  --fs-small:13px;
  --fs-body-s:14.5px;
  --fs-body:16px;
  --fs-lead:clamp(16.5px,4.2vw,20px);
  --fs-h3:17px;
  --fs-h2:clamp(23px,5.6vw,32px);
  --fs-h1:clamp(32px,8.5vw,60px);

  --lh-tight:1.15;
  --lh-body:1.6;
  --track-tight:-.035em;
  --track-h:-.022em;
  --track-caps:.09em;

  /* -- Space, radius, layout ------------------------------------------------ */
  --s-1:5px;  --s-2:8px;  --s-3:12px; --s-4:14px; --s-5:18px;
  --s-6:22px; --s-7:26px; --s-8:32px; --s-9:44px; --s-10:60px;
  --r-sm:6px; --r-md:11px; --r-lg:14px; --r-pill:999px;
  --wrap:1120px;
  --w-glabel:11rem;      /* generation table's field-label column */
  --w-delta:10rem;      /* price-difference column; the comparer header and the
                           rows must share one track or the columns stop lining up */
  --gutter:20px;
  --gutter-md:28px;
}

/* Dark theme. Three viewer states must all resolve, so tokens are redefined
   for prefers-color-scheme (the un-stamped default) AND for an explicit stamp.
   Only token values change below — never component rules. */
:root:not([data-theme="light"]){
  @media (prefers-color-scheme:dark){
    --accent:#5C9DFF;   --accent-soft:#14263D;
    --ground:#0A0E12;   --surface:#121920;  --raised:#18212A;
    --ink:#EDF1F5;      --ink-2:#C2CCD6;    --muted:#94A2AF;
    --line:#232C35;     --line-2:#33404C;
    --yes:#4CC79A;      --yes-soft:#102A22;
    --no:#7B8794;       --no-soft:#1A222A;
    --diff:#5C9DFF;     --diff-soft:#111C2B;
    --shadow-1:0 1px 2px rgba(0,0,0,.5);
    --scrim:rgba(0,0,0,.5);
  }
}
[data-theme="dark"]{
  --accent:#5C9DFF;   --accent-soft:#14263D;
  --ground:#0A0E12;   --surface:#121920;  --raised:#18212A;
  --ink:#EDF1F5;      --ink-2:#C2CCD6;    --muted:#94A2AF;
  --line:#232C35;     --line-2:#33404C;
  --yes:#4CC79A;      --yes-soft:#102A22;
  --no:#7B8794;       --no-soft:#1A222A;
  --diff:#5C9DFF;     --diff-soft:#111C2B;
  --shadow-1:0 1px 2px rgba(0,0,0,.5);
  --scrim:rgba(0,0,0,.5);
}

/* ============================================================================
   AppleVersus — components. Tokens only: no colour, typeface or size literal
   may appear in this file. The build refuses to run if one does.
   Order: reset -> chrome -> page furniture -> topic components -> responsive.
   ========================================================================== */

*{box-sizing:border-box}
body{margin:0;background:var(--ground);color:var(--ink);font-family:var(--body);
  font-size:var(--fs-body);line-height:var(--lh-body);-webkit-font-smoothing:antialiased}
.wrap{width:100%;max-width:var(--wrap);margin-inline:auto;padding-inline:var(--gutter)}
h1,h2,h3{font-family:var(--display);text-wrap:balance;margin:0;letter-spacing:var(--track-h)}
p{margin:0}
a{color:var(--accent)}
a:focus-visible,select:focus-visible,button:focus-visible{
  outline:2px solid var(--accent);outline-offset:2px}
.mono{font-family:var(--mono);font-variant-numeric:tabular-nums}
.skip{position:absolute;left:-9999px}
.skip:focus{left:var(--s-3);top:var(--s-3);z-index:50;background:var(--surface);
  color:var(--ink);padding:var(--s-2) var(--s-4);border-radius:var(--r-md);
  border:1px solid var(--line-2)}

/* ---- chrome: identical on every page, composed from shared functions ---- */
header{position:sticky;top:0;z-index:20;
  background:color-mix(in srgb,var(--ground) 88%,transparent);
  backdrop-filter:saturate(180%) blur(14px);border-bottom:1px solid var(--line)}
.bar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:var(--s-2) var(--s-3);padding-block:var(--s-3);min-height:52px}
.brand{font-family:var(--display);font-weight:800;font-size:var(--fs-body);
  letter-spacing:var(--track-tight);color:var(--ink);text-decoration:none;white-space:nowrap}
.brand span{color:var(--accent)}
.navset{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-2)}
/* Currency is a page-level setting: it changes every price on the page, so it sits
   in the chrome beside the language switcher rather than inside one section. */
.fxsel{width:auto;font-size:var(--fs-small);font-weight:600;
  padding:var(--s-1) var(--s-6) var(--s-1) var(--s-3);border-radius:var(--r-pill);
  background-color:var(--raised);border-color:var(--line);
  background-position:calc(100% - 15px) 13px,calc(100% - 10px) 13px}
.langs{display:flex;gap:2px;background:var(--raised);border-radius:var(--r-pill);padding:3px}
.langs a{font-size:var(--fs-small);font-weight:600;text-decoration:none;color:var(--muted);
  padding:var(--s-1) var(--s-3);border-radius:var(--r-pill);line-height:1}
.langs a[aria-current]{background:var(--surface);color:var(--ink);box-shadow:var(--shadow-1)}

footer{padding-block:var(--s-8) var(--s-9);color:var(--muted);
  font-size:var(--fs-small);line-height:var(--lh-body)}
footer .disc{max-width:72ch;margin-bottom:var(--s-4)}
footer .fb{font-family:var(--display);font-weight:800;color:var(--ink);
  letter-spacing:var(--track-tight)}
footer .flinks{display:flex;flex-wrap:wrap;gap:var(--s-4);margin-bottom:var(--s-4)}

/* ---- page furniture ---- */
.hero{padding-block:var(--s-9) var(--s-8);border-bottom:1px solid var(--line)}
.kicker{font-size:var(--fs-label);font-weight:700;letter-spacing:var(--track-caps);
  text-transform:uppercase;color:var(--accent);margin-bottom:var(--s-4)}
.hero h1{font-size:var(--fs-h1);font-weight:800;line-height:1.05;
  letter-spacing:var(--track-tight);margin-bottom:var(--s-5)}
.lead{font-size:var(--fs-lead);color:var(--ink-2);max-width:60ch;line-height:1.55}
/* The one jump out of the hero. Tinted rather than solid: --accent on --accent-soft
   keeps its contrast in both themes, where text on a solid accent fill would need a
   fixed light colour that inverts wrongly in one of them. */
.jump{display:inline-flex;align-items:center;gap:var(--s-2);margin-top:var(--s-6);
  padding:var(--s-3) var(--s-5);border-radius:var(--r-pill);background:var(--accent-soft);
  color:var(--accent);text-decoration:none;font-weight:700;font-size:var(--fs-small);
  border:1px solid transparent}
.jump:hover{border-color:var(--accent)}
/* The header is sticky, so an anchored section must clear it or it lands underneath. */
#compare{scroll-margin-top:var(--s-8)}
.stamp{display:inline-flex;align-items:center;gap:var(--s-2);margin-top:var(--s-6);
  font-size:var(--fs-small);color:var(--muted);background:var(--raised);
  border:1px solid var(--line);border-radius:var(--r-pill);padding:var(--s-1) var(--s-4)}
.dot{width:6px;height:6px;border-radius:50%;background:var(--yes);flex:none}
/* Coverage is a property of the data, so it is stated where the date is stated —
   a reader must not have to infer the sample from which cards happen to exist. */
.stampnote{margin-top:var(--s-2);font-size:var(--fs-small);color:var(--muted);
  max-width:62ch;line-height:1.5}
section{padding-block:var(--s-9);border-bottom:1px solid var(--line)}
section:last-of-type{border-bottom:0}
.eyebrow{font-size:var(--fs-label);font-weight:700;letter-spacing:var(--track-caps);
  text-transform:uppercase;color:var(--muted);margin-bottom:var(--s-2)}
h2{font-size:var(--fs-h2);font-weight:800;margin-bottom:var(--s-2);line-height:var(--lh-tight)}
.sub{color:var(--ink-2);max-width:58ch;margin-bottom:var(--s-7)}
/* A trailing .sub closes a section, so it carries no bottom gap. This exists as a class
   rather than a style attribute because style-src is 'self': an inline style would need
   'unsafe-inline' in the policy, for one margin. */
.sub.last{margin-bottom:0}
.prose{max-width:65ch;display:grid;gap:var(--s-4);color:var(--ink-2)}
.prose h3{font-size:var(--fs-h3);font-weight:700;color:var(--ink);margin-top:var(--s-3)}

/* ---- findings: each is a selector for the markets it applies to ---- */
.finds{display:grid;gap:var(--s-4)}
.find{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:var(--s-6);text-align:left;font:inherit;color:inherit;cursor:pointer;
  display:flex;flex-direction:column;gap:var(--s-1);
  transition:border-color .12s ease,background-color .12s ease}
.find:hover{border-color:var(--line-2)}
.find h3{font-size:var(--fs-h3);font-weight:700;letter-spacing:var(--track-h)}
.find p{font-size:var(--fs-body-s);color:var(--ink-2);line-height:1.55}
.find .mk{margin-top:auto;padding-top:var(--s-3);font-size:var(--fs-label);
  font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--muted)}
.find[aria-pressed="true"]{border-color:var(--diff);background:var(--diff-soft)}
.find[aria-pressed="true"] .mk{color:var(--diff)}
.find:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* ---- market cards ---- */
.cards.filtering .card{opacity:.38;transition:opacity .12s ease}
.cards.filtering .card.hit{opacity:1;border-color:var(--diff);
  box-shadow:inset 0 0 0 1px var(--diff)}

/* ---- comparer ---- */
.picker{display:grid;gap:var(--s-3);margin-bottom:var(--s-7)}
/* The selects name the table's columns, so they sit on the table's grid and
   inside its padding. A plain 50/50 picker parts company with the columns the
   moment the difference column exists. */
.picker.withdelta{grid-template-columns:1fr 1fr}
.field{display:flex;flex-direction:column;gap:var(--s-1)}
.field label{font-size:var(--fs-label);font-weight:700;letter-spacing:.07em;
  text-transform:uppercase;color:var(--muted)}
select{font-family:var(--body);font-size:var(--fs-body);font-weight:600;color:var(--ink);
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-md);
  padding:var(--s-3) var(--s-4);width:100%;appearance:none;
  background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),
                   linear-gradient(135deg,currentColor 50%,transparent 50%);
  background-position:calc(100% - 20px) 22px,calc(100% - 15px) 22px;
  background-size:5px 5px,5px 5px;background-repeat:no-repeat}
.rows{display:flex;flex-direction:column;border:1px solid var(--line);
  border-radius:var(--r-lg);overflow:hidden;background:var(--surface)}
/* The two market names belong here once, not repeated on every row.
   Deliberately NOT position:sticky — .rows sets overflow:hidden to clip its
   rounded corners, which makes it the sticky containing block; the header then
   detaches, leaves a gap at the top of the container and paints over the first
   row's label. Static is correct here. */
.cmphead{display:grid;grid-template-columns:1fr 1fr;gap:var(--s-3);
  padding:var(--s-3) var(--s-5);background:var(--raised);
  border-bottom:1px solid var(--line-2)}
.cmphead span{font-size:var(--fs-label);font-weight:700;letter-spacing:.05em;
  text-transform:uppercase;color:var(--ink);white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
.row{padding:var(--s-5);border-bottom:1px solid var(--line)}
.row:last-child{border-bottom:0}
.row.differ{background:var(--diff-soft);box-shadow:inset 3px 0 0 var(--diff)}
.rowhead{display:flex;align-items:center;justify-content:space-between;gap:var(--s-2);
  margin-bottom:var(--s-3)}
.rowhead .name{font-size:var(--fs-label);font-weight:700;letter-spacing:.07em;
  text-transform:uppercase;color:var(--muted)}
.tag{font-size:var(--fs-micro);font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  padding:3px var(--s-2);border-radius:var(--r-sm);background:var(--no-soft);color:var(--no);
  white-space:nowrap}
/* A word, not a glyph: a dot inside a ring reads as an unselected radio button. */
.tag.diff{background:var(--diff-soft);color:var(--diff)}
.pair{display:grid;grid-template-columns:1fr 1fr;gap:var(--s-3)}
/* The price difference sits in a third column, under the DIFFERS tag it belongs to.
   Mobile wins: below 640px a third money column would crush all three, so the deltas
   span the full width underneath instead of squeezing the prices they describe. */
.pair.withdelta .delta{grid-column:1/-1}
@media (min-width:640px){
  .pair.withdelta,.cmphead.withdelta{grid-template-columns:1fr 1fr var(--w-delta)}
  .pair.withdelta .delta{grid-column:auto}
}
.delta .plines{gap:var(--s-2)}
.dline{text-align:right;font-family:var(--mono);font-variant-numeric:tabular-nums;
  font-size:var(--fs-small);font-weight:600;color:var(--diff);white-space:nowrap}
/* DIFFERS sits directly above these figures, so its TEXT has to share their right
   edge. The badge's own horizontal padding would otherwise inset it by that much. */
.rowhead .tag{margin-right:calc(-1 * var(--s-2))}
.cell{min-width:0}
.cell .val{font-size:var(--fs-body-s);font-weight:600;line-height:1.4;word-break:break-word}
.val.is-yes{color:var(--yes)} .val.is-no{color:var(--no)}
.val .extra{color:var(--diff);font-size:var(--fs-small);font-weight:700}
.val .cap{color:var(--muted)}
.samenote{padding:var(--s-5);background:var(--yes-soft);color:var(--yes);
  font-weight:600;font-size:var(--fs-body-s)}

/* Conversion is opt-in; this explains it, and only appears once it is switched on. */
.fxnote{margin-bottom:var(--s-5);padding:var(--s-3) var(--s-4);background:var(--raised);
  border:1px solid var(--line);border-left:3px solid var(--diff);
  border-radius:var(--r-md);font-size:var(--fs-small);color:var(--ink-2);line-height:1.5}
/* Two lines, never inline: inline, the second figure overflowed the price column and
   painted over the neighbouring one. `pri` is whatever currency the reader chose, so
   the bold figure reads the same everywhere and the columns stay comparable.
   NOT scoped to .amt -- the comparison rows use the same pair, and scoping it to the
   card class left them running together as "HKD 9,403USD 1,199". */
.pri{display:block;white-space:nowrap}
.sec{display:block;white-space:nowrap;color:var(--muted);font-weight:400;
  font-size:var(--fs-micro);margin-top:1px}
/* An empty placeholder still has to occupy its line, or the alignment it exists to
   preserve collapses. */
.sec:empty::before{content:"\00a0"}

/* Comparison price row: capacity label and figures on a shared baseline grid, so the
   four capacities line up down the column instead of running as one paragraph. */
.plines{display:grid;gap:var(--s-2)}
.pline{display:grid;grid-template-columns:4.2em 1fr;gap:var(--s-2);align-items:baseline}
.pline .cap{font-size:var(--fs-micro);font-weight:700;color:var(--muted);
  letter-spacing:.04em}
.pline .pv{min-width:0;font-family:var(--mono);font-variant-numeric:tabular-nums;
  font-size:var(--fs-small);font-weight:600}
/* The verdict: the answer the reader came for, so it reads a step above body text and
   carries the accent rule that marks it as the page's conclusion rather than a note. */
.verdict{border-left:3px solid var(--accent);padding-left:var(--s-4);max-width:68ch}
.verdict p{font-size:var(--fs-lead);color:var(--ink)}



/* Rival rows. The incomparable ones are marked, not hidden: the point of the row is
   that both makers published something and the two things are not the same quantity. */
.rrow{padding:var(--s-5);border-bottom:1px solid var(--line)}
.rrow:last-child{border-bottom:0}
.rrow.incomp{background:var(--raised)}
.rv{font-family:var(--mono);font-size:var(--fs-small);font-weight:600;color:var(--ink)}
.rmiss{font-size:var(--fs-small);color:var(--muted)}
.tag.rincomp{background:var(--no-soft);color:var(--muted)}
.tag.rsame{background:var(--yes-soft);color:var(--yes)}
.rnote{margin-top:var(--s-3);font-size:var(--fs-small);color:var(--ink-2);
  line-height:1.5;max-width:70ch}

/* Generation table: one row per field, one column per model. A label column plus N
   equal columns, so every generation gets the same width and the eye can run across a
   row. Scrolls sideways below 640px rather than crushing four columns onto a phone,
   because a squeezed comparison is not a comparison. */
.gtable{border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;
  background:var(--surface)}
.grow{display:grid;grid-template-columns:var(--w-glabel) repeat(4,1fr);
  border-bottom:1px solid var(--line)}
.grow:last-child{border-bottom:0}
.ghrow{background:var(--raised);border-bottom:1px solid var(--line-2)}
.glabel{padding:var(--s-4) var(--s-5);font-size:var(--fs-label);font-weight:700;
  letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
.gcell{padding:var(--s-4) var(--s-5);min-width:0;display:flex;flex-direction:column;
  gap:2px;justify-content:center}
.ghead{font-size:var(--fs-label);font-weight:700;color:var(--ink);letter-spacing:.03em}
.gv{font-family:var(--mono);font-size:var(--fs-small);font-weight:600;
  font-variant-numeric:tabular-nums;color:var(--ink)}
.gw{font-size:var(--fs-micro);color:var(--muted)}
@media (max-width:639px){
  .gwrap{overflow-x:auto}
  .gtable{min-width:44rem}
}

/* Topic cards: the index, and the "See also" block at the foot of every topic. One
   component for both, so a topic looks the same wherever it is offered. */
.tcards{display:grid;gap:var(--s-3)}
.tcard{display:block;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-lg);padding:var(--s-5);text-decoration:none;color:inherit}
.tcard:hover{border-color:var(--accent)}
.tcard h3{font-size:var(--fs-h3);font-weight:800;letter-spacing:var(--track-h);
  margin-bottom:var(--s-2)}
.tcard p{color:var(--ink-2)}
.tcard .mk{display:block;margin-top:var(--s-3);font-family:var(--mono);
  font-size:var(--fs-micro);color:var(--muted)}
@media (min-width:640px){.tcards{grid-template-columns:1fr 1fr}}

.cards{display:grid;gap:var(--s-3);scroll-margin-top:var(--s-8)}
.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:var(--s-5);display:flex;flex-direction:column;gap:var(--s-3)}
.ctop{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-2)}
.ctop h3{font-size:var(--fs-h3);font-weight:800;letter-spacing:var(--track-h)}
.ctop .sfx{font-family:var(--mono);font-size:var(--fs-label);color:var(--muted)}
.chips{display:flex;flex-wrap:wrap;gap:var(--s-1)}
.chip{font-size:var(--fs-label);font-weight:600;padding:var(--s-1) var(--s-2);
  border-radius:var(--r-sm);background:var(--no-soft);color:var(--muted);white-space:nowrap}
.chip.on{background:var(--yes-soft);color:var(--yes)}
.chip.warn{background:var(--diff-soft);color:var(--diff);box-shadow:inset 0 0 0 1px currentColor}
.chip.mono{font-family:var(--mono);font-size:var(--fs-micro)}
/* Two columns, not four. Six-digit JPY ("429,800") does not fit a quarter of a
   card at desktop width, and word-break split it mid-number — a price broken
   across lines is worse than one that wraps to a second row. */
/* Grouped by model, one capacity ladder each, stacked. Two side-by-side columns were
   tried and fail: at >=1000px the cards go 4-up, leaving ~180px of card width, which
   wraps "Pro Max" onto two lines and collides the label of one column with the figures
   of the next. A card is too narrow to hold two money columns. */
.plist{display:grid;gap:var(--s-4);
  border-top:1px solid var(--line);padding-top:var(--s-3)}
.pgrp{min-width:0}
.pgh{font-size:var(--fs-micro);font-weight:800;color:var(--muted);letter-spacing:.08em;
  text-transform:uppercase;margin-bottom:var(--s-2)}
/* Tax status belongs beside the number it qualifies, not in a section further down
   that a reader scanning prices will never connect back to this card. */
.pfoot{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--s-1) var(--s-2);
  margin-top:auto;padding-top:var(--s-1)}
.taxlab{font-size:var(--fs-micro);font-weight:600;padding:2px var(--s-2);
  border-radius:var(--r-sm);background:var(--no-soft);color:var(--muted)}
.taxlab.tax-excl{background:var(--diff-soft);color:var(--diff);
  box-shadow:inset 0 0 0 1px currentColor}
.taxlab.tax-incl{background:var(--yes-soft);color:var(--yes)}

/* ---- notes and sources ---- */
.note{background:var(--raised);border:1px solid var(--line);
  border-left:3px solid var(--diff);border-radius:var(--r-md);padding:var(--s-5)}
.note h3{font-size:var(--fs-body-s);font-weight:700;margin-bottom:var(--s-1)}
.note p{font-size:var(--fs-body-s);color:var(--ink-2);line-height:1.55}
.srcs{display:grid;gap:var(--s-2);margin-bottom:var(--s-5)}
.src{font-size:var(--fs-body-s);color:var(--ink-2)}
.src b{display:block;font-size:var(--fs-micro);font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;color:var(--muted);margin-bottom:3px}
.src code{font-family:var(--mono);font-size:var(--fs-label);color:var(--ink-2);
  word-break:break-all}

/* ---- responsive: mobile is the base; wider screens add columns ---- */
@media (min-width:640px){
  .wrap{padding-inline:var(--gutter-md)}
  .finds{grid-template-columns:1fr 1fr}
  .picker{grid-template-columns:1fr 1fr;gap:var(--s-3);
    padding-inline:calc(var(--s-5) + 1px)}
  .picker.withdelta{grid-template-columns:1fr 1fr var(--w-delta)}
  .cards{grid-template-columns:1fr 1fr}
}
@media (min-width:1000px){
  section{padding-block:var(--s-10)}
  .hero{padding-block:70px var(--s-10)}
  .finds{grid-template-columns:repeat(4,1fr)}
  .cards{grid-template-columns:repeat(4,1fr)}
}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
