/* ⚠️ WEIGHT CEILING IS 600, EVERYWHERE ON THIS SURFACE. Measured 2026-08-11 by
   feral-ui-check on the painted welcome screen: the four facts — including "No
   obligation to post", the one the whole programme's defensibility rests on —
   were painting at Inter 700 against an estate ceiling of 600. Semibold already
   carries every one of them; 700 was reach, not emphasis. Eleven rules changed.
   Do not reintroduce a 700 here without moving the law first. */
/* ════════════════════════════════════════════════════════════════════════
   feral·seed — the creator's store.  THE STOREFRONT SYSTEM, DARK.

   🚨 DARK IS THE DESIGN, NOT A THEME. Harry, 2026-08-10: *"should be dark mode
   not white"* — and the standing brief for the 2026 storefront is **"the
   cleanest dark mode ever"**: premium-quiet like Bottega, ALD or Apple in
   dark, NOT club-flyer-busy. So there is no light variant and no
   `prefers-color-scheme` fork: one ground, decided, the same for everybody.
   `color-scheme: dark` is set so the browser's own form controls, scrollbars
   and search-cancel glyphs come out dark too rather than punching white holes
   in it.

   ⚠️ AND IT IS WHY THE PRODUCT PHOTOGRAPHY WORKS HERE. FERAL shoots on black.
   On a near-black ground the image frame dissolves and the garment floats —
   which is the whole Bottega effect, free, and the single strongest argument
   for this not being a preference.

   THE RULES THAT SURVIVE THE FLIP:
     · Inter for everything. Anton ONLY on the two display lines — the welcome
       headline and the ending. Nowhere else, ever.
     · 🚨 NO MONOSPACE ANYWHERE A CUSTOMER CAN SEE. Not for prices, not for
       order numbers, not for "tabular" reasons. `font-variant-numeric:
       tabular-nums` does that job without leaving Inter.
     · RED IS FOUR THINGS AND NOTHING ELSE: the period after a headline, the
       dot inside the primary CTA, the one key fact, and a live error. On a
       dark ground red reads twice as loud as it did on white, so the budget
       got SMALLER on the flip, not larger — the meter and the "−80%" both
       gave theirs up.
     · Whitespace over decoration. No glow, no gradient, no chrome. Every
       element earns its place.

   NO HEX LITERAL OUTSIDE THE :root BLOCK.
   ════════════════════════════════════════════════════════════════════════ */

@font-face{font-family:'Inter';src:url('font/inter-var.woff2') format('woff2');
  font-weight:100 900;font-style:normal;font-display:swap}
@font-face{font-family:'Anton';src:url('font/anton.woff2') format('woff2');
  font-weight:400;font-style:normal;font-display:swap}

:root{
  color-scheme:dark;

  /* ground — four steps, all near-black, so a card is a whisper not a slab */
  --paper:#08080a; --well:#0e0e11; --sunk:#141417; --card:#0d0d10;
  --line:rgba(255,255,255,.075); --line2:rgba(255,255,255,.17);

  /* ink — every one measured ≥4.5:1 on --paper */
  --ink:#f4f4f5; --sec:#a8a8b0; --mut:#8a8a93; --faint:#7d7d86;
  --on-ink:#08080a;

  /* the one accent */
  --red:#ff0033; --red-soft:rgba(255,0,51,.13); --red-line:rgba(255,0,51,.36);

  /* one status register, kept apart from the accent */
  --good:#3ad48c; --good-soft:rgba(58,212,140,.12);
  --warn:#f0b429; --warn-soft:rgba(240,180,41,.12);

  --sans:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  --display:'Anton','Inter',sans-serif;

  /* ⚠️ FOUR VALUES, AND `50%` IS NOT A FIFTH. A circle on a square box is
     `--r-pill` by another spelling, and writing it both ways made
     `feral-ui-check` count five distinct radii against a limit of four — a real
     law firing on a real inconsistency, even though nothing looked wrong. Every
     `border-radius:50%` on this surface is now the token. */
  --r-s:8px; --r:12px; --r-l:16px; --r-pill:999px;
  /* ⚠️ SHADOWS BARELY EXIST ON A NEAR-BLACK GROUND — depth here is carried by
     the four surface steps and by --line, not by a glow. These are kept only
     for things that genuinely float above the page. */
  --sh-1:0 1px 2px rgba(0,0,0,.6);
  --sh-2:0 8px 28px -10px rgba(0,0,0,.8);
  --sh-3:0 24px 64px -20px rgba(0,0,0,.9);
  --ease:cubic-bezier(.2,.7,.3,1);
  --pad:clamp(20px,5vw,56px);
  /* the phone's bottom tab bar — read by `.bar` and `#main` so the three
     cannot drift apart. Desktop never paints it. */
  --nav:56px;
  --bar:clamp(72px,14vw,84px);
}

*,*::before,*::after{box-sizing:border-box}
html,body{margin:0;padding:0}
html{-webkit-text-size-adjust:100%}
body{
  background:var(--paper); color:var(--ink);
  font:400 16px/1.55 var(--sans);
  font-variant-numeric:tabular-nums;
  -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
  overscroll-behavior-y:none;
}
img{display:block;max-width:100%}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
a{color:inherit}
:focus-visible{outline:2px solid var(--ink);outline-offset:3px;border-radius:4px}
/* ⚠️ THE HEADING IS FOCUSED BY SCRIPT ON EVERY STAGE CHANGE so a keyboard or
   screen-reader user lands on the new screen instead of at the top of the
   document. Chrome paints a focus ring for that programmatic focus, which drew
   a box around "Choose your pieces" on arrival. It is not tabbable, so
   suppressing the ring costs nobody anything. */
[tabindex="-1"]:focus,[tabindex="-1"]:focus-visible{outline:none}

/* ── shared furniture ───────────────────────────────────────────────── */
.wrap{max-width:1180px;margin:0 auto;padding:0 var(--pad)}
.eyebrow{font:600 11px/1 var(--sans);letter-spacing:.15em;text-transform:uppercase;color:var(--sec)}
.display{font:400 clamp(34px,8.5vw,68px)/.96 var(--display);letter-spacing:-.005em;
  text-transform:uppercase;margin:0}
.display .dot{color:var(--red)}
.h2{font:600 clamp(19px,2.6vw,24px)/1.2 var(--sans);letter-spacing:-.012em;margin:0}
.lede{font:400 clamp(16px,2vw,18px)/1.6 var(--sans);color:var(--sec);margin:0}

.btn{display:inline-flex;align-items:center;justify-content:center;gap:10px;
  /* ⚠️ AN `<a class="btn">` IS STILL A LINK TO THE BROWSER and picks up the UA's
     underline inside the pill — measured on "Message Andie" and on every
     `hey@feralclo.com` ghost button on this surface. A button is a shape, not
     a run of text; the underline is the one thing that stops it reading as one. */
  text-decoration:none;
  background:var(--ink);color:var(--on-ink);border-radius:var(--r-pill);
  padding:16px 28px;font:600 15px/1 var(--sans);letter-spacing:.005em;
  transition:transform .18s var(--ease), box-shadow .18s var(--ease), background .18s var(--ease);
  box-shadow:var(--sh-1);white-space:nowrap}
.btn:hover{transform:translateY(-1px);box-shadow:var(--sh-2)}
.btn:active{transform:translateY(0)}
.btn[disabled]{opacity:.4;pointer-events:none}
.btn .pip{width:6px;height:6px;border-radius:var(--r-pill);background:var(--red)}
.btn.ghost{background:transparent;color:var(--ink);box-shadow:none;
  border:1px solid var(--line2);padding:15px 24px}
.btn.ghost:hover{border-color:var(--ink)}
/* ⚠️ 38px is not a tap target. The small button is used for tracking and
   for a returning creator's last checkout — both on a phone, both once. */
.btn.sm{padding:13px 18px;min-height:44px;font-size:14px}

/* ══ THE PROGRESS RAIL ══════════════════════════════════════════════════
   Law 12: every stage of a multi-step journey looks like its own job. This
   is the only thing that persists across all three, so it is the only thing
   that says where you are. */
.top{position:sticky;top:0;z-index:40;background:color-mix(in srgb,var(--paper) 82%,transparent);
  backdrop-filter:saturate(1.6) blur(14px);border-bottom:1px solid var(--line)}
.topin{display:flex;align-items:center;gap:16px;height:60px}
.mark{display:flex;align-items:center;gap:10px;min-height:44px;padding-right:8px;text-decoration:none;font:600 12px/1 var(--sans);
  letter-spacing:.16em;text-transform:uppercase;color:var(--ink)}
.mark svg{height:14px;width:auto;fill:currentColor;flex:none}
.steps{display:flex;align-items:center;gap:6px;margin-left:auto}
.step{display:flex;align-items:center;gap:7px;font:600 12px/1 var(--sans);color:var(--faint);
  min-height:44px;padding:7px 6px;transition:color .2s var(--ease)}
.step .n{width:19px;height:19px;border-radius:var(--r-pill);display:grid;place-items:center;
  border:1px solid var(--line2);font-size:10.5px;transition:all .2s var(--ease)}
.step.on{color:var(--ink)}
.step.on .n{background:var(--ink);color:var(--on-ink);border-color:var(--ink)}
.step.done{color:var(--sec)}
.step.done .n{background:var(--sunk);border-color:transparent}
.step .sep{width:14px;height:1px;background:var(--line2)}
/* ⚠️ the label goes on a phone, so the step must keep a thumb-sized box of its
   own — a 31px target is the commonest way a 44px rule is passed on desktop
   and broken on the device it was written for. */
/* ⚠️ AND THE WORDMARK KEEPS ITS SECOND WORD AGAIN. It was dropped below 560px
   because 390px had to carry a mark, five tabs AND the market button — 55px of
   a 350px row that the tabs needed more. The tabs are at the bottom of the
   screen now, so the header carries a mark and one pill in 350px and there is
   no reason for a baddie's store to stop saying what it is. */
@media (max-width:330px){ .mark span{display:none} }
@media (max-width:620px){ .step span.lbl{display:none}
  .step{min-width:44px;justify-content:center;padding:7px 0} .step .sep{width:10px} }

/* ══ 1 · WELCOME ════════════════════════════════════════════════════════
   Law 1: one frame, no scroll. Everything a creator needs to decide whether
   to carry on is above the fold on a phone. */
.welcome{min-height:calc(100dvh - 60px);display:grid;align-content:center;
  padding-block:clamp(28px,6vh,64px) clamp(24px,5vh,40px)}
.welcome .display{margin:16px 0 0;max-width:14ch}
.welcome .lede{margin:16px 0 0;max-width:44ch}
.welcome .go{margin:26px 0 0;display:flex;gap:12px;align-items:center;flex-wrap:wrap}

/* ══════════════════════════════════════════════════════════════════════════
   THE PACKAGE — ONE OBJECT, NOT A CARD PLUS A LIST.

   🚨 IT REPLACES THE SPEND CARD AND THE FACT LIST, WHICH WERE TWO OBJECTS
   SAYING ONE THING. Measured at 390x844 before this: eyebrow, headline, lede, a
   124px spend card carrying a progress track, the press, the fine print and
   THEN a four-row fact list at y=980 repeating the same two numbers the card
   had already given. The reasoning is over `packCard` in store.js — the short
   of it is that a ratio against a limit is the right drawing for somebody
   mid-basket and the wrong first impression for somebody just invited.

   ⚠️ THE FIGURE IS A ROW OF THE PACKAGE, NOT A SEPARATE CARD, so a phone reads
   one bordered object from the top with no second heading to parse.
   ⚠️ NO TRACK, NO METER, NO RATIO ON THIS SCREEN. The bar on CHOOSE is where a
   ratio against a limit belongs and it is drawn there, live.
   ══════════════════════════════════════════════════════════════════════════ */
.pack{margin:24px 0 0;max-width:560px;display:grid;gap:1px;
  border:1px solid var(--line2);border-radius:var(--r);overflow:hidden;background:var(--line)}
.packk{background:var(--card);padding:14px 18px 0;
  font:600 10.5px/1 var(--sans);letter-spacing:.22em;text-transform:uppercase;color:var(--mut)}
.packrow{background:var(--card);padding:14px 18px;display:flex;gap:14px;align-items:flex-start}
.packrow .k{display:block;font:600 15px/1.35 var(--sans);letter-spacing:-.01em}
.packrow .v{display:block;font:400 13.5px/1.5 var(--sans);color:var(--sec);margin-top:3px}
.packrow .dot{width:7px;height:7px;border-radius:var(--r-pill);background:var(--line2);margin-top:7px;flex:none}
.packrow.key .dot{background:var(--red)}
/* the lead row carries the number, so it is a stack rather than a dotted line */
.packrow.lead{flex-direction:column;gap:6px;padding:6px 18px 16px}
.pfig{display:flex;align-items:baseline;gap:10px;font:600 clamp(34px,9vw,46px)/1 var(--sans);
  letter-spacing:-.035em;color:var(--ink);font-variant-numeric:tabular-nums}
.pfig i{font-style:normal;font:400 13px/1.35 var(--sans);letter-spacing:0;color:var(--mut)}
.psub{font:400 13px/1.55 var(--sans);color:var(--sec)}
/* ⚠️ THE FIGURE'S CAPTION IS TWO LINES NOW — "a month to spend / and free
   delivery" — so it aligns to the BOTTOM of the number rather than to its
   baseline, which on a 46px figure left the second line hanging in mid-air. */
.pfig i{align-self:flex-end;padding-bottom:4px}
/* ── the closer: the one block on this screen that says why THEM ──────────
   ⚠️ NOT A `.packrow`. It carries no dot and no key/value shape because it is
   not another thing they get — it is the sentence under the list, and giving it
   the list's furniture would make it read as a sixth perk. */
.packclose{background:var(--card);padding:16px 18px 18px;border-top:1px solid var(--line)}
.packclose b{display:block;font:600 15px/1.35 var(--sans);letter-spacing:-.01em;color:var(--ink)}
.packclose span{display:block;font:400 13.5px/1.55 var(--sans);color:var(--sec);margin-top:4px}
/* ⚠️ THE ONE EMPHASIS IN THE LEDE — "BADDIE RATE" — is a weight, not a colour.
   Red is spent on four things on this surface and a proper noun is not one of
   them; 600 is the ceiling, so this is the loudest it is allowed to be. */
.lede b{font-weight:600;color:var(--ink)}

/* ⚠️ TWO COLUMNS ONLY WHERE THERE IS ROOM FOR TWO, AND PLACED BY AREA RATHER
   THAN BY GUESSED HEIGHTS. The welcome screen wasted the whole right half of a
   1512px window — measured, the widest thing on it was a 560px fact list on a
   1290px page.

   ⚠️ THE SOURCE ORDER IS THE PHONE ORDER and nothing below reorders it: the
   figure lands under the promise and the press lands above the small print,
   which is what puts "Choose your pieces" over the fold at 390×844. Areas, not
   `grid-row: n / span m`, because the row count is a guess the moment a lede
   wraps to a third line and a wrong guess overlaps two blocks silently. */
@media (min-width:900px){
  .welcome{grid-template-columns:minmax(0,1fr) minmax(0,460px);
    column-gap:clamp(40px,6vw,88px);align-items:start;align-content:center;
    grid-template-areas:
      "eye  pack"
      "h    pack"
      "lede pack"
      "go   pack"
      "fine pack"}
  /* 🚨 DIRECT CHILDREN, NOT DESCENDANTS, AND THIS IS A MEASURED BUG NOT A
     TIDY-UP. Written as `.welcome .go`, the rule matched the `.go` INSIDE the
     join form as well — a grid item of the FORM, not of the welcome — so
     `grid-area:go` resolved against a grid that has no such area, created an
     implicit one, and put the submit button on the same row as the password
     field at half its width. Seen at 1440×900 on 2026-08-20: the email input
     truncated to "demo@feralclo.co" and the password box 170px wide beside a
     button. `grid-area` only ever applies to a DIRECT child of the grid, so a
     descendant selector here is always either a no-op or a bug. */
  .welcome > .eyebrow{grid-area:eye}
  .welcome > .display{grid-area:h}
  .welcome > .lede{grid-area:lede}
  .welcome > .go{grid-area:go}
  .welcome > .enterform{grid-area:go}
  .welcome > .fine{grid-area:fine}
  /* ⚠️ THE FIGURE STAYS IN THE LEFT COLUMN AND THE TERMS GO RIGHT. Tried the
     other way round first and measured a 128px hole: the card is 124px tall and
     the three rows it would have spanned (eyebrow, a two-line display headline
     and a two-line lede) are 242, so the facts under it started a row and a
     half below where the card ended. Grid rows are shared between columns —
     the only way to close that gap is not to straddle them. This way the left
     column reads promise → figure → press, which is the order somebody decides
     in, and the right column is one object top-aligned against it. */
  .welcome > .pack{grid-area:pack;align-self:start;margin-top:6px;max-width:none;width:100%}
}
.welcome .fine{margin:16px 0 0;font:400 13px/1.6 var(--sans);color:var(--mut);max-width:52ch}
.welcome .fine a{color:var(--ink);text-underline-offset:3px;
  display:inline-block;padding:12px 5px;margin:-12px -5px}

/* ══ 2 · THE STORE ══════════════════════════════════════════════════════ */
.storehead{padding-block:34px 10px}
.storehead .h2{margin-bottom:6px}

/* the group chooser — one parcel, one group, said kindly */
.groups{display:flex;gap:8px;overflow-x:auto;padding:16px 0 22px;
  scrollbar-width:none;-webkit-overflow-scrolling:touch}
.groups::-webkit-scrollbar{display:none}
.grp{flex:none;padding:9px 15px;border-radius:var(--r-pill);border:1px solid var(--line2);
  font:600 13.5px/1 var(--sans);color:var(--sec);white-space:nowrap;
  transition:all .18s var(--ease);background:var(--card)}
.grp:hover{border-color:var(--ink);color:var(--ink)}
.grp.on{background:var(--ink);color:var(--on-ink);border-color:var(--ink)}
.grp.locked{opacity:.34;pointer-events:none}
.grp .c{opacity:.55;margin-left:6px;font-weight:500}
/* 🚨 33.5px, MEASURED BY feral-ui-check AT 390px, ON THE STORE'S PRIMARY
   NAVIGATION. `padding:9px 15px` on a 13.5px/1 font is a 34px capsule, and the
   law is 44 on a phone. It was reported as ten failing targets across every
   group pill, which is the whole filter bar: the control a creator uses most
   was the smallest thing on the screen. `min-height` rather than more padding
   so the capsule grows without the label moving off centre. */
@media (max-width:620px){
  .grp{min-height:44px;display:inline-flex;align-items:center}
}

.lockline{display:flex;align-items:center;gap:9px;padding:11px 14px;margin:0 0 18px;
  background:var(--well);border-radius:var(--r-s);font:400 13.5px/1.45 var(--sans);color:var(--sec)}
.lockline .lt{min-width:0}
.lockline b{color:var(--ink);font-weight:600}
.lockline .x{margin-left:auto;font:600 13px/1 var(--sans);color:var(--ink);
  text-decoration:underline;text-underline-offset:3px;cursor:pointer;flex:none;
  /* ⚠️ 15px WAS THE WHOLE TAP TARGET. Measured 2026-08-17: the "Start again"
     control — the ONLY way out of a committed parcel group — was an 80×15px
     hit box. Padded to 44 in a way that does not move the text, since the
     line is measured against its neighbours. */
  min-height:44px;display:inline-flex;align-items:center;padding-inline:2px}
/* 🚨 MEASURED 2026-08-17 AT 390px: 80.7px tall, 4.12 lines, and the "Start
   again" button pushed by `margin-left:auto` into a ragged third column with
   two words per line. A single row of flex with a text button pinned right
   only works while the text fits on one line, and this text never does on a
   phone.

   ⚠️ TWO ROWS WAS THE OBVIOUS FIX AND IT MEASURED WORSE — 103.1px, because a
   44px tap target on a row of its own is 44px of height. On a phone it stops
   being a flex row at all: the sentence FLOWS and "Start again" is the last
   thing in it, an inline link, with the tap target bought by padding that is
   cancelled by an equal negative margin. Same trick as `.welcome .fine a` and
   `.doc a` above, for the same reason: 44px of hit box, 0px of layout. */
@media (max-width:560px){
  .lockline{display:block;padding:11px 14px}
  .lockline .lt{display:inline}
  /* 16px, not 13: the font is 13px/1, so 13+13+13 measured 39 and the law is
     44. Verified by elementFromPoint at the centre, not by the rectangle. */
  .lockline .x{display:inline;margin:0 0 0 5px;min-height:0;padding:16px 4px;
    margin-block:-16px;margin-right:-4px}
}

/* ── the confirmation state — see `paintChrome` ─────────────────────────
   ⚠️ IT IS THE SAME COMPONENT, NOT A DIALOG. A modal over a shop to ask "are
   you sure" is heavier than the decision deserves, and it would cover the very
   pieces the creator is deciding between. The line grows a red edge, states
   what it costs in pieces, and offers both answers in place.
   ⚠️ AND THE DESTRUCTIVE ONE IS NOT THE EMPHASISED ONE. `.x.on` is ink; the way
   back is the plain link beside it, because it is the answer that costs
   nothing. */
.lockline.warn{border-left:2px solid var(--red);gap:14px;flex-wrap:wrap}
.lockline .x.on{color:var(--red)}
@media (max-width:560px){ .lockline.warn{display:block} }

.grid{display:grid;gap:clamp(14px,2.2vw,28px);padding-bottom:calc(var(--bar) + 28px);
  grid-template-columns:repeat(auto-fill,minmax(min(46%,230px),1fr))}

.card{background:var(--card);border-radius:var(--r);overflow:hidden;
  border:1px solid var(--line);transition:box-shadow .25s var(--ease),border-color .25s var(--ease)}
.card:hover{box-shadow:var(--sh-2);border-color:var(--line2)}
.shot{position:relative;aspect-ratio:4/5;background:var(--sunk);overflow:hidden}
.shot img{width:100%;height:100%;object-fit:cover;transition:transform .85s var(--ease)}
.card:hover .shot img{transform:scale(1.04)}
.shot .none{position:absolute;inset:0;display:grid;place-items:center;color:var(--faint);
  font:600 11px/1 var(--sans);letter-spacing:.14em;text-transform:uppercase}
.tag{position:absolute;left:10px;top:10px;background:var(--ink);color:var(--on-ink);
  border-radius:var(--r-pill);padding:5px 10px;font:600 11px/1 var(--sans);
  letter-spacing:.03em;box-shadow:var(--sh-1)}
.cbody{padding:13px 14px 15px}
.ctitle{font:600 14.5px/1.3 var(--sans);letter-spacing:-.008em;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;min-height:2.6em}
.prices{display:flex;align-items:baseline;gap:8px;margin-top:7px}
.now{font:600 17px/1 var(--sans);letter-spacing:-.02em}
.was{font:500 13px/1 var(--sans);color:var(--mut);text-decoration:line-through;
  text-decoration-thickness:1px}
/* the discount is stated by the two prices next to it; a red badge as well
   is the same fact three times and it spends accent on a non-event */
.off{font:600 11px/1 var(--sans);color:var(--mut);letter-spacing:.02em}

/* ══════════════════════════════════════════════════════════════════════════
   SIZES — drawn by us, never a native <select> (law 12), and ONE ROW.

   🚨 EQUAL COLUMNS OFF `--n`, WHICH `sizeBlock` SETS FROM THE VARIANT COUNT.
   Harry, 2026-08-21: *"it should show all the sizes on one row, really
   beautifully and nicely."*

   ⛔ WHY NOT A FLEX ROW THAT WRAPS (what this was): at 390px a six-size wall
   broke 4 + 2, and a wrapped run of the same thing reads as two GROUPS with an
   orphan — XXL alone on row two is the odd one out, not the biggest size.
   ⛔ AND WHY NOT `auto-fill,minmax(74px,1fr)` (what the popover was): auto-fill
   counts columns off the WIDTH, so the same product broke 5 + 1 in a 557px
   window and 4 + 2 in a 390px one. The column count is a fact about the
   GARMENT — it has six sizes — and it belongs to the thing that knows it.
   ⛔ AND WHY NOT `flex:1` ON A ROW: four chips of one width and then two of
   half the panel each says XL and XXL are bigger, more important choices than
   M. Equal columns say what is true — these are six of the same thing.

   ⚠️ SO `min-width` GOES TO 0 AND THE PADDING SHRINKS. A 44px floor inside a
   fixed column count is not a touch target, it is an overflow: seven chips at
   44px + gaps need 350px and the narrowest phone this store sees offers 280.
   The HEIGHT is what keeps the thumb honest, and it is untouched — 44px in the
   page, 52px in the popover, where a size is chosen without the garment in
   view. Measured across all 104 offered products at 320/390/430/1040: zero
   overflow, because a chip says the size and nothing else (`variantLabel`).
   ══════════════════════════════════════════════════════════════════════════ */
.sizes{display:grid;grid-template-columns:repeat(var(--n,6),minmax(0,1fr));
  gap:6px;margin-top:12px}
.size{min-width:0;height:44px;padding:0 6px;border-radius:var(--r-s);
  border:1px solid var(--line2);font:600 12.5px/1 var(--sans);color:var(--sec);
  white-space:nowrap;
  display:grid;place-items:center;transition:all .15s var(--ease)}
.size:hover{border-color:var(--ink);color:var(--ink)}
.size.on{background:var(--ink);color:var(--on-ink);border-color:var(--ink)}
.size.out{opacity:.3;pointer-events:none;text-decoration:line-through}
.size.blocked{opacity:.38;pointer-events:none}

/* ── the basket bar. Persistent, honest, and it is also the allowance. ── */
.bar{position:fixed;left:0;right:0;bottom:0;z-index:50;
  background:color-mix(in srgb,var(--paper) 88%,transparent);
  backdrop-filter:saturate(1.6) blur(16px);border-top:1px solid var(--line);
  transform:translateY(110%);transition:transform .3s var(--ease);
  padding-bottom:env(safe-area-inset-bottom)}
.bar.on{transform:none}
.barin{display:flex;align-items:center;gap:16px;height:var(--bar)}
.meter{flex:1;min-width:0}
.mrow{display:flex;align-items:baseline;gap:8px;font:600 13.5px/1 var(--sans)}
.mrow .of{color:var(--mut);font-weight:500}
.mrow .money{margin-left:auto;font:600 15px/1 var(--sans);letter-spacing:-.02em}
.mrow .money s{color:var(--mut);font-weight:500;font-size:12.5px;margin-right:7px}
.track{height:3px;background:var(--sunk);border-radius:var(--r-pill);margin-top:9px;overflow:hidden}
.track i{display:block;height:100%;background:var(--ink);border-radius:var(--r-pill);
  transition:width .35s var(--ease)}

/* 🚨 `padding-block`, NOT `padding`. MEASURED 2026-08-17 at 390px: `.barin` is
   also a `.wrap`, and `.wrap` carries the page's only horizontal gutter
   (`padding:0 var(--pad)`). The shorthand here came later in the file at the
   same specificity, so it did not add a vertical padding — it REPLACED the
   whole box and zeroed the sides. The result on a phone was the meter's "1
   piece of 3" starting at x=0 and the total "£20" ending at x=390.0, both
   touching the glass, and the Review button spanning edge to edge with no
   gutter at all: the one component every creator sees on every screen was the
   only one on the page not aligned to the grid. The longhand cannot do that. */
/* ── the bar before anything is picked ────────────────────────────────────
   ⚠️ QUIETER, NOT SMALLER. It is the same component saying a standing fact
   rather than a live total, so it keeps its height and its position and drops
   only the emphasis — a bar that changes size when the first piece lands is a
   page that jumps under a thumb. */
.bar.idle{background:color-mix(in srgb,var(--paper) 80%,transparent)}
.mrow.idle{justify-content:flex-start;gap:12px;flex-wrap:wrap}
.mrow.idle b{font-weight:600}
.mrow.idle .of{font:500 12.5px/1.35 var(--sans);color:var(--mut)}
.barhint{font:400 12px/1 var(--sans);color:var(--mut);flex:none}

/* ══════════════════════════════════════════════════════════════════════════
   THE BAR OPENS — see `basketList` in store.js for why it had to.

   ⚠️ THE LIST IS `order:-1`, SO IT GROWS UPWARDS out of a bar that is pinned to
   the bottom of the screen. Putting it after the meter in the flex order would
   push the meter and the Review button off the bottom of the viewport on a
   phone, which is the one direction a bottom sheet must never move.
   ⚠️ AND IT SCROLLS AT 46vh. A creator with nine pieces in a parcel must not be
   handed a drawer taller than the shop behind it. */
.meterbtn{flex:1;min-width:0;display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  background:none;border:0;padding:0;margin:0;color:inherit;text-align:left;cursor:pointer;font:inherit}
.meterbtn .meter{flex:1;min-width:0}
.mchev{flex:none;font:500 12px/1 var(--sans);color:var(--mut);
  text-decoration:underline;text-underline-offset:3px}
.meterbtn:hover .mchev,.meterbtn.open .mchev{color:var(--ink)}
.barin.wide{height:auto;flex-wrap:wrap;padding-block:0 14px}
.barlist{flex-basis:100%;order:-1;margin-top:12px;max-height:min(46vh,380px);overflow:auto;
  border:1px solid var(--line);border-radius:var(--r);background:var(--card)}
.bline{display:flex;gap:12px;padding:10px 12px;align-items:center;border-bottom:1px solid var(--line)}
.bline:last-child{border-bottom:0}
.bline .th{width:44px;height:55px;border-radius:var(--r-s);object-fit:cover;background:var(--sunk);flex:none}
.bline .t{font:600 14px/1.3 var(--sans);letter-spacing:-.008em}
.bline .s{font:400 12.5px/1.4 var(--sans);color:var(--sec);margin-top:2px}
.bline .p{margin-left:auto;text-align:right;flex:none}
.bline .p .now{display:block;font-size:14px}
.bline .p .was{display:block;margin-top:2px;font-size:12px}
.bline .p .cov{font-size:12px;color:var(--mut)}
/* ⚠️ 34px AND ROUND, NOT AN 11px ✕. It is the only destructive control in the
   drawer and it is pressed with a thumb on a moving list. */
.bx{flex:none;margin-left:6px;width:34px;height:34px;border-radius:var(--r-pill);
  border:1px solid var(--line);background:none;color:var(--mut);
  font:500 13px/1 var(--sans);cursor:pointer}
.bx:hover{color:var(--ink);border-color:var(--line2)}
.bfoot{padding:9px 12px;display:flex;justify-content:flex-end;border-top:1px solid var(--line)}
@media (max-width:520px){ .mchev{flex-basis:100%;margin-top:2px} }
@media (max-width:520px){ .barin{height:auto;padding-block:12px 14px;flex-wrap:wrap}
  .barin .btn{width:100%}
  /* ⚠️ AND THE IDLE HINT GOES ON A PHONE. At 390px the row already wraps to two
     lines; a third saying "pick something and it lands here" is a paragraph in
     a bar, which is the fault this component's own note warns about. */
  .barhint{display:none} }

/* ══ 3 · REVIEW ═════════════════════════════════════════════════════════ */
/* ⚠️ LEFT-ALIGNED, NOT CENTRED, AND THE `margin-inline` IS WHY. `.wrap` sets
   `margin:0 auto`, so narrowing `max-width` on the same element centres the
   whole column — which put the parcel screen's left edge 230px right of the
   demo banner and the footer directly above and below it. A checkout column
   centred on its own page is calm; centred inside an application whose every
   other room starts at the same gutter, it reads as a different site. The
   `+ var(--pad)*2` is what keeps 720px of CONTENT after the wrap's own
   padding, so the measure is unchanged. */
.review{padding-block:30px 60px;max-width:calc(720px + var(--pad) * 2);
  margin-inline:0 auto}
.lines{border:1px solid var(--line);border-radius:var(--r);overflow:hidden;margin:22px 0 0}
.line{display:flex;gap:14px;padding:14px;border-bottom:1px solid var(--line);align-items:center}
.line:last-child{border-bottom:0}
.line .th{width:56px;height:70px;border-radius:var(--r-s);object-fit:cover;background:var(--sunk);flex:none}
.line .t{font:600 14.5px/1.35 var(--sans);letter-spacing:-.008em}
.line .s{font:400 13px/1.4 var(--sans);color:var(--sec);margin-top:3px}
.line .p{margin-left:auto;text-align:right;flex:none}
.line .p .now{display:block;font-size:15px}
.line .p .was{display:block;margin-top:3px;font-size:12.5px}
.line .rm{margin-left:14px;color:var(--mut);font:500 13px/1 var(--sans);flex:none;
  text-decoration:underline;text-underline-offset:3px}
.line .rm:hover{color:var(--ink)}

.tot{margin:22px 0 0;border-top:1px solid var(--line);padding-top:18px}
.trow{display:flex;justify-content:space-between;align-items:baseline;padding:6px 0;
  font:400 14.5px/1.4 var(--sans);color:var(--sec)}
.trow b{color:var(--ink);font-weight:600}
/* a qualifier under a total, not another total — no right-hand figure at all */
.trow.note{display:block;padding-top:8px;font:400 12.5px/1.5 var(--sans);color:var(--mut)}
.trow.big{padding-top:14px;margin-top:8px;border-top:1px solid var(--line);
  font-size:17px;color:var(--ink)}
.trow.big .hero{font:600 clamp(30px,7vw,42px)/1 var(--sans);letter-spacing:-.03em}
.saved{display:inline-flex;align-items:center;gap:7px;margin-top:16px;
  background:var(--good-soft);color:var(--good);border-radius:var(--r-pill);
  padding:8px 14px;font:600 13px/1 var(--sans)}

.note{margin:18px 0 0;padding:15px 17px;background:var(--well);border-radius:var(--r);
  font:400 13.5px/1.6 var(--sans);color:var(--sec)}
.note b{color:var(--ink);font-weight:600}
.note a{color:var(--ink);text-underline-offset:3px;
  display:inline-block;padding:14px 4px;margin:-14px -4px}
.review .go{margin:26px 0 0;display:flex;gap:12px;align-items:center;flex-wrap:wrap}

/* ── states ─────────────────────────────────────────────────────────── */
.state{min-height:70dvh;display:grid;place-content:center;text-align:center;gap:14px;padding-block:40px}
.state .h2{max-width:22ch;margin-inline:auto}
.state .lede{max-width:40ch;margin-inline:auto}
.err{display:flex;gap:10px;align-items:flex-start;background:var(--red-soft);
  border-radius:var(--r-s);padding:13px 15px;margin:16px 0 0;
  font:500 13.5px/1.5 var(--sans);color:var(--ink)}
.err .dot{width:7px;height:7px;border-radius:var(--r-pill);background:var(--red);margin-top:6px;flex:none}

.skel{background:var(--sunk);border-radius:var(--r-s);
  animation:pulse 1.4s var(--ease) infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.55}}
@media (prefers-reduced-motion:reduce){ *{animation-duration:.01ms!important;
  transition-duration:.01ms!important} .card:hover .shot img{transform:none} }

.foot{border-top:1px solid var(--line);padding-block:26px 40px;margin-top:40px;
  font:400 12.5px/1.6 var(--sans);color:var(--mut)}
/* ⚠️ THE ROW MOVED INSIDE A `.wrap` WHEN THE FOOTER GREW A CONTROL. `.foot` was
   the flex row itself, so its items sat against the page edge and the "Sign
   out" button that arrived with the account had nowhere to be. The row is now
   the `.wrap`, which is what every other band on this surface uses to find the
   same left edge — a footer 24px out of line with the content above it is the
   kind of thing nobody names and everybody sees. */
.footin{display:flex;gap:18px;flex-wrap:wrap;align-items:center}
.foot a{color:var(--sec);text-underline-offset:3px;display:inline-flex;align-items:center;
  min-height:44px;padding:0 4px}
.foot .sp{margin-left:auto}

/* ── the terms page ─────────────────────────────────────────────────── */
.doc{max-width:660px;padding-block:clamp(36px,8vh,72px) 80px}
.doc .display{margin-bottom:10px}
.doc h2{font:600 17px/1.3 var(--sans);letter-spacing:-.01em;margin:34px 0 10px}
/* ⚠️ THE HEADER IS STICKY AND 60px TALL. Without this, following `/terms#covered`
   from the store lands the heading UNDER the header and the reader starts
   mid-paragraph in the lane that is not theirs. */
.doc h2[id]{scroll-margin-top:76px}
.doc p{font:400 15.5px/1.7 var(--sans);color:var(--sec);margin:0 0 14px}
/* 🚨 `li strong` AS WELL AS `p strong`, AND THE LIST IS WHY. Measured by
   feral-ui-check 2026-08-17, the moment the terms page gained a two-item list
   naming the two lanes: an unstyled <strong> inherits the browser's `bolder`,
   which against Inter's variable range paints 700 and breaks the ceiling this
   file's opening comment exists to defend. The rule was written for the shape
   the page had, not for the shape a <strong> has. */
.doc p strong,.doc li strong{color:var(--ink);font-weight:600}
/* an inline link in a legal document is still something a thumb has to hit */
.doc a{color:var(--ink);text-underline-offset:3px;
  display:inline-block;padding:13px 4px;margin:-13px -4px}
.doc ul{margin:0 0 14px;padding-left:20px}
.doc li{font:400 15.5px/1.7 var(--sans);color:var(--sec);margin-bottom:7px}
.doc .stamp{margin-top:40px;padding-top:18px;border-top:1px solid var(--line);
  font:400 13px/1.6 var(--sans);color:var(--mut)}

/* ══ ADDED IN THE SECOND PASS ═══════════════════════════════════════════
   Search, the product sheet, quantity, and the ending. Same token set, same
   four radii, same single accent — nothing below introduces a colour, a
   typeface or a corner that was not already in use above.
   ══════════════════════════════════════════════════════════════════════ */

/* screen-reader-only live region. Visually gone, never display:none — a
   display:none live region announces nothing at all. */
.sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}

/* ── search ─────────────────────────────────────────────────────────── */
.search{padding:2px 0 0}
.search input{width:100%;min-height:48px;background:var(--card);color:var(--ink);
  border:1px solid var(--line);border-radius:var(--r-pill);padding:12px 20px;
  font:400 15px/1 var(--sans);outline:none;transition:border-color .18s var(--ease)}
.search input:focus{border-color:var(--ink)}
.search input::placeholder{color:var(--mut)}
.search input::-webkit-search-cancel-button{cursor:pointer}

.nores{grid-column:1/-1;padding:44px 20px;text-align:center;background:var(--well);
  border-radius:var(--r);display:flex;flex-direction:column;gap:6px}
.nores b{font:600 15px/1.4 var(--sans);color:var(--ink)}
.nores span{font:400 13.5px/1.5 var(--sans);color:var(--sec)}

/* ── card additions ─────────────────────────────────────────────────── */
.card{display:block;width:100%;text-align:left;padding:0;cursor:pointer;
  font:inherit;color:inherit}
/* 🚨 NO SIZE LIST ON THE CARD. Standing rule for FERAL product cards: image,
   title, price — resist stock dots, fit indicators and size badges, and put
   them one layer deeper. The sheet has every size, with quantity. */
.inbag{position:absolute;left:10px;bottom:10px;right:10px;display:flex;align-items:center;gap:6px;
  background:var(--ink);color:var(--on-ink);border-radius:var(--r-pill);
  padding:7px 12px;font:600 11.5px/1 var(--sans)}
.inbag .t{font-size:11px}

/* ── the product sheet ──────────────────────────────────────────────── */
.sheetlayer{position:fixed;inset:0;z-index:80;display:none}
.sheetlayer.on{display:block}
.sheetscrim{position:absolute;inset:0;background:rgba(0,0,0,.66);
  backdrop-filter:blur(4px);animation:fade .22s var(--ease)}
@keyframes fade{from{opacity:0}to{opacity:1}}
/* ══════════════════════════════════════════════════════════════════════════
   🚨 ON A PHONE THIS IS A PRODUCT PAGE, NOT A BOTTOM SHEET — ONE SCROLLER,
   AND A BAR.

   ⛔ MEASURED 2026-08-20 at 375×667, which is what a phone gives you once the
   browser has taken its chrome: gallery 375 · WORDS 48 · foot 190, in a 614px
   sheet. Forty-eight pixels held 180px of content, so the garment's own NAME
   was clipped through the middle of its letters and its price, description and
   fit sat below a fold 48px tall. Harry: *"the mobile view looks really weird…
   like it's cut off, it's horrible, the scroll."*

   ⛔ AND THE CAUSE WAS THE FIX BEFORE IT. Three bands, of which two were rigid:
   the picture had a `100vw` FLOOR — a square, unconditionally, whatever the
   window's height — and the pinned band had grown to 190px when the size chips
   moved into it. The words got the remainder. On a tall phone that was mean;
   on a short one it clipped; on a wide, short window the floor is taller than
   the window itself.

   ⇒ THE PICTURE, THE WORDS AND THE SIZES ARE ONE PAGE THAT SCROLLS, and the
     DECISION is a 76px bar pinned under it. Nothing is paid for by anything
     else: the picture is the 4:5 the shop shoots, the words have the room they
     need, the chips WRAP instead of scrolling sideways, and the sizes are one
     press from anywhere on the page because the bar opens them as a popover.

   ⚠️ THE THREE INVARIANTS THAT SURVIVE, because each was paid for once:
     · the decision is never below a fold          (the bar, always visible)
     · nothing in the pinned bar changes height    (one row, fixed both ways)
     · a size press never rebuilds the picture     (`syncSheet`, in shop.js)
   ══════════════════════════════════════════════════════════════════════════ */
.sheet{position:absolute;inset:0;
  background:var(--card);
  animation:rise .3s var(--ease);
  display:flex;flex-direction:column;overflow:hidden}
/* 🚨 THE ONLY SCROLL REGION ON THE PHONE. `overscroll-behavior:contain` stops a
   flick at the end of it scrolling the store underneath — which on iOS is the
   gesture that dismisses the page you were reading. */
.sheetscroll{flex:1;min-height:0;overflow:auto;overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch}
/* ⚠️ CAPPED IN dvh, NOT IN vw. The cap exists for ONE reason — that the title
   and the price are above the fold on a short phone — so it is expressed in the
   thing it is protecting (the window's height) and not in its width. At 390×844
   the 4:5 wins outright (487 < 540); at 375×667 the cap trims it to 427, which
   is still taller than a square; and on a landscape phone it is a short, wide
   frame, which is the correct answer there rather than a picture taller than
   the screen. */
/* ⚠️ A HEIGHT, NOT AN `aspect-ratio` — MEASURED. `aspect-ratio:4/5` with a
   `max-height` clamps the HEIGHT and then re-derives the WIDTH from the ratio,
   so at 375×667 the picture came out 342px wide inside a 375px gallery: a
   full-bleed photograph with a 16px letterbox down both sides. `125vw` IS 4:5
   at full width, and capping it in dvh trims the frame rather than the frame's
   width — `object-fit:cover` on the <img> does the rest. */
.gal .big{width:100%;height:min(125vw,64dvh);background:var(--sunk);overflow:hidden;
  position:relative}
.sheet .gal .strip{display:none}
.sheetfoot{flex:none;position:relative;display:flex;flex-direction:column;
  padding:12px var(--pad) calc(12px + env(safe-area-inset-bottom));
  border-top:1px solid var(--line);background:var(--card)}
.footrow{display:flex;align-items:center;gap:14px;min-height:46px}
/* ⚠️ A BUTTON NOW — it is the way back to the sizes once one is picked, and on
   the phone it is the ONLY one. See the block in shop.js. The reset is here
   rather than on `.btn` because it must look like the text it replaced: this
   is a press you discover by reading it, not a control that announces itself
   beside the real one. */
.sfsum{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1;
  background:none;border:0;padding:0;margin:0;text-align:left;
  font:inherit;color:inherit;cursor:pointer}
.sfsum b{font:600 13.5px/1.25 var(--sans);color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sfsum span{font:400 12px/1.3 var(--sans);color:var(--sec);
  font-variant-numeric:tabular-nums;
  display:flex;align-items:baseline;gap:8px;min-width:0}
/* ⚠️ ON THE LINE THAT EXISTS, NOT ON A NEW ONE — the bar must be the same
   height empty and full. Underlined because it is the only thing in the row
   that is a link to somewhere, and the row around it is not. */
.sfchg{font:600 12px/1.3 var(--sans);color:var(--ink);font-style:normal;
  text-decoration:underline;text-underline-offset:2px;flex:none}
@media (min-width:860px){
  /* ⛔ NOTHING TO OPEN HERE. `.sizepop` is `display:none` on a pointer and every
     size is already in the column, so an affordance pointing at it would be a
     press that does nothing. `phoneSheet()` guards the handler on the same
     860px this rule switches on. */
  .sfchg{display:none}
  .sfsum{cursor:default}
}
/* ⚠️ THE BAR'S BUTTON IS NOT `.btn.wide` — it shares the row with the summary.
   `.btn.wide`'s `width:100%` would push the summary out of the row entirely.
   ⚠️ AND ITS HEIGHT IS PINNED, because the class swaps between solid and ghost
   when something is picked and `.sm` is 3px shorter — 3px of the bar moving
   under a thumb, on every press. */
.sheetfoot .btn.wide{width:auto;margin-top:0;flex:none;min-width:132px;justify-content:center;
  height:46px;min-height:46px}

/* ── the sizes, in the page ─────────────────────────────────────────────
   ⛔ THEY WRAP, THEY DO NOT SCROLL SIDEWAYS. The horizontal scroller was
   correct inside a pinned band — eight sizes would have made that band taller
   than four and moved the picture between products — and is wrong in a page,
   where a second row costs a page that already scrolls exactly nothing and a
   sideways scroller with no affordance hides sizes nobody knows are there. */
.decide{display:flex;flex-direction:column;gap:10px;margin:22px 0 0}
/* ⚠️ THE GRID IS INHERITED FROM `.sizes` — this only widens the gutter, because
   the words column has the room for it and 6px between chips at 380px reads as
   one segmented control rather than six choices. */
.decide .sizes{gap:8px}
.decide .lowline{margin:0}
.pickrow{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:16px}
.pickhead.pickhead{margin:0}
/* ⚠️ SOLD OUT IS DRAWN AND UNPRESSABLE, NOT ABSENT. A size that is not on the
   wall reads as a size FERAL does not make, and the next thing that happens is
   a message somebody has to answer. */
.size.out{opacity:.34;text-decoration:line-through;cursor:not-allowed}
.size.out:hover{background:none;border-color:var(--line2);color:var(--sec)}

/* ══════════════════════════════════════════════════════════════════════════
   🚨 THE SIZES, OVER THE BAR THAT OPENED THEM. Harry, 2026-08-20: *"maybe you
   need some sort of submenus with pop-ups."*

   It is the SAME control as the one in the page — one builder, `sizeBlock`,
   lit by the same two lines of `syncSheet` — surfaced where a thumb already is
   so that choosing does not depend on where the page happens to be scrolled to.
   ⚠️ IT IS A CHILD OF THE BAR, not of the scroll, so it opens in the same place
   from the top of the page and from the bottom of it.
   ══════════════════════════════════════════════════════════════════════════ */
.sizepop{position:absolute;left:0;right:0;bottom:100%;z-index:4;
  background:var(--card);border-top:1px solid var(--line);
  box-shadow:var(--sh-3);padding:14px var(--pad) 16px;
  animation:poprise .2s var(--ease)}
.sizepop[hidden]{display:none}
@keyframes poprise{from{transform:translateY(10px);opacity:0}to{transform:none;opacity:1}}
.sizepophead{display:flex;align-items:center;justify-content:space-between;gap:12px}
.sizepophead .l{font:600 13.5px/1.3 var(--sans);color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.popx{width:44px;height:44px;margin:-8px -12px -8px 0;border-radius:var(--r-pill);
  background:none;border:0;color:var(--sec);display:grid;place-items:center;
  font:400 15px/1 var(--sans)}
.popx:hover{color:var(--ink)}
.sizepop .decide{margin:10px 0 0}
/* the scrim over the page while the popover is open — the bar itself stays lit */
.sheet.popped .sheetscroll{opacity:.42;transition:opacity .2s var(--ease)}

@media (max-width:859px){
  /* ══════════════════════════════════════════════════════════════════════
     🚨 ONE PICKER ON THE PHONE, AND IT IS THE POPOVER. THE MIRROR OF THE RULE
     ON THE POINTER SIDE, WHICH WAS WRITTEN AND THIS ONE WAS NOT.

     Below `.sheet .sizepop{display:none}` the pointer block already says it:
     *"Every size is already visible in the column, so a popover would be a
     second copy of a control nobody is looking for."* Exactly the same is true
     here with the sides swapped — and the copy left standing was the WORSE of
     the two, because it is the one you find by scrolling.

     ⛔ MEASURED 2026-08-20 at 390×664, the sheet as shipped that morning:
     picture 425 · title 464 · price 501 · description 539 · CHIPS 607, in a
     scroll region 593 tall. The chips were 14px below the fold, so the control
     a creator reaches for by instinct was the one they could not see. Harry:
     *"on mobile view, you have to scroll down to select your size, and you may
     not realize to scroll down."*

     ⛔ AND THE BAR ABOVE THEM ALREADY SAID "Choose your size". That is the
     tell: the screen was offering two answers to one question and rewarding
     the slow one. A fold is not fixed by moving a control 60px up the page —
     the next phone, or the next long description, puts it back under.

     ⇒ THE PICTURE, THE WORDS AND THE PRICE ARE THE PAGE. The size is a press
       on a bar that never leaves the screen, so it does not matter where the
       page is scrolled to, or how tall the phone is. Harry, 2026-08-20:
       *"maybe you need some sort of submenus with pop-ups."*

     ⚠️ THE NODE IS BUILT AND SYNCED EITHER WAY — `syncSheet` lights both copies
     from one truth and must keep doing so, because this is a WIDTH rule and a
     rotation crosses it with the sheet open.
     ⚠️ AND `.inparcel` IS NOT THIS. What was picked, and its quantity control,
     still live in the page; they are the receipt, not the picker.
     ══════════════════════════════════════════════════════════════════════ */
  .sheet .decide.inline{display:none}
  /* ⚠️ THE COLUMN COUNT IS `--n` AND IT COMES FROM THE PRODUCT — see `.sizes`
     at the top of this file for the four layouts this replaced and why each
     one lied about the wall. What is LOCAL to the popover is the height: these
     are taller than the chips in the page, because it is a thumb-height control
     opened by a thumb, and the one place a size is chosen without the garment
     in view.
     ⚠️ AND THE GUTTER STAYS AT THE 6px BASE HERE. At 390px, seven chips with an
     8px gutter leave 43px of column and the label starts riding its own border;
     6px leaves 45px. The two pixels are the difference between one row and a
     row that is visibly too tight. */
  .sizepop .size{min-height:52px;width:100%}
}
@keyframes rise{from{transform:translateY(14px);opacity:.6}to{transform:none;opacity:1}}

@media (min-width:860px){
  /* ══════════════════════════════════════════════════════════════════════
     THE POINTER GETS TWO COLUMNS, AND `display:contents` IS WHAT LETS ONE DOM
     SERVE BOTH. The phone's single scroll region is a real element; on a
     pointer it must not be a box at all, or the gallery and the words become
     one grid item and the layout collapses into a column.
     ══════════════════════════════════════════════════════════════════════ */
  .sheet{inset:50% auto auto 50%;transform:translate(-50%,-50%);width:min(1040px,92vw);
    height:min(88dvh,720px);border-radius:var(--r-l);display:grid;
    grid-template-columns:minmax(0,1fr) minmax(0,380px);
    grid-template-rows:minmax(0,1fr) auto;gap:0;overflow:hidden;
    border:1px solid var(--line2);box-shadow:var(--sh-3)}
  .sheetscroll{display:contents}
  .sheet .gal{grid-column:1;grid-row:1 / 3;
    display:flex;flex-direction:column;min-height:0;overflow:hidden}
  /* ⚠️ THE THUMBNAIL STRIP MUST STAY IN THE SHEET. With the big image on a
     fixed 4:5 ratio the gallery column grew past the box and the strip fell off
     the bottom — a gallery whose other images you cannot reach. On a pointer
     the big image takes the space that is left and the strip is pinned under
     it; `max-height:none` undoes the phone's dvh cap, which would otherwise
     clip the column it is meant to fill. */
  .sheet .gal .big{flex:1;min-height:0;height:auto;max-height:none}
  .sheet .gal .strip{display:flex;flex:none;padding:10px}
  /* ⚠️ DOTS ARE A PHONE CONTROL. With thumbnails under the picture they are a
     second index of the same thing, in the same glance. */
  .sheet .gdots{display:none}
  .sheet .sideb{grid-column:2;grid-row:1;overflow:auto;border-left:1px solid var(--line);
    display:flex;flex-direction:column}
  /* ⚠️ THE CLOSE SITS IN THE WORDS COLUMN ON A POINTER, so the two things at the
     top of that column have to get out from under it — measured at 1440×900,
     the ✕ was overlapping the second line of a two-line product name. */
  .sheet .sideb .eyebrow,.sheet .sheettitle{padding-right:52px}
  /* ⚠️ THE PARCEL ROW SITS AT THE FOOT OF THE WORDS COLUMN. The column is `1fr`
     of a fixed-height sheet, so a short description left it floating in the
     middle of a void. */
  .sheet .sideb .inparcel{margin-top:auto}
  .sheet .sheetfoot{grid-column:2;grid-row:2;border-left:1px solid var(--line)}
  /* ⛔ AND THE POPOVER NEVER OPENS HERE. Every size is already visible in the
     column, so a popover would be a second copy of a control nobody is looking
     for; `phoneSheet()` in shop.js asks the same 860px this rule switches on. */
  .sheet .sizepop{display:none}
  @keyframes rise{from{transform:translate(-50%,-48%);opacity:.6}
                  to{transform:translate(-50%,-50%);opacity:1}}
}
/* ⚠️ THE CLOSE SITS OVER THE PICTURE AND HAS TO SURVIVE ANY PHOTOGRAPH under
   it — FERAL shoots on black and also on white studio paper, so a transparent
   glyph is invisible half the time. It is a filled disc with a blur behind it. */
.sheetx{position:absolute;right:12px;top:12px;z-index:3;width:44px;height:44px;
  border-radius:var(--r-pill);background:rgba(8,8,10,.62);backdrop-filter:blur(10px);
  border:1px solid var(--line2);color:var(--ink);
  display:grid;place-items:center;font:400 15px/1 var(--sans)}
.sheetx:hover{background:var(--sunk)}
/* ⚠️ AND THE DIALOG ITSELF SHOWS NO RING. It is focused on open so the keyboard
   starts inside the modal; a 940px outline around the whole sheet is not what
   that is for. Every control inside it keeps its own. */
.sheet:focus{outline:none}
.gal{position:relative;background:var(--sunk)}
/* ══════════════════════════════════════════════════════════════════════════
   🚨 EVERY IMAGE IS IN THE DOM FROM THE FIRST PAINT, SIDE BY SIDE. The old
   gallery swapped `src` on one <img>, so changing image was a network round
   trip and a blank frame — and, worse, a size press rebuilt that <img> too,
   which is the flash Harry reported. A scroll-snap track makes a thumbnail
   press and a thumb-swipe the same gesture, and neither is a repaint.
   ⚠️ `overscroll-behavior-x: contain` or a swipe at the last image walks the
   browser's back gesture on iOS. */
.gtrack{display:flex;height:100%;overflow-x:auto;overflow-y:hidden;
  scroll-snap-type:x mandatory;scrollbar-width:none;overscroll-behavior-x:contain;
  -webkit-overflow-scrolling:touch}
.gtrack::-webkit-scrollbar{display:none}
.gcell{flex:0 0 100%;height:100%;scroll-snap-align:center;scroll-snap-stop:always}
.gal .big img{width:100%;height:100%;object-fit:cover;display:block}
/* ⚠️ THE DOTS SAY "THERE ARE THREE OF THESE" IN 30 PIXELS. They are not a
   control — the gesture is the swipe — so they are `aria-hidden` and take no
   tap target, and they carry a scrim so they read on a pale photograph. */
.gdots{position:absolute;left:0;right:0;bottom:12px;display:flex;justify-content:center;
  gap:6px;pointer-events:none}
.gd{width:6px;height:6px;border-radius:var(--r-pill);background:rgba(255,255,255,.42);
  box-shadow:0 1px 3px rgba(0,0,0,.5);transition:all .2s var(--ease)}
.gd.on{background:var(--ink);width:18px}
.gal .strip{display:flex;gap:8px;padding:10px;overflow-x:auto;scrollbar-width:none}
.gal .strip::-webkit-scrollbar{display:none}
.gal .th{flex:none;width:56px;height:70px;border-radius:var(--r-s);overflow:hidden;
  border:1px solid transparent;opacity:.6;transition:all .18s var(--ease);padding:0}
.gal .th img{width:100%;height:100%;object-fit:cover}
.gal .th.on{opacity:1;border-color:var(--ink)}
.sideb{padding:22px var(--pad) 28px}
.sheettitle{margin:6px 0 0;font:600 clamp(21px,5vw,26px)/1.18 var(--sans);letter-spacing:-.015em}
.prices.big{margin-top:12px;gap:10px}
.prices.big .now{font-size:24px}
.prices.big .was{font-size:15px}
.sheetdesc{margin:14px 0 0;font:400 14.5px/1.6 var(--sans);color:var(--sec)}
.fit{display:flex;align-items:center;gap:9px;margin:14px 0 0;padding:10px 13px;
  background:var(--well);border-radius:var(--r-s);font:500 13px/1.4 var(--sans);color:var(--ink)}
.fit .d{width:6px;height:6px;border-radius:var(--r-pill);background:var(--ink);flex:none}
.pickhead{margin:20px 0 9px;font:600 11px/1 var(--sans);letter-spacing:.14em;
  text-transform:uppercase;color:var(--sec)}
/* 🚨 THE REFUSAL FLOATS. It is the one thing around the pinned bar that
   genuinely appears and disappears, and letting it take layout would move the
   bar up under the thumb that just pressed a size — the resize bug, in the one
   band whose whole job is to be still. */
.sheeterr{position:absolute;left:var(--pad);right:var(--pad);bottom:calc(100% + 8px);z-index:5}
.sheeterr:not(:empty){display:flex;gap:9px;align-items:flex-start;
  background:var(--red-soft);border:1px solid var(--line2);border-radius:var(--r-s);
  padding:11px 13px;font:500 13px/1.45 var(--sans);color:var(--ink);
  box-shadow:var(--sh-2);backdrop-filter:blur(8px)}
.sheeterr .dot{width:6px;height:6px;border-radius:var(--r-pill);background:var(--red);margin-top:6px;flex:none}
/* ⚠️ THE SECOND-SIZE DOOR IS DELIBERATELY THE QUIETEST THING ON THE SHEET. It
   is a real need — a baddie taking one hoodie for herself and one for someone
   else — and making it the default gesture is exactly the bug it replaces. */
.second{display:block;margin:0;padding:0;flex:none;white-space:nowrap;
  font:500 11.5px/16px var(--sans);color:var(--sec);
  text-decoration:underline;text-underline-offset:3px}
.second:hover{color:var(--ink)}
.second.on{color:var(--ink)}
.second[hidden]{display:none}
.inparcel:empty{display:none}
.inparcel{margin-top:22px;border-top:1px solid var(--line);padding-top:16px;
  display:flex;flex-direction:column;gap:9px}
.ipk{font:600 11px/1 var(--sans);letter-spacing:.14em;text-transform:uppercase;color:var(--sec)}
.ip{display:flex;align-items:center;gap:12px}
.ip .l{font:500 13.5px/1.3 var(--sans);color:var(--ink)}
.ip .qty{margin-left:auto}
/* ⚠️ SCOPED OUT OF THE SHEET FOOT — see `.sheetfoot .btn.wide` above. */
.btn.wide{width:100%;margin-top:18px}
/* ── quantity ───────────────────────────────────────────────────────── */
.qty{display:flex;align-items:center;gap:2px;flex:none;
  border:1px solid var(--line2);border-radius:var(--r-pill);padding:2px}
/* ⚠️ 44px ON A PHONE, MEASURED. These were 36×36 everywhere — `feral-ui-check`
   reported both on the product sheet — and a minus that misses is a piece
   removed by accident on a surface with a three-piece ceiling. Quieter 36 stays
   on a pointer, where the law is not the same. */
.qty .q{width:36px;height:36px;border-radius:var(--r-pill);display:grid;place-items:center;
  font:500 15px/1 var(--sans);color:var(--sec);transition:all .15s var(--ease)}
@media (pointer:coarse), (max-width:620px){ .qty .q{width:44px;height:44px} }
.qty .q:hover{background:var(--sunk);color:var(--ink)}
.qty .n{min-width:22px;text-align:center;font:600 14px/1 var(--sans);
  font-variant-numeric:tabular-nums}

/* ── review line, now that it carries a quantity control ────────────── */
.line .lt{min-width:0;flex:1}
.line .qty{margin-left:auto}
.line .p{margin-left:0;min-width:78px}
.line.small .p{min-width:72px}
/* 🚨 `flex-wrap:wrap` ALONE NEVER WRAPPED ANYTHING, AND THAT IS THE BUG.
   MEASURED 2026-08-17 at 390px: the review line's title box was 40.0px wide
   and 4 lines tall, so "404 ACID WASH HOODIE" read as a vertical stack of
   single words next to a full-size thumbnail, quantity stepper and price. The
   cause is that `.line .lt` is `flex:1` with `min-width:0`, which is an
   instruction to SHRINK rather than to wrap — the row always "fits", however
   little is left, so `flex-wrap` had nothing to act on. Giving the title a
   flex-basis of the row minus the thumbnail is what forces the break: the
   piece and its size get row one, the stepper and the price get row two,
   indented past the thumbnail so the block still reads as one line item. */
@media (max-width:560px){
  .line{flex-wrap:wrap;row-gap:10px}
  .line .lt{flex:1 1 calc(100% - 70px)}
  .line .qty{order:9;margin-left:70px}
  .line .p{order:10;margin-left:auto;text-align:right}
  .line.small .lt{flex:1 1 auto}
  .line.small .qty,.line.small .p{margin-left:auto}
}

/* ── the ending ─────────────────────────────────────────────────────── */
.donepill{display:flex;align-items:center;gap:8px;min-height:44px;padding:0 4px;
  font:600 12px/1 var(--sans);color:var(--ink)}
.donepill .tick{width:19px;height:19px;border-radius:var(--r-pill);background:var(--ink);
  color:var(--on-ink);display:grid;place-items:center;font-size:10.5px}
.placed{padding-block:clamp(20px,4vh,44px) 40px;max-width:720px}
.placed .display{margin:12px 0 0;font-size:clamp(32px,7vw,54px)}
.placed .lede{margin:16px 0 0}
.placed .lines{margin-top:20px}
.trackcard{margin:18px 0 0;padding:16px 18px;background:var(--well);
  border-radius:var(--r);display:flex;flex-direction:column;gap:6px;align-items:flex-start}
.trackcard .tt{font:600 15px/1.3 var(--sans);color:var(--ink)}
.trackcard .ts{font:400 13.5px/1.55 var(--sans);color:var(--sec);max-width:52ch}
.trackcard .btn{margin-top:8px}
.trackcard .tnum{margin-top:6px;font:600 14px/1 var(--sans);color:var(--ink);
  font-variant-numeric:tabular-nums}

/* ══ ADDED IN THE THIRD PASS ════════════════════════════════════════════
   Two lanes (a parcel they buy, a parcel we cover), a monthly allowance, and
   per-creator curation. Same token set, same four radii, same single accent.
   🚨 NOTHING BELOW INTRODUCES A COLOUR. In particular the covered lane does
   NOT get a colour of its own: "on us" is stated in words, at --sec, because
   a second accent on a page whose whole argument is one accent would make the
   gift lane look like a different product rather than the same one.
   ══════════════════════════════════════════════════════════════════════ */

/* ── the standing terms, in one sentence ────────────────────────────────
   Under the header on CHOOSE. Numbers in full ink, the rest at --sec, so the
   eye takes "£79" and "1 September" and can leave the sentence alone. */
.standing{margin:8px 0 0;font:400 13.5px/1.6 var(--sans);color:var(--sec);max-width:62ch}
.standing b{color:var(--ink);font-weight:600;font-variant-numeric:tabular-nums}

/* a way back to parcels already sent. A row, not a button: it is navigation
   to a thing that already happened, not an action. */
.seeorders{display:inline-flex;align-items:center;gap:6px;margin:12px 0 0;min-height:44px;
  padding:0 2px;font:600 13px/1 var(--sans);color:var(--ink);
  text-decoration:underline;text-underline-offset:4px}
.seeorders .ch{font-weight:400;opacity:.6;text-decoration:none}
.seeorders:hover{opacity:.75}

/* ── the covered price ──────────────────────────────────────────────────
   ⛔ NEVER A STRIKE-THROUGH AND NEVER BESIDE A "YOU PAY". `.was` is the sale
   lane's evidence of a markdown; there is no markdown here. The word sits
   where `.was` would, at the same weight, so the price column keeps its
   rhythm across both lanes without borrowing its meaning. */
.cov{font:600 12px/1 var(--sans);color:var(--sec);letter-spacing:.04em;text-transform:uppercase}
.prices.big .cov{font-size:13px}
.mrow .money.cov{font:600 13.5px/1 var(--sans);color:var(--ink);letter-spacing:.04em;
  text-transform:uppercase}

/* ── low stock, in the sheet only ───────────────────────────────────────
   ⚠️ NOT RED, AND NOT A BADGE. It is a fact about the shelf, not an alarm.
   Red here would spend the page's one accent on inventory and would read as
   pressure, which is the opposite of the brand. */
.lowline{margin:10px 0 0;font:500 12.5px/1.5 var(--sans);color:var(--mut)}
.lowline b{color:var(--sec);font-weight:600}

/* ── the consent, gift lane only ────────────────────────────────────────
   🚨 A REAL CONTROL WITH A REAL PRESSED STATE. This is what starts a 90-day
   rights clock, so it has to look like something that was pressed on purpose
   and has to be as obviously OFF as it is ON: unpressed it is an empty box on
   the page's ground, pressed it is filled with ink. It is never pre-set.
   ⚠️ The tick is in the box at all times and inherits the box's colour, so
   the two states differ by fill rather than by content and the row cannot
   change height when it flips. */
.consent{display:flex;gap:13px;align-items:flex-start;text-align:left;width:100%;
  margin:18px 0 0;padding:15px 17px;background:var(--well);border:1px solid var(--line);
  border-radius:var(--r);transition:border-color .18s var(--ease),background .18s var(--ease)}
.consent:hover{border-color:var(--line2)}
.consent.on{border-color:var(--line2);background:var(--sunk)}
.consent .cbox{flex:none;width:22px;height:22px;border-radius:6px;margin-top:1px;
  border:1px solid var(--line2);display:grid;place-items:center;
  font:600 12px/1 var(--sans);color:transparent;transition:all .18s var(--ease)}
.consent.on .cbox{background:var(--ink);border-color:var(--ink);color:var(--on-ink)}
.consent .ct{display:flex;flex-direction:column;gap:5px;min-width:0}
.consent .ct b{font:600 14px/1.45 var(--sans);color:var(--ink);letter-spacing:-.005em}
.consent .ct span{font:400 13px/1.55 var(--sans);color:var(--sec)}

/* ── a parcel, on the ending screen ─────────────────────────────────────
   One box per parcel now that a monthly account has more than one. The first
   is open ground; the rest are set back so the newest reads as the answer to
   "where is my order" and the others as history. */
.parcel{margin:20px 0 0}
.parcel:not(.first){padding:16px 18px;background:var(--well);border-radius:var(--r);opacity:.86}
.parcel:not(.first) .lines{margin-top:12px}
.parcel:not(.first) .trackcard{background:var(--sunk)}
.phead{display:flex;align-items:baseline;gap:12px;font:600 13px/1 var(--sans);color:var(--ink)}
.phead .pd{margin-left:auto;font-weight:400;color:var(--mut);font-size:12.5px}
.gonote{font:400 13px/1.5 var(--sans);color:var(--mut);max-width:34ch}

/* the group chooser goes when there is only one group to choose */
.groups.gone{display:none}

/* the rail's ending pill, when the account is not actually ended */
.donepill.back{cursor:pointer}
.donepill.back:hover{opacity:.75}

/* ════════════════════════════════════════════════════════════════════════
   ══ THE ACCOUNT ══ added 2026-08-20 with the dashboard.

   🚨 NOT A SECOND DESIGN SYSTEM. Every rule below is built out of the tokens
   at the top of this file and nothing else: four near-black surfaces, one ink
   ramp, one accent with a counted budget, Inter under 600, Anton on display
   lines only, no monospace anywhere a person can see. A dashboard that arrived
   with its own greys is how a set of surfaces stops looking like one company.

   ⛔ THE RED BUDGET DID NOT GROW. It is still exactly four things — the period
   after a headline, the dot in the primary, the one key fact, and a live error
   — and the rooms below add NONE. The lit rail tab is white-on-ink, the market
   button is a hairline, the meter is ink on sunk. On a near-black ground red
   reads twice as loud as it does on white; a fifth use would take it from an
   accent to a scheme.
   ════════════════════════════════════════════════════════════════════════ */

/* ── the rail ─────────────────────────────────────────────────────────────
   ⚠️ IT REPLACED THE NUMBERED STEPS AND IT REUSES `#steps`. Same node, same
   place in the markup, so the sticky header keeps its measured 60px and the
   phone rules that already exist for it keep applying. */
.tab{display:inline-flex;align-items:center;min-height:38px;padding:0 13px;
  border-radius:var(--r-pill);font:600 13px/1 var(--sans);letter-spacing:-.005em;
  color:var(--faint);transition:color .18s var(--ease),background .18s var(--ease)}
.tab:hover{color:var(--ink)}
.tab.on{background:var(--ink);color:var(--on-ink)}
/* ⚠️ THE TABS SCROLL AND NOTHING SAYS SO, WHICH IS CORRECT HERE. The group
   pills on the shop already behave exactly this way and have since the store
   shipped; a scrollbar under a 38px rail is more chrome than the overflow it
   describes. `min-width:0` is the half everybody forgets — without it a flex
   item refuses to shrink below its content and the row overflows the header
   instead of scrolling inside it. */
/* 🚨 `margin-left:auto`, NOT `flex:1` + `justify-content:flex-end`, AND THAT IS
   A REAL BUG NOT A PREFERENCE. In an OVERFLOWING flex container, `flex-end`
   pushes the overflow off the START edge — where, in every engine, it cannot be
   scrolled back to. Measured at 390x844: the rail painted "Parcels Things You"
   and "Home" and "Shop" were gone, unreachably, with Home the lit tab. Shrinking
   to content and pushing the whole box right gets the desktop alignment with
   none of that: overflow then goes off the END, which scrolls. */
.steps{gap:2px;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none;
  min-width:0;margin-left:auto;scroll-padding-inline:8px}
.steps::-webkit-scrollbar{display:none}
/* ⚠️ 44px ON A PHONE AND 38 ON A POINTER. The estate's law is a 44px tap target
   and the desktop rail wants to be quieter than that; `pointer:coarse` is the
   honest test — it asks what is doing the pressing rather than guessing from a
   width, so a touch laptop gets the big target and a narrow desktop window
   does not get a chunky one for no reason. */
.tab{flex:none}
.mktslot{flex:none;display:flex;align-items:center}

/* ── the market button, and its panel ─────────────────────────────────────
   ⚠️ THE COUNTRY CODE IS THE GLYPH AND THERE IS NO FLAG EMOJI. Flags render
   differently on every platform, several are politically loaded, and on
   Windows they fall back to two letters in a box anyway — which is what this
   draws deliberately, once, everywhere. */
.mktbtn{display:inline-flex;align-items:center;gap:7px;margin-left:8px;flex:none;
  min-height:38px;padding:0 12px;border-radius:var(--r-pill);
  border:1px solid var(--line2);color:var(--sec);
  transition:border-color .18s var(--ease),color .18s var(--ease)}
.mktbtn:hover,.mktbtn.open{border-color:var(--ink);color:var(--ink)}
.mktbtn .flag{font:600 12px/1 var(--sans);letter-spacing:.08em}
.mktbtn .cur{font:500 11.5px/1 var(--sans);color:var(--mut);letter-spacing:.04em}
.mktbtn.open .cur{color:var(--sec)}
/* ══════════════════════════════════════════════════════════════════════════
   🚨 ON A PHONE THE RAIL GOES TO THE BOTTOM, AND THAT IS BECAUSE FIVE TABS DO
   NOT FIT AT THE TOP. NOT A PREFERENCE — ARITHMETIC.

   ⛔ MEASURED 2026-08-20 at 390×844, off the painted header:
        `#steps` visible  188px      content  319px
        the market button starts at x=293 and is 77px wide
        `Things` sits at x=280 and `You` at x=351
     So 131px of the rail was off-screen, `You` was entirely gone, and the two
     that were left sat UNDER the market button — `feral-ui-check` reported it
     as "Things is in the DOM but span.flag answers at its centre", which is the
     hittability law catching a navigation bug nobody had seen.
     Two of the five rooms in a baddie's account — "Things we want you in" and
     "You" — were unreachable on the one device this store exists for, behind a
     horizontal scroll with no affordance whatsoever.

   ⛔ AND IT CANNOT BE PADDED AWAY. 390 − 40 gutter − 46 mark − 32 gaps leaves
     272 for five tabs that need 319 at ZERO padding plus the market control.
     Every version of "make it fit" is a version of "make it smaller than the
     tap law", which is how the last three of these were introduced.

   ⇒ THE TABS TAKE THE BOTTOM OF THE SCREEN, where a thumb already is and where
     every application a baddie already uses puts them. The header keeps the
     mark and the market button and stops being a competition. Above 620px
     nothing changes: the rail is at the top, on the right, where it was.

   ⚠️ IT STACKS UNDER THE BASKET BAR RATHER THAN FIGHTING IT — `.bar` is lifted
   by exactly this bar's height (`--nav`) so the two read as one foot. The
   product sheet is a modal and covers both, so it is not in this negotiation.
   ══════════════════════════════════════════════════════════════════════════ */
/* the phone's bar, and it is invisible above 620px — the desktop rail is the
   one in the header and nothing else changes */
.navbar{display:none}
@media (max-width:620px){
  /* the header rail goes; the bar arrives */
  .steps{display:none}
  .navbar{position:fixed;left:0;right:0;bottom:0;z-index:55;
    display:flex;align-items:stretch;justify-content:space-around;
    background:color-mix(in srgb,var(--paper) 92%,transparent);
    backdrop-filter:saturate(1.6) blur(16px);
    border-top:1px solid var(--line);
    padding:0 4px env(safe-area-inset-bottom)}
  .navbar .tab{flex:1 1 0;min-width:0;justify-content:center;min-height:var(--nav);
    border-radius:0;background:none;padding:0 4px;
    font-size:11.5px;letter-spacing:.01em}
  /* ⚠️ THE LIT TAB IS AN UNDERLINE HERE, NOT A FILLED PILL. A solid white pill
     the height of the bar is the loudest object on the screen, permanently, on
     every room — the accent budget's own failure mode in monochrome. */
  .navbar .tab.on{background:none;color:var(--ink);
    box-shadow:inset 0 2px 0 0 var(--ink)}
  /* the page has to end above the bar, and so does the sticky basket */
  #main{padding-bottom:calc(var(--nav) + env(safe-area-inset-bottom))}
  .bar{bottom:calc(var(--nav) + env(safe-area-inset-bottom));padding-bottom:0}
  /* ⚠️ THE SLOT IS THE FLEX CHILD, NOT THE BUTTON. `margin-left:auto` on
     `.mktbtn` moves it inside a `.mktslot` that is already only as wide as it
     is, so nothing happens — the market pill sat next to the wordmark with 200px
     of empty to its right. */
  .mktslot{margin-left:auto}
}

/* ⚠️ WIDTH *OR* POINTER, AND BOTH ARE NEEDED. `pointer:coarse` is the honest
   test and it is the one a desktop browser at 390px does not satisfy — which
   is exactly the window a session measures in, so a target that is 44px on a
   real phone and 38px in every measurement is a law that only ever gets
   checked in the state where it passes. The width clause makes the two agree. */
@media (pointer:coarse), (max-width:620px){
  .tab{min-height:44px} .mktbtn{min-height:44px}
}
/* 🚨 AND IT SITS *BELOW* `.mktbtn`, NOT ABOVE IT. Placed with the `.tab` rules
   it was overridden by `.mktbtn{min-height:38px}` fifteen lines later — same
   specificity, later wins — so the tabs grew to 44 and the one control beside
   them stayed at 38. Measured at 390px: `mktbtn 77x38`. A media query is not a
   specificity boost, and this is the failure mode that looks like it should be. */

.mktlayer{position:fixed;inset:0;z-index:90}
.mktscrim{position:absolute;inset:0;background:rgba(0,0,0,.66);backdrop-filter:blur(2px)}
.mktpanel{position:absolute;right:clamp(12px,4vw,40px);top:70px;
  width:min(420px,calc(100vw - 24px));max-height:min(72dvh,620px);
  display:flex;flex-direction:column;
  background:var(--well);border:1px solid var(--line2);border-radius:var(--r-l);
  box-shadow:var(--sh-3);overflow:hidden}
.mkthead{padding:20px 20px 14px;flex:none}
.mkthead .h2{margin-bottom:6px}
.mkthead .lede{font-size:13.5px;line-height:1.55;max-width:38ch}
.mktpanel .sheetx{top:12px;right:12px}
.mktlist{flex:1;min-height:0;overflow:auto;padding:0 10px 6px;display:grid;gap:1px}
.mktrow{display:flex;align-items:center;gap:11px;width:100%;text-align:left;
  padding:11px 12px;border-radius:var(--r-s);min-height:44px;color:var(--sec);
  transition:background .14s var(--ease),color .14s var(--ease)}
.mktrow:hover{background:var(--sunk);color:var(--ink)}
.mktrow.on{background:var(--ink);color:var(--on-ink)}
.mktrow .cc{flex:none;width:26px;font:600 11.5px/1 var(--sans);letter-spacing:.08em}
.mktrow .nm{font:500 14px/1.3 var(--sans);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mktrow .mk{margin-left:auto;flex:none;font:500 11px/1 var(--sans);color:var(--mut);
  letter-spacing:.04em;text-transform:uppercase}
.mktrow.on .mk{color:rgba(8,8,10,.55)}
.mktpanel .fine{flex:none;margin:0;padding:12px 20px 18px;border-top:1px solid var(--line);
  font:400 12px/1.55 var(--sans);color:var(--mut)}
@media (max-width:560px){
  /* ⚠️ A SHEET FROM THE BOTTOM ON A PHONE, NOT A DROPDOWN FROM A 38px BUTTON.
     Anchored to the button it would open 70px from the top and run off the
     screen; a thumb is at the other end anyway. */
  .mktpanel{right:0;left:0;top:auto;bottom:0;width:auto;max-height:80dvh;
    border-radius:var(--r-l) var(--r-l) 0 0;border-bottom:0}
}

/* ── home ───────────────────────────────────────────────────────────────── */
.home{padding-block:clamp(26px,5vh,48px) 20px}
.hometop .display{max-width:16ch}
.hometop .lede{margin-top:14px;max-width:44ch}

/* ⚠️ ONE COLUMN ON A PHONE, TWO WHERE THERE IS ROOM, AND THE HERO SPANS BOTH.
   `auto-fit` with a 300px floor rather than a fixed breakpoint: the cards are
   independent objects and the number that fits is a function of the window,
   not of a device somebody named. */
.rooms{margin-top:clamp(22px,4vh,34px);display:grid;gap:clamp(12px,1.6vw,16px);
  grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr))}
.rcard{background:var(--card);border:1px solid var(--line);border-radius:var(--r-l);
  padding:20px;display:flex;flex-direction:column;align-items:flex-start;gap:10px}
.rcard .eyebrow{color:var(--mut)}
.rcard .btn{margin-top:auto}
/* the spend card is the reason the room exists, so it takes the width */
/* ══════════════════════════════════════════════════════════════════════════
   🚨 THE HERO IS TWO COLUMNS WHERE THERE IS ROOM, AND THAT IS A MEASURED FIX.
   At 1440x900 it spanned the full width with everything stacked left, so the
   figure, the sentence and the press occupied a 440px column and the other
   800px of the widest object on the page was empty. A card that spans a
   surface has to USE it or not span it.

   ⚠️ THE PRESS GOES RIGHT AND STAYS VERTICALLY CENTRED against the figure,
   because that is the pair somebody's eye actually makes: "£150" and "choose
   your pieces". The meter, when there is one, spans both columns underneath —
   it is a fact about the whole card, not about either half.
   ⚠️ AND IT COLLAPSES BACK TO ONE COLUMN UNDER 760px, where source order is the
   phone order: figure, sentence, press. */
.rcard.hero{grid-column:1/-1;background:var(--well);border-color:var(--line2);
  padding:clamp(22px,3vw,30px)}
@media (min-width:760px){
  .rcard.hero{display:grid;align-items:center;column-gap:clamp(24px,4vw,56px);
    grid-template-columns:minmax(0,1fr) auto;
    grid-template-areas:"eye  go" "fig  go" "sub  go" "trk  trk" "foot foot"}
  .rcard.hero > .eyebrow{grid-area:eye;align-self:end}
  .rcard.hero > .rbig{grid-area:fig}
  .rcard.hero > .rsub{grid-area:sub}
  .rcard.hero > .track{grid-area:trk;margin-top:14px}
  .rcard.hero > .rfoot{grid-area:foot;margin-top:8px}
  .rcard.hero > .go{grid-area:go;margin-top:0;justify-self:end}
  /* ⚠️ THE STACK FALLBACK RESETS ALIGNMENT, NOT JUST `display`. `.rcard.hero`
     also sets `align-items:center` for the grid, and that survives a bare
     `display:flex` — in a column flex box it centres every child horizontally,
     so the card came out middle-aligned with the eyebrow floating on the right.
     Measured 2026-08-20 on `You, in numbers`. */
  .rcard.hero:not(:has(.rbig)){display:flex;flex-direction:column;align-items:flex-start}
  /* ⚠️ AND `align-self` HAS TO GO WITH IT. The grid layout puts `align-self:end`
     on the eyebrow to sit it on the figure's baseline; in a COLUMN flex box the
     cross axis is horizontal, so the same declaration threw the eyebrow to the
     right-hand edge. Same family as `grid-area-only-binds-a-direct-child`: a
     placement property that is inert in one layout and load-bearing in the
     other, and nothing warns you at the switch. */
  /* ⚠️ AND IT HAS TO OUT-SPECIFY `.rcard.hero > .eyebrow` (0,3,0). `.numhero…`
     alone is (0,2,0) and lost silently — the eyebrow stayed pinned right
     through two rounds of "fixed". Class count, not intent, decides. */
  .rcard.hero:not(:has(.rbig)) > *,
  .younums .rcard.numhero:not(:has(.go)) > *{align-self:flex-start}
}
.rbig{font:600 clamp(38px,9vw,58px)/1 var(--sans);letter-spacing:-.035em;
  font-variant-numeric:tabular-nums}
.rbig.sm{font:600 clamp(19px,2.4vw,23px)/1.25 var(--sans);letter-spacing:-.015em}
.rsub{margin:0;font:400 14px/1.6 var(--sans);color:var(--sec);max-width:52ch}
.rfoot{font:500 12.5px/1 var(--sans);color:var(--mut)}
.rcard .track{width:100%;margin-top:4px}
.rcard .go{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:6px}

/* ══════════════════════════════════════════════════════════════════════════
   THE RAIL — the pieces, on the first screen.

   🚨 IT BLEEDS TO THE VIEWPORT EDGE AND THAT IS THE WHOLE AFFORDANCE. `.wrap`
   holds `padding:0 var(--pad)`; the strip cancels it with a negative margin and
   pays it back as padding, so the first tile lines up with the headline above
   and the last one runs off the right edge. A rail that ends inside the padding
   reads as a row that fits, and nobody scrolls a row that fits.

   ⚠️ NO SECOND ACCENT AND NO NEW RADIUS. The tile is the product card's own
   language at a smaller size — 4:5 shot, title, the two prices — so the shop
   and the dashboard read as one store rather than two designs of it. */
.railwrap{grid-column:1/-1;display:flex;flex-direction:column;gap:12px;
  margin-top:clamp(6px,1vh,12px)}
.railhead{display:flex;align-items:baseline;justify-content:space-between;gap:16px}
/* ⚠️ IT KEEPS A THUMB-SIZED BOX AND COSTS NO LAYOUT. Measured 2026-08-20:
   `min-height:0` made "See all 104" a 67×13 target — under the 44px law, on the
   surface the law was written for. The padding gives it the box back and the
   negative margin gives the row its height back. */
.railhead .linkbtn{min-height:44px;padding:0;
  display:inline-flex;align-items:center;margin-block:-14px}
/* ⚠️ `scroll-padding-inline` OR THE STRIP OPENS 20px SCROLLED. Measured
   2026-08-20: snapping aligns a tile to the container's SCROLLPORT edge, which
   ignores the padding that produces the bleed — so the first tile snapped flush
   to the viewport and the rail started out of line with its own heading. */
.railstrip{display:flex;gap:12px;overflow-x:auto;scroll-snap-type:x proximity;
  scrollbar-width:none;-webkit-overflow-scrolling:touch;
  margin-inline:calc(var(--pad) * -1);padding-inline:var(--pad);padding-bottom:2px;
  scroll-padding-inline:var(--pad)}
.railstrip::-webkit-scrollbar{display:none}
.tile{flex:none;width:clamp(140px,38vw,176px);scroll-snap-align:start;
  display:flex;flex-direction:column;gap:0;text-align:left;padding:0;background:none}
.tshot{position:relative;aspect-ratio:4/5;width:100%;background:var(--sunk);
  border-radius:var(--r);overflow:hidden}
.tshot img{width:100%;height:100%;object-fit:cover;display:block;
  transition:transform .85s var(--ease)}
.tile:hover .tshot img{transform:scale(1.04)}
.tshot .none{position:absolute;inset:0;display:grid;place-items:center;color:var(--faint);
  font:600 12px/1 var(--sans);letter-spacing:.2em}
.ttitle{margin-top:10px;font:600 12.5px/1.35 var(--sans);color:var(--ink);
  letter-spacing:-.008em;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden}
.prices.sm{margin-top:4px;gap:6px}
.prices.sm .now{font-size:13.5px}
.prices.sm .was{font-size:11.5px}
.prices.sm .cov{font-size:11px}
/* ⚠️ THE TAIL TILE IS THE END OF THE RAIL, SAID OUT LOUD. Without it the last
   tile is simply cut off and there is no way to know whether that was twelve
   pieces or a hundred. */
.tile.tail{justify-content:center;align-items:flex-start;gap:4px;
  border:1px solid var(--line);border-radius:var(--r);padding:0 16px;
  aspect-ratio:auto;align-self:stretch}
.tile.tail:hover{border-color:var(--line2)}
.tile.tail .tn{font:600 24px/1 var(--sans);color:var(--ink);letter-spacing:-.03em;
  font-variant-numeric:tabular-nums}
.tile.tail .tl{font:400 12px/1.35 var(--sans);color:var(--sec)}
@media (prefers-reduced-motion:reduce){ .tile:hover .tshot img{transform:none} }

/* ── your person at FERAL ─────────────────────────────────────────────────
   🚨 THE FACE IS THE CARD. A name and an address is a support desk; the person
   who actually replies is what makes a platform feel like people. 56px because
   it has to read as a photograph of somebody and not as an avatar chip. */
.prow{display:flex;align-items:center;gap:14px;width:100%}
.face{width:56px;height:56px;border-radius:var(--r-pill);flex:none;overflow:hidden;
  background:var(--sunk);display:grid;place-items:center;
  font:600 20px/1 var(--sans);color:var(--mut);text-transform:uppercase}
.face img{width:100%;height:100%;object-fit:cover}
.pwho{min-width:0}
.pn{font:600 16px/1.25 var(--sans);letter-spacing:-.012em}
.pl{font:400 13.5px/1.5 var(--sans);color:var(--sec);margin-top:3px}

/* ── the facts that are pairs ─────────────────────────────────────────── */
.kv{width:100%;display:grid;gap:1px;background:var(--line);border-radius:var(--r);overflow:hidden}
.kvr{display:flex;justify-content:space-between;align-items:baseline;gap:14px;
  background:var(--card);padding:10px 13px;font:400 13.5px/1.4 var(--sans);color:var(--sec)}
.kvr b{color:var(--ink);font-weight:600;font-variant-numeric:tabular-nums}

/* ══════════════════════════════════════════════════════════════════════════
   YOU, IN NUMBERS — the stat block above the form.

   ⚠️ IT REUSES `.rcard`, `.rbig`, `.kv` AND `.track` RATHER THAN INVENTING A
   SECOND SET. The dashboard already owns a language for "a figure on a card"
   and a second one would be the feral·inbox failure — 22 of 60 values copied
   out of a system and the meaning-carrying ones dropped.
   ══════════════════════════════════════════════════════════════════════════ */
.younums{margin-top:clamp(22px,4vh,34px);display:grid;gap:clamp(12px,1.6vw,16px);
  grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr))}
.younums .rcard{gap:12px}
/* the closing line sits on the floor of its card, so two cards of different
   content length still read as a pair rather than as one with a hole in it */
.younums .rcard > .rfoot{margin-top:auto;padding-top:4px}
.numhero .rsub{max-width:46ch}
/* ⚠️ THE POPULATED HERO HAS NO PRESS, SO IT MUST NOT TAKE THE TWO-COLUMN GRID.
   `.rcard.hero` puts a `.go` in a right-hand column at ≥760px; with nothing to
   put there the card was a figure in its left half and 320px of empty in its
   right. Same rule the empty-state card already carries one block up. */
.numhero:not(:has(.go)){display:flex;flex-direction:column;align-items:flex-start}
/* the second heading in the room: the admin, under its own line */
.youhead{margin-top:clamp(34px,6vh,56px);padding-top:clamp(24px,4vh,34px);
  border-top:1px solid var(--line);display:block;color:var(--mut)}
.lede.small{margin-top:10px;font-size:14.5px;max-width:52ch}

/* ── the rhythm: one bar a month ─────────────────────────────────────────
   ⚠️ ONE INK, HEIGHT AS THE ONLY VARIABLE, AND NO AXIS. Two numbers are named
   in the line underneath — the tallest month and how many there are — which is
   everything a reader can do anything with. A y-axis on a six-bar chart in a
   300px card is furniture. */
/* ⚠️ THE COLUMNS ARE CAPPED, NOT JUST FLEXED. Four months in a 300px card gave
   68px-wide bars, which read as four white blocks rather than as a series — the
   eye compares HEIGHTS and a bar wider than it is tall stops looking like one. */
.months{width:100%;display:flex;align-items:flex-end;gap:8px;height:84px}
/* ⚠️ THE COLUMN FILLS, THE BAR IS CAPPED. Capping the COLUMN clumped four
   months into the left 200px of a 380px card with the rest empty; capping the
   bar inside a full-width column spreads them evenly at any count and keeps a
   four-month chart from painting four fat white blocks. */
.mcol{flex:1 1 0;min-width:0;display:flex;flex-direction:column;
  justify-content:flex-end;height:100%;gap:7px}
/* ⚠️ THE FILL IS ROUNDED AT THE TOP ONLY. `--r-s` on all four corners of a
   44px-wide bar reads as a floating pill rather than as a column standing on a
   baseline — and a bar that does not touch its own axis is a bar the eye
   cannot compare. Same radius, three corners fewer. */
.mbar{flex:1;min-height:0;width:100%;max-width:44px;margin-inline:auto;
  display:flex;align-items:flex-end;
  background:var(--sunk);border-radius:var(--r-s) var(--r-s) 0 0;overflow:hidden}
.mbar i{display:block;width:100%;background:var(--ink);
  border-radius:var(--r-s) var(--r-s) 0 0}
.mlab{font:500 10.5px/1 var(--sans);color:var(--mut);text-align:center;
  letter-spacing:.04em;text-transform:uppercase}

/* ── things we want you in ──────────────────────────────────────────────── */
.things{padding-block:clamp(26px,5vh,48px) 20px}
.things .display{max-width:16ch;margin-top:14px}
.things .lede{margin-top:14px;max-width:48ch}
.opps{margin-top:clamp(22px,4vh,32px);display:grid;gap:clamp(12px,1.6vw,16px);
  grid-template-columns:repeat(auto-fit,minmax(min(320px,100%),1fr))}
.opp{background:var(--card);border:1px solid var(--line);border-radius:var(--r-l);overflow:hidden;
  display:flex;flex-direction:column}
.opp.answered{opacity:.9}
.oshot{aspect-ratio:16/9;background:var(--sunk);overflow:hidden}
.oshot img{width:100%;height:100%;object-fit:cover}
.obody{padding:18px 20px 20px;display:flex;flex-direction:column;gap:9px;flex:1}
.otitle{font:600 18px/1.25 var(--sans);letter-spacing:-.018em}
.ometa{font:500 12px/1 var(--sans);letter-spacing:.06em;text-transform:uppercase;color:var(--mut)}
.oblurb{margin:0;font:400 14px/1.6 var(--sans);color:var(--sec)}
/* ⚠️ THE ASK CARRIES THE RED DOT AND IT IS THE ONLY RED IN THIS ROOM. What is
   actually wanted from somebody is the thing they will be held to; the budget
   is spent on that and not on the heading. */
.oask{display:flex;gap:9px;align-items:flex-start;padding:11px 13px;background:var(--sunk);
  border-radius:var(--r);font:500 13.5px/1.5 var(--sans);color:var(--ink)}
.oask .dot{width:7px;height:7px;border-radius:var(--r-pill);background:var(--red);margin-top:6px;flex:none}
.oclose{font:500 12.5px/1 var(--sans);color:var(--mut)}
.oans{display:flex;gap:9px;align-items:center;flex-wrap:wrap;margin-top:auto;padding-top:6px}
/* ⚠️ BOTH ANSWERS STAY PRESSABLE AFTER ONE IS GIVEN. Somebody who said no on
   Tuesday and can make it on Thursday must not have to email anybody. */
.oans .btn.ghost.on{border-color:var(--ink);color:var(--ink)}
.osaid{font:400 12.5px/1.4 var(--sans);color:var(--mut)}

/* ── how we know you ────────────────────────────────────────────────────── */
/* ⚠️ SAME `margin-inline` AS `.review`, AND FOR THE SAME REASON. `.wrap` centres
   itself, so narrowing it on the same element centres the whole room — which
   started this form 180px right of the demo banner directly above it. */
.you{padding-block:clamp(26px,5vh,48px) 40px;
  max-width:calc(820px + var(--pad) * 2);margin-inline:0 auto}
.you .display{max-width:14ch;margin-top:14px}
.you .lede{margin-top:14px;max-width:46ch}
.youform{margin-top:clamp(24px,4vh,34px);display:grid;gap:clamp(16px,2.4vw,22px)}
.fieldset{background:var(--card);border:1px solid var(--line);border-radius:var(--r-l);padding:20px}
.flabel{font:600 10.5px/1 var(--sans);letter-spacing:.22em;text-transform:uppercase;
  color:var(--mut);margin-bottom:14px}
.fgrid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(min(200px,100%),1fr))}
.field{display:flex;flex-direction:column;gap:6px;min-width:0}
.field.wide{grid-column:1/-1}
.fl{font:500 12.5px/1 var(--sans);color:var(--sec)}
/* ⚠️ 48px MINIMUM AND 16px TEXT. Under 16px iOS Safari zooms the whole page on
   focus and does not zoom back out, which turns a five-field form into a
   pinch-and-scroll. This is the one place a font size is a functional
   requirement rather than a typographic choice. */
.field input{width:100%;min-height:48px;background:var(--sunk);color:var(--ink);
  border:1px solid var(--line2);border-radius:var(--r-s);padding:0 14px;
  font:400 16px/1 var(--sans);transition:border-color .18s var(--ease)}
.field input:focus{border-color:var(--ink);outline:none}
.field input::placeholder{color:var(--faint)}
.fixedrow{display:flex;gap:12px;align-items:baseline;flex-wrap:wrap;
  font:600 16px/1.4 var(--sans);letter-spacing:-.012em}
.fixedrow .m{font:400 14px/1.4 var(--sans);color:var(--sec)}
.fnote{margin:12px 0 0;font:400 12.5px/1.55 var(--sans);color:var(--mut);max-width:56ch}
.youform .go{display:flex;gap:14px;align-items:center;flex-wrap:wrap}

/* ── the two ways in ────────────────────────────────────────────────────── */
.enterform{display:grid;gap:14px;margin:26px 0 0;max-width:400px}
.enterform .go{margin:4px 0 0}
.enterform .btn{width:100%}
/* the live error under a form: one red dot's worth of the budget, no more */
.formerr:empty{display:none}
.formerr{display:flex;gap:9px;align-items:flex-start;background:var(--red-soft);
  border:1px solid var(--red-line);border-radius:var(--r);padding:11px 13px;
  font:500 13.5px/1.5 var(--sans);color:var(--ink)}

.signin{min-height:calc(100dvh - 60px);display:grid;place-content:center;
  padding-block:clamp(28px,6vh,64px)}
.signbox{width:min(430px,100%)}
.signbox .display{margin:14px 0 0;max-width:12ch}
.signbox .lede{margin:14px 0 0}
.signbox .enterform{max-width:none}
.signbox .fine{margin:20px 0 0;font:400 13px/1.6 var(--sans);color:var(--mut)}
.signbox .fine a{color:var(--ink);text-underline-offset:3px}

/* ── what just happened ─────────────────────────────────────────────────── */
.flash{margin:16px 0 0;padding:12px 15px;background:var(--well);border-radius:var(--r);
  border-left:2px solid var(--line2);font:500 13.5px/1.5 var(--sans);color:var(--sec)}

/* ── the footer, now that it holds a control ────────────────────────────── */
.linkbtn{font:500 13px/1 var(--sans);color:var(--sec);min-height:44px;
  display:inline-flex;align-items:center;text-underline-offset:3px;text-decoration:underline}
.linkbtn:hover{color:var(--ink)}
.linkbtn.quiet{color:var(--mut)}
