/* ---------------------------------------------------------------------------
   TWNW — prototype storefront

   Monochrome, typographic, product-forward. The reference brands the range was
   benchmarked against (ACRONYM, VTMNTS, Enfants Riches Déprimés, TOVE) all
   share one habit: they refuse to explain themselves. Navigation is three or
   four words, product metadata is terse and coded, and the page is mostly
   empty. That restraint is doing real work here — a campaign shop that shouts
   reads like a charity mailer, and a campaign shop that whispers reads like
   something you would actually wear.

   Colour is deliberately near-monochrome. The single acid accent exists to
   mark one thing per page and is never used for decoration.
   --------------------------------------------------------------------------- */

:root {
  --ink: #0A0A0A;
  --paper: #F4F4F1;
  --card: #FFFFFF;   /* product photography is shot on white; drawings match it */
  --line: #D7D7D0;
  --muted: #75756E;
  --accent: #B7F32B;

  --gutter: clamp(18px, 4vw, 56px);
  --head: 61px;   /* height of the sticky header; sticky panes offset by it */
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
  --sans: "Helvetica Neue", Helvetica, Arial, sans-serif;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 15px/1.55 var(--sans);
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
img, svg { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
h1, h2, h3, p, figure, ul { margin: 0; }
ul { padding: 0; list-style: none; }

/* Shared type ------------------------------------------------------------- */

.mono {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.display {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: -0.035em;
  line-height: 0.86;
}

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

/* Header ------------------------------------------------------------------ */

.head {
  position: sticky;
  top: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 15px var(--gutter);
  background: var(--paper);
  border-bottom: 1px solid var(--line);
}

.head__mark { width: clamp(104px, 12vw, 140px); flex: 0 0 auto; }

.nav { display: flex; gap: clamp(14px, 2.4vw, 30px); }
.nav a { padding: 4px 0; border-bottom: 1px solid transparent; }
.nav a:hover, .nav a[aria-current] { border-bottom-color: var(--ink); }

/* Never break a label across lines. "The case" splitting into "The" above
   "case", and "Basket" above "(1)", is what made the header read as broken on a
   phone. If the row genuinely cannot fit, wrap between whole items instead and
   keep them hard right. */
.nav > * { white-space: nowrap; }

/* THE BASKET LIGHTS UP ONLY WHEN IT HOLDS SOMETHING. Empty, it is a link like
   any other and should stay quiet — a permanently acid header trains people to
   ignore it. Full, it is the one thing on the page that has to be findable,
   because the failure it fixes is a customer who has chosen and cannot see
   where to pay.

   PAINTED BY paintCount() IN store.js, not by the markup, so every page
   carrying the header gets it without editing seven files.

   IT FOLLOWS .mini--guide, which is the house pattern for an acid control:
   acid ground, ink text, and a hover that INVERTS rather than dims, because a
   grey hover on an acid chip looks disabled. */
.nav a.nav__basket--full {
  padding: 4px 10px;
  color: var(--ink);
  background: var(--accent);
  border-bottom-color: transparent;
}
.nav a.nav__basket--full:hover {
  color: var(--accent);
  background: var(--ink);
  border-bottom-color: transparent;
}

@media (max-width: 620px) {
  .head { gap: 12px; padding: 12px var(--gutter); }
  .head__mark { width: 86px; }
  .nav { gap: 13px; flex-wrap: wrap; justify-content: flex-end; row-gap: 4px; }
  .nav > * { font-size: 10px; letter-spacing: 0.05em; }
}

/* At 320px the row is 15px short once the basket reaches two digits. Trimming
   the mark and the gaps buys that back, so the nav holds one line on the
   narrowest phones still in use. */
@media (max-width: 380px) {
  .head__mark { width: 76px; }
  .nav { gap: 10px; }
  /* The chip gives its padding back here. The note above measured the row
     with a plain link; 20px of chip would spend the 15px that trimming won. */
  .nav a.nav__basket--full { padding: 3px 6px; }
}

/* Hero -------------------------------------------------------------------- */

/* Top padding was set when a full-width lockup sat above the statement. With
   the statement leading, that much air just reads as a gap under the header. */
.hero {
  padding: clamp(30px, 5.5vh, 72px) var(--gutter) clamp(36px, 6vh, 76px);
  border-bottom: 1px solid var(--line);
}

/* The page lands on the statement, not the mark. TW≠NW reads as nothing to a
   first-time visitor, and at hero size it pushed the subline and both buttons
   below the fold on a laptop. The header carries the identity instead. */

/* Each sentence is its own block so the break always falls between them —
   left to wrap on its own the antithesis splits mid-phrase, which kills it.
   Within a sentence, balance the rag when it does have to wrap. */
.hero__line {
  font-size: clamp(1.85rem, 4.8vw, 3.9rem);
  /* Generous, because `ch` is measured on "0" and these are caps, which are
     wider. The span blocks below already guarantee the sentence break, so this
     only needs to stay out of the way until the viewport forces a wrap. */
  max-width: 40ch;
}

.hero__line span { display: block; text-wrap: balance; }

/* Statement left, anchor piece right. The garment is sized in viewport height
   rather than width so the whole hero — argument, buttons and product — still
   clears the fold on a laptop. Below the breakpoint it simply stacks under the
   buttons, so the page still lands on the argument. */
.hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(36px, 5vw, 64px);
  align-items: center;
}

@media (min-width: 1000px) {
  .hero__grid { grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr); }
}

.piece { display: block; }

/* The photograph is shot on white, and white floating on tinted paper reads as
   a mistake. Given an edge it reads as a plate, and matches the shop cards. */
.piece img {
  width: auto;
  height: clamp(230px, 40vh, 440px);
  max-width: 100%;
  margin: 0 auto;
  object-fit: contain;
  background: var(--card);
  border: 1px solid var(--line);
  transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.piece:hover img { transform: scale(1.02); }

.piece__meta {
  display: block;
  margin-top: 18px;
  color: var(--muted);
  text-align: center;
}

.piece:hover .piece__meta { color: var(--ink); }

.hero__sub {
  margin-top: clamp(20px, 3vw, 34px);
  max-width: 46ch;
  font-size: clamp(1rem, 1.5vw, 1.32rem);
  line-height: 1.4;
}

.hero__actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 34px; }

/* Buttons ----------------------------------------------------------------- */

.btn {
  display: inline-block;
  padding: 14px 26px;
  border: 1px solid var(--ink);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  transition: background 0.14s, color 0.14s;
}

.btn:hover { background: var(--ink); color: var(--paper); }
.btn--solid { background: var(--ink); color: var(--paper); }
.btn--solid:hover { background: var(--accent); color: var(--ink); border-color: var(--accent); }
.btn--wide { width: 100%; text-align: center; }
.btn[disabled] { opacity: 0.35; pointer-events: none; }

/* Generic section --------------------------------------------------------- */

.section { padding: clamp(48px, 8vw, 104px) var(--gutter); border-bottom: 1px solid var(--line); }
.section__head { display: flex; justify-content: space-between; align-items: baseline; gap: 20px; margin-bottom: clamp(24px, 4vw, 44px); }

/* Page heading ------------------------------------------------------------ *

   ONE TEMPLATE FOR EVERY PAGE THAT IS NOT THE FRONT PAGE. There were five
   different treatments: the shop carried an inline font-size override, /chain
   was set at 14vw and broke "The chain" across two lines, /chains inherited the
   browser's h1 and looked like a subheading, and The case and Info had no
   heading at all — they opened on a grey lead paragraph, which is the quietest
   possible way to begin an argument.

   The scale is the shop's, which was the one that worked, and the line-height
   is the hero's 0.86. Anything added later gets this and looks like it belongs
   without anybody having to decide again.

     <header class="page-head">
       <p class="page-head__eyebrow mono">optional context</p>
       <h1 class="page-head__title display">THE CASE FOR CHANGE</h1>
       <p class="page-head__lead">One sentence saying what this page is.</p>
     </header>
*/
.page-head { margin-bottom: clamp(30px, 5vw, 56px); }

/* THE EYEBROW'S SIZE IS PINNED HERE, AND IT WAS NEVER CHOSEN BEFORE.

   Found 27 August 2026 when the shop's eyebrow came out visibly smaller than
   every other page's. Nothing was wrong with the markup: the size was an
   accident of WHERE the element sat.

     .mono      font-size: 11px    specificity 0,0,1,0
     .prose p   font-size: 16px    specificity 0,0,1,1   <- wins, and comes later

   So an eyebrow INSIDE .prose renders at 16px and an identical one outside it
   renders at 11px. Five pages happen to nest theirs in .prose; the shop's sits
   in a flex row beside "Delivery calculated at checkout" and cannot, because
   .prose is a 62ch measure. Same classes, same template, two sizes.

   THE NEAR MISS THAT PROVES NOBODY MEANT IT: `.prose p:not(.mono)` two hundred
   lines below excludes mono paragraphs from the prose COLOUR. Somebody saw this
   exact interaction for one property and never extended it to the other.

   SO THE TEMPLATE NOW PINS ITS OWN SIZE rather than inheriting one. `.mono` and
   `.page-head__eyebrow` together beat `.prose p`, so it is 16px wherever it is
   put — which is what the template promised in the first place: "anything added
   later gets this and looks like it belongs without anybody having to decide
   again." It could not keep that promise while the answer depended on ancestry.

   16px because that is what the five shipped pages already render. The value is
   not new; only the fact that it is now stated rather than inherited. */
.page-head__eyebrow { margin: 0 0 18px; color: var(--muted); }
.page-head__eyebrow.mono { font-size: 16px; }

.page-head__title {
  font-size: clamp(2rem, 5vw, 3.6rem);
  line-height: 0.86;
  /* Wide enough that the short titles hold one line, narrow enough that a long
     one breaks somewhere deliberate rather than at the window edge. */
  max-width: 20ch;
  text-wrap: balance;
  margin: 0;
}

/* Only when a lead follows, so a title on its own does not carry a trailing gap
   the next section then adds to. */
.page-head__title + .page-head__lead { margin-top: clamp(16px, 2.5vw, 24px); }

.page-head__lead {
  font-size: clamp(1.2rem, 2.2vw, 1.6rem);
  line-height: 1.35;
  letter-spacing: -0.012em;
  max-width: 46ch;
  margin: 0;
}

/* NESTED INSIDE .prose, THE LEAD LOST ITS SIZE. `.prose p` is (0,1,1) and
   `.page-head__lead` is (0,1,0), so the ordinary paragraph rule won and every
   page-head lead inside prose rendered at 16px — the size of body copy, on the
   one line meant to carry the page. /mats/ escaped it only because it uses
   `.prose p.lead`, which is more specific again.

   Found 26 August 2026 by measuring the computed size rather than reading the
   rule: the rule was correct, present and parsed, and simply outranked. */
.prose .page-head__lead {
  font-size: clamp(1.2rem, 2.2vw, 1.6rem);
  line-height: 1.35;
  letter-spacing: -0.012em;
}

.statement {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(24px, 4vw, 56px);
  max-width: 1500px;
}

.statement p { font-size: clamp(1.28rem, 2.7vw, 2.1rem); line-height: 1.24; letter-spacing: -0.015em; max-width: 24ch; }
.statement p + p { max-width: 34ch; font-size: clamp(1rem, 1.35vw, 1.16rem); line-height: 1.55; color: var(--muted); }

@media (min-width: 860px) {
  .statement { grid-template-columns: 1.15fr 1fr; align-items: start; }
}

/* Product grid ------------------------------------------------------------ */

.grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(10px, 1.4vw, 20px) clamp(10px, 1.2vw, 16px);
}

@media (min-width: 720px) { .grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1080px) { .grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* Anchored section headings must clear the sticky header when jumped to. */
[id] { scroll-margin-top: calc(var(--head) + 24px); }

.group { margin: clamp(38px, 5vw, 64px) 0 16px; }
.group:first-of-type { margin-top: 0; }

/* A rail rather than a truncated grid: four visible, the rest one arrow or one
   swipe away. It is a real scroll container, so touch works with no JS. */
.rail {
  display: flex;
  gap: clamp(10px, 1.2vw, 16px);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.rail::-webkit-scrollbar { display: none; }
.rail .card { flex: 0 0 calc((100% - 3 * clamp(10px, 1.2vw, 16px)) / 4); scroll-snap-align: start; }

@media (max-width: 1080px) { .rail .card { flex-basis: calc((100% - 2 * clamp(10px, 1.2vw, 16px)) / 3); } }
@media (max-width: 720px) { .rail .card { flex-basis: calc((100% - clamp(10px, 1.2vw, 16px)) / 2); } }

.rail__nav { display: flex; align-items: center; gap: 10px; }

.rail__btn {
  width: 34px;
  height: 34px;
  border: 1px solid var(--line);
  font-size: 16px;
  line-height: 1;
  transition: background 0.14s, color 0.14s, border-color 0.14s;
}

.rail__btn:hover:not([disabled]) { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.rail__btn[disabled] { opacity: 0.28; cursor: default; }

.card { display: block; }

/* Product drawings have slightly different native proportions, so the figure
   fixes the ratio and lets the artwork sit inside it. Without this the grid
   rows go ragged wherever a tote sits next to a hoodie. */
.card__fig { position: relative; aspect-ratio: 1 / 1; background: var(--card); overflow: hidden; }
.card__fig img { width: 100%; height: 100%; object-fit: contain; transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.2, 1); }

/* The photographs sit at about 78% of their frame; a drawing fills its frame
   edge to edge. Insetting the drawings keeps the two at a comparable size when
   they appear in the same grid, until the rest of the range is photographed. */
.card__fig img.drawn { padding: 10%; }
.card:hover .card__fig img { transform: scale(1.035); }

.card__tag {
  position: absolute;
  top: 10px;
  left: 10px;
  padding: 4px 8px;
  background: var(--accent);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

/* Out of stock is information, not promotion — it should read as a state,
   not as another badge competing for attention. */
.card__tag--off { background: transparent; color: var(--muted); border: 1px solid var(--line); }

/* Coming soon is a state too, but the opposite kind: nothing has gone wrong and
   the answer is "not yet" rather than "not now". So it holds its weight — ink
   on paper — instead of receding like OFF, while staying off the accent, which
   belongs to things you can actually put in a basket. Three badge treatments
   for three different messages, and none of them mistakable for another. */
.card__tag--soon { background: var(--ink); color: var(--paper); }

.card__meta { display: flex; justify-content: space-between; gap: 12px; margin-top: 10px; }
.card__name { font-size: 13px; }
.card__code { color: var(--muted); }
.card__price { font-family: var(--mono); font-size: 12px; white-space: nowrap; }

/* Product detail ---------------------------------------------------------- */

.pdp { display: grid; grid-template-columns: minmax(0, 1fr); }

.pdp__media {
  display: flex;
  align-items: stretch;
  justify-content: center;
  background: var(--card);
}

/* One large view with thumbnails beneath. A product with a single picture
   hides the strip, so there is one code path rather than two layouts. */
.gallery {
  display: flex;
  flex-direction: column;
  gap: 14px;
  width: 100%;
  min-height: 0;
}

.gallery__main { flex: 1 1 auto; min-height: 0; width: 100%; object-fit: contain; }

/* The picture sits in a frame it stays centred in whatever its shape, with the
   step arrows over the edges rather than beside them — so the image keeps the
   full width of the column instead of losing it to controls. */
.gallery__frame { position: relative; flex: 1 1 auto; min-height: 0; display: flex;
  align-items: center; justify-content: center; }
.gallery__frame .gallery__main { max-width: 100%; }

.gallery__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 40px; height: 40px; border: 1px solid var(--line); border-radius: 50%;
  background: var(--card); color: var(--ink); font-size: 22px; line-height: 1;
  opacity: 0.35; transition: opacity 0.14s; cursor: pointer; z-index: 1;
}
.gallery__nav:hover, .gallery__nav:focus-visible { opacity: 1; }
.gallery__nav--prev { left: 6px; }
.gallery__nav--next { right: 6px; }
.gallery__nav[hidden] { display: none; }

/* THE STRIP SCROLLS INSIDE THE COLUMN RATHER THAN SETTING ITS WIDTH. min-width
   on the scroller is what actually does it: a flex item defaults to min-content,
   which for twenty thumbnails is twenty thumbnails wide. */
.gallery__strip { flex: 0 0 auto; display: flex; align-items: center; gap: 6px; min-width: 0; }
.gallery__strip[hidden] { display: none; }
.gallery__scroll { flex: 0 0 auto; width: 26px; height: 64px; border: 1px solid var(--line);
  background: var(--card); color: var(--ink); opacity: 0.45; cursor: pointer; }
.gallery__scroll:hover { opacity: 1; }
.gallery__thumbs { flex: 1 1 auto; min-width: 0; display: flex; gap: 8px;
  overflow-x: auto; scroll-behavior: smooth; scrollbar-width: thin; padding-bottom: 2px; }
.gallery__thumbs[hidden] { display: none; }
/* Same guard as the line above, for the same reason: a class that sets any
   display would beat the [hidden] attribute on specificity and the arrow would
   stay on screen. .gallery__scroll does not set one today, so this is insurance
   against the next edit rather than a fix. */
.gallery__scroll[hidden] { display: none; }
.gallery__thumb { flex: 0 0 auto; }

.gallery__thumb {
  width: 64px;
  height: 64px;
  padding: 5px;
  border: 1px solid var(--line);
  background: var(--card);
  transition: border-color 0.14s;
}

.gallery__thumb img { width: 100%; height: 100%; object-fit: contain; }
.gallery__thumb:hover { border-color: var(--muted); }
.gallery__thumb[aria-pressed="true"] { border-color: var(--ink); }

/* A product drawing has an aspect ratio but no intrinsic pixel size, so left to
   itself an <img> takes the full width of its column — which on a wide screen
   makes it taller than the window and pushes the bottom of the garment below
   the fold. Both panes are pinned to exactly one viewport instead, with the
   drawing contained inside. */
@media (min-width: 900px) {
  /* HALF AND HALF, AND minmax(0) SO IT STAYS THAT WAY. Without the minmax a
     grid column cannot shrink below its contents, so twenty thumbnails in a row
     that neither wrapped nor scrolled dragged the media side open and squeezed
     the buying panel to a third of the page. The split is a decision about the
     page, not a consequence of how many pictures a product happens to have. */
  .pdp { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }

  .pdp__media {
    position: sticky;
    top: var(--head);
    height: calc(100vh - var(--head));
    height: calc(100dvh - var(--head));
    padding: clamp(16px, 2.5vw, 44px);
    border-right: 1px solid var(--line);
  }

  .pdp__media img { width: auto; height: 100%; max-width: 100%; object-fit: contain; }

  .pdp__side {
    position: sticky;
    top: var(--head);
    align-self: start;
    max-height: calc(100vh - var(--head));
    max-height: calc(100dvh - var(--head));
    overflow-y: auto;
  }
}

/* Single column: still keep the drawing above the fold so the detail below it
   is discoverable without a scroll of pure image. */
@media (max-width: 899px) {
  .pdp__media { padding: 18px; }
  /* Scoped to the main view — the thumbnails size themselves. */
  .gallery__main { max-height: 58vh; max-height: 58dvh; }
}
.pdp__side { padding: clamp(28px, 4vw, 56px) var(--gutter); }
.pdp__name { font-size: clamp(1.7rem, 3.4vw, 2.9rem); margin: 10px 0 6px; }
.pdp__price { font-family: var(--mono); font-size: 15px; margin-bottom: 26px; }
.pdp__desc { max-width: 44ch; margin-bottom: 28px; }

.opts { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 26px; }
.opt { padding: 11px 16px; border: 1px solid var(--line); font-family: var(--mono); font-size: 11px; letter-spacing: 0.06em; }
.opt:hover { border-color: var(--ink); }
.opt[aria-pressed="true"] { background: var(--ink); color: var(--paper); border-color: var(--ink); }

.spec { border-top: 1px solid var(--line); margin-top: 34px; padding-top: 20px; }
.spec li { display: flex; justify-content: space-between; gap: 16px; padding: 9px 0; border-bottom: 1px solid var(--line); font-size: 13px; }
.spec li span:first-child { color: var(--muted); font-family: var(--mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; }
.spec li span:last-child { text-align: right; }

/* Money strip ------------------------------------------------------------- */

.money { background: var(--ink); color: var(--paper); padding: clamp(48px, 8vw, 104px) var(--gutter); }
.money h2 { font-size: clamp(1.7rem, 4.4vw, 3.4rem); max-width: 20ch; margin-bottom: 34px; }
.money__flow { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1px; background: #262622; border: 1px solid #262622; }

@media (min-width: 760px) { .money__flow { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.money__step { background: var(--ink); padding: 26px 22px; }
.money__step h3 { font-family: var(--mono); font-size: 11px; letter-spacing: 0.09em; text-transform: uppercase; color: var(--accent); margin-bottom: 12px; }
.money__step p { font-size: 14px; color: #C9C9C2; }

/* Case page --------------------------------------------------------------- */

.prose { max-width: 62ch; }
.prose h2 { font-size: clamp(1.4rem, 2.6vw, 2rem); letter-spacing: -0.02em; margin: 48px 0 14px; }
.prose h2:first-child { margin-top: 0; }
/* BODY COPY IS INK. THE TEMPLATE INSISTS.
 *
 * .muted and .mono are for LABELS — a column heading, a SKU, a stat caption, a
 * line of metadata under a figure. They are not for sentences somebody is meant
 * to read, and they had drifted into being exactly that: whole paragraphs of
 * argument set in grey on /chain, /chains and /mats, and on Info a paragraph of
 * measuring instructions set in monospace.
 *
 * Grey copy undercuts an argument the rest of the site makes in black and
 * white, and there is no version of "this sentence matters slightly less" that
 * is worth saying in a colour.
 *
 * So this is a rule rather than a convention: a paragraph inside .prose is ink
 * whatever classes it carries. The :not(.mono) exemption keeps genuine labels —
 * "Put their address in at the checkout" — grey, because those are captions and
 * are set in the label face to prove it. If a sentence needs to be quieter,
 * make it shorter. */
.prose p:not(.mono), .prose li:not(.mono) { color: var(--ink); }

.prose p { margin-bottom: 16px; font-size: 16px; line-height: 1.62; }
.prose p.lead { font-size: clamp(1.2rem, 2.2vw, 1.6rem); line-height: 1.35; letter-spacing: -0.012em; }

/* The exit-tax line is the only copy on this page written to be read as a
   slogan rather than an argument, so it breaks the prose measure. The
   concession is set back in muted; the demand carries full weight — the
   contrast does the same work as the sentence. */
/* No max-width: the two spans already force the break, and a ch-based measure
   is computed in this element's own display-sized font, which split each line
   in two. */
/* SHOUT-OUT LINES FOLLOW THE HERO. That is the model for any of these: full
   ink, one sentence per line, set tight.

   The first line used to be muted, which read as a lead-in to the second rather
   than half of an antithesis — and a grey half undercuts an argument the whole
   site makes in black and white. Both lines now carry the same weight, and the
   line-height matches .hero__line rather than inheriting the 1.62 that .prose p
   sets for body copy, which was pushing the two apart into separate thoughts. */
.prose .pull {
  font-size: clamp(1.5rem, 4.6vw, 2.9rem);
  line-height: 0.86;
  margin: 34px 0 38px;
}
.prose .pull span { display: block; text-wrap: balance; }

/* Cart -------------------------------------------------------------------- */

/* Its own layout rather than borrowing `.statement`, whose `p` rule sets a
   display-sized font and was quietly inflating every note in the summary. */
.cart { display: grid; grid-template-columns: minmax(0, 1fr); gap: clamp(24px, 4vw, 56px); align-items: start; }

@media (min-width: 860px) { .cart { grid-template-columns: 1.15fr 1fr; } }

.line { display: grid; grid-template-columns: 88px minmax(0, 1fr) auto; gap: 18px; align-items: center; padding: 18px 0; border-bottom: 1px solid var(--line); }
.line__fig { background: var(--card); }
.line__qty { display: flex; align-items: center; gap: 10px; margin-top: 8px; font-family: var(--mono); font-size: 12px; }
.line__qty button { width: 24px; height: 24px; border: 1px solid var(--line); line-height: 1; }
.line__qty button:hover { border-color: var(--ink); }
.line__price { font-family: var(--mono); font-size: 13px; text-align: right; }
.line__remove { display: block; margin-top: 8px; font-family: var(--mono); font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); text-align: right; }
.line__remove:hover { color: var(--ink); }

.totals { display: flex; justify-content: space-between; gap: 16px; padding: 13px 0; font-family: var(--mono); font-size: 14px; }

/* Promo code and donation ------------------------------------------------- */

.promo, .donate__form { display: flex; gap: 8px; margin: 12px 0 2px; }

.promo input, .donate__form input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 11px 12px;
  border: 1px solid var(--line);
  background: transparent;
  color: inherit;
  font: 13px var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.donate__form input { text-transform: none; }
.promo input:focus, .donate__form input:focus { outline: none; border-color: var(--ink); }
.promo .btn, .donate__form .btn { flex: 0 0 auto; }

.promo__msg { margin-top: 6px; text-transform: none; letter-spacing: 0.03em; line-height: 1.5; }

/* Inline actions sitting next to a total's label. */
.mini {
  margin-left: 10px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
  border-bottom: 1px solid transparent;
}

.mini:hover { color: var(--ink); border-bottom-color: var(--ink); }

/* THE SIZE GUIDE IS THE ONE ACID THING ON A PRODUCT PAGE. That is the rule at
   the top of this file rather than a decoration — the accent marks one thing per
   page and never ornaments — and nothing else on a product page uses it, so this
   spends the page's single accent on the control most worth finding.

   WHY THIS ONE. It was a grey .mini beside the SIZE label and people missed it.
   Missing it matters more than missing most links: the alternative to reading a
   size guide is guessing, and a guessed size comes back as a return we pay to
   handle and a garment that has flown twice.

   IT IS A MODIFIER AND NOT A CHANGE TO .mini. That class also carries the
   basket's Change and Remove controls, which must stay quiet — restyling .mini
   would light those up too.

   The hover inverts rather than dims, so the control still reads as a button at
   both states; a grey hover on an acid chip looks disabled. */
/* KEEP SHOPPING GETS THE SAME TREATMENT, on the basket page only. It shares the
   selector rather than copying the rule, so the acid control is defined once and
   the two cannot drift.

   IT IS THE SECOND ACID THING ON THAT PAGE, which the rule at the top of this
   file says should not happen — the nav basket chip is already acid once the
   basket has something in it. Asked for deliberately on 24 August 2026, and it
   is defensible here: the chip is chrome and reads as a count, this is the only
   way back to the shop from a page whose other control is CHECKOUT, and a
   customer who cannot find it abandons the basket rather than adding to it. */
.mini--guide,
.cart-keep {
  padding: 5px 9px;
  color: var(--ink);
  background: var(--accent);
  border-bottom: 0;
}
.mini--guide:hover,
.cart-keep:hover {
  color: var(--accent);
  background: var(--ink);
  border-bottom-color: transparent;
}

/* WHAT THE SIZE UNDER THE POINTER COSTS — grey, and deliberately not acid.

   ADDED 20 SEPTEMBER 2026 after customer feedback: a product with a price that
   moves with the size said "from £18" and named the real figure only once the
   thing was in the basket. This chip and the Add to basket button both carry it
   now, both fed from the register.

   GREY BECAUSE THE ACCENT IS ALREADY SPENT. The rule at the top of this file
   allows one acid thing per page and the size guide is it. A second acid chip
   beside the first would make neither of them mean anything, and this one does
   not need to be found — it sits where the eye already is, next to the size the
   pointer is on.

   IT IS A SPAN AND HAS NO HOVER STATE, because it is a readout rather than a
   control. A chip that lights up invites a click that does nothing. */
.mini--price {
  display: inline-block;
  padding: 5px 9px;
  color: var(--ink);
  background: var(--line);
  border-bottom: 0;
  white-space: nowrap;
}
/* The typed field is a chip among chips now, so it must not carry the top
   margin the standalone form had, and its input has to be narrow enough that
   the whole row still fits beside the presets rather than wrapping under them. */
.chips .donate { margin: 0; }
.chips .donate__form { margin: 0; }
.chips .donate__form input { width: 6.5rem; padding: 9px 10px; }

.donate { margin-bottom: 4px; }
.donate .mini { display: inline-block; margin: 8px 0 0; }

/* Tap targets for the donation. Sized for a thumb, not a cursor. */
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 10px; }

.chips button {
  padding: 9px 14px;
  border: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  transition: background 0.14s, color 0.14s, border-color 0.14s;
}

.chips button:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.empty { padding: 60px 0; color: var(--muted); }

/* The donation acknowledgement, standing where "your basket is empty" would.
   Set in the body face at body weight rather than as a heading: it is a thank
   you and not an announcement, and a shop that congratulates itself in display
   type for taking somebody's money has misread the moment. Padded to the same
   60px as the empty line it replaces, so the panel does not jump when the first
   donation lands. */
.thanks { padding: 60px 0; max-width: 34rem; }
.thanks__lead { margin: 0 0 10px; }
.thanks__note { margin: 0; color: var(--muted); }
.thanks__line { margin: 10px 0 0; }

/* Sign-up ----------------------------------------------------------------- */

.signup { display: flex; flex-wrap: wrap; gap: 10px; max-width: 460px; margin-top: 22px; }
.signup input {
  flex: 1 1 220px;
  padding: 13px 14px;
  border: 1px solid var(--line);
  background: transparent;
  font: 14px var(--sans);
  color: inherit;
}
.signup input:focus { outline: none; border-color: var(--ink); }
.signup__done { margin-top: 14px; font-family: var(--mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; }

/* Footer ------------------------------------------------------------------ */

.foot { background: var(--ink); color: var(--paper); padding: clamp(48px, 7vw, 88px) var(--gutter) 26px; }
.foot__mark { width: 168px; filter: invert(1); margin-bottom: 22px; }
.foot__line { max-width: 40ch; color: #C9C9C2; font-size: 14px; }
.foot__cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 28px; margin: 48px 0 40px; }

@media (min-width: 760px) { .foot__cols { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.foot__cols h3 { color: #75756E; margin-bottom: 12px; }
.foot__cols li { margin-bottom: 7px; }
.foot__cols a:hover { color: var(--accent); }
.foot__base { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 12px; padding-top: 22px; border-top: 1px solid #262622; color: #75756E; }

/* Overlay ----------------------------------------------------------------- */

.overlay { position: fixed; inset: 0; z-index: 100; display: none; align-items: center; justify-content: center; padding: 24px; background: rgba(10, 10, 10, 0.82); }
.overlay[open] { display: flex; }
.overlay__panel { width: min(460px, 100%); background: var(--paper); padding: clamp(28px, 4vw, 44px); }
.overlay__panel h2 { font-size: 1.5rem; letter-spacing: -0.02em; margin-bottom: 12px; }
.overlay__panel p { color: var(--muted); font-size: 14px; margin-bottom: 20px; }
.overlay__close { position: absolute; top: 18px; right: 22px; }
.overlay__panel--wide { width: min(580px, 100%); max-height: 86vh; overflow-y: auto; }

/* Size guide ------------------------------------------------------------- */

.sizes { width: 100%; border-collapse: collapse; margin: 4px 0 2px; }

.sizes th, .sizes td {
  padding: 9px 6px;
  border-bottom: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 12px;
  text-align: right;
}

.sizes th {
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.sizes th:first-child, .sizes td:first-child { text-align: left; }

/* Label/value pairs rather than a matrix — the label column carries the weight. */
.sizes--spec td:first-child { color: var(--muted); width: 45%; }
.sizes--spec td:last-child { text-align: right; }

.mini--solo { display: inline-block; margin: 0 0 24px; }

/* THE WAY OUT AT THE MOMENT IT IS WANTED — the chip that appears under the add
   button once something is in the basket, and the bar at the foot of the grid.

   NEITHER IS ACID, and that is the point. The header basket already carries the
   accent; a second acid thing on the same screen spends exactly what makes the
   first one work. The rule above still holds — the size guide is the one acid
   thing in a product page's own content. */
.mini--basket { display: inline-block; margin: 14px 0 0; }
/* THE SAME SPECIFICITY TRAP AS .btn[hidden] BELOW, and it bites harder here:
   the chip sets display:inline-block, so a class beats the [hidden] attribute
   and the chip would sit on an empty basket saying "Go to basket". */
.mini--basket[hidden] { display: none; }

/* HIDDEN HAS TO BEAT .btn's display, or the bar shows at zero. [hidden] is a
   plain attribute selector and .btn is a class, so the class wins on
   specificity and the element stays inline-block — visible, saying "0 items".
   This is the whole bug that pattern produces. */
.btn[hidden] { display: none; }

.gridfoot { margin-top: 34px; }

.units { display: flex; gap: 6px; margin: 0 0 12px; }

.units button {
  padding: 7px 13px;
  border: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.units button:hover { border-color: var(--ink); }
.units button[aria-pressed="true"] { background: var(--ink); color: var(--paper); border-color: var(--ink); }

.sizes__fit { font-size: 14px; line-height: 1.5; margin-bottom: 14px; }
/* Prose, not a caption — it tells somebody how to measure themselves. */
.sizes__note { margin: 12px 0 0; font-size: 15px; line-height: 1.6; }
.sizes__title { margin: 34px 0 10px; letter-spacing: 0.09em; }
.sizes__wrap + .sizes__title { margin-top: 40px; }

/* Utility ----------------------------------------------------------------- */

.muted { color: var(--muted); }
.vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* The gallery caption names the view where a view is a nameable thing — a phone
   model rather than a camera angle. Hidden by default and shown only when the
   catalogue sends labels, so nothing else on the site gains a stray line. */
.gallery__caption { margin: 8px 0 0; font-size: 13px; color: var(--ink-muted, #666); text-align: center; }
