/* ALLIN MAKI · landing page chrome
   Loaded only by index.html, so the order form is untouched by any of it. The
   form has its own equivalent in css/order.css and the two are deliberately
   set to the same shape, blur and timing.

   1. the masthead flattens into a floating pill once the page moves
   2. a sticky bar keeps the one action reachable, and stands down at the CTA
*/

/* How much chrome is actually on screen once the page has moved: the 10px of
   breathing room above the pill plus the pill itself. Anything that scrolls
   itself into view should clear this rather than --header-h + --banner-h,
   because by the time a jump lands the tall header is long gone. */
:root { --pill-h: 68px; }
@media (max-width: 1149px) { :root { --pill-h: 64px; } }

/* ---- 1. The header becomes a pill --------------------------------------- */
.site-head {
  transition: padding-block-start .35s cubic-bezier(.2, .8, .2, 1);
}
/* the promo bar collapses rather than disappearing, so nothing jumps */
.banner {
  overflow: hidden; max-height: 96px;
  transition: max-height .35s cubic-bezier(.2, .8, .2, 1), opacity .25s ease;
}
.masthead { transition: background-color .3s ease; }
.masthead__inner {
  margin-inline: auto; border-radius: 0;
  transition: min-height .35s cubic-bezier(.2, .8, .2, 1),
              max-width .35s cubic-bezier(.2, .8, .2, 1),
              padding-inline .35s cubic-bezier(.2, .8, .2, 1),
              background-color .3s ease, box-shadow .3s ease, border-radius .3s ease;
}

body.is-pilled .banner { max-height: 0; opacity: 0; }
body.is-pilled .site-head { padding-block-start: 10px; }
body.is-pilled .masthead { background: transparent; }
/* 1320, not 1120. Scrolled, the bar closes into a floating pill, and the pill
   carried its own width cap: 1120, whatever the screen. So the logo, five
   links and three buttons had to fit a box narrower than the breakpoint that
   decided whether to show them at all, and the last button sat on top of the
   one before it at EVERY width, 1600 included. Measured rather than guessed:
   the contents need 1210px in English and 1284 in Spanish, so the cap is 1320
   and the pill is still inset from the 1360 container. The first sweep missed
   this because it never scrolled, and unscrolled the bar is not a pill. */
body.is-pilled .masthead__inner {
  max-width: 1320px; min-height: 58px; padding-inline: var(--s-20);
  border-radius: var(--r-pill);
  /* glass: the page reads through it, the blur keeps the type legible */
  /* .78 rather than .58: this glass was tuned against photographs and tinted
     panels, and every section head on the homepage is centred, so a 54px
     Champ line crosses the middle of the bar five times on the way down and
     ghosts through it behind the smallest type on the screen. */
  background: rgba(243, 230, 207, .78);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
  backdrop-filter: blur(18px) saturate(160%);
  box-shadow: 0 10px 30px rgba(70, 58, 0, .11);
}
@media (max-width: 1149px) {
  body.is-pilled .masthead__inner { min-height: 54px; padding-inline: var(--s-16); }
}

/* The buttons sit inside a 58px bar, so they are sized to it rather than to the
   page, and the bar closes up into a pill once you scroll.
   The TEXT stays 16px through both states. At 15px, and worse at 14, Chrome
   rounds each letter's outline onto the device pixel grid, and on a screen at
   125% scaling that lands some letters a pixel lower than their neighbours:
   the u of "su" drops while the s beside it does not. Nothing in CSS switches
   that off, and it is not a Spanish problem, the English button does it too.
   More pixels per stem is the only lever a stylesheet has. The box still
   shrinks on scroll; only the type holds still. */
.masthead__actions .btn {
  min-width: 0; min-height: 42px; white-space: nowrap;
  padding: var(--s-10) var(--s-16); font-size: 16px;
}
body.is-pilled .masthead__actions .btn {
  min-height: 38px; padding: var(--s-8) var(--s-12); font-size: 16px;
}

/* The range photographs are square, 800 and 1200 across, and the card frame is
   452 by 380. Covering that loses 72px of height, and centred that is 36px off
   the top, which is exactly where the ears are: Naturals and Life-size both
   lost theirs. Aiming at 12 per cent keeps the whole head with a little air
   above it and spends the loss at the bottom instead, where there are only
   feet and the white sweep of the studio floor. The two landscape photographs
   in the same stack lose no height at all, so this does nothing to them.
   Aimed at the very top rather than a little under it, because the photographs
   themselves carry almost no air above the ears: at 12 per cent the ear tips
   still sat against the frame. */
#range .fold__media > img { object-position: 50% 0%; }

/* ---- 2. The sticky action bar ------------------------------------------- */
.orderbar {
  position: fixed; z-index: 25; left: 0; right: 0; bottom: 0;
  display: flex; align-items: center; justify-content: center; gap: var(--s-24);
  padding: 12px var(--gutter) calc(12px + env(safe-area-inset-bottom, 0px));
  background: rgba(253, 251, 246, .88);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  border-top: 1px solid var(--ink-a10);
  transform: translateY(110%);
  transition: transform .4s cubic-bezier(.2, .8, .2, 1);
}
.orderbar.is-in { transform: translateY(0); }
.orderbar__text { display: grid; gap: 2px; }
.orderbar__text b { font-family: var(--font-body); font-weight: 600; font-size: 15px; }
.orderbar__text span {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: .1em;
  text-transform: uppercase; color: rgba(0, 0, 0, .60);
}
/* Black, not the accent. The bar floats over a parchment page and over the
   pale tint of half the sections on it, and the accent button was competing
   with the tint rather than sitting on top of it. Same object on every page:
   the bar is one component and one button. */
.orderbar .btn {
  min-width: 200px; min-height: 48px; padding-block: var(--s-12); margin: 0;
  background: var(--ink); border-color: var(--ink); color: var(--paper);
}
.orderbar .btn:hover { background: #1B1B1B; border-color: #1B1B1B; }
.orderbar .btn:active { background: #2A2A2A; border-color: #2A2A2A; }
@media (max-width: 640px) {
  .orderbar { gap: var(--s-12); padding-inline: var(--s-16); }
  .orderbar__text { display: none; }
  .orderbar .btn { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .site-head, .banner, .masthead, .masthead__inner, .orderbar { transition: none; }
}

/* ---- The four reasons: a fold stack on a phone, four cards on a desktop ----
   One set of markup. Below 768 the cards pin and fold over each other exactly
   as the range does further down the page, because on a phone four stacked
   cards are four screenfuls of scrolling with nothing happening. From 768 the
   stack stands down: --fold-run: 0 tells js/main.js to stop painting and to
   put back the inline styles it set, and .switch__grid does the rest. */
.fold-stack--why { --fold-run: 1; --fold-top: 92px; }
.fold-stack--why > .fold {
  min-height: 296px; align-content: center;
  padding: var(--s-32) var(--s-24) var(--s-36);
}
/* The numeral is the media of these cards: it is what fades first as the next
   card climbs over, the way a photograph does on the range. */
.fold-stack--why > .fold .usp__n { opacity: var(--fold-media-opacity); }
.fold-stack--why > .fold .usp__t,
.fold-stack--why > .fold .usp__b { opacity: var(--fold-text-opacity); }

@media (min-width: 768px) {
  .fold-stack--why {
    --fold-run: 0;
    gap: var(--s-16); margin-bottom: 0;
  }
  .fold-stack--why > .fold {
    position: relative; min-height: 0; align-content: start;
    padding: var(--s-28) var(--s-20) var(--s-32);
    margin-bottom: 0; will-change: auto;
  }
}

/* ---- A panel that deals itself out ----------------------------------------
   Apron animates its feature cards with a looping 513x440 clip per card. This
   is the same choreography done in the DOM: the panel arrives, holds still
   long enough to be read, then the lines land one at a time and the piece
   count runs up to meet them. No video file, the text stays text, and it
   still reads at any size.

   The rows keep their space from the start, they are only invisible, so the
   panel never changes height and the card it sits in never reflows while the
   fold stack is pinning it.

   Everything hangs off .is-seq, which the driver adds only once it has
   decided to run. Without JavaScript, or with reduced motion asked for, the
   markup is already the finished panel and none of this applies. */
[data-seq] .mock__total { font-variant-numeric: tabular-nums; transform-origin: 100% 50%; }

[data-seq].is-seq { opacity: 0; transform: translateY(18px); }
[data-seq].is-seq.is-on {
  opacity: 1; transform: none;
  transition: opacity .5s ease, transform .62s cubic-bezier(.2, .8, .2, 1);
}

/* One landing for everything, on all three panels: nine pixels up and a
   fade, same easing, one at a time. The cards differ in what lands and what
   it means, never in how it moves. */
/* .14 rather than 0. The rows keep their space so a pinned card cannot reflow
   under the reader, and at zero that space is a white rectangle with a heading
   and two dotted rules around nothing: on the ordering stack a card sits still
   for a whole viewport while the reader scrolls past it, so the void is what
   they look at. A faint form waiting to be filled says the same thing and
   still leaves the landing somewhere to travel from. */
[data-seq].is-seq [data-seq-step],
[data-seq].is-seq [data-seq-row] { opacity: .14; transform: translateY(9px); }
[data-seq].is-seq [data-seq-step].is-on,
[data-seq].is-seq [data-seq-row].is-on {
  opacity: 1; transform: none;
  transition: opacity .36s ease, transform .38s cubic-bezier(.2, .8, .2, 1);
}

/* The one exception, and it earns it: the 24 hours at the foot of the
   quotation is a promise being given, not an item being listed, so it
   presses down onto the paper instead of rising into place. */
[data-seq].is-seq [data-seq-in="stamp"] { opacity: 0; transform: scale(1.45) rotate(-7deg); }
[data-seq].is-seq [data-seq-in="stamp"].is-on {
  opacity: 1; transform: none;
  transition: opacity .16s ease, transform .34s cubic-bezier(.2, .9, .3, 1);
}

/* The freight margin is the argument the ex works card is making, so the
   line it lands on is lit for a moment and then let go. The spread carries
   the wash past the text without giving the row a box of its own. */
[data-seq] [data-seq-flash] { border-radius: 6px; }
[data-seq].is-seq [data-seq-flash].is-on { animation: seq-flash 1.25s ease .1s both; }
@keyframes seq-flash {
  0%   { background-color: rgba(228, 180, 111, 0);   box-shadow: 0 0 0 7px rgba(228, 180, 111, 0); }
  20%  { background-color: rgba(228, 180, 111, .55); box-shadow: 0 0 0 7px rgba(228, 180, 111, .55); }
  100% { background-color: rgba(228, 180, 111, 0);   box-shadow: 0 0 0 7px rgba(228, 180, 111, 0); }
}

/* The button is the card's own argument. It is dead while the order is under
   the minimum and it lights the moment the counter passes 100, which is the
   one number this card exists to explain. */
[data-seq].is-seq .mock__btn {
  background: var(--ink-a10); color: var(--ink-a40);
  transition: background-color .45s ease, color .45s ease;
}
[data-seq].is-seq .mock__btn.is-on { background: var(--accent); color: var(--ink); }

/* The count and the line that caused it belong to each other, so the total
   takes a small knock every time one lands. */
[data-seq] .mock__total.is-bump { animation: seq-bump .3s cubic-bezier(.2, .8, .2, 1); }
@keyframes seq-bump {
  0%   { transform: scale(1); }
  42%  { transform: scale(1.07); }
  100% { transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  [data-seq].is-seq, [data-seq].is-seq [data-seq-step], [data-seq].is-seq [data-seq-row] { opacity: 1; transform: none; }
  [data-seq].is-seq .mock__btn { background: var(--accent); color: var(--ink); }
}

/* ---- What each panel does with its moment ---------------------------------
   The landing is the same everywhere. These are the three payoffs, one per
   card, and they are the reason each card is worth watching once. */

/* The quotation is being worked out, so the ring snaps out around the promise
   at the foot of it rather than the promise simply appearing. */
.mock__stamp { position: relative; display: inline-block; }
[data-seq].is-seq [data-seq-in="stamp"].is-on .mock__stamp::after {
  content: ""; position: absolute; inset: -10px -15px;
  border: 2px solid var(--accent); border-radius: var(--r-pill);
  animation: seq-ring .72s cubic-bezier(.2, .8, .2, 1) both;
}
@keyframes seq-ring {
  0%   { transform: scale(.7);  opacity: 0; }
  28%  { opacity: .95; }
  100% { transform: scale(1.6); opacity: 0; }
}

/* Nine cartons, drawn as the count climbs to nine. steps(9) on the clip is
   what makes them arrive one at a time rather than wiping across. */
.cartons { display: flex; gap: 4px; margin-top: -4px; }
.cartons i { flex: 1; height: 10px; border-radius: 2px; background: var(--ink-a20); }
[data-seq].is-seq .cartons { clip-path: inset(0 100% 0 0); }
[data-seq].is-seq [data-seq-step].is-on + .cartons {
  clip-path: inset(0 0 0 0);
  transition: clip-path .63s steps(9);
}

/* The freight margin keeps its wash and gains a snap, because a zero that
   arrives like the three figures above it is not doing its job. */
[data-seq].is-seq [data-seq-flash].is-on .mono {
  display: inline-block;
  animation: seq-pop .44s cubic-bezier(.2, .9, .3, 1) .06s both;
}
@keyframes seq-pop {
  0%   { transform: scale(1.26); color: var(--accent-active); }
  100% { transform: scale(1);    color: inherit; }
}

/* The handover arrives from the edge, because that is the direction the goods
   go, and the arrow travels once as it lands. */
[data-seq].is-seq [data-seq-in="right"] { opacity: 0; transform: translateX(18px); }
[data-seq].is-seq [data-seq-in="right"].is-on {
  opacity: 1; transform: none;
  transition: opacity .34s ease, transform .46s cubic-bezier(.2, .8, .2, 1);
}
.mock__hand { display: flex; align-items: center; gap: 8px; }
.mock__hand svg { width: 14px; height: 12px; flex: none; }
[data-seq].is-seq .mock__hand.is-on svg {
  animation: seq-travel .8s cubic-bezier(.3, .8, .3, 1) .26s both;
}
@keyframes seq-travel {
  0%   { transform: translateX(-7px); opacity: 0; }
  46%  { transform: translateX(3px);  opacity: 1; }
  100% { transform: translateX(0);    opacity: 1; }
}

/* ---- The compliance pair --------------------------------------------------
   Green is doing work on this page for the first time here, so it is spent
   in exactly two places: the passes as they land, and the seal they add up
   to. Nothing else in the report is coloured. */
.mock__row--pass .mono { color: var(--pass-fill); font-weight: 500; letter-spacing: .06em; }
[data-seq].is-seq .mock__row--pass.is-on .mono {
  display: inline-block;
  animation: seq-pass .36s cubic-bezier(.2, .9, .3, 1) .16s both;
}
/* A beat behind its own line, so the standard is read and then answered
   rather than both arriving in the same instant. */
@keyframes seq-pass {
  0%   { opacity: 0; transform: scale(1.32) translateY(-2px); }
  100% { opacity: 1; transform: none; }
}

.mock__seal {
  position: relative;
  display: flex; align-items: center; justify-content: center; gap: 10px;
  height: 52px; margin-top: var(--s-16); border-radius: var(--r-8);
  background: var(--pass-fill); color: var(--paper);
  font-family: var(--font-display); font-weight: 800; font-size: 16px;
  letter-spacing: .1em; text-transform: uppercase;
}
.mock__seal svg { width: 16px; height: 16px; flex: none; }
[data-seq].is-seq .mock__seal.is-on::after {
  content: ""; position: absolute; inset: -9px -11px;
  border: 2px solid var(--pass-fill); border-radius: var(--r-16);
  animation: seq-ring .72s cubic-bezier(.2, .8, .2, 1) both;
}

/* The hundred per cent is earned rather than announced: the pill starts as an
   empty track and the accent fills it as the figure climbs. A hard-stop
   gradient rather than a bar behind the text, so nothing has to be layered
   over anything and the finished pill is exactly the pill it always was. */
.pill-wrap { width: 100%; display: grid; justify-items: center; }
[data-seq].is-seq .pill-big {
  background: linear-gradient(to right,
    var(--accent) calc(var(--p, 0) * 100%), var(--ink-a05) 0);
}
[data-seq].is-seq .pill-ring {
  position: absolute; inset: -14px -18px; opacity: 0;
  border: 2px solid var(--accent); border-radius: var(--r-pill);
  transform: none;
}
[data-seq].is-seq .pill-ring.is-on { animation: seq-ring .82s cubic-bezier(.2, .8, .2, 1) both; }

/* ---- the hundred per cent, on the herd ------------------------------------
   The claim and the thing it is a claim about were two cards in a row, which
   is two screens for one idea. Both children take the same grid cell, so the
   track is the width of the clip and the pill centres on it. */
.fold__media--over > video { grid-area: 1 / 1; }
/* the wrap keeps its full width: it is what the pill's own percentage width
   resolves against, and shrink-wrapping it collapsed the pill to nothing */
.fold__media--over .pill-wrap--over {
  grid-area: 1 / 1; z-index: 2; align-self: center; pointer-events: none;
}
/* the ring is absolute against the nearest positioned ancestor, which used to
   be .fold__media, so it drew a hoop the size of the whole card. Here it
   should hug the pill. */
.fold__media--over .pill-big {
  position: relative;
  width: clamp(168px, 62%, 292px);
  font-size: clamp(40px, 9vw, 74px);
  box-shadow: 0 12px 34px rgba(0, 0, 0, .28);
}

/* A third landing, for anything that reads as a quantity of something
   rather than an item: it draws from its own left edge instead of
   rising. The two halves of a payment, for one. */
[data-seq].is-seq [data-seq-in="grow"] { opacity: 1; transform: scaleX(0); transform-origin: left center; }
[data-seq].is-seq [data-seq-in="grow"].is-on {
  transform: scaleX(1);
  transition: transform .52s cubic-bezier(.2, .8, .2, 1);
}

/* ===========================================================================
   ROUND 3 · the home page
   =========================================================================== */

/* ---- 1. The hero ----------------------------------------------------------
   Awesomic's arrangement (awesomic.com, read 2026-09-07), re-keyed to fleece:
   a headline with one word that keeps changing, the terms of trade in the
   corner where they had an email capture, two numbers, and a rail of four
   rounded cards standing in for four paragraphs nobody reads.

   White rather than the page's parchment, because every photograph in the
   cards was shot on a white ground and the cards float better on it.
   --------------------------------------------------------------------------- */
.hero {
  position: relative; background: var(--white); overflow: clip;
  display: block; min-height: 0; grid-template: none;
  padding-bottom: var(--s-48);
  border-bottom: 1px solid var(--ink-a10);
}
.hero__in { padding-block: var(--s-32) 0; }
@media (min-width: 1024px) { .hero__in { padding-block: var(--s-56) 0; } }

/* ---- the headline and the corner ------------------------------------------ */
.hero__top { display: grid; gap: var(--s-28); align-items: stretch; }
@media (min-width: 1024px) {
  .hero__top { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); gap: var(--s-48); }
}
/* headline at the top, the two numbers pushed to the foot of the column, so
   they finish level with the buttons opposite instead of leaving a hole */
.hero__lead { display: grid; align-content: space-between; gap: var(--s-32); }
.hero__h {
  margin: 0; font-family: var(--font-display); font-weight: 800;
  /* the changing word has a line to itself, so the longest line that has to
     fit is "animals from Peru" */
  font-size: clamp(31px, 5.6vw, 78px); line-height: 0.95; letter-spacing: -0.035em;
}
/* Below 1024 the hero is one column and the heading has the whole width, so it
   takes it. The 5.6vw above only starts to grow on a laptop, which left every
   phone at the 31px floor.

   At this size the written lines no longer fit a phone, and that is the point:
   they break, and balance decides where, rather than leaving one word hanging.
   What cannot break is the rotating word. The rule was set for the longest
   shade name, Cotton Candy, at about 6.6em, and 0.142 of the column leaves
   that a margin for Safari setting Champ a hair wider than Chrome. The words
   that rotate now (sustainable, sostenibles) are shorter, so the same rule
   holds both languages with room to spare. */
@media (max-width: 1023px) {
  .hero__h { font-size: clamp(34px, calc((100vw - 64px) * 0.142), 72px); line-height: 0.92; text-wrap: balance; }
}

/* The one word that changes, in the ink of the rest of the headline. Every
   variant sits in the same grid cell, so the slot is as wide as the longest
   word and the line never reflows.

   The word does not cross fade any more. Its letters are hinged just under
   the baseline and roll away one after another, left to right, while the next
   word's letters roll up into the places they leave, so you read a hand over
   instead of a dissolve. The perspective sits on the row of letters, which
   means they share one vanishing point and the ones at the ends turn a little
   further than the ones in the middle: that is what makes it read as paper
   turning rather than type sliding. Leaving is quick and evenly spaced,
   arriving is slower and lands with a small overshoot, so the eye follows the
   new word rather than the old one.

   js/landing.js cuts each word into letters and numbers them in --i. Without
   it the words stay plain text and only the resting one is shown, which is
   also what happens when the reader asks for less movement. */
.hrot { display: inline-grid; vertical-align: baseline; }
.hrot__w { grid-area: 1 / 1; white-space: nowrap; opacity: 0; }
.hrot__w.is-on, .hrot__w.is-out { opacity: 1; }
.hrot__ls { display: inline-block; perspective: 700px; }
.hrot__l {
  display: inline-block; transform-origin: 50% 82%;
  opacity: 0; transform: translateY(.42em) rotateX(-62deg);
  /* the letter clears faster than it travels, so the word on its way out is
     already faint by the time the new one reaches the same band and you see a
     shuffle rather than two words printed over each other */
  transition: opacity .18s linear, transform .3s cubic-bezier(.4, 0, .9, .35);
  transition-delay: calc(var(--i, 0) * 20ms);
}
.hrot__w.is-on .hrot__l {
  opacity: 1; transform: none;
  transition: opacity .3s linear, transform .6s cubic-bezier(.17, .89, .28, 1.02);
  transition-delay: calc(var(--i, 0) * 34ms + 80ms);
}
.hrot__w.is-out .hrot__l {
  opacity: 0; transform: translateY(-.42em) rotateX(62deg);
  transition-delay: calc(var(--i, 0) * 20ms);
}

.hero__aside { display: grid; gap: var(--s-20); align-content: start; }
@media (min-width: 1024px) { .hero__aside { padding-top: var(--s-8); } }
.hero__lede {
  margin: 0; max-width: 46ch; font-size: clamp(15px, 1.2vw, 17px);
  line-height: 1.55; color: var(--ink-a70);
}

/* The four numbers a purchase order needs, in the box Awesomic gave to an
   email field. Rounded to match the cards below. */
.hero__facts {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-20) var(--s-16); margin: 0;
  padding: var(--s-20); border: 1px solid var(--ink-a10); border-radius: var(--r-22);
}
.hero__facts > div { display: grid; gap: 5px; min-width: 0; }
.hero__facts dt {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-a60);
}
.hero__facts dd {
  margin: 0; font-family: var(--font-body); font-weight: 700;
  font-size: clamp(14px, 1.2vw, 16px); line-height: 1.2;
}
.hero__ctas { display: flex; flex-wrap: wrap; gap: var(--s-10); }
/* .btn carries margin-inline: auto for centred stacks; in a flex row that
   shoves the pair apart */
.hero__ctas .btn { width: auto; margin-inline: 0; }

.hero__stats { display: flex; flex-wrap: wrap; gap: var(--s-24) clamp(28px, 5vw, 72px); }
.hstat { margin: 0; display: flex; align-items: baseline; gap: var(--s-10); }
.hstat b {
  font-family: var(--font-display); font-weight: 800; line-height: 1;
  font-size: clamp(30px, 3.4vw, 46px); letter-spacing: -.03em;
}
.hstat span { font-size: 12.5px; line-height: 1.25; color: var(--ink-a60); }

/* ---- the four cards -------------------------------------------------------
   A snap rail under 720 so all four stay reachable on a phone without
   stacking the hero four screens tall, then two up, then four across. */
.hero__cards {
  display: grid; gap: var(--s-12);
  grid-auto-flow: column; grid-auto-columns: minmax(250px, 76vw);
  overflow-x: auto; scroll-snap-type: x mandatory;
  margin-inline: calc(var(--gutter) * -1); padding-inline: var(--gutter);
  /* without this the mandatory snap fires on load, lands on the first card's
     own left edge and scrolls the gutter straight back off again */
  scroll-padding-inline: var(--gutter);
  margin-top: var(--s-48); padding-bottom: var(--s-4);
  scrollbar-width: none;
}
.hero__cards::-webkit-scrollbar { display: none; }
@media (min-width: 720px) {
  .hero__cards {
    grid-auto-flow: row; grid-template-columns: repeat(2, minmax(0, 1fr));
    overflow: visible; margin-inline: 0; padding-inline: 0; gap: var(--s-16);
  }
}
/* not at 1024: four ways into a 1024 container leaves 220px a card, and the
   test report in the third one has nowhere to put a row. */
@media (min-width: 1180px) {
  .hero__cards { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.hcard {
  position: relative; display: flex; flex-direction: column; overflow: hidden;
  min-height: clamp(380px, 33vw, 460px);
  padding: var(--s-20); border-radius: var(--r-22);
  text-decoration: none; color: inherit; scroll-snap-align: start;
  transition: transform .4s cubic-bezier(.2, .8, .2, 1), box-shadow .4s ease;
}
/* only the two cards that are links lift: a card that moves under the
   cursor and then does nothing when you click it is worse than a card
   that never moved. */
a.hcard:hover { transform: translateY(-5px); box-shadow: var(--shadow-frame); }
.hcard__t {
  margin: 0; font-family: var(--font-display); font-weight: 500;
  font-size: clamp(18px, 1.5vw, 21px); line-height: 1.1; letter-spacing: -.02em;
}

/* ---- cards 1 and 3 · the compliance page's own two panels, at card size ----
   Card one is the document pack panel: a heading you can read across the
   room and five lines with a green mark against each, nothing else. Card
   three is the .mock test report from the compliance band further down this
   page, unchanged except for scale, on a brown dark enough that the white
   sheet reads as a sheet of paper. */
.hcard__t--lg { font-size: clamp(22px, 1.9vw, 27px); font-weight: 800; }

.hlist {
  flex: 1; display: grid; align-content: space-between; gap: var(--s-16);
  margin-top: var(--s-20); padding-top: var(--s-20);
  border-top: 1px solid var(--ink-a10);
}
.hlist__r {
  display: grid; grid-template-columns: 30px 1fr; gap: var(--s-12); align-items: center;
  font-size: clamp(15px, 1.25vw, 17.5px); font-weight: 600; line-height: 1.25;
  letter-spacing: -.015em;
}
.hmark {
  width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center;
  background: var(--pass-fill); color: var(--tint-card);
  box-shadow: 0 0 0 4px rgba(15, 140, 115, .16);
}
.hmark svg { width: 15px; height: 15px; }

.hcard--paper { background: var(--tint-card); }

/* Dark brown, near enough to the ink of the headline that the two read as
   the same family. Not a token: nothing else on the site uses it, and adding
   one for a single card would be worse. */
.hcard--doc { background: #17100A; color: var(--white); }
.hcard--doc .hcard__sub { color: var(--white-a60); }

/* the report at card size. Everything it is made of already exists in
   components.css and landing.css; only the scale is new. */
.mock--mini {
  display: flex; flex-direction: column; flex: 1;
  max-width: none; margin-top: var(--s-16); padding: var(--s-16);
}
.mock--mini .mock__title { font-size: 16px; line-height: 22px; }
.mock--mini .mock__rows { flex: 1; align-content: space-between; gap: 9px; padding-block: 12px; }
.mock--mini .mock__row { font-size: 13px; line-height: 18px; }
.mock--mini .mock__row .mono { font-size: 12px; }
/* the five passes are the whole point of the card, so they are the one
   thing on it set heavier than the line it answers */
.mock--mini .mock__row--pass .mono { font-size: 14px; font-weight: 800; letter-spacing: .09em; }
.mock--mini .mock__seal { height: 42px; margin-top: var(--s-12); font-size: 14px; }
.mock--mini .mock__seal svg { width: 14px; height: 14px; }

.hpills { display: flex; flex-wrap: wrap; gap: 5px; }
.hpills b {
  font-family: var(--font-body); font-weight: 500; font-size: 11.5px; line-height: 1;
  padding: 6px 10px; border-radius: var(--r-pill);
  background: var(--white-a15); border: 1px solid var(--white-a30);
}

/* ---- cards 2 and 4 · the words sit on the media -------------------------
   No bar and no gradient. The two cards carry different photography, so the
   caption is coloured for what is actually behind it: ink on the collection
   card, whose three shots are all on studio white at the foot, and white on
   the film card, where both takes are a workshop floor. */
.hcard--dark { background: var(--fleece-obsidian); padding: 0; }
.hcard--dark > .hshots { position: absolute; inset: 0; flex: none; }
.hcard__foot {
  position: relative; z-index: 2; margin-top: auto;
  display: grid; gap: var(--s-12); padding: var(--s-20);
}

.hcard--shots { color: var(--ink); }
.hcard--shots .hpills b {
  background: rgba(255, 255, 255, .55); border-color: var(--ink-a20); color: var(--ink);
}
/* The film card is the one caption that cannot pick a side: the two takes
   run from a dark lap to a pale bench in a few seconds. A shadow tight under
   the words and a fill behind the pills, rather than a band across the
   picture. */
.hcard--film { color: var(--white); }
/* The film card's words change with the shot under them, so they fade
   rather than cut. js/landing.js adds .is-swapping for the length of the
   fade, to the line and to the row of pills together. */
.hcard--film .hcard__t, .hcard--film .hpills { transition: opacity .2s ease; }
.hcard__t.is-swapping, .hpills.is-swapping { opacity: 0; }
.hcard--film .hcard__t { text-shadow: 0 1px 2px rgba(0, 0, 0, .55), 0 2px 14px rgba(0, 0, 0, .45); }
.hcard--film .hpills b { background: rgba(0, 0, 0, .58); border-color: rgba(255, 255, 255, .38); }

/* Photographs or clips sliding, one at a time.

   The track keeps the width of one window and lets its children overflow it,
   so a translate of -100% is exactly one window. Sizing the track at --n
   windows and dividing the transform by --n is the same distance in theory
   and a sub-pixel short in practice, which showed as a sliver of the next
   photograph down the right edge. */
.hshots { position: relative; overflow: hidden; }
.hshots__track {
  display: flex; width: 100%; height: 100%;
  transform: translateX(calc(var(--i, 0) * -100%));
  transition: transform .9s cubic-bezier(.65, 0, .2, 1);
}
/* --shot-pos aims a wide shot inside the tall card, the same hook the
   private-label shots use. */
.hshots__track > * { flex: 0 0 100%; width: 100%; height: 100%; object-fit: cover; object-position: var(--shot-pos, 50% 50%); }
.hshots__dots {
  position: absolute; left: var(--s-20); top: var(--s-20); z-index: 2;
  display: flex; gap: 5px;
}
/* ink on the collection card, because all three photographs are shot on
   studio white; white on the film card, because both takes are dark */
.hshots__dots i { display: block; width: 18px; height: 3px; border-radius: 2px; background: var(--ink-a20); transition: background var(--t-hover); }
.hshots__dots i.is-on { background: var(--ink); }
.hshots--film .hshots__dots i { background: var(--white-a30); }
.hshots--film .hshots__dots i.is-on { background: var(--white); }

/* ---- the fleece run -------------------------------------------------------
   The band closes the hero. Nine squares, ivory to cacao, each one cropped
   out of a photograph of an animal that exists. */
.fleecerun {
  position: relative; display: block; text-decoration: none; color: inherit;
  margin-top: var(--s-24); border-top: 1px solid var(--ink-a10);
  padding-top: var(--s-20);
}
.fleecerun__strip {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  height: 84px; overflow: hidden;
}
@media (min-width: 760px) { .fleecerun__strip { height: 116px; } }
.fleecerun__strip i {
  /* the image itself comes from an inline style on the element: a relative
     url() inside a custom property resolves against this file, not the page. */
  display: block; background-size: cover; background-position: center;
  transition: transform .5s cubic-bezier(.2, .8, .2, 1), filter .5s ease;
}
.fleecerun:hover .fleecerun__strip i { filter: saturate(.82) brightness(1.03); }
.fleecerun:hover .fleecerun__strip i:hover { filter: none; transform: scale(1.06); }

.fleecerun__cap {
  position: absolute; top: 4px; z-index: 2;
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .13em;
  text-transform: uppercase; color: var(--ink-a60);
  padding: 0 var(--gutter);
}
.fleecerun__cap--l { left: 0; }
.fleecerun__cap--r { right: 0; }
@media (max-width: 759px) { .fleecerun__cap { display: none; } }

.fleecerun__mid {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-8) var(--s-16);
  padding: var(--s-12) var(--gutter) 0;
  font-family: var(--font-body); font-size: 13.5px; color: var(--ink-a60);
}
.fleecerun__mid b {
  display: inline-flex; align-items: center; gap: 6px;
  font-weight: 600; color: var(--ink);
}
.fleecerun__mid svg { width: 13px; height: 12px; transition: transform var(--t-link); }
.fleecerun:hover .fleecerun__mid svg { transform: translateX(3px); }

/* ---- one entrance, on load ------------------------------------------------ */
/* `translate` rather than `transform`: an animation with fill both wins over
   the hover rule, and the cards would never lift. The two compose. */
@keyframes hero-rise { from { opacity: 0; translate: 0 18px; } to { opacity: 1; translate: none; } }
@keyframes hero-wipe { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
.hero__h, .hero__aside, .hstat, .hcard {
  animation: hero-rise .8s cubic-bezier(.2, .8, .2, 1) both;
}
.hero__aside { animation-delay: .10s; }
.hstat:nth-child(1) { animation-delay: .22s; }
.hstat:nth-child(2) { animation-delay: .28s; }
.hcard:nth-child(1) { animation-delay: .34s; }
.hcard:nth-child(2) { animation-delay: .40s; }
.hcard:nth-child(3) { animation-delay: .46s; }
.hcard:nth-child(4) { animation-delay: .52s; }
/* the run wipes on left to right, ivory first, which is the order the fleece
   itself runs out in */
.fleecerun__strip { animation: hero-wipe 1.1s cubic-bezier(.3, .8, .2, 1) .5s both; }
@media (prefers-reduced-motion: reduce) {
  .hero__h, .hero__aside, .hstat, .hcard, .fleecerun__strip { animation: none; }
  .hrot__w, .hrot__l, .hshots__track { transition: none; }
}

/* ---- 2. The range cards arrive from the side ------------------------------
   --fold-step gives every card in the stack its own pinned height, so each
   one shows a strip of the fawn card behind it rather than covering it dead
   flat. Before this, three of the five cards pinned at exactly 40px and hid
   each other completely. --fold-slide is how far to the right a card starts.
   js/main.js reads both.
   --------------------------------------------------------------------------- */
/* Nothing here any more: dealing sideways is what every stack on the site
   does now. See css/sections.css, .fold-stack. */

/* ---- 3. The workshop, with nothing in a box -------------------------------
   The one full-bleed moment on the page. The frame holds still, the beats
   pass through it, and the clip behind changes with them.
   --------------------------------------------------------------------------- */
.section-flush { width: 100%; }
.shopfilm { position: relative; background: var(--fleece-obsidian); color: var(--paper); }
.shopfilm__stage { position: relative; min-height: 620px; display: grid; }
@media (min-width: 900px) { .shopfilm__stage { min-height: 760px; } }

.shopfilm__media { position: absolute; inset: 0; overflow: hidden; }
.shopfilm__clip {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity .9s ease;
}
.shopfilm__clip[data-on="true"] { opacity: 1; }
.shopfilm__scrim {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(36, 30, 26, .58) 0%, rgba(36, 30, 26, .72) 46%, rgba(36, 30, 26, .92) 100%);
}
@media (min-width: 900px) {
  .shopfilm__scrim {
    background: linear-gradient(90deg, rgba(36, 30, 26, .93) 0%, rgba(36, 30, 26, .84) 44%, rgba(36, 30, 26, .32) 100%);
  }
}

.shopfilm__copy { position: relative; z-index: 1; align-self: end; padding-block: var(--s-56) var(--s-40); }
@media (min-width: 900px) { .shopfilm__copy { align-self: center; padding-block: var(--s-80); } }
.shopfilm__kick { color: rgba(253, 251, 246, .5); margin-bottom: var(--s-24); }

.shopfilm__beats { list-style: none; margin: 0; padding: 0; display: grid; }
.shopfilm__beats > li {
  grid-area: 1 / 1; max-width: 38ch; opacity: 0; transform: translateY(14px);
  transition: opacity .55s ease, transform .55s cubic-bezier(.2, .8, .2, 1);
  pointer-events: none;
}
.shopfilm__beats > li[data-on="true"] { opacity: 1; transform: none; pointer-events: auto; }
.shopfilm__n {
  display: block; font-family: var(--font-mono); font-size: 12px; letter-spacing: .14em;
  color: var(--accent); margin-bottom: var(--s-12);
}
.shopfilm__t { margin: 0 0 var(--s-12); font-size: clamp(30px, 4.4vw, 54px); line-height: 1.06; }
.shopfilm__b { margin: 0; color: rgba(253, 251, 246, .76); max-width: 44ch; }

.shopfilm__foot { display: flex; align-items: center; justify-content: flex-start; gap: var(--s-24); margin-top: var(--s-32); flex-wrap: wrap; }
/* margin-inline: 0 undoes .btn's own auto margins, which in a flex row eat
   the free space and had pushed this button 490px away from the arrows it is
   supposed to sit beside. */
.shopfilm__foot .btn { flex: 0 0 auto; width: auto; margin-inline: 0; }
.shopfilm__rule { display: flex; gap: 6px; }
.shopfilm__rule i {
  display: block; width: 34px; height: 3px; border-radius: 2px;
  background: rgba(253, 251, 246, .22); transition: background-color .4s ease;
}
.shopfilm__rule i[data-on="true"] { background: var(--accent); }

/* Quiet enough that the beats stay the loudest thing in the section, and only
   where there is something to step through: below 900 the three beats are
   simply stacked one after another. */
.shopfilm__nav { display: flex; gap: var(--s-8); }
.filmnav {
  display: grid; place-items: center; width: 38px; height: 38px; padding: 0;
  border: 1px solid rgba(253, 251, 246, .28); border-radius: 50%;
  background: transparent; color: var(--paper); cursor: pointer;
  transition: background-color var(--t-hover), border-color var(--t-hover),
              opacity var(--t-hover);
}
.filmnav svg { width: 15px; height: 15px; }
.filmnav:hover { background: rgba(253, 251, 246, .12); border-color: rgba(253, 251, 246, .55); }
.filmnav:disabled { opacity: .28; cursor: default; }
.filmnav:disabled:hover { background: transparent; border-color: rgba(253, 251, 246, .28); }

/* On a phone the three beats simply follow one another: pinning a scene on a
   short screen leaves the reader scrolling past nothing. */
@media (max-width: 899px) {
  .shopfilm__beats { gap: var(--s-32); }
  .shopfilm__beats > li { grid-area: auto; opacity: 1; transform: none; max-width: none; }
  .shopfilm__t { font-size: clamp(26px, 7vw, 34px); }
  .shopfilm__rule, .shopfilm__nav { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .shopfilm__clip { transition: none; }
  .shopfilm__beats > li { transition: none; }
}
