/* ==========================================================================
   BETTR RITUAL — Design System
   Palette, type and components taken from the pack: olive green, butter
   yellow, lime accent, heavy black grotesque.

   1. Tokens  2. Reset  3. Primitives  4. Chrome  5. Components
   6. Interactive modules  7. Pages  8. Responsive  9. Motion
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. TOKENS
   -------------------------------------------------------------------------- */
:root {
  /* Brand — sampled from the packaging */
  --olive:       #4e5a23;
  --olive-deep:  #333b15;
  --olive-mid:   #66742f;
  --olive-soft:  #8a9a53;
  --lime:        #c8dc8b;
  --lime-bright: #dfeeb0;
  --lime-deep:   #a8bf63;
  --butter:      #f7f5ee;
  --butter-deep: #eceadf;
  --butter-pale: #fcfbf7;

  /* Neutrals */
  --ink:      #0a0a0a;
  --ink-2:    #2e2f26;
  /* Muted text tuned to clear WCAG AA (4.5:1) on --butter, not just to look
     soft. Measured: --ink-3 ≈ 5.6:1, --ink-4 ≈ 4.6:1. */
  --ink-3:    #5a5d47;
  --ink-4:    #6d7057;
  --line:     #dcd8ca;
  --line-soft:#e9e6dc;
  --white:    #ffffff;

  --berry:    #a8433c;
  --amber:    #c78b2a;
  /* Lighter amber, for a filled panel that still takes near-black text.
     Measured 8.9:1 against --ink. */
  --amber-soft: #e3b23c;

  /* Semantic */
  --bg:         var(--butter);
  --surface:    var(--white);
  --text:       var(--ink);
  --text-muted: var(--ink-3);
  --border:     var(--line);
  --accent:     var(--olive);
  --focus:      #1a56db;

  /* Type — Archivo Expanded echoes the wordmark */
  --font-display: "Archivo", "Helvetica Neue", Arial, sans-serif;
  --font-ui: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: ui-monospace, "Cascadia Code", "SF Mono", Consolas, monospace;

  /* Spacing */
  --s-1: 4px;   --s-2: 8px;   --s-3: 12px;  --s-4: 16px;
  --s-5: 24px;  --s-6: 32px;  --s-7: 48px;  --s-8: 64px;
  --s-9: 96px;  --s-10: 128px;

  /* Radius — mostly soft, with hard edges where we want attitude */
  --r-sm: 8px;  --r-md: 14px; --r-lg: 22px; --r-xl: 34px; --r-full: 999px;

  /* Elevation — olive-tinted, never neutral grey */
  --sh-1: 0 1px 2px rgba(51, 59, 21, .07), 0 1px 3px rgba(51, 59, 21, .05);
  --sh-2: 0 4px 12px rgba(51, 59, 21, .08), 0 2px 4px rgba(51, 59, 21, .05);
  --sh-3: 0 12px 32px rgba(51, 59, 21, .12), 0 4px 8px rgba(51, 59, 21, .05);
  --sh-4: 0 24px 64px rgba(51, 59, 21, .2), 0 8px 16px rgba(51, 59, 21, .08);
  /* The hard offset shadow that gives cards their chunk */
  --sh-hard: 5px 5px 0 var(--ink);
  --sh-hard-sm: 3px 3px 0 var(--ink);

  --ease: cubic-bezier(.4, 0, .2, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --ease-spring: cubic-bezier(.34, 1.56, .64, 1);
  --dur: .28s;

  --container: 1240px;
  --header-h: 70px;

  /* Translucent surfaces need their own tokens rather than a literal rgba().
     A hardcoded cream here is exactly what left the header glowing over a
     dark page until it was caught in a screenshot. */
  /* Sampled from the corners of the supplied packaging photograph, so any
     plate the photo sits on matches it exactly instead of nearly. */
  --photo-bg:   #fbfaf5;

  --veil:       rgba(247, 245, 238, .86);
  --veil-solid: rgba(247, 245, 238, .98);
}

/* --------------------------------------------------------------------------
   2. RESET + BASE
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scroll-padding-top: 90px; }

body {
  margin: 0;
  /* Paper ground with three soft green washes laid over it, fixed so they
     stay put while the page scrolls. Flat cream reads as stationery; the
     washes are what make it read as green-and-white without tinting any
     single surface green. */
  background-color: var(--bg);
  background-image:
    radial-gradient(62% 46% at 10% 4%,  rgba(110, 145, 52, .22), transparent 70%),
    radial-gradient(54% 40% at 92% 18%, rgba(168, 196, 104, .20), transparent 72%),
    radial-gradient(76% 52% at 48% 98%, rgba(78, 90, 35, .16),   transparent 72%),
    radial-gradient(40% 30% at 74% 62%, rgba(200, 220, 139, .18), transparent 74%);
  background-attachment: fixed;
  background-repeat: no-repeat;
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* clip, not hidden. `overflow: hidden` turns body into a scroll container,
     which silently stops `position: sticky` working anywhere inside it —
     and the slide-up stack in section 19 depends on sticky. `clip` does the
     same clipping without creating that container. */
  overflow-x: clip;
}
body.is-locked { overflow: hidden; }

img, svg, video { display: block; max-width: 100%; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 800;
  line-height: .98;
  letter-spacing: -.03em;
  margin: 0 0 var(--s-4);
  font-variation-settings: "wdth" 112;
  text-transform: uppercase;
}
h1 { font-size: clamp(2.6rem, 7vw, 5.6rem); }
h2 { font-size: clamp(2rem, 4.6vw, 3.6rem); }
h3 { font-size: clamp(1.2rem, 2vw, 1.6rem); }
h4 { font-size: 1rem; letter-spacing: -.01em; }

/* Sentence-case headings where shouting would be wrong */
.h-soft { text-transform: none; letter-spacing: -.02em; font-variation-settings: "wdth" 100; }

p { margin: 0 0 var(--s-4); }
p:last-child { margin-bottom: 0; }
a { color: inherit; text-decoration: none; }
ul, ol { margin: 0 0 var(--s-4); padding-left: 1.2em; }
li { margin-bottom: var(--s-2); }
button, input, select, textarea { font: inherit; color: inherit; }

:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; border-radius: 4px; }
::selection { background: var(--lime); color: var(--olive-deep); }

.skip-link {
  position: absolute; left: var(--s-4); top: -100px; z-index: 200;
  padding: var(--s-3) var(--s-5);
  background: var(--ink); color: var(--butter);
  border-radius: var(--r-full); font-weight: 700;
  transition: top var(--dur) var(--ease);
}
.skip-link:focus { top: var(--s-4); }

.visually-hidden {
  position: absolute !important; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* --- Scroll reveal ---
   POSITION IS LOAD-BEARING. This has to be declared before the components,
   not after them.

   `.reveal.is-in { transform: none }` and `.card:hover { transform: … }`
   have identical specificity, so whichever comes last in the file wins.
   With the reveal further down, every component lift declared above it was
   silently dead once the element had revealed — which is exactly what had
   happened to the cards and the three intro boxes. Declared up here, the
   components below win on the hover state and the reveal still owns the
   resting state, which is the way round it should be.

   The only casualty is hovering something mid-reveal, which drops the
   entry offset early. Nobody will ever see it. */
.reveal {
  opacity: 0; transform: translateY(24px);
  transition: opacity .7s var(--ease-out), transform .7s var(--ease-out);
}
.reveal.is-in { opacity: 1; transform: none; }

/* --------------------------------------------------------------------------
   3. PRIMITIVES
   -------------------------------------------------------------------------- */
.container { width: 100%; max-width: var(--container); margin-inline: auto; padding-inline: var(--s-5); }
.container--narrow { max-width: 800px; }
.container--wide { max-width: 1400px; }

/* Named, because the swap section has to cancel exactly this much when it is
   jumped to — see #swap below. Hardcoding the number there would come apart
   the moment this clamp changed or the window was a different width. */
:root { --section-pad: clamp(var(--s-7), 8vw, var(--s-9)); }
.section { padding-block: var(--section-pad); }

/* "Put it in the swap tool" on the quiz result jumps here. Landing on the
   section's own top edge put the eyebrow 122px below the sticky header —
   the section's top padding, on top of the 90px the header already reserves
   — so the reader arrived looking at a band of empty page. A negative
   scroll margin of exactly that padding brings the heading up to where the
   section visually begins. */
#swap { scroll-margin-top: calc(var(--section-pad) * -1); }
.section--tight { padding-block: var(--s-7); }
.section--butter { background: var(--butter-deep); }
.section--pale   { background: var(--butter-pale); }
.section--olive  { background: var(--olive); color: var(--butter); }
.section--olive h2, .section--olive h3, .section--olive h4 { color: var(--butter); }
.section--deep   { background: var(--olive-deep); color: var(--butter); }
.section--deep h2, .section--deep h3 { color: var(--butter); }
.section--lime   { background: var(--lime); }

.stack > * + * { margin-top: var(--s-4); }

.eyebrow {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-family: var(--font-ui);
  font-size: .72rem; font-weight: 700;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--olive); margin-bottom: var(--s-4);
}
.eyebrow::before { content: ""; width: 20px; height: 2px; background: currentColor; }
.section--olive .eyebrow, .section--deep .eyebrow { color: var(--lime); }

.section-head { max-width: 700px; margin-bottom: var(--s-7); }
.section-head--center { margin-inline: auto; text-align: center; }
.section-head p { color: var(--text-muted); font-size: 1.08rem; }
.section--olive .section-head p, .section--deep .section-head p { color: rgba(247,245,238,.78); }

.lede { font-size: 1.18rem; line-height: 1.6; color: var(--ink-2); }
.section--olive .lede, .section--deep .lede { color: rgba(247,245,238,.85); }

/* --- Buttons --- */
.btn {
  --btn-bg: var(--ink);
  --btn-fg: var(--butter);
  --btn-bd: var(--ink);
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  padding: 14px var(--s-6); min-height: 50px;
  border: 2px solid var(--btn-bd); border-radius: var(--r-full);
  background: var(--btn-bg); color: var(--btn-fg);
  font-family: var(--font-ui);
  font-size: .95rem; font-weight: 700; letter-spacing: -.01em;
  cursor: pointer; text-align: center;
  transition: transform .18s var(--ease-out), box-shadow var(--dur) var(--ease),
              background-color var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.btn:hover { transform: translate(-2px, -2px); box-shadow: var(--sh-hard-sm); }
.btn:active { transform: translate(0, 0); box-shadow: none; }

.btn--olive { --btn-bg: var(--olive); --btn-fg: var(--butter); --btn-bd: var(--olive); }
.btn--lime  { --btn-bg: var(--lime); --btn-fg: var(--olive-deep); --btn-bd: var(--ink); }
.btn--ghost { --btn-bg: transparent; --btn-fg: var(--ink); --btn-bd: var(--ink); }
.btn--ghost:hover { --btn-bg: var(--lime); }
.btn--light { --btn-bg: var(--butter); --btn-fg: var(--olive-deep); --btn-bd: var(--butter); }
.btn--quiet { --btn-bg: transparent; --btn-fg: var(--ink-2); --btn-bd: transparent; padding-inline: var(--s-3); }
.btn--quiet:hover { --btn-fg: var(--olive); box-shadow: none; transform: none; text-decoration: underline; }
.btn--block { display: flex; width: 100%; }
.btn--lg { min-height: 58px; padding-inline: var(--s-7); font-size: 1.02rem; }
.btn--sm { min-height: 38px; padding: 7px var(--s-4); font-size: .84rem; border-width: 1.5px; }
.btn[disabled], .btn[aria-disabled="true"] { opacity: .4; cursor: not-allowed; transform: none; box-shadow: none; }
.btn-row { display: flex; flex-wrap: wrap; gap: var(--s-3); }

.link {
  color: var(--olive); font-weight: 700;
  display: inline-flex; align-items: center; gap: var(--s-2);
  border-bottom: 2px solid transparent;
  transition: border-color var(--dur) var(--ease), gap var(--dur) var(--ease);
}
.link:hover { border-color: currentColor; gap: 11px; }
.link svg { width: 16px; height: 16px; }
.section--olive .link, .section--deep .link { color: var(--lime); }

/* --- Pills --- */
.pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 13px; border-radius: var(--r-full);
  background: var(--lime); color: var(--olive-deep);
  font-size: .7rem; font-weight: 800; letter-spacing: .08em;
  text-transform: uppercase; white-space: nowrap;
}
.pill--dark    { background: var(--ink); color: var(--butter); }
.pill--olive   { background: var(--olive); color: var(--butter); }
.pill--berry   { background: #f5e0de; color: var(--berry); }
.pill--gold    { background: #f6e6c2; color: #7a5312; }
.pill--outline { background: transparent; border: 1.5px solid var(--ink); color: var(--ink); }

/* --- Fields --- */
.field { margin-bottom: var(--s-4); }
.field__label { display: block; font-size: .82rem; font-weight: 700; margin-bottom: 6px; color: var(--ink-2); }
.field__hint { font-size: .8rem; color: var(--text-muted); margin-top: 6px; }
.field__error { display: none; font-size: .8rem; color: var(--berry); margin-top: 6px; font-weight: 600; }
.field.has-error .field__error { display: block; }
.field.has-error .input { border-color: var(--berry); background: #fdf6f5; }

.input {
  width: 100%; padding: 13px var(--s-4);
  background: var(--white);
  border: 2px solid var(--line);
  border-radius: var(--r-md);
  font-size: .95rem;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.input::placeholder { color: var(--ink-4); }
.input:focus { outline: none; border-color: var(--olive); box-shadow: 0 0 0 4px rgba(78,90,35,.14); }
select.input {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' stroke='%235a5d47' stroke-width='1.8' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--s-4) center; background-size: 12px;
  padding-right: var(--s-7); cursor: pointer;
}
.field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 var(--s-4); }
.field-grid--3 { grid-template-columns: 2fr 1fr 1fr; }
.field-grid > .field--full { grid-column: 1 / -1; }

/* --- Quantity stepper --- */
.qty {
  display: inline-flex; align-items: center;
  border: 2px solid var(--ink); border-radius: var(--r-full);
  background: var(--white); overflow: hidden;
}
.qty__btn {
  width: 42px; height: 46px; border: 0; background: transparent;
  cursor: pointer; color: var(--ink); font-size: 1.15rem; line-height: 1; font-weight: 700;
  transition: background-color var(--dur) var(--ease);
}
.qty__btn:hover:not(:disabled) { background: var(--lime); }
.qty__btn:disabled { opacity: .3; cursor: not-allowed; }
.qty__value { min-width: 38px; text-align: center; font-weight: 800; font-variant-numeric: tabular-nums; }
.qty--sm .qty__btn { width: 32px; height: 34px; font-size: 1rem; }
.qty--sm .qty__value { min-width: 28px; font-size: .85rem; }

/* .stars is back for the home page's quote strip. .rating-line is not — the
   product cards and the pack picker carry no rating any more. */
.stars { display: inline-flex; align-items: center; gap: 3px; }
.stars svg { width: 15px; height: 15px; color: var(--olive); }

.price { font-weight: 800; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.price--was { color: var(--ink-4); text-decoration: line-through; font-weight: 500; margin-left: 6px; }

/* --------------------------------------------------------------------------
   4. CHROME
   -------------------------------------------------------------------------- */
/* --- Announcement bar — CURRENTLY UNUSED ---
   Nothing renders .announce any more: the bar was removed from mountHeader
   in layout.js at the brand owner's request, in both its versions. These
   rules are kept so that restoring it there is a one-line change rather than
   a one-line change plus rewriting the styling from memory. Delete this
   block if the bar is never coming back. */
.announce {
  background: var(--ink); color: var(--butter);
  text-align: center; font-size: .78rem; font-weight: 600;
  letter-spacing: .05em; padding: 10px var(--s-4);
  text-transform: uppercase;
}
.announce strong { color: var(--lime); font-weight: 800; }
/* The sales-paused variant: the link is underlined rather than coloured like
   a discount code — it is a way through, not an offer. */
.announce--paused a { color: var(--lime); text-decoration: underline; text-underline-offset: 3px; }
.announce--paused a:hover { color: var(--white); }

/* .stock-note-paused removed with the note it styled: nothing explains the
   disabled buy button beside the button any more. */

/* The cross cursor on anything that cannot be bought. A disabled <button>
   still receives cursor styling, but it does NOT fire pointer events — so the
   rule has to sit on the control itself; a parent's cursor would not show
   through. Repeated for the cart's add buttons, which are .btn too but worth
   naming so this is not lost in a future refactor. */
.btn[disabled], .btn[aria-disabled="true"],
.card__add[disabled], #size-add[disabled], #pdp-add[disabled] { cursor: not-allowed; }

.header {
  position: sticky; top: 0; z-index: 60;
  /* Opaque, not a blurred veil. A backdrop-filter on a sticky header is the
     most expensive thing on the page: the header never leaves the screen, so
     the browser re-blurs a full-width strip of whatever is behind it on every
     single scroll frame. The veil was 86% opaque anyway, so almost nothing
     was showing through to be blurred — it was paying the whole cost for an
     effect that was barely visible. */
  background: var(--bg);
  border-bottom: 2px solid transparent;
  transition: border-color var(--dur) var(--ease);
}
.header.is-stuck { border-bottom-color: var(--ink); }
.header__inner { display: flex; align-items: center; gap: var(--s-5); height: var(--header-h); }

.logo { display: block; flex-shrink: 0; }
.logo__img { height: 42px; width: auto; filter: var(--logo-filter, none); }
.logo--footer { --logo-filter: invert(1); }
.logo--footer .logo__img { height: 50px; }

.nav { display: flex; align-items: center; gap: 2px; margin-left: auto; }
.nav__link {
  padding: 9px var(--s-3); border-radius: var(--r-full);
  font-size: .9rem; font-weight: 600; color: var(--ink-2);
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.nav__link:hover { background: var(--lime); color: var(--ink); }
.nav__link.is-active { color: var(--ink); font-weight: 800; }

.header__actions { display: flex; align-items: center; gap: 0; }
.icon-btn {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 42px; height: 42px; border: 0; border-radius: var(--r-full);
  background: transparent; color: var(--ink-2); cursor: pointer;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.icon-btn:hover { background: var(--lime); color: var(--ink); }
.icon-btn svg { width: 21px; height: 21px; }

.cart-count {
  position: absolute; top: 2px; right: 1px;
  min-width: 18px; height: 18px; padding: 0 5px;
  display: grid; place-items: center;
  background: var(--olive); color: var(--butter);
  border-radius: var(--r-full);
  font-size: .66rem; font-weight: 800; font-variant-numeric: tabular-nums;
  border: 2px solid var(--butter);
  transform: scale(0); transition: transform .3s var(--ease-spring);
}
.cart-count.is-visible { transform: scale(1); }
.cart-count.is-bumped { animation: bump .45s var(--ease-spring); }
@keyframes bump { 0%,100% { transform: scale(1); } 40% { transform: scale(1.4); } }

.nav-toggle { display: none; }

.mobile-nav {
  position: fixed; inset: 0; z-index: 90;
  background: var(--butter); padding: var(--s-5);
  transform: translateX(100%); visibility: hidden;
  transition: transform .34s var(--ease-out), visibility 0s linear .34s;
  overflow-y: auto;
}
.mobile-nav.is-open { transform: translateX(0); visibility: visible; transition: transform .34s var(--ease-out), visibility 0s; }
.mobile-nav__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--s-6); }
.mobile-nav__link {
  display: block; padding: var(--s-4) 0;
  border-bottom: 2px solid var(--line);
  font-family: var(--font-display); font-weight: 800;
  font-size: 1.7rem; text-transform: uppercase; letter-spacing: -.02em;
}

/* Footer */
.footer { background: var(--olive-deep); color: rgba(247,245,238,.72); padding-block: var(--s-8) var(--s-5); font-size: .9rem; }
/* The rule and the padding under it were there to divide these columns from
   the copyright bar. With that bar gone they were drawing a line under the
   footer and leaving a band of empty green below it. */
.footer__grid {
  display: grid; grid-template-columns: 1.7fr repeat(3, 1fr);
  gap: var(--s-7) var(--s-5);
}
.footer__blurb { max-width: 32ch; margin-top: var(--s-4); line-height: 1.65; }
.footer__social { display: flex; gap: var(--s-3); margin-top: var(--s-5); }
.footer__social a {
  display: grid; place-items: center;
  width: 40px; height: 40px; border-radius: var(--r-full);
  border: 1.5px solid rgba(247, 245, 238, .3);
  color: rgba(247, 245, 238, .82);
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease),
              background-color var(--dur) var(--ease);
}
.footer__social a:hover {
  color: var(--olive-deep);
  background: var(--lime);
  border-color: var(--lime);
}
.footer__social svg { width: 20px; height: 20px; }
.footer h4 {
  color: var(--lime); font-family: var(--font-ui);
  font-size: .74rem; font-weight: 800; letter-spacing: .14em;
  text-transform: uppercase; margin-bottom: var(--s-4);
}
.footer ul { list-style: none; padding: 0; margin: 0; }
.footer li { margin-bottom: 10px; }
.footer a:hover { color: var(--lime); }
.newsletter { display: flex; gap: var(--s-2); margin-top: var(--s-4); max-width: 350px; }
.newsletter .input { background: rgba(247,245,238,.1); border-color: rgba(247,245,238,.25); color: var(--butter); }
.newsletter .input::placeholder { color: rgba(247,245,238,.45); }
.newsletter .input:focus { border-color: var(--lime); box-shadow: 0 0 0 4px rgba(200,220,139,.15); }

/* --------------------------------------------------------------------------
   5. COMPONENTS
   -------------------------------------------------------------------------- */

/* --- Marquee ticker --- */
.ticker {
  /* The deep olive the footer and the closing band use, rather than flat
     black — a black strip across a cream page read as a foreign object. */
  background: var(--olive-deep); color: var(--butter);
  padding: var(--s-3) 0; overflow: hidden;
  border-block: 2px solid var(--olive-deep);
}
.ticker--lime { background: var(--lime); color: var(--ink); }
.ticker--olive { background: var(--olive); color: var(--butter); }
.ticker__track {
  display: flex; gap: var(--s-6); width: max-content;
  animation: ticker 34s linear infinite;
}
/* The strip does not stop for the pointer — there is nothing on it to read
   carefully or click, so pausing only made it look like it had snagged. */
.ticker__item {
  display: inline-flex; align-items: center; gap: var(--s-3);
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(.9rem, 1.6vw, 1.2rem);
  letter-spacing: .02em; text-transform: uppercase; white-space: nowrap;
}
.ticker__item svg { width: 16px; height: 16px; opacity: .6; }
@keyframes ticker { to { transform: translateX(-50%); } }

/* --- Cards --- */
.product-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); gap: var(--s-5); }

.card {
  position: relative; display: flex; flex-direction: column;
  background: var(--surface); border: 2px solid var(--ink);
  border-radius: var(--r-lg); overflow: hidden;
  transition: transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease);
}
.card:hover { transform: translate(-3px, -3px); box-shadow: var(--sh-hard); }
.card__media {
  position: relative; aspect-ratio: 1 / 1;
  background: var(--butter-deep);
  display: grid; place-items: center; overflow: hidden;
}
.card__media img { width: 84%; height: 84%; object-fit: contain; transition: transform .5s var(--ease-out); }
.card:hover .card__media img { transform: scale(1.05) rotate(-2deg); }
.card__flags { position: absolute; top: var(--s-3); left: var(--s-3); display: flex; flex-direction: column; gap: 6px; align-items: flex-start; z-index: 2; }
.card__wish {
  position: absolute; top: var(--s-3); right: var(--s-3); z-index: 2;
  width: 36px; height: 36px; border: 2px solid var(--ink); border-radius: var(--r-full);
  background: var(--white); display: grid; place-items: center;
  cursor: pointer; color: var(--ink); opacity: .85;
  transition: all var(--dur) var(--ease);
}
@media (hover: hover) {
  .card__wish { opacity: 0; transform: translateY(-4px); }
  .card:hover .card__wish, .card__wish:focus-visible, .card__wish.is-on { opacity: 1; transform: translateY(0); }
}
.card__wish:hover, .card__wish.is-on { background: var(--berry); color: var(--white); }
.card__wish.is-on svg { fill: currentColor; }
.card__wish svg { width: 17px; height: 17px; }

.card__body { padding: var(--s-4); display: flex; flex-direction: column; flex: 1; gap: 6px; }
.card__name { font-family: var(--font-display); font-size: 1.05rem; font-weight: 800; margin: 0; text-transform: uppercase; letter-spacing: -.02em; }
.card__name a::after { content: ""; position: absolute; inset: 0; z-index: 1; }
.card__meta { font-size: .84rem; color: var(--text-muted); }
.card__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); margin-top: auto; padding-top: var(--s-3); }
.card__price { font-size: 1.1rem; }
.card__add { position: relative; z-index: 2; }

/* --- Chips --- */
.shop-toolbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3);
  padding: var(--s-4) 0 var(--s-5);
  border-bottom: 2px solid var(--line); margin-bottom: var(--s-6);
}
.chip-row { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.chip {
  padding: 8px var(--s-4); border: 2px solid var(--ink); border-radius: var(--r-full);
  background: transparent; font-size: .85rem; font-weight: 700; color: var(--ink);
  cursor: pointer; transition: all var(--dur) var(--ease);
}
.chip:hover { background: var(--lime); }
.chip[aria-pressed="true"] { background: var(--ink); color: var(--butter); }
.result-count { font-size: .86rem; color: var(--text-muted); margin-left: auto; }

.empty-state { text-align: center; padding: var(--s-9) var(--s-5); color: var(--text-muted); }
.empty-state svg { width: 60px; height: 60px; margin: 0 auto var(--s-4); color: var(--ink-4); }

/* --- Drawer / scrim --- */
.scrim {
  position: fixed; inset: 0; z-index: 80;
  background: rgba(51, 59, 21, .45); backdrop-filter: blur(3px);
  opacity: 0; pointer-events: none; transition: opacity var(--dur) var(--ease);
}
.scrim.is-open { opacity: 1; pointer-events: auto; }

.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 85;
  width: min(440px, 100vw); background: var(--butter);
  display: flex; flex-direction: column;
  transform: translateX(100%);
  /* `visibility` is what removes the panel from the tab order. Transform alone
     leaves links inside focusable while off-screen. */
  visibility: hidden;
  transition: transform .38s var(--ease-out), visibility 0s linear .38s;
  box-shadow: var(--sh-4); border-left: 2px solid var(--ink);
}
.drawer.is-open { transform: translateX(0); visibility: visible; transition: transform .38s var(--ease-out), visibility 0s; }
.drawer__head { display: flex; align-items: center; justify-content: space-between; padding: var(--s-4) var(--s-5); border-bottom: 2px solid var(--ink); flex-shrink: 0; }
.drawer__title { font-family: var(--font-display); font-size: 1.3rem; font-weight: 800; text-transform: uppercase; margin: 0; display: flex; align-items: center; gap: 10px; }
.drawer__body { flex: 1; overflow-y: auto; padding: var(--s-4) var(--s-5); }
.drawer__foot { flex-shrink: 0; padding: var(--s-5); border-top: 2px solid var(--ink); background: var(--white); }

.ship-meter { margin-bottom: var(--s-4); }
.ship-meter__label { font-size: .82rem; margin-bottom: 8px; color: var(--ink-2); }
.ship-meter__track { height: 8px; border-radius: var(--r-full); background: var(--line); overflow: hidden; border: 1px solid var(--ink); }
.ship-meter__fill { height: 100%; background: var(--olive); transition: width .5s var(--ease-out); }

/* --- Line items --- */
.line { display: grid; grid-template-columns: 78px 1fr auto; gap: var(--s-4); padding: var(--s-4) 0; border-bottom: 1px solid var(--line); }
.line:last-child { border-bottom: 0; }
.line__media { width: 78px; height: 78px; border-radius: var(--r-md); background: var(--butter-deep); border: 1.5px solid var(--ink); display: grid; place-items: center; overflow: hidden; }
.line__media img { width: 80%; height: 80%; object-fit: contain; }
.line__name { font-weight: 700; font-size: .94rem; margin: 0 0 2px; }
.line__meta { font-size: .8rem; color: var(--text-muted); margin-bottom: var(--s-3); }
.line__controls { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
.line__remove { border: 0; background: none; padding: 0; font-size: .8rem; color: var(--ink-3); cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.line__remove:hover { color: var(--berry); }
.line__price { text-align: right; font-weight: 800; font-variant-numeric: tabular-nums; }

/* --- Totals --- */
.totals { font-size: .92rem; }
.totals__row { display: flex; justify-content: space-between; gap: var(--s-4); padding: 7px 0; color: var(--ink-2); }
.totals__row span:last-child { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--ink); }
.totals__row--discount span:last-child { color: var(--olive); }
.totals__row--grand {
  margin-top: var(--s-3); padding-top: var(--s-4); border-top: 2px solid var(--ink);
  font-family: var(--font-display); font-size: 1.25rem; font-weight: 800; text-transform: uppercase;
}
.totals__row--grand span:last-child { font-size: 1.25rem; font-weight: 800; }

/* --- Toast --- */
.toast-stack { position: fixed; bottom: var(--s-5); left: 50%; transform: translateX(-50%); z-index: 100; display: flex; flex-direction: column; gap: var(--s-2); align-items: center; pointer-events: none; }
.toast {
  display: flex; align-items: center; gap: var(--s-3);
  padding: 13px var(--s-5); background: var(--ink); color: var(--butter);
  border-radius: var(--r-full); box-shadow: var(--sh-4);
  font-size: .88rem; font-weight: 600; animation: toast-in .4s var(--ease-spring);
}
.toast svg { width: 17px; height: 17px; color: var(--lime); flex-shrink: 0; }
.toast.is-leaving { animation: toast-out .3s var(--ease) forwards; }
@keyframes toast-in  { from { opacity: 0; transform: translateY(16px) scale(.96); } }
@keyframes toast-out { to   { opacity: 0; transform: translateY(8px) scale(.98); } }

/* --- Accordion --- */
.acc__item { border-bottom: 2px solid var(--line); }
.acc__trigger {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  padding: var(--s-4) 0; border: 0; background: none;
  font-family: var(--font-ui); font-size: 1.02rem; font-weight: 700;
  text-align: left; cursor: pointer; color: var(--ink);
}
.acc__trigger:hover { color: var(--olive); }
.acc__icon { flex-shrink: 0; width: 22px; height: 22px; position: relative; }
.acc__icon::before, .acc__icon::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 14px; height: 2.4px; background: var(--ink);
  transform: translate(-50%, -50%); transition: transform var(--dur) var(--ease);
}
.acc__icon::after { transform: translate(-50%, -50%) rotate(90deg); }
.acc__trigger[aria-expanded="true"] .acc__icon::after { transform: translate(-50%, -50%) rotate(0); }
.acc__panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .32s var(--ease); }
.acc__panel[data-open="true"] { grid-template-rows: 1fr; }
.acc__panel > div { overflow: hidden; }
.acc__panel p, .acc__panel ul { color: var(--ink-2); font-size: .95rem; padding-bottom: var(--s-4); }

/* --- Feature tiles --- */
.feature-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--s-5); }
.feature {
  padding: var(--s-5); background: var(--white);
  border: 2px solid var(--ink); border-radius: var(--r-lg);
  transition: transform .34s var(--ease-spring), box-shadow .34s var(--ease);
}
/* Lifts up and slightly back toward the light, with the hard offset shadow
   the rest of the site uses for the same gesture. Only the box under the
   cursor moves — the other two stay put. */
.feature:hover {
  transform: translate(-4px, -6px);
  box-shadow: 7px 9px 0 var(--ink);
}
.feature__icon {
  width: 50px; height: 50px; border-radius: var(--r-md);
  background: var(--lime); color: var(--olive-deep);
  display: grid; place-items: center; margin-bottom: var(--s-4);
  border: 2px solid var(--ink);
  transition: transform .34s var(--ease-spring), background-color .34s var(--ease);
}
/* The icon leads the lift by a hair, which is what stops the box reading as
   one flat slab sliding. */
.feature:hover .feature__icon { transform: translateY(-3px) rotate(-4deg); }
.feature__icon svg { width: 25px; height: 25px; }
.feature h3 { font-size: 1.05rem; margin-bottom: 6px; }
.feature p { font-size: .91rem; color: var(--text-muted); margin: 0; }
.section--olive .feature, .section--deep .feature { background: rgba(247,245,238,.07); border-color: var(--lime); }
.section--olive .feature:hover, .section--deep .feature:hover { box-shadow: 5px 5px 0 var(--lime); }
.section--olive .feature__icon, .section--deep .feature__icon { background: var(--lime); border-color: var(--lime); }
.section--olive .feature p, .section--deep .feature p { color: rgba(247,245,238,.72); }

/* --- Stats --- */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--s-5); text-align: center; }
.stat__num {
  font-family: var(--font-display); font-size: clamp(2.6rem, 5vw, 4.4rem);
  font-weight: 800; line-height: .9; color: var(--olive); letter-spacing: -.05em;
}
.section--olive .stat__num, .section--deep .stat__num { color: var(--lime); }
.stat__label { font-size: .82rem; color: var(--text-muted); margin-top: 10px; font-weight: 600; }
.section--olive .stat__label, .section--deep .stat__label { color: rgba(247,245,238,.7); }

.trust-strip { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: var(--s-4) var(--s-7); padding-block: var(--s-6); border-block: 2px solid var(--line); }
.trust-mark { display: flex; align-items: center; gap: var(--s-2); font-size: .78rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-2); }
.trust-mark svg { width: 20px; height: 20px; color: var(--olive); }

/* --- Quotes --- */
.quote-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--s-5); }
.quote { padding: var(--s-6); background: var(--white); border: 2px solid var(--ink); border-radius: var(--r-lg); display: flex; flex-direction: column; gap: var(--s-4); }
.quote__text { font-family: var(--font-ui); font-size: 1.05rem; line-height: 1.55; font-weight: 500; margin: 0; }
.quote__who { display: flex; align-items: center; gap: var(--s-3); margin-top: auto; }
.quote__avatar { width: 56px; height: 56px; border-radius: var(--r-full); background: var(--lime); border: 2px solid var(--ink); color: var(--olive-deep); display: grid; place-items: center; font-weight: 800; font-size: .95rem; flex-shrink: 0; }
/* The same ringed circle, filled with the reviewer's photo. */
.quote__avatar--photo { overflow: hidden; padding: 0; }
.quote__avatar--photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.quote__name { font-size: .88rem; font-weight: 700; }
.quote__role { font-size: .78rem; color: var(--text-muted); }

/* --- Crumbs / steps --- */
.crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); font-size: .82rem; color: var(--text-muted); padding-block: var(--s-4); }
.crumbs--tight { padding-block: var(--s-2); }
.crumbs a:hover { color: var(--olive); text-decoration: underline; }
.crumbs span[aria-current] { color: var(--ink); font-weight: 600; }

.steps { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
.step { display: flex; align-items: center; gap: var(--s-2); font-size: .84rem; color: var(--ink-4); font-weight: 600; }
.step__dot { width: 24px; height: 24px; border-radius: var(--r-full); border: 2px solid currentColor; display: grid; place-items: center; font-size: .68rem; font-weight: 800; }
.step.is-current { color: var(--ink); }
.step.is-current .step__dot { background: var(--ink); border-color: var(--ink); color: var(--butter); }
.step.is-done { color: var(--olive); }
.step.is-done .step__dot { background: var(--olive); border-color: var(--olive); color: var(--butter); }
.step__bar { width: 28px; height: 2px; background: var(--line); }

/* --------------------------------------------------------------------------
   6. INTERACTIVE MODULES
   The education layer. These are the reason to visit the site.
   -------------------------------------------------------------------------- */

/* --- Shared module shell --- */
.module {
  background: var(--white); border: 2px solid var(--ink);
  border-radius: var(--r-xl); padding: clamp(var(--s-5), 4vw, var(--s-7));
  box-shadow: var(--sh-hard);
}
.module--flush { padding: 0; overflow: hidden; }
.module__head { margin-bottom: var(--s-6); }
.module__title { font-size: clamp(1.5rem, 3vw, 2.2rem); margin-bottom: var(--s-3); }

/* --- 1. Swap calculator --- */
.swap {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: clamp(var(--s-5), 4vw, var(--s-7)); align-items: center;
}
.swap__control { margin-bottom: var(--s-6); }
.swap__readout {
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(2.4rem, 6vw, 3.6rem); line-height: 1;
  letter-spacing: -.04em; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.swap__unit { font-size: .38em; color: var(--text-muted); letter-spacing: 0; }

/* Range input, restyled in both engines */
.range { width: 100%; appearance: none; background: transparent; height: 34px; cursor: pointer; }
.range:focus { outline: none; }
.range:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 4px rgba(26,86,219,.4); }
.range::-webkit-slider-runnable-track { height: 10px; border-radius: var(--r-full); background: var(--butter-deep); border: 2px solid var(--ink); }
.range::-webkit-slider-thumb {
  appearance: none; width: 30px; height: 30px; margin-top: -12px;
  border-radius: var(--r-full); background: var(--lime);
  border: 2px solid var(--ink); cursor: grab;
  transition: transform .15s var(--ease-spring);
}
.range::-webkit-slider-thumb:active { cursor: grabbing; transform: scale(1.15); }
.range::-moz-range-track { height: 10px; border-radius: var(--r-full); background: var(--butter-deep); border: 2px solid var(--ink); }
.range::-moz-range-thumb { width: 26px; height: 26px; border-radius: var(--r-full); background: var(--lime); border: 2px solid var(--ink); cursor: grab; }

.swap__scale { display: flex; justify-content: space-between; font-size: .76rem; color: var(--text-muted); font-weight: 600; margin-top: 4px; }

.swap__results { display: grid; gap: var(--s-3); }
.swap__stat {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-4);
  padding: var(--s-4); border-radius: var(--r-md);
  background: var(--butter); border: 2px solid var(--ink);
}
.swap__stat--win { background: var(--lime); }
.swap__stat-label { font-size: .86rem; font-weight: 600; color: var(--ink-2); }
.swap__stat-value { font-family: var(--font-display); font-weight: 800; font-size: 1.5rem; letter-spacing: -.03em; font-variant-numeric: tabular-nums; white-space: nowrap; }

.swap__visual { display: grid; gap: var(--s-4); }
.swap__jar { position: relative; }
.swap__bar-label { display: flex; justify-content: space-between; font-size: .82rem; font-weight: 700; margin-bottom: 6px; }
.swap__bar-track { height: 46px; border: 2px solid var(--ink); border-radius: var(--r-md); background: var(--white); overflow: hidden; }
.swap__bar-fill { height: 100%; transition: width .5s var(--ease-out); display: flex; align-items: center; padding-left: 12px; font-weight: 800; font-size: .86rem; color: var(--butter); white-space: nowrap; }
.swap__bar-fill--sugar { background: var(--berry); }
.swap__bar-fill--bettr { background: var(--olive); }

/* --- 2. Glucose curve --- */
/* The chart sits in a light .module card, but that card is inside
   .section--deep, which sets the text colour to cream. The legend and the
   minute counter were therefore cream on white — the same defect as the
   callout below them. Setting the colour here covers everything in the
   module. */
.glucose { position: relative; color: var(--ink); }
/* Capped so that the chart and the "drag through the two hours" control are
   on screen together — the point of the section is watching the marker move
   along the curve, which does not work if you have to scroll between the
   slider and the line it is driving. */
.glucose__chart { width: 100%; max-width: 860px; height: auto; margin-inline: auto; overflow: visible; }
/* --- The two travellers ---
   The curves are hidden behind a full-length dash and uncovered from the
   start as each character advances, so the shape of what is coming is never
   given away. Both the reveal and the movement are transitions rather than
   keyframes, which means dragging the slider tracks the pointer exactly and
   pressing play animates between steps for free. */
.glu-line { transition: stroke-dashoffset .5s var(--ease-out); }
.glu-actor { transition: transform .5s var(--ease-out); }
/* SVG transform via attribute and CSS animation on the same element fight
   each other, so the wobble lives on an inner group that carries no
   positioning of its own. */
#glu-sugar-body { transform-box: fill-box; transform-origin: 50% 50%; }
/* Both the swing and the speed are written in by JS from the current fear
   level, so the shake deepens as the reading climbs rather than being one
   fixed jitter for the whole ascent. */
#glu-sugar-body.is-panic {
  animation: glu-panic var(--panic-dur, .34s) var(--ease) infinite;
}
@keyframes glu-panic {
  0%, 100% { transform: rotate(calc(var(--panic-deg, 4deg) * -1)); }
  50%      { transform: rotate(var(--panic-deg, 4deg)); }
}

.glucose__legend { display: flex; gap: var(--s-5); flex-wrap: wrap; margin-top: var(--s-4); }
/* baseline, not center: the second key ends in the brand mark, and only a
   baseline alignment puts the BETTR wordmark on the same line as the words
   before it. The swatch opts back out to center — it is a rule, not type, and
   has no baseline worth sitting on. */
.glucose__key { display: flex; align-items: baseline; gap: var(--s-2); font-size: .85rem; font-weight: 600; }
.glucose__key .glucose__swatch { align-self: center; }
/* The name of what was in the cup is the part worth reading at a glance. */
.glucose__who { font-weight: 800; text-transform: uppercase; letter-spacing: .05em; }
/* The mark that finishes the second key's sentence, sized to the words
   around it rather than to itself.

   The number is measured, not chosen. The artwork is 2026x497 with the arced
   RITUAL occupying the top 23.7% and the BETTR wordmark the bottom 76.3%, so
   the file is always taller than its letterforms. "SUGAR" in this legend has
   a cap height of exactly 10px, and 10 / 0.763 = 13.1px of image height puts
   BETTR's capitals on the same 10px. At the 20px it was set to before, BETTR
   was rendering 15.3px tall — half again the size of the type beside it.

   Two selectors deep so it beats .logo__img's own 42px, set later in the
   file. Baseline alignment comes from .glucose__key above: the bottom edge of
   a replaced element is its baseline, and BETTR sits on the bottom edge of
   the artwork, so the wordmark lands on the text baseline. */
.glucose__key .glucose__logo { height: 13px; width: auto; }
.glucose__swatch { width: 26px; height: 5px; border-radius: 3px; }
.glucose__scrub { margin-top: var(--s-5); }
.glucose__time { font-family: var(--font-display); font-weight: 800; font-size: 1.1rem; letter-spacing: -.02em; }
.glucose__callout {
  margin-top: var(--s-4); padding: var(--s-4);
  background: var(--butter); border: 2px solid var(--ink); border-radius: var(--r-md);
  /* This box sits inside .section--deep, which sets the text colour to cream.
     Without an explicit colour here the running commentary was cream on a
     cream background — present, readable by a screen reader, and completely
     invisible on screen, so the box looked like an empty decorative slab. */
  color: var(--ink);
  font-size: .92rem;
}
.glucose__callout strong {
  display: block; margin-bottom: 4px; color: var(--olive);
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: -.01em;
}

/* ==========================================================================
   MARQUEE ROW

   A strip of cards that travels sideways forever.

   The track holds the same set of cards twice and shifts by exactly half its
   own width. At the end of the animation the second copy sits precisely where
   the first one started, so the jump back to 0% is invisible — the row never
   appears to restart, it just keeps going.

   That only works if half the track width is exactly one set. `gap` breaks
   it: over 2n items there are 2n−1 gaps, so half the track is one set minus
   half a gap, and the loop slips by that much every cycle. Trailing margin on
   every item gives 2n gaps instead, and the maths comes out exact.
   ========================================================================== */
.marquee {
  position: relative;
  overflow: hidden;
  /* Sideways overflow is clipped here, so the reveal animation must not also
     be trying to lift these cards from below. */
  padding-block: 4px;
  /* Breaks out of the centred container to the full width of the window.
     Clipped at the container instead, cards appeared and vanished against a
     hundred pixels of visible paper on either side, which gives away that
     they are being cut rather than travelling past. Safe because body uses
     overflow-x: clip — see section 2. */
  width: 100vw;
  margin-left: calc(50% - 50vw);
}
.marquee__track {
  display: flex;
  width: max-content;
  animation: marquee-left var(--marquee-dur, 90s) linear infinite;
  will-change: transform;
}
.marquee--right .marquee__track { animation-name: marquee-right; }
.marquee.is-paused .marquee__track { animation-play-state: paused; }

.marquee__item {
  flex: 0 0 auto;
  width: var(--marquee-w, 300px);
  /* Longhand on all four sides rather than margin-right alone: a <figure>
     carries a user-agent margin, and zeroing it inline on the element was
     overriding the trailing space and butting the cards together. */
  margin: 0 var(--s-5) 0 0;
}
/* Review cards are all one size. Every card in the strip takes the height of
   the tallest, so the quote is capped at ten and a half lines (room for any
   ordinary review, with no half-line tease of scrolling) and a longer one
   scrolls inside its card — the same treatment as the myth cards' verdicts,
   with the same thin scrollbar. The strip itself never pauses. */
/* Only a quote that actually overflows becomes a scroll box
   (.quote__text--scroll, added in index.html). Every quote used to be one,
   with overscroll-behavior: contain, and Chrome then swallowed the mouse
   wheel over every review card — the page would not scroll with the
   pointer on a card, even a short one with nothing to scroll. The long one
   now scrolls its text and then hands the wheel back to the page. */
.marquee .quote__text {
  max-height: calc(1.55em * 10.5);
  overflow: hidden;
  padding-right: 6px;
}
.marquee .quote__text--scroll {
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(10,10,10,.4) transparent;
}
.marquee .quote__text--scroll::-webkit-scrollbar { width: 6px; }
.marquee .quote__text--scroll::-webkit-scrollbar-track { background: transparent; }
.marquee .quote__text--scroll::-webkit-scrollbar-thumb { background: rgba(10,10,10,.35); border-radius: var(--r-full); }
.marquee .quote__text--scroll:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; border-radius: 4px; }

@keyframes marquee-left {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}
@keyframes marquee-right {
  from { transform: translate3d(-50%, 0, 0); }
  to   { transform: translate3d(0, 0, 0); }
}

/* Nothing should drift on its own for someone who has asked it not to. */
@media (prefers-reduced-motion: reduce) {
  .marquee__track { animation: none; }
  .marquee { overflow-x: auto; }
}

/* --- 3. Myth or fact cards --- */
.myth-deck { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--s-5); }
.myth {
  perspective: 1400px; background: none; border: 0; padding: 0;
  /* Raised from 260px: the longest verdict needed 362px, and while the body
     now scrolls, a card that has to be scrolled on every visit is a worse
     card. At 300px only the two longest need any scrolling at all. */
  cursor: pointer; text-align: left; min-height: 300px;
}
.myth__inner {
  /* This is a <span> — chosen because the card is a <button>, and a button
     may only contain phrasing content. Without blockifying it, the element
     stays inline, width/height are ignored, and the two absolutely
     positioned faces resolve inset:0 against a text-sized inline box rather
     than the card. The result was a 52px stub with the claim wrapping one
     word per line. */
  display: block;
  position: relative; width: 100%; height: 100%; min-height: 300px;
  transition: transform .6s var(--ease-out); transform-style: preserve-3d;
}
.myth[aria-pressed="true"] .myth__inner { transform: rotateY(180deg); }
.myth__face {
  position: absolute; inset: 0;
  backface-visibility: hidden; -webkit-backface-visibility: hidden;
  border: 2px solid var(--ink); border-radius: var(--r-lg);
  padding: var(--s-5); display: flex; flex-direction: column; gap: var(--s-3);
}
.myth__face--front { background: var(--white); }
.myth__face--back { background: var(--olive); color: var(--butter); transform: rotateY(180deg); }
.myth__tag { font-size: .68rem; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; }
.myth__claim { font-family: var(--font-display); font-size: 1.25rem; font-weight: 800; text-transform: uppercase; letter-spacing: -.02em; line-height: 1.05; margin: 0; }
.myth__verdict { font-family: var(--font-display); font-size: 2rem; font-weight: 800; text-transform: uppercase; letter-spacing: -.03em; margin: 0; color: var(--lime); }
.myth__body { font-size: .9rem; line-height: 1.55; margin: 0; }

/* --- The verdict side ---
   The explanation used to run straight out of the bottom of the card, over
   whatever was beneath it. It now scrolls inside the card, with the tag and
   the verdict staying put at the top so you never lose which claim you are
   reading about. `min-height: 0` is the part that actually does it: a flex
   child defaults to min-height:auto and refuses to shrink below its content,
   so overflow alone has nothing to act on. */
.myth__face--back .myth__body {
  color: rgba(247,245,238,.9);
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  /* Reaching the end of the text should not start scrolling the page. */
  overscroll-behavior: contain;
  padding-right: 6px;
  scrollbar-width: thin;
  scrollbar-color: rgba(247,245,238,.55) transparent;
}
.myth__face--back .myth__body::-webkit-scrollbar { width: 6px; }
.myth__face--back .myth__body::-webkit-scrollbar-track { background: transparent; }
.myth__face--back .myth__body::-webkit-scrollbar-thumb {
  background: rgba(247,245,238,.5); border-radius: var(--r-full);
}

/* --- Verdict colour ---
   Three verdicts, three weights of certainty. "Myth" keeps the full-strength
   olive; "Mostly true" steps back from it; "Half true" leaves the green
   entirely, because a hedged answer shown in the same colour as a settled one
   reads as settled. */
.myth[data-verdict="mostly-true"] .myth__face--back { background: var(--olive-mid); }
.myth[data-verdict="half-true"] .myth__face--back {
  background: var(--amber-soft); color: var(--ink);
}
.myth[data-verdict="half-true"] .myth__verdict { color: var(--olive-deep); }
.myth[data-verdict="half-true"] .myth__body { color: rgba(10,10,10,.82); }
.myth[data-verdict="half-true"] .myth__tag { color: var(--olive-deep) !important; }
.myth[data-verdict="half-true"] .myth__body { scrollbar-color: rgba(10,10,10,.45) transparent; }
.myth[data-verdict="half-true"] .myth__body::-webkit-scrollbar-thumb { background: rgba(10,10,10,.4); }
.myth__flip { margin-top: auto; font-size: .76rem; font-weight: 700; opacity: .65; display: flex; align-items: center; gap: 6px; }
.myth:hover .myth__face--front { box-shadow: var(--sh-hard); }

/* --- 4. Sweetener showdown --- */
.showdown { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.showdown table { width: 100%; min-width: 620px; border-collapse: collapse; }
.showdown th, .showdown td { padding: var(--s-4) var(--s-3); text-align: center; border-bottom: 1px solid var(--line); }
.showdown thead th {
  font-family: var(--font-display); font-size: .82rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: .02em;
  border-bottom: 2px solid var(--ink); position: sticky; top: 0; background: var(--white);
}
.showdown tbody th { text-align: left; font-size: .88rem; font-weight: 700; white-space: nowrap; }
.showdown .col-hero { background: var(--lime); }
.showdown thead .col-hero { background: var(--lime); border-radius: var(--r-md) var(--r-md) 0 0; }
.showdown td { font-size: .9rem; font-weight: 600; font-variant-numeric: tabular-nums; }
/* The cross is a text glyph and centres on the cell's text-align. The tick is
   an SVG, and `img, svg, video { display: block }` in section 2 turns it into
   a block box inside an inline span, which drops out of the centred line box
   and sits hard against the left edge of the cell. Making both marks
   inline-flex puts them back on the same footing. */
.showdown .yes,
.showdown .no {
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 800;
}
.showdown .yes { color: var(--olive); }
.showdown .no { color: var(--berry); }

/* --- 5. Anatomy hotspots --- */
.anatomy { position: relative; display: grid; grid-template-columns: 1.1fr .9fr; gap: var(--s-7); align-items: center; }
.anatomy__stage { position: relative; }
.anatomy__hotspot {
  position: absolute; width: 38px; height: 38px;
  border-radius: var(--r-full); border: 2px solid var(--ink);
  background: var(--lime); color: var(--ink);
  font-weight: 800; font-size: .88rem;
  display: grid; place-items: center; cursor: pointer;
  transform: translate(-50%, -50%);
  transition: transform .25s var(--ease-spring), background-color var(--dur) var(--ease);
}
.anatomy__hotspot::after {
  content: ""; position: absolute; inset: -6px;
  border-radius: var(--r-full); border: 2px solid var(--lime);
  opacity: 0; animation: ping 2.4s var(--ease-out) infinite;
}
.anatomy__hotspot[aria-pressed="true"] { background: var(--ink); color: var(--lime); transform: translate(-50%,-50%) scale(1.15); }
.anatomy__hotspot[aria-pressed="true"]::after { animation: none; }
@keyframes ping { 0% { opacity: .9; transform: scale(.85); } 70%,100% { opacity: 0; transform: scale(1.45); } }
.anatomy__panel { min-height: 220px; }
.anatomy__label { font-family: var(--font-display); font-size: clamp(1.6rem,3vw,2.4rem); font-weight: 800; text-transform: uppercase; letter-spacing: -.03em; margin-bottom: var(--s-3); }

/* The photograph of whichever part is selected. It lives in the panel column,
   never over the diagram, so all four numbered points stay clickable while it
   is up. Re-rendering the panel restarts the animation, which is what makes
   each click feel like the picture popping up. */
.anatomy__shot {
  margin: 0 0 var(--s-5);
  border: 2px solid var(--ink);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--butter-deep);
  box-shadow: var(--sh-hard);
  transform-origin: 50% 100%;
  animation: anatomy-pop .42s var(--ease-spring) both;
}
.anatomy__shot img {
  width: 100%;
  /* A fixed band, because the four photographs are different shapes and a
     panel that jumps height on every click is worse than a crop. */
  height: 210px;
  object-fit: cover;
  display: block;
}
/* Sits over the bottom of the photograph rather than below it, in translucent
   olive — solid black read as a letterbox bar bolted onto the picture, where
   a tinted band lets the image carry on underneath it. The blur keeps the
   label legible over a busy photo without having to darken it further. */
.anatomy__shot { position: relative; }
.anatomy__shot figcaption {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 10px var(--s-4);
  font-size: .74rem; font-weight: 800;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--butter);
  background: rgba(51, 59, 21, .68);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
@keyframes anatomy-pop {
  from { opacity: 0; transform: scale(.92) translateY(14px); }
  to   { opacity: 1; transform: none; }
}

/* --- WhatsApp call-to-action ---
   Used on the wholesale panel. WhatsApp green rather than the brand olive,
   because the whole point of the button is that it is instantly recognisable
   as WhatsApp; dressing it in our own colours would cost more than the
   consistency gains. */
.wa-btn {
  display: flex; align-items: center; gap: var(--s-3);
  margin-top: var(--s-5); padding: 14px var(--s-4);
  background: #25d366; color: #05311a;
  border: 2px solid var(--ink); border-radius: var(--r-md);
  text-decoration: none; font-weight: 700;
  transition: transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease);
}
.wa-btn:hover { transform: translate(-2px, -2px); box-shadow: var(--sh-hard); }
.wa-btn__mark { width: 28px; height: 28px; flex-shrink: 0; }
.wa-btn strong { display: block; font-size: .95rem; }
.wa-btn small { display: block; font-size: .78rem; font-weight: 600; opacity: .75; }

/* --- 6. Quiz --- */
.quiz { max-width: 640px; margin-inline: auto; }
.quiz__progress { height: 8px; background: var(--butter-deep); border: 2px solid var(--ink); border-radius: var(--r-full); overflow: hidden; margin-bottom: var(--s-6); }
.quiz__progress-fill { height: 100%; background: var(--olive); transition: width .4s var(--ease-out); }
.quiz__step { animation: quiz-in .4s var(--ease-out); }
@keyframes quiz-in { from { opacity: 0; transform: translateX(18px); } }
.quiz__question { font-size: clamp(1.4rem, 3vw, 2rem); margin-bottom: var(--s-5); }
.quiz__options { display: grid; gap: var(--s-3); }
.quiz__option {
  display: flex; align-items: center; gap: var(--s-4);
  padding: var(--s-4) var(--s-5); border: 2px solid var(--ink); border-radius: var(--r-md);
  background: var(--white); cursor: pointer; text-align: left;
  font-size: 1rem; font-weight: 600;
  transition: all .18s var(--ease-out);
}
.quiz__option:hover { background: var(--lime); transform: translate(-2px,-2px); box-shadow: var(--sh-hard-sm); }
.quiz__option.is-picked { background: var(--ink); color: var(--butter); }
.quiz__emoji { font-size: 1.5rem; line-height: 1; }
.quiz__result { text-align: center; animation: quiz-in .5s var(--ease-out); }
.quiz__big {
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(3.4rem, 12vw, 7rem); line-height: .88;
  letter-spacing: -.05em; color: var(--olive);
  font-variant-numeric: tabular-nums;
}
.quiz__big-unit { display: block; font-size: .2em; letter-spacing: .1em; color: var(--ink-2); text-transform: uppercase; }

/* --- 7. Size picker (2 SKUs) ---
   The .size-picker / .size-opt set was removed. The product page's own pack
   picker is gone: it now renders the .size-pill chooser below, the same one
   the home page uses, so there is a single set of rules for choosing a pack
   size instead of two that had already drifted apart from each other. */

/* --- 8. Ritual tracker --- */
.ritual-grid { display: grid; grid-template-columns: repeat(10, 1fr); gap: 6px; max-width: 440px; }
.ritual-day {
  aspect-ratio: 1; border: 2px solid var(--ink); border-radius: 6px;
  background: var(--white); cursor: pointer; font-size: .68rem; font-weight: 700;
  display: grid; place-items: center; color: var(--ink-4);
  transition: all .18s var(--ease-spring);
}
.ritual-day:hover { transform: scale(1.12); }
.ritual-day.is-done { background: var(--olive); color: var(--butter); border-color: var(--olive); }

/* The reveal utility used to live here. It has moved up to section 2 — see
   the note there. Sitting at this point in the file, its `transform: none`
   silently beat every component hover lift declared above it. */

/* --- Search overlay --- */
.search {
  position: fixed; inset: 0; z-index: 95;
  background: var(--veil-solid);
  backdrop-filter: saturate(180%) blur(20px); -webkit-backdrop-filter: saturate(180%) blur(20px);
  opacity: 0; visibility: hidden;
  transition: opacity .24s var(--ease), visibility 0s linear .24s; overflow-y: auto;
}
.search.is-open { opacity: 1; visibility: visible; transition: opacity .24s var(--ease), visibility 0s; }
.search__bar { display: flex; align-items: center; gap: var(--s-3); padding: var(--s-4) 0; border-bottom: 3px solid var(--ink); }
.search__bar svg { width: 24px; height: 24px; color: var(--ink-3); flex-shrink: 0; }
.search__input {
  flex: 1; border: 0; background: none;
  font-family: var(--font-display); font-size: clamp(1.4rem, 4vw, 2.2rem);
  font-weight: 800; text-transform: uppercase; letter-spacing: -.03em; padding: var(--s-2) 0;
}
.search__input:focus { outline: none; }
.search__input::placeholder { color: var(--ink-4); }
.search__meta { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; padding: var(--s-4) 0; font-size: .86rem; color: var(--text-muted); }
.search__section-title { font-size: .72rem; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; color: var(--text-muted); margin: var(--s-5) 0 var(--s-4); }
.search mark { background: var(--lime); color: inherit; border-radius: 3px; padding: 0 3px; }
.search-hit { display: grid; grid-template-columns: 66px 1fr auto; gap: var(--s-4); align-items: center; padding: var(--s-3); border-radius: var(--r-md); transition: background-color var(--dur) var(--ease); }
.search-hit:hover, .search-hit:focus-visible { background: var(--lime); }
.search-hit__media { width: 66px; height: 66px; border-radius: var(--r-sm); background: var(--white); border: 2px solid var(--ink); display: grid; place-items: center; }
.search-hit__media img { width: 82%; height: 82%; object-fit: contain; }
.search-hit__name { font-weight: 800; font-size: .96rem; }
.search-hit__meta { font-size: .8rem; color: var(--text-muted); }

/* The reviews block (summary score, star histogram, review cards) was
   removed along with the invented reviews it presented. */

/* --- Tabs --- */
.tabs { display: flex; gap: var(--s-2); border-bottom: 2px solid var(--line); margin-bottom: var(--s-6); overflow-x: auto; scrollbar-width: none; }
.tabs::-webkit-scrollbar { display: none; }
.tab {
  padding: var(--s-3) var(--s-4); border: 0; background: none;
  border-bottom: 3px solid transparent; margin-bottom: -2px;
  font-size: .92rem; font-weight: 700; color: var(--text-muted);
  cursor: pointer; white-space: nowrap;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.tab:hover { color: var(--ink); }
.tab[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--ink); font-weight: 800; }

/* --- Account --- */
.account-layout { display: grid; grid-template-columns: 250px 1fr; gap: var(--s-7); align-items: start; }
.account-nav { display: grid; gap: 4px; }
.account-nav__link {
  display: flex; align-items: center; gap: var(--s-3);
  padding: 11px var(--s-4); border: 2px solid transparent; background: none;
  border-radius: var(--r-md); font-size: .92rem; font-weight: 700;
  color: var(--ink-2); cursor: pointer; text-align: left; width: 100%;
  transition: all var(--dur) var(--ease);
}
.account-nav__link svg { width: 18px; height: 18px; }
.account-nav__link:hover { background: var(--butter-deep); }
.account-nav__link[aria-current="true"] { background: var(--ink); color: var(--butter); border-color: var(--ink); }

.order-row { border: 2px solid var(--ink); border-radius: var(--r-lg); padding: var(--s-5); margin-bottom: var(--s-4); background: var(--white); }
.order-row__head { display: flex; flex-wrap: wrap; gap: var(--s-4); justify-content: space-between; align-items: center; padding-bottom: var(--s-4); border-bottom: 1px solid var(--line); margin-bottom: var(--s-4); }
.order-row__num { font-weight: 800; font-size: .95rem; }
.order-row__date { font-size: .82rem; color: var(--text-muted); }
.order-thumbs { display: flex; gap: var(--s-2); flex-wrap: wrap; }
.order-thumb { width: 50px; height: 50px; border-radius: var(--r-sm); background: var(--butter-deep); border: 1.5px solid var(--ink); display: grid; place-items: center; }
.order-thumb img { width: 80%; height: 80%; object-fit: contain; }

.address-card { border: 2px solid var(--ink); border-radius: var(--r-md); padding: var(--s-4); font-size: .9rem; line-height: 1.7; background: var(--white); position: relative; }
.address-card--default { box-shadow: var(--sh-hard-sm); }
.address-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: var(--s-4); }

/* --- Express checkout --- */
.express { margin-bottom: var(--s-6); }
.express__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-3); }
.express__btn {
  min-height: 54px; border: 2px solid var(--ink); border-radius: var(--r-md); cursor: pointer;
  display: flex; align-items: center; justify-content: center; gap: 6px;
  font-size: .9rem; font-weight: 800;
  transition: transform .18s var(--ease-out), box-shadow var(--dur) var(--ease);
}
.express__btn:hover { transform: translate(-2px,-2px); box-shadow: var(--sh-hard-sm); }
.express__btn svg { width: 20px; height: 20px; }
.express__btn--apple  { background: #000; color: #fff; }
.express__btn--paypal { background: #ffc439; color: #003087; }
.express__btn--shop   { background: #5a31f4; color: #fff; }
.express__divider { display: flex; align-items: center; gap: var(--s-4); margin: var(--s-5) 0; font-size: .82rem; font-weight: 700; color: var(--text-muted); text-transform: uppercase; letter-spacing: .08em; }
.express__divider::before, .express__divider::after { content: ""; flex: 1; height: 2px; background: var(--line); }

/* --- Status --- */
/* .stock-note removed with the tick-and-pill stock line; the stock message
   is now the chooser's plain .size-detail__stock text. */

.inline-alert { display: flex; gap: var(--s-3); padding: var(--s-3) var(--s-4); border-radius: var(--r-md); font-size: .86rem; margin-block: var(--s-3); border: 2px solid var(--ink); }
.inline-alert svg { width: 18px; height: 18px; flex-shrink: 0; margin-top: 1px; }
.inline-alert--warn { background: #f8ecd0; }
.inline-alert--info { background: var(--lime); }

.demo-banner { display: flex; gap: var(--s-3); padding: var(--s-4); background: #f8ecd0; border: 2px solid var(--ink); border-radius: var(--r-md); font-size: .86rem; color: #5e4310; margin-bottom: var(--s-6); }
.demo-banner svg { width: 20px; height: 20px; flex-shrink: 0; margin-top: 1px; }
.demo-banner strong { display: block; margin-bottom: 2px; }

.spinner { width: 17px; height: 17px; border: 2px solid rgba(247,245,238,.35); border-top-color: var(--butter); border-radius: var(--r-full); animation: spin .7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* --- Panels --- */
.panel { background: var(--surface); border: 2px solid var(--ink); border-radius: var(--r-lg); padding: var(--s-5); }
.panel--sticky { position: sticky; top: calc(var(--header-h) + var(--s-4)); }
.panel__title { font-family: var(--font-display); font-size: 1rem; font-weight: 800; text-transform: uppercase; letter-spacing: -.01em; margin-bottom: var(--s-4); padding-bottom: var(--s-4); border-bottom: 2px solid var(--line); }

.promo-row { display: flex; gap: var(--s-2); margin-block: var(--s-4); }
.promo-row .input { flex: 1; }
.promo-applied { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); padding: 10px var(--s-4); background: var(--lime); border: 2px solid var(--ink); border-radius: var(--r-md); font-size: .85rem; margin-block: var(--s-4); }

/* --- Recipes --- */
.recipe-card { display: flex; flex-direction: column; background: var(--white); border: 2px solid var(--ink); border-radius: var(--r-lg); overflow: hidden; transition: transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease); }
.recipe-card:hover { transform: translate(-3px,-3px); box-shadow: var(--sh-hard); }
.recipe-card__media { aspect-ratio: 4/3; background: var(--butter-deep); display: grid; place-items: center; overflow: hidden; border-bottom: 2px solid var(--ink); }
.recipe-card__media img { width: 100%; height: 100%; object-fit: cover; }
.recipe-card__body { padding: var(--s-4); display: flex; flex-direction: column; gap: 8px; flex: 1; }
.recipe-card__meta { display: flex; gap: var(--s-4); flex-wrap: wrap; font-size: .8rem; color: var(--text-muted); font-weight: 600; margin-top: auto; padding-top: var(--s-3); }

/* --- Prose --- */
.prose { max-width: 68ch; }
.prose h2 { font-size: 1.7rem; margin-top: var(--s-7); }
.prose h3 { font-size: 1.1rem; font-family: var(--font-ui); text-transform: none; letter-spacing: -.01em; margin-top: var(--s-6); margin-bottom: var(--s-3); }
.prose p, .prose li { color: var(--ink-2); }
.prose table { width: 100%; border-collapse: collapse; margin-block: var(--s-4); font-size: .92rem; }
.prose th, .prose td { text-align: left; padding: 11px 0; border-bottom: 1px solid var(--line); }
.prose th { font-weight: 800; font-size: .76rem; letter-spacing: .1em; text-transform: uppercase; color: var(--text-muted); }
.prose__updated { font-size: .84rem; color: var(--text-muted); font-style: italic; }

/* .compliance removed. Its .78rem type and butter-deep panel were what made
   the Safety & legal accordion look unlike every other accordion; that copy
   is now an ordinary list inside .acc__panel. */

/* --------------------------------------------------------------------------
   7. PAGES
   -------------------------------------------------------------------------- */

/* --- Hero --- */
.hero { position: relative; overflow: hidden; background: var(--butter); }
.hero__inner { display: grid; grid-template-columns: 1.05fr .95fr; gap: var(--s-7); align-items: center; padding-block: clamp(var(--s-6), 6vw, var(--s-9)); }
/* --- The hero kicker ---
   The product category, and the first thing the eye should land on.

   Split across two colours exactly as the pack does it: the product in the
   page's near-black and the word "sweetener" in the brand green. That split
   is what carries it — the lime highlighter band this had at first was an
   invention that the packaging does not use, and it fought with green type
   sitting on top of it.

   Display type rather than the tiny tracked-out eyebrow it replaces. The
   headline below is far larger, but this is read first because it sits
   first and because the colour break stops the eye. */
.hero__kicker {
  /* The headline below runs at line-height .98, so its cap tops sit very
     close to the top of its own box, so margin here buys less visible space
     than the number suggests: 24px of it still read as one block with the
     headline. 48px is what actually separates them. */
  margin: 0 0 var(--s-7);
  font-family: var(--font-display);
  font-weight: 800;
  font-variation-settings: "wdth" 112;
  font-size: clamp(1.15rem, 2.4vw, 1.85rem);
  line-height: 1.16;
  letter-spacing: .01em;
  text-transform: uppercase;
  color: var(--ink);
}
.hero__kicker span { color: var(--olive); }

.hero h1 { margin-bottom: var(--s-5); }
/* Was scoped to `.hero h1`, which meant the olive highlight only worked on
   the home page. It is a general device — one phrase of a headline picked out
   in the brand green — so it is now available to any heading. */
.hl { color: var(--olive); }
/* The quieter partner to .hl, for the half of a headline that is the problem
   rather than the answer. --ink-3 is the muted text token, measured at 5.6:1
   on the paper background, so it reads as grey without going soft. */
.hl-grey { color: var(--ink-3); }
.hero__sub { font-size: 1.15rem; color: var(--ink-2); max-width: 45ch; margin-bottom: var(--s-6); }

/* --- Hero entrance -------------------------------------------------------
   The headline is set as three masked lines: each .line is an overflow box
   and the span inside it starts pushed below that box, so the type is
   uncovered rather than faded in. The kicker, the standfirst and the buttons
   follow it up on a short stagger.

   Two gates, both deliberate:

   .hero-anim is added to <html> by an inline script in the page head. Every
   hidden state below is scoped to it, so a visitor with no JavaScript — or
   one where the script fails — gets the hero fully visible rather than a
   blank column. Nothing here can hide content permanently.

   .hero-in is added once the webfont has arrived. Starting the reveal on the
   fallback face means the headline animates in and then reflows under itself
   when Archivo lands, which is worse than a slightly later start.

   The mask needs no extra room for descenders: h1 is uppercase, so there are
   none to clip at this line-height.

   The class is .hero__line, not .line. `.line` is already taken by the cart
   line item, which carries 16px of vertical padding and a bottom border —
   so the first version of this put a hairline rule under two thirds of the
   headline and pushed the lines 32px apart. */
.hero__line { display: block; overflow: hidden; }
.hero__line > span { display: block; }

.hero-anim .hero__line > span {
  transform: translateY(105%);
  transition: transform .95s var(--ease-out);
}
.hero-anim.hero-in .hero__line > span { transform: none; }
.hero-anim.hero-in .hero__line:nth-child(2) > span { transition-delay: .08s; }
.hero-anim.hero-in .hero__line:nth-child(3) > span { transition-delay: .16s; }

.hero-anim .hero__kicker,
.hero-anim .hero__sub,
.hero-anim .hero__inner .btn-row {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .8s var(--ease-out), transform .8s var(--ease-out);
}
.hero-anim.hero-in .hero__kicker,
.hero-anim.hero-in .hero__sub,
.hero-anim.hero-in .hero__inner .btn-row { opacity: 1; transform: none; }
.hero-anim.hero-in .hero__sub { transition-delay: .34s; }
.hero-anim.hero-in .hero__inner .btn-row { transition-delay: .46s; }

/* Asked for less movement: show everything, move nothing. */
@media (prefers-reduced-motion: reduce) {
  .hero-anim .hero__line > span { transform: none; }
  .hero-anim .hero__kicker,
  .hero-anim .hero__sub,
  .hero-anim .hero__inner .btn-row { opacity: 1; transform: none; }
}
.hero__proof { display: flex; align-items: center; gap: var(--s-4); margin-top: var(--s-6); flex-wrap: wrap; }
/* Nothing in here is text anyone would want to copy — it is a photograph, a
   sprig of leaves, a pair of googly eyes and six badges — so dragging across
   the hero should not leave it all highlighted in lime. user-drag goes with
   it: without that the fruit and the leaf can be picked up and dragged off
   the page as loose images, which is the other half of what makes the hero
   feel like it is coming apart under the cursor. */
.hero__art {
  position: relative;
  -webkit-user-select: none;
  user-select: none;
}
.hero__art img {
  -webkit-user-drag: none;
  user-drag: none;
}
.hero__art img { width: 100%; }
/* Six of these ring the fruit now rather than two, so they are a little
   smaller and carry a little padding — at the old 118px the longer labels
   ("glycemic index", "swap for sugar") had nowhere to wrap. */
.hero__badge {
  position: absolute; display: flex; flex-direction: column; align-items: center; justify-content: center;
  width: 106px; height: 106px; border-radius: var(--r-full);
  padding: 0 9px;
  background: var(--lime); border: 2px solid var(--ink);
  font-family: var(--font-display); font-weight: 800; text-transform: uppercase;
  line-height: 1; text-align: center; box-shadow: var(--sh-hard);
  animation: float 5s ease-in-out infinite;
}
.hero__badge b { font-size: 1.75rem; letter-spacing: -.04em; display: block; }
.hero__badge span { font-size: .55rem; letter-spacing: .05em; margin-top: 4px; line-height: 1.25; }
/* A tick where there is no number to show, so every badge keeps the same
   two-part shape. */
.hero__badge b.hero__badge__tick { font-size: 1.5rem; }

/* --- Placement ---
   Three rows at equal spacing, mirrored left to right: the two columns use
   the same three `top` values, and the same inset measured from their own
   edge, so one side is the reflection of the other.

   The middle row sits further out than the top and bottom. That is not a
   break in the symmetry — it is symmetric — it just follows the fruit, which
   is at its widest halfway down. */
.hero__badge--l1, .hero__badge--r1 { top: 5%; }
.hero__badge--l2, .hero__badge--r2 { top: 37%; }
.hero__badge--l3, .hero__badge--r3 { top: 69%; }

.hero__badge--l1 { left: 3%; }
.hero__badge--l2 { left: -7%; }
.hero__badge--l3 { left: 3%; }
.hero__badge--r1 { right: 3%; }
.hero__badge--r2 { right: -7%; }
.hero__badge--r3 { right: 3%; }

/* White for the two in the middle row, green for the four around them. */
.hero__badge--plain { background: var(--white); }

/* Staggered so all six do not bob in unison, which reads as one object
   rather than six. Paired across the mirror so the two columns do not drift
   into lockstep with each other either. */
.hero__badge--l1 { animation-delay: -0.6s; }
.hero__badge--r1 { animation-delay: -3.1s; }
.hero__badge--l2 { animation-delay: -1.7s; }
.hero__badge--r2 { animation-delay: -4.3s; }
.hero__badge--l3 { animation-delay: -2.5s; }
.hero__badge--r3 { animation-delay: -3.9s; }
/* Drives the individual `translate` and `rotate` properties, not `transform`.
   That matters: interact.js writes `transform` on these badges to push them
   away from the cursor, and the two compose. Animating `transform` here
   would simply overwrite whatever the script set. */
@keyframes float { 0%,100% { translate: 0 0; rotate: -5deg; } 50% { translate: 0 -12px; rotate: -1deg; } }

/* --- PDP --- */
/* Laid out like the shop page's size chooser, which this page is the long
   form of: the same gap, capped at 1020px and centred in the container
   rather than spanning its full 1192px. Across the full width the buy column
   ran to ~780px, which is a punishing line length for the blurb and the
   accordion copy.

   The columns are 1.06/0.94 rather than the chooser's exact 1fr/1fr. At a
   true half the pouch filled its column to the pixel and could not grow any
   further, so the extra width is borrowed from the buy side — which has
   room to spare — while the outer 1020px footprint and the centring stay
   identical, and with them the alignment between the two pages.

   align-items stays `start`, not the chooser's `center`: the gallery here is
   sticky, and centring it in a grid row as tall as the accordion stack would
   drop the pack halfway down the page before it ever pinned. */
.pdp { display: grid; grid-template-columns: minmax(0, 1.06fr) minmax(0, .94fr); gap: clamp(var(--s-5), 4vw, var(--s-8)); align-items: start; max-width: 1020px; margin-inline: auto; padding-bottom: var(--s-8); }
.pdp__gallery { position: sticky; top: calc(var(--header-h) + var(--s-4)); }
/* An explicit height rather than aspect-ratio: 1/1.
   The square was two separate problems at once. The pouch is a tall
   860x1144 cut-out, so a square stage as wide as the column came out 575px
   tall while the image laid out at 571x760 — and overflow:hidden then cut
   185px off the bottom of the pack. (height:100% on the image did not save
   it: a percentage height needs a definite containing block, and a height
   that comes from aspect-ratio does not give it one, so the image fell back
   to its own ratio and overflowed.) The square also made the stage grow with
   the column width, which is what pushed the thumbnails towards the fold.
   A height in svh fixes both: the whole pack is visible, smaller, and the
   thumbnail row is guaranteed to sit inside the same screen.

   The 225px budget is what sits around the stage inside the viewport: the
   space above the gallery (149px with the tightened breadcrumb), the 52px
   thumbnail row, its 12px gap and a little air underneath.

   685px is the ceiling and it is measured, not picked. The pouch is 0.7517
   wide for every 1 tall, so at 673px of height it is already 506px wide —
   the full width of the column — and max-width pins it there. A taller
   stage past that point cannot grow the pack, it only adds transparent
   space above and below it and pushes the thumbnails down. */
.pdp__stage { --stage-h: min(685px, calc(100svh - 225px)); height: var(--stage-h); background: var(--butter-deep); border: 2px solid var(--ink); border-radius: var(--r-xl); display: grid; place-items: center; overflow: hidden; position: relative; }
/* The height is the custom property, not 100%. A percentage height on this
   image resolved to auto no matter what the stage was sized by, so the tall
   pouch laid out at its own ratio and overflowed; sizing from the same value
   the stage uses sidesteps percentage resolution altogether. The 12px is
   room for the drop-shadow, which overflow:hidden would otherwise cut. */
.pdp__stage img { width: auto; height: calc(var(--stage-h) - 12px); max-width: 100%; object-fit: contain; }
/* Capped tiles, centred under the pack, rather than five columns stretched
   across the full width. Stretched, they came out 87px square and ate the
   vertical room the pack wanted; the strip is a picker, not a feature, so it
   gives way to the thing it is picking. Centring keeps it under the pouch,
   which is itself centred in the stage. */
.pdp__thumbs { display: grid; grid-template-columns: repeat(5, minmax(0, 52px)); justify-content: center; gap: var(--s-3); margin-top: var(--s-3); }
.pdp__thumb { aspect-ratio: 1 / 1; border: 2px solid var(--line); border-radius: var(--r-md); background: var(--white); cursor: pointer; padding: 6px; transition: all var(--dur) var(--ease); }
.pdp__thumb[aria-selected="true"] { border-color: var(--ink); box-shadow: var(--sh-hard-sm); }
.pdp__thumb img { width: 100%; height: 100%; object-fit: contain; }
/* .pdp__title, .pdp__price-row, .pdp__price and .pdp__buy removed. The buy
   column is the size chooser's now — .size-chooser__title, .size-detail__price
   and .size-detail__buy do this work, and keeping a second, differently-sized
   set of the same components was how the two pages drifted apart. */

.purchase-options { display: grid; gap: var(--s-3); margin-block: var(--s-5); }
.purchase-option { display: flex; align-items: flex-start; gap: var(--s-3); padding: var(--s-4); border: 2px solid var(--line); border-radius: var(--r-md); cursor: pointer; background: var(--white); transition: all var(--dur) var(--ease); }
.purchase-option:hover { border-color: var(--ink); }
.purchase-option:has(input:checked) { border-color: var(--ink); background: var(--lime); }
.purchase-option input { margin-top: 3px; accent-color: var(--olive); width: 18px; height: 18px; flex-shrink: 0; }
.purchase-option__title { font-weight: 800; font-size: .95rem; display: flex; gap: var(--s-2); align-items: center; flex-wrap: wrap; }
.purchase-option__note { font-size: .82rem; color: var(--ink-2); }

.pdp__assurances { display: grid; gap: var(--s-3); padding: var(--s-4) 0; border-block: 2px solid var(--line); margin-block: var(--s-5); }
.assurance { display: flex; align-items: center; gap: var(--s-3); font-size: .89rem; color: var(--ink-2); font-weight: 500; }
.assurance svg { width: 19px; height: 19px; color: var(--olive); flex-shrink: 0; }

.nutrition { border: 3px solid var(--ink); border-radius: var(--r-sm); padding: var(--s-4); background: var(--white); max-width: 330px; }
.nutrition__title { font-family: var(--font-display); font-size: 1.6rem; margin: 0 0 2px; }
.nutrition__serving { font-size: .8rem; padding-bottom: 6px; border-bottom: 9px solid var(--ink); }
/* Three columns rather than flex space-between: the label has a Nutrient /
   Amount / % Daily Value structure, and with space-between the middle and
   right values landed in a different place on every row depending on how
   long the nutrient name was. */
.nutrition__row { display: grid; grid-template-columns: 1fr auto 74px; gap: var(--s-3); align-items: baseline; padding: 5px 0; border-bottom: 1px solid var(--line); font-size: .85rem; }
.nutrition__amount { font-variant-numeric: tabular-nums; }
.nutrition__dv { text-align: right; font-variant-numeric: tabular-nums; color: var(--ink-2); }
.nutrition__row--head { font-size: .72rem; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-2); border-bottom: 9px solid var(--ink); padding-bottom: 6px; }
.nutrition__row--head .nutrition__dv { color: var(--ink-2); }
.nutrition__row--big { font-size: 1.1rem; font-weight: 800; border-bottom-width: 4px; border-color: var(--ink); }
.nutrition__foot { font-size: .7rem; color: var(--text-muted); padding-top: var(--s-3); }

/* --- Cart / checkout --- */
.cart-layout { display: grid; grid-template-columns: 1fr 380px; gap: var(--s-7); align-items: start; padding-bottom: var(--s-8); }
.checkout-layout { display: grid; grid-template-columns: 1fr 410px; gap: var(--s-7); align-items: start; padding-bottom: var(--s-8); }
.checkout-section { margin-bottom: var(--s-7); }
.checkout-section__head { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-5); }
.checkout-section__num { width: 30px; height: 30px; flex-shrink: 0; border-radius: var(--r-full); background: var(--ink); color: var(--butter); display: grid; place-items: center; font-size: .82rem; font-weight: 800; }
.checkout-section__head h2 { font-size: 1.3rem; margin: 0; }

.pay-tabs { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-2); margin-bottom: var(--s-5); }
.pay-tab { padding: var(--s-3); border: 2px solid var(--line); border-radius: var(--r-md); background: var(--white); cursor: pointer; font-size: .84rem; font-weight: 700; display: flex; flex-direction: column; align-items: center; gap: 6px; transition: all var(--dur) var(--ease); }
.pay-tab svg { width: 22px; height: 22px; color: var(--ink-3); }
.pay-tab[aria-selected="true"] { border-color: var(--ink); background: var(--lime); }
.pay-tab[aria-selected="true"] svg { color: var(--ink); }

.card-input-wrap { position: relative; }
.card-brand { position: absolute; right: var(--s-4); top: 50%; transform: translateY(-50%); font-size: .7rem; font-weight: 800; letter-spacing: .06em; color: var(--ink-3); text-transform: uppercase; }

.order-line { display: grid; grid-template-columns: 56px 1fr auto; gap: var(--s-3); align-items: center; padding: var(--s-3) 0; font-size: .88rem; }
.order-line__media { position: relative; width: 56px; height: 56px; border-radius: var(--r-sm); background: var(--butter-deep); border: 1.5px solid var(--ink); display: grid; place-items: center; }
.order-line__media img { width: 80%; height: 80%; object-fit: contain; }
.order-line__qty { position: absolute; top: -8px; right: -8px; min-width: 21px; height: 21px; padding: 0 5px; border-radius: var(--r-full); background: var(--ink); color: var(--butter); font-size: .68rem; font-weight: 800; display: grid; place-items: center; }

/* --- Confirmation --- */
.confirm-hero { text-align: center; padding-block: var(--s-8) var(--s-6); }
.confirm-check { width: 88px; height: 88px; margin: 0 auto var(--s-5); border-radius: var(--r-full); background: var(--lime); border: 2px solid var(--ink); color: var(--ink); display: grid; place-items: center; animation: pop .6s var(--ease-spring); }
.confirm-check svg { width: 42px; height: 42px; }
@keyframes pop { 0% { transform: scale(.4); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }
.confirm-grid { display: grid; grid-template-columns: 1.3fr 1fr; gap: var(--s-6); align-items: start; }
.kv { display: grid; grid-template-columns: auto 1fr; gap: var(--s-2) var(--s-5); font-size: .9rem; }
.kv dt { color: var(--text-muted); }
.kv dd { margin: 0; font-weight: 700; text-align: right; }

.track { display: flex; align-items: flex-start; margin-block: var(--s-6); }
.track__step { flex: 1; text-align: center; position: relative; }
.track__step::before { content: ""; position: absolute; top: 15px; left: -50%; width: 100%; height: 3px; background: var(--line); }
.track__step:first-child::before { display: none; }
.track__step.is-done::before { background: var(--olive); }
.track__dot { position: relative; z-index: 1; width: 32px; height: 32px; margin: 0 auto var(--s-2); border-radius: var(--r-full); background: var(--white); border: 2px solid var(--line); color: var(--ink-4); display: grid; place-items: center; }
.track__dot svg { width: 15px; height: 15px; }
.track__step.is-done .track__dot { background: var(--olive); border-color: var(--olive); color: var(--butter); }
.track__label { font-size: .78rem; font-weight: 800; }
.track__date { font-size: .72rem; color: var(--text-muted); }

/* --- About --- */
.story-grid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(var(--s-5), 5vw, var(--s-8)); align-items: center; }
.story-grid--flip > *:first-child { order: 2; }
.story-art { border-radius: var(--r-xl); overflow: hidden; border: 2px solid var(--ink); }
/* The frame held a 520x440 SVG and never needed to size its child. It holds a
   photograph now, so: fill the width, and height:auto so the height attribute
   on the img is not taken as a literal pixel height. display:block removes the
   inline-image baseline gap that would otherwise show as a sliver of page
   between the photo and the frame's bottom border. */
.story-art img { width: 100%; height: auto; display: block; }

/* The .timeline, .team-grid/.member and .founder rule sets were removed with
   the "Short history" and founder sections on the About page. Nothing else
   used them: the team grid had already been replaced by the founder block,
   and the founder copy now sits as ordinary paragraphs in the sourcing
   section. (home5.html has a .timeline of its own, but that page carries its
   own inline styles and does not load this file.) */

/* --- 404 --- */
.notfound { text-align: center; padding-block: var(--s-9); }
.notfound__code { font-family: var(--font-display); font-size: clamp(5rem, 18vw, 11rem); line-height: .85; color: var(--olive); opacity: .2; letter-spacing: -.06em; margin-bottom: calc(-1 * var(--s-5)); }

/* --------------------------------------------------------------------------
   8. RESPONSIVE
   -------------------------------------------------------------------------- */
@media (max-width: 1040px) {
  .cart-layout, .checkout-layout { grid-template-columns: 1fr; }
  .panel--sticky { position: static; }
  .footer__grid { grid-template-columns: 1fr 1fr; }
  .account-layout { grid-template-columns: 1fr; }
  .account-nav { grid-auto-flow: column; overflow-x: auto; }
  .swap, .anatomy { grid-template-columns: 1fr; }
}

@media (max-width: 880px) {
  .nav { display: none; }
  .nav-toggle { display: inline-flex; }
  .hero__inner { grid-template-columns: 1fr; text-align: center; }
  .hero__sub { margin-inline: auto; }
  .hero__proof, .hero .btn-row { justify-content: center; }
  .hero__art { order: -1; max-width: 400px; margin-inline: auto; }
  /* The column is narrow here, so the middle pair is pulled back inside it
     rather than hanging over the edges of a centred hero. */
  .hero__badge--l2 { left: 0; }
  .hero__badge--r2 { right: 0; }
  .pdp { grid-template-columns: 1fr; }
  .pdp__gallery { position: static; }
  .story-grid { grid-template-columns: 1fr; }
  .story-grid--flip > *:first-child { order: 0; }
  .confirm-grid { grid-template-columns: 1fr; }
}

@media (max-width: 560px) {
  :root { --s-7: 36px; --s-8: 48px; --s-9: 64px; }
  .container { padding-inline: var(--s-4); }
  .product-grid { grid-template-columns: 1fr; }
  .field-grid, .field-grid--3 { grid-template-columns: 1fr; }
  .footer__grid { grid-template-columns: 1fr; gap: var(--s-6); }
  .line { grid-template-columns: 64px 1fr; }
  .line__media { width: 64px; height: 64px; }
  .line__price { grid-column: 2; text-align: left; }
  .pay-tabs, .express__grid { grid-template-columns: 1fr; }
  .drawer { width: 100vw; }
  .search-hit { grid-template-columns: 54px 1fr; }
  .search-hit__media { width: 54px; height: 54px; }
  .ritual-grid { grid-template-columns: repeat(7, 1fr); }
  .track__label { font-size: .68rem; }
  /* The art leads on a phone (order: -1 above), so its height decides how far
     down the headline starts. At the 400px cap it came out 557px tall and
     pushed the kicker and the headline off the first screen entirely — the
     product was the only thing a visitor saw. 300px keeps the pack large enough
     to read and still leaves the headline above the fold on a 844px screen. */
  .hero__art { max-width: 300px; }
  .hero__badge { width: 92px; height: 92px; }
  .hero__badge b { font-size: 1.4rem; }
}

/* --------------------------------------------------------------------------
   9. MOTION + PRINT
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .reveal { opacity: 1; transform: none; }
  .ticker__track { animation: none; }
}

@media print {
  .header, .footer, .drawer, .scrim, .announce, .toast-stack, .btn, .ticker { display: none !important; }
  body { background: #fff; }
}


/* ==========================================================================
   LIGHT PANEL INSIDE A DARK SECTION

   .section--deep repaints every heading inside it cream, because everything
   in it normally sits on a dark ground. The sugar quiz breaks that rule: it
   is a light card dropped into the dark section, so its <h3> was rendering
   cream-on-cream and the question was simply invisible. The eyebrow above it
   was lime on cream, which was barely better.

   This class re-establishes dark-on-light for anything inside it, and is the
   fix rather than weakening the .section--deep rules, which are correct for
   every other element in there.
   ========================================================================== */
.panel-light,
.section--deep .panel-light { color: var(--ink); }
.section--deep .panel-light h1,
.section--deep .panel-light h2,
.section--deep .panel-light h3,
.section--deep .panel-light h4 { color: var(--ink); }
.section--deep .panel-light .eyebrow { color: var(--olive); }
.section--deep .panel-light p,
.section--deep .panel-light .lede { color: var(--ink-2); }
.section--deep .panel-light .stat__num { color: var(--olive); }
.section--deep .panel-light .stat__label { color: var(--ink-3); }
.section--deep .panel-light .link { color: var(--olive); }

/* Default size for icons from BETTR.icon(). They ship with a viewBox and no
   width or height, so without this they stretch to fill whatever flex or
   grid cell they land in. Deliberately a single class: every existing
   `.component svg { width: … }` rule is more specific and still wins, so
   this only catches the call sites nobody styled. */
.ico { width: 1.15em; height: 1.15em; flex: none; }

/* Visible to a screen reader, absent from the page. Used for table captions
   and other structure that sighted users get from layout alone. */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ==========================================================================
   16. SIZE CHOOSER

   One photograph of the pouch beside a row of size pills, the pattern that
   single-product stores use. The packaging is the same for both SKUs, so the
   image does not change when you switch — only the price, the servings and
   the "lasts you" line do.
   ========================================================================== */
.size-chooser {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(var(--s-5), 4vw, var(--s-8));
  align-items: center;
  max-width: 1020px; margin-inline: auto;
}
.size-chooser__media {
  /* No plate, by request: the pouch sits directly on the page. The cut-out
     PNG makes that possible — there is no rectangle left to hide. */
  background: none;
  border: 0;
  overflow: visible;
}
.size-chooser__media img { width: 100%; height: auto; display: block; }

/* --- Pack regions that expand under the cursor ---
   Each layer is a region cut from the pack shot and replaced exactly over
   its own original, so at rest the overlay is invisible. When the cursor is
   over that specific region it grows from its own centre.

   Growing rather than lifting is the trick: translating a cut-out slides it
   off the original underneath and you see the seam, while scaling keeps the
   two registered at the centre and the feathered edge blends the growing
   copy into the untouched pack around it. The shadow turns "bigger" into
   "closer".

   `is-on` is set by the hit test in interact.js, not by :hover — the fruit
   need circular hit areas, and these overlays must stay click-through. */
.pouch-stage { position: relative; }

/* ==========================================================================
   PRODUCT GALLERY (the size chooser's left-hand column)

   A strip of images you scroll through, with a row of dots underneath.

   It is built on native overflow scrolling with snap points rather than a
   JS carousel: a trackpad swipe, a touch drag, the arrows and the dots all
   drive the same scroll position, there is no animation loop, and momentum
   and rubber-banding come free. The dots are kept in step by an
   IntersectionObserver watching the slides against the strip, so nothing
   runs while the visitor is not touching it.
   ========================================================================== */
.shots { position: relative; }
.shots__track {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.shots__track::-webkit-scrollbar { display: none; }
/* No aspect-ratio here: the slides are flex children and stretch to the
   track, which is itself sized by the chooser's grid row, so an aspect-ratio
   would be overridden anyway. Height comes from the column beside it. */
.shots__slide {
  flex: 0 0 100%;
  scroll-snap-align: center;
  margin: 0;
  display: grid;
  place-items: center;
  min-height: 320px;
  padding: var(--s-4);
}
/* Not the full slide: the pack sat right up against the edges of its column,
   which left it crowding the dots and the panel beside it. A little air on
   all four sides reads as a product shot rather than a crop. */
.shots__slide img { width: 88%; height: 88%; object-fit: contain; }

.shots__dots {
  display: flex; justify-content: center; gap: 9px;
  margin-top: var(--s-3);
}
.shots__dot {
  width: 9px; height: 9px; padding: 0;
  border-radius: var(--r-full);
  border: 1.5px solid var(--ink-3);
  background: transparent;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}
.shots__dot[aria-selected="true"] {
  background: var(--olive); border-color: var(--olive); transform: scale(1.25);
}

/* Arrows are for the mouse — there is no swipe on a desktop, and a trackpad
   scroll sideways is not something most people think to try. They stay out
   of the way until the pointer is over the gallery. */
.shots__arrow {
  position: absolute; top: 50%; translate: 0 -50%;
  width: 40px; height: 40px; border-radius: var(--r-full);
  display: grid; place-items: center;
  background: var(--surface); color: var(--ink);
  border: 2px solid var(--ink);
  cursor: pointer; opacity: 0; z-index: 2;
  transition: opacity var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.shots__arrow svg { width: 18px; height: 18px; }
.shots__arrow--prev { left: 2px; }
.shots__arrow--next { right: 2px; }
.shots:hover .shots__arrow,
.shots__arrow:focus-visible { opacity: 1; }
.shots__arrow:hover { background: var(--lime); }

@media (prefers-reduced-motion: reduce) {
  .shots__track { scroll-behavior: auto; }
}

.size-chooser__title { font-size: clamp(1.8rem, 3.4vw, 2.6rem); margin-bottom: var(--s-3); }
.size-chooser__label { margin-top: var(--s-5); margin-bottom: var(--s-3); }

.size-pills { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-3); }
.size-pill {
  position: relative; display: flex; flex-direction: column; gap: 2px;
  padding: var(--s-4); text-align: left;
  border: 2px solid var(--line); border-radius: var(--r-md);
  background: var(--white); cursor: pointer;
  font: inherit; color: inherit;
  transition: transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease), border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.size-pill:hover { border-color: var(--ink); }
.size-pill[aria-pressed="true"] {
  border-color: var(--ink); background: var(--lime);
  box-shadow: var(--sh-hard-sm); transform: translate(-2px, -2px);
}
.size-pill__flag {
  position: absolute; top: -11px; right: var(--s-3);
  padding: 2px 9px; border-radius: var(--r-full);
  background: var(--ink); color: var(--butter);
  font-size: .64rem; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
}
.size-pill__weight { font-family: var(--font-display); font-weight: 800; font-size: 1.4rem; letter-spacing: -.03em; }
.size-pill__servings { font-size: .76rem; font-weight: 600; color: var(--text-muted); }
.size-pill[aria-pressed="true"] .size-pill__servings { color: var(--ink-2); }
.size-pill__price { font-weight: 800; font-size: .95rem; margin-top: 4px; font-variant-numeric: tabular-nums; }

/* The packaging photograph is now a cut-out PNG with a transparent ground,
   so there is no longer a baked-in cream rectangle to match. Small thumbs
   keep a near-white plate so the pouch reads against surrounding chrome;
   the hero and the size chooser get no plate at all, which is what makes
   the pouch sit on the page rather than in a box. */
.pdp__thumb, .line__media, .order-thumb,
.order-line__media, .search-hit__media { background: var(--photo-bg); }
.pdp__stage { background: transparent; border-color: transparent; }
.card__media { background: transparent; }

/* .pdp__stage img is sized in the PDP block above, from --stage-h. This
   rule used to restate width/height:100% here, later in the file and at the
   same specificity, so it quietly won — and its height:100% was the
   declaration that resolved to auto and let the pouch overflow its stage. */
.card__media img { width: 100%; height: 100%; }

/* The cut-out has no ground of its own, so it needs a shadow to sit on the
   page instead of floating. Drop-shadow follows the alpha channel, which is
   the whole point of cutting it out. */
.hero__art img,
.size-chooser__media img,
.pdp__stage img,
.card__media img { filter: drop-shadow(0 26px 34px rgba(43, 51, 19, .26)); }
/* Photographs (files named photo-*) are the opposite case: they carry their
   own ground, so the shadow only draws a box round them. They show at their
   own proportions, never larger than the file, with nothing behind them. */
.size-chooser__media img[src*="/photo-"],
.pdp__stage img[src*="/photo-"] { filter: none; }
.shots__slide img[src*="/photo-"] { width: auto; max-width: 100%; }
.pdp__thumb:has(img[src*="/photo-"]) { background: transparent; }

/* A gallery slide that is a link (the Linktree card). The anchor covers the
   slide rather than wrapping the image, so the image keeps the sizing it
   gets from the slide. */
.shots__slide { position: relative; }
.shots__hit { position: absolute; inset: 0; z-index: 1; cursor: pointer; border-radius: var(--r-xl); }
.shots__hit[hidden] { display: none; }
.shots__hit:focus-visible { outline: 3px solid var(--ink); outline-offset: -3px; }

/* Mouse drag on the home gallery (interactive.js). Snapping is off while the
   strip is held or gliding to a slide, and back on once it settles. */
.shots__track { cursor: grab; user-select: none; -webkit-user-select: none; }
.shots__track.is-free { scroll-snap-type: none; }
.shots__track.is-dragging,
.shots__track.is-dragging .shots__hit { cursor: grabbing; }
.shots__slide img { -webkit-user-drag: none; }

/* The shop's stage holds the same strip. It fills the stage, and each image
   keeps the stage's own sizing (from --stage-h) rather than the home
   chooser's 88%, so the pack is exactly as big as it was before it slid. */
.pdp__shots, .pdp__shots .shots, .pdp__shots .shots__track { width: 100%; height: 100%; }
.pdp__stage .shots__slide { min-height: 0; padding: 0; }
.pdp__stage .shots__slide img { width: auto; height: calc(var(--stage-h) - 12px); max-width: 100%; object-fit: contain; }
.pdp__stage .card__flags { z-index: 3; }
/* Eight thumbnails in one row. Five fixed columns wrapped the last three
   onto a second row, pushing the strip below the stage. */
.pdp__thumbs { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: minmax(0, 52px); }
/* Except the hero fruit. That one is translated by the parallax and rotated
   by the tilt on every frame, and a 34px blurred drop-shadow on a element
   that never stops moving has to be re-rastered each time. The photograph
   already carries its own shadow underneath it, so this was buying very
   little for a per-frame cost. */
.hero__art img { filter: none; }

/* The hero shows the photo with no plate at all, so it needs the corners
   softened or it reads as a pasted-in rectangle — particularly in dark mode,
   where it is the one bright thing on the page.

   It is also capped by height rather than width. The photograph is a 3:4
   portrait where the old drawn pack was nearly square, so letting it fill
   the column pushed the headline and the buttons off the first screen. */
.hero__art img {
  border-radius: var(--r-xl);
  width: auto; max-width: 100%;
  max-height: min(66vh, 580px);
  margin-inline: auto;
}

.size-detail { margin-top: var(--s-5); }
.size-detail__price { display: flex; align-items: baseline; gap: var(--s-3); flex-wrap: wrap; }
.size-detail__now { font-family: var(--font-display); font-weight: 800; font-size: 2.1rem; letter-spacing: -.04em; }
.size-detail__rate { font-size: .84rem; color: var(--text-muted); margin-top: 4px; }
.size-detail .yours { margin-top: var(--s-4); }
.size-detail__buy { display: flex; gap: var(--s-3); align-items: stretch; margin-top: var(--s-5); flex-wrap: wrap; }
.size-detail__buy .btn { flex: 1; min-width: 200px; }
.size-detail__stock { font-size: .84rem; font-weight: 600; margin: var(--s-3) 0 var(--s-4); }

@media (max-width: 880px) {
  .size-chooser { grid-template-columns: minmax(0, 1fr); }
  .size-chooser__media { max-width: 420px; margin-inline: auto; }
}
@media (max-width: 400px) {
  .size-pills { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   11. TEXTURE

   Every image on this site is flat vector. Flat vector on flat colour looks
   like a diagram. A single grain layer over the whole page is what makes it
   look printed instead — the same trick the packaging uses.
   ========================================================================== */
body::after {
  content: "";
  position: fixed; inset: 0; z-index: 9999;
  pointer-events: none;
  opacity: .035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ==========================================================================
   12. PERSONALISATION

   Once someone has taken the sugar quiz we know a real number about them.
   These are the surfaces that then speak to that number instead of to a
   generic visitor.
   ========================================================================== */
.yours {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  background: var(--lime); border: 2px solid var(--ink); border-radius: var(--r-md);
  font-size: .88rem; font-weight: 600; line-height: 1.45;
}
.yours svg { width: 20px; height: 20px; flex-shrink: 0; }
.yours strong { font-family: var(--font-display); font-weight: 800; letter-spacing: -.02em; }
.yours__clear {
  margin-left: auto; flex-shrink: 0;
  background: none; border: 0; padding: 4px 8px; cursor: pointer;
  font: inherit; font-size: .76rem; font-weight: 700;
  text-decoration: underline; text-underline-offset: 3px; color: inherit; opacity: .72;
}
.yours__clear:hover { opacity: 1; }

/* The inline "lasts you about five weeks" line on pouch cards and the PDP. */
.lasts {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: .8rem; font-weight: 700; color: var(--olive);
}
.lasts::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--olive); flex-shrink: 0;
}

/* Header chip carrying the number from page to page. */
.num-chip {
  display: none; align-items: center; gap: 6px;
  padding: 5px 11px; border: 2px solid var(--ink); border-radius: var(--r-full);
  background: var(--lime); font-size: .74rem; font-weight: 800;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.num-chip.is-on { display: inline-flex; }
.num-chip:hover { box-shadow: var(--sh-hard-sm); transform: translate(-1px,-1px); }
@media (max-width: 1100px) { .num-chip { display: none !important; } }

/* ==========================================================================
   13. STICKY MOBILE BUY BAR

   On a phone the add-to-cart button on a product page scrolls away within a
   swipe and never comes back. This keeps price and action in the thumb zone
   for the whole page, which is the single biggest conversion difference
   between a good mobile PDP and a bad one.
   ========================================================================== */
.buybar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 70;
  display: none; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  padding-bottom: calc(var(--s-3) + env(safe-area-inset-bottom, 0px));
  background: var(--white); border-top: 2px solid var(--ink);
  box-shadow: 0 -8px 24px rgba(0,0,0,.14);
  transform: translateY(105%);
  transition: transform .32s var(--ease-out);
}
.buybar.is-up { transform: translateY(0); }
.buybar__info { min-width: 0; }
.buybar__name {
  font-size: .78rem; font-weight: 700; color: var(--text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.buybar__price { font-family: var(--font-display); font-weight: 800; font-size: 1.15rem; letter-spacing: -.03em; }
.buybar .btn { margin-left: auto; flex-shrink: 0; }

/* Keyed off the element, not the page name: product.html is marked
   data-page="shop" so that the Shop nav link highlights while you are on it,
   and product.js is the only thing that ever creates this bar. */
@media (max-width: 880px) {
  .buybar { display: flex; }
  /* Room so the footer links are not permanently under the bar. */
  body.has-buybar .footer { padding-bottom: calc(var(--s-8) + 76px); }
}

/* ==========================================================================
   14. RESPONSIVE, PART TWO

   The first pass had three breakpoints and assumed a mouse. These are the
   places that actually fall apart on a 390px screen.
   ========================================================================== */

/* --- Comparison table ---
   Six columns will never fit a phone. Rather than hiding data or duplicating
   the markup into cards, the row label is pinned and the rest scrolls with
   snap points, so a swipe lands cleanly on the next sweetener. */
.showdown { scroll-snap-type: x proximity; scrollbar-width: thin; }
/* Pinning the row label is what turns an unusable sideways scroll into a
   readable one — you always know which row the number belongs to. */
.showdown tbody th {
  position: sticky; left: 0; z-index: 2;
  background: var(--bg);
  box-shadow: 1px 0 0 var(--line);
}
.section--butter .showdown tbody th { background: var(--butter-deep); }
.showdown thead th { scroll-snap-align: start; }
.showdown-hint {
  display: none; align-items: center; gap: 6px;
  font-size: .78rem; font-weight: 700; color: var(--text-muted);
  margin-bottom: var(--s-3);
}
.showdown-hint svg { width: 16px; height: 16px; animation: nudge 1.8s var(--ease-out) infinite; }
@keyframes nudge { 0%,100% { transform: translateX(0); } 50% { transform: translateX(5px); } }

@media (max-width: 760px) {
  .showdown-hint { display: flex; }
}

/* --- Touch targets ---
   The anatomy hotspots were sized for a cursor. 44px is the documented
   minimum for a fingertip, and the dots were well under it. */
@media (pointer: coarse) {
  .anatomy__hotspot { min-width: 44px; min-height: 44px; }
  .ritual-day { min-height: 40px; }
  .chip { padding-block: 10px; }
  .qty__btn { min-width: 44px; min-height: 44px; }

  /* Every other control a finger has to hit. 44px is the documented minimum;
     these were all sized for a cursor and came in between 28 and 38px. */
  .btn, .btn--sm, .size-pill, .chip, .tab, .acc__trigger,
  .nav__link, .mobile-nav__link, .icon-btn, .account-nav__link,
  .pdp__thumb, .shots__arrow, .filter-pill {
    min-height: 44px;
  }
  .btn, .btn--sm { display: inline-flex; align-items: center; justify-content: center; }
  /* Footer and policy links sit in dense lists, so they get their room from
     spacing rather than a min-height that would stretch the list. */
  .footer__col a, .policy-nav a { padding-block: 6px; display: inline-block; }
  /* The gallery dots are deliberately left alone: they are 8px marks in a
     row, and a 44px minimum would turn them into buttons and break the row.
     The strip is swipeable, and the arrows beside it are full-size. */
}

/* --- Tablet --- */
@media (max-width: 1040px) {
  .glucose__chart { min-width: 0; }
}

@media (max-width: 880px) {
  .quote-grid { grid-template-columns: 1fr; }
  .myth-deck { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 640px) {
  /* Display type at 5.6rem on a phone is a wall, not a headline. */
  h1 { font-size: clamp(2.3rem, 11vw, 3.2rem); }
  h2 { font-size: clamp(1.75rem, 8vw, 2.4rem); }
  .myth-deck { grid-template-columns: 1fr; }
  .stats { grid-template-columns: 1fr 1fr; gap: var(--s-4); }
  .btn-row { flex-direction: column; align-items: stretch; }
  .btn-row .btn { width: 100%; justify-content: center; }
  .swap__results { grid-template-columns: 1fr 1fr; }
  .quiz__big { font-size: clamp(3.4rem, 18vw, 5rem); }
  /* Edge-to-edge modules read as native on a phone; a 16px gutter around a
     bordered card wastes the only width there is. */
  .module { border-radius: var(--r-md); padding: var(--s-4); }
  .yours { align-items: flex-start; }
  .yours__clear { margin-left: 0; }
}

/* --- Product gallery overflow ---
   A grid column is minmax(auto, 1fr) by default, and `auto` means "at least
   as wide as my widest child". The thumbnail strip was five fixed 78px tiles,
   so on a phone it forced the whole column to ~440px and pushed the pack shot
   off the side of the screen. The tiles are now five minmax(0, 1fr) columns
   that shrink with the strip, so they cannot force anything — but the
   minmax(0, …) cap stays as the belt to that braces.

   The column ratio has to be restated here. This rule sets
   grid-template-columns after the .pdp rule above at the same specificity,
   so whatever it says wins; leaving the old 1.02/.98 here silently undid the
   narrower gallery set above. */
.pdp { grid-template-columns: minmax(0, 1.06fr) minmax(0, .94fr); }
.pdp__gallery { min-width: 0; }

@media (max-width: 880px) {
  .pdp { grid-template-columns: minmax(0, 1fr); }
}

/* --- Header crowding ---
   The icon buttons plus the
   wordmark no longer fit a 390px screen — the menu button was being pushed
   off the edge. Account and saved items already have entries in the mobile
   menu, so they come out of the bar rather than the bar getting smaller. */
/* The account icon these rules hid on small screens has been removed
   altogether, along with the account page. */
@media (max-width: 400px) {
  .announce { font-size: .7rem; }
  .logo__img { height: 30px; }
  .hero__art { max-width: 260px; }
  .hero__badge { width: 76px; height: 76px; }
  .hero__badge b { font-size: 1.15rem; }
  .hero__badge span { font-size: .5rem; }
  .swap__results { grid-template-columns: 1fr; }
}

/* Landscape phones: a 70px header eats a lot of a short viewport, and an
   open menu needs to be able to scroll. The .announce rule here is a
   leftover safety net — the bar is no longer rendered at all (see
   mountHeader in layout.js), but if it is ever restored it should still
   stay out of the way in landscape. */
@media (max-height: 520px) and (orientation: landscape) {
  .announce { display: none; }
  .mobile-nav { overflow-y: auto; }
}

/* Respect the notch. Scoped to landscape phones on purpose — applying the
   env() floor to .container globally would quietly override the 16px gutter
   set at 560px and give every phone a wider margin than intended. */
@media (orientation: landscape) and (max-height: 560px) {
  .container {
    padding-inline:
      max(var(--s-4), env(safe-area-inset-left, 0px))
      max(var(--s-4), env(safe-area-inset-right, 0px));
  }
}
.drawer__foot { padding-bottom: calc(var(--s-5) + env(safe-area-inset-bottom, 0px)); }
.mobile-nav { padding-bottom: calc(var(--s-6) + env(safe-area-inset-bottom, 0px)); }

/* ==========================================================================
   15. MOTION

   Three things, all of which switch themselves off under
   prefers-reduced-motion via the block in section 9.
   ========================================================================== */

/* --- Staggered reveal ---
   A row of four cards arriving together is a slab. Arriving 70ms apart, it
   reads as a hand dealing them. The index is set inline by app.js. */
.reveal { transition-delay: calc(var(--reveal-i, 0) * 70ms); }

/* --- Count-up numbers ---
   Tabular figures so the width does not jitter while the digits climb. */
[data-countup] { font-variant-numeric: tabular-nums; }

/* --- Pointer tilt on the hero pouch ---
   Driven by two custom properties app.js writes on pointer move. */
.hero__art { perspective: 1100px; }
/* The tilt lives on an inner wrapper rather than on the image itself, because
   the parallax layer writes an inline transform onto [data-par] and the two
   would otherwise be fighting over the same property — the tilt simply lost.
   Nesting them gives each its own element, and the eyes, being children of
   the tilted wrapper, stay pinned to the fruit as it turns. */
.hero__art .fruit-face__inner {
  position: relative;
  transform: rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg));
  /* No transition: interact.js eases the angle itself and rewrites it every
     frame, and a transition on top of that only adds lag. */
  will-change: transform;
  transform-style: preserve-3d;
}

/* --- The watching fruit ---
   Two eyes laid over the photograph, with pupils that turn toward the
   pointer. Positions are percentages of the image box, so they stay put at
   any size. The image keeps the pouch's proportions, which is why the fruit
   sits between 9.6% and 90.4% of the box height and its centre is at 50%. */
/* The wrapper shrinks to the image rather than filling the column, so the
   eyes — placed in percentages of it — land on the fruit rather than on the
   empty space beside it. The image keeps exactly the sizing rule it had as a
   pouch (height-capped, width follows), restated here so that adding this
   wrapper cannot change how big it comes out. */
.fruit-face {
  position: relative;
  width: fit-content;
  margin-inline: auto;
}
.fruit-face img {
  display: block;
  /* height:auto is load-bearing. The img carries height="1144" as an
     attribute, which is a presentational hint that max-width does not
     override — so with only the two max-* bounds the browser kept the
     attribute's height, clamped it to max-height, clamped the width
     separately to max-width, and stretched the fruit. On a phone both bounds
     bite at once and it rendered 260x557 against a true 260x346. With
     height:auto the intrinsic ratio is preserved and the two maximums simply
     bound the box. */
  width: auto; height: auto;
  max-width: 100%;
  max-height: min(66vh, 580px);
}
.fruit-face__eye {
  position: absolute;
  width: 15%;
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  background: #fdfaf2;
  border: 2px solid #2b2110;
  box-shadow: inset 0 4px 7px rgba(43, 33, 16, .3);
  display: grid; place-items: center;
  transform: translate(-50%, -50%);
  overflow: hidden;
}
.fruit-face__eye--l { left: 37%; top: 43%; }
.fruit-face__eye--r { left: 63%; top: 43%; }
/* A fixed catchlight, so the eye reads as wet rather than as a flat disc.
   It belongs to the eye, not the pupil, so it does not slide about. */
.fruit-face__eye::after {
  content: ""; position: absolute; left: 22%; top: 16%;
  width: 20%; height: 20%; border-radius: 50%;
  background: rgba(255, 255, 255, .9);
  z-index: 1; pointer-events: none;
}
.fruit-face__pupil {
  width: 44%; height: 44%;
  border-radius: 50%;
  background: #201705;
  will-change: transform;
}

/* --- The leaves ---
   Positioned by their own stem junction rather than by a corner: the sprig's
   stems meet at 34% across and 68% down its own box, so translating it back
   by exactly that puts the junction on whatever left/top say — which is the
   fruit's stem nub. transform-origin sits on the same point so the tilt of
   the sprig pivots where it is attached, the way a real stem would.
   A drop shadow in the fruit's own light direction stops it reading as a
   sticker laid on top. */
/* Scoped through the parent so it outranks `.fruit-face img`, which sets
   width:auto and a max-height for the fruit itself and would otherwise win
   on specificity and leave the sprig at its full natural size. */
.fruit-face .fruit-face__leaf {
  position: absolute;
  left: 49%;
  top: 14%;
  width: 52%;
  height: auto;
  max-width: none;
  max-height: none;
  transform-origin: 34% 68%;
  /* Mirrored so the sprig fans up and to the left. Unflipped it grew straight
     into the "0 calorie" badge in the top right corner, and two things
     competing for the same corner read as a collision rather than a bunch of
     leaves. scaleX is applied about the same origin as everything else, so
     the stem junction does not move when it flips. */
  transform: translate(-34%, -68%) scaleX(-1) rotate(-7deg);
  filter: drop-shadow(4px 7px 6px rgba(38, 30, 12, .38));
  pointer-events: none;
}

/* --- Reading progress on long pages --- */
.progress-bar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 80;
  height: 3px; background: transparent; pointer-events: none;
}
.progress-bar__fill {
  height: 100%; width: 0%;
  background: var(--olive);
  transition: width .08s linear;
}

/* ==========================================================================
   17. POINTER INTERACTION

   The page moves against the pointer in layers, the hero fruit watches it and
   leans toward it, and the dark sections light up under it. All of it is
   driven from one rAF loop in interact.js; this file only describes what the
   moving parts look like.

   The monk fruit cursor used to live here too, in its own stylesheet. It is
   gone and the system pointer is back, which also retires a `cursor: none`
   rule that matched every element on the page.
   ========================================================================== */

/* Parallax layers are moved by inline transform, so they only need the
   compositor hint. */
[data-par] { will-change: transform; }

/* --- Ambient depth ---
   Soft shapes at different simulated distances, behind the hero content.
   They are what the parallax has to bite on: without something in the
   middle distance, moving only the product reads as a wobble rather than
   as a scene with depth. Purely decorative, so they are aria-hidden and
   never intercept a pointer. */
.depth { position: absolute; inset: 0; pointer-events: none; overflow: hidden; z-index: 0; }
.depth span {
  position: absolute; border-radius: 50%; display: block;
  will-change: transform;
}
.depth .d1 { width: 340px; height: 340px; left: -6%;  top: 8%;
  background: radial-gradient(circle at 34% 30%, rgba(200,220,139,.55), rgba(120,150,60,.18) 62%, transparent 74%); }
.depth .d2 { width: 190px; height: 190px; left: 38%;  top: -6%;
  background: radial-gradient(circle at 34% 30%, rgba(168,196,104,.42), transparent 70%); }
.depth .d3 { width: 260px; height: 260px; right: 4%;  bottom: -8%;
  background: radial-gradient(circle at 34% 30%, rgba(78,90,35,.22), transparent 70%); }
.depth .d4 { width: 120px; height: 120px; left: 12%;  bottom: 12%;
  background: radial-gradient(circle at 34% 30%, rgba(109,138,52,.38), transparent 68%); }
.depth .d5 { width: 90px;  height: 90px;  right: 28%; top: 14%;
  background: radial-gradient(circle at 34% 30%, rgba(200,220,139,.5), transparent 68%); }
/* Leaf-ish, to break up a field of circles */
.depth .leafy { border-radius: 0 78% 0 78%; }

.hero { position: relative; }
.hero__inner { position: relative; z-index: 1; }
.btn, .size-pill, .chip { transition: transform .28s var(--ease-out), background-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }

/* A soft light that follows the pointer across the dark sections. */
.has-lamp { position: relative; isolation: isolate; }
.has-lamp::after {
  content: ""; position: absolute; inset: 0; z-index: 0;
  pointer-events: none; opacity: .9;
  background: radial-gradient(420px circle at var(--lampx, 50%) var(--lampy, 50%),
              rgba(200, 220, 139, .16), transparent 68%);
}
.has-lamp > * { position: relative; z-index: 1; }

@media (prefers-reduced-motion: reduce) {
  /* .mf-cursor is handled in cursor.css, alongside the rest of it. */
  .has-lamp::after { display: none; }
  [data-par] { transform: none !important; }
}

/* ==========================================================================
   18. THE FADE-OUT

   The page used to end on a hard edge: a lime band, then an abrupt switch to
   the dark footer. This dissolves the last third instead — the section has
   no top edge at all, it emerges out of the paper, darkens as you scroll and
   arrives at the footer colour before the footer starts, so the seam between
   them disappears entirely.

   Behind it sits the word RITUAL at display size, masked so it fades as it
   descends. It is decoration, so it is aria-hidden and unselectable.
   ========================================================================== */
.fade-out {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding: var(--s-10) 0 var(--s-9);
  text-align: center;
  color: var(--butter);
  /* Transparent at the top so whatever is above shows through and there is
     nothing to see as an edge. Fully at the footer colour by 64%, which
     leaves a solid run before the footer begins.

     `--fade-base` is what sits behind that transparent top. On the home page
     the preceding section is the page's own paper, so the default of nothing
     is right. Where a tinted band comes first — the cream .section--butter on
     Learn and Recipes — the transparent top instead revealed the body's
     lighter --butter, and the step between the two read as a white line
     across the page. Matching the base to the band above removes it. */
  background-color: var(--fade-base, transparent);
  background-image: linear-gradient(180deg,
    rgba(51, 59, 21, 0)    0%,
    rgba(51, 59, 21, .28) 14%,
    rgba(51, 59, 21, .72) 32%,
    rgba(51, 59, 21, .95) 48%,
    var(--olive-deep)     64%);
}

/* Set from whatever precedes the fade, so the join is invisible on any page. */
.section--butter + .fade-out { --fade-base: var(--butter-deep); }
.section--deep   + .fade-out { --fade-base: var(--olive-deep); }
.section--olive  + .fade-out { --fade-base: var(--olive); }
.section--lime   + .fade-out { --fade-base: var(--lime); }

/* A green bloom rising through the fade, so the darkening is not a flat
   grey ramp. */
.fade-out::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(60% 40% at 22% 30%, rgba(200, 220, 139, .20), transparent 70%),
    radial-gradient(52% 38% at 80% 58%, rgba(126, 148, 64, .24), transparent 72%);
}

.fade-out__word {
  position: absolute; left: 50%; top: 8%;
  transform: translateX(-50%);
  z-index: -1; pointer-events: none; user-select: none;
  font-family: var(--font-display); font-weight: 900;
  font-variation-settings: "wdth" 118;
  font-size: clamp(6rem, 21vw, 19rem); line-height: .82;
  letter-spacing: -.05em; text-transform: uppercase;
  white-space: nowrap;
  color: rgba(247, 245, 238, .09);
  /* Fades as it descends, so the word dissolves rather than being cropped. */
  -webkit-mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,.45) 52%, transparent 88%);
  mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,.45) 52%, transparent 88%);
}

/* Anything in a plain .container inside the fade needs lifting above the
   background word too, not just the centred pitch block. */
.fade-out > .container { position: relative; z-index: 1; }
.fade-out__inner { position: relative; z-index: 1; max-width: 680px; }

/* --- Variant: a closing section that is not a centred call to action ---
   Recipes ends on an accordion rather than a pitch. The dissolve into the
   footer is the part worth having on every page; centring a list of things
   not to cook is not. So this keeps the section's own alignment and width
   and changes only what the darker ground requires. The accordion itself
   sits inside a white .module, so it stays readable as it is. */
.fade-out--plain { text-align: left; }
.fade-out--plain .eyebrow { color: var(--lime); justify-content: flex-start; }
.fade-out--plain .section-head p,
.fade-out--plain > .container > p { color: rgba(247, 245, 238, .74); }
.fade-out h2 { color: var(--butter); margin-bottom: var(--s-4); }
.fade-out .eyebrow { color: var(--lime); justify-content: center; }
.fade-out .lede { color: rgba(247, 245, 238, .76); margin-bottom: var(--s-6); }
.fade-out__note {
  margin-top: var(--s-4); font-size: .8rem; color: rgba(247, 245, 238, .5);
}

/* Subscribe row */
.subscribe { display: flex; gap: var(--s-2); max-width: 460px; margin: 0 auto; }
.subscribe .input {
  flex: 1; background: rgba(247, 245, 238, .08);
  border: 1.5px solid rgba(247, 245, 238, .28);
  color: var(--butter); border-radius: var(--r-full); padding: 15px var(--s-5);
}
.subscribe .input::placeholder { color: rgba(247, 245, 238, .45); }
.subscribe .input:focus {
  outline: none; border-color: var(--lime);
  box-shadow: 0 0 0 4px rgba(200, 220, 139, .16);
}
.subscribe .btn { flex: none; }

/* The footer now continues the same colour, so its own top edge is removed
   and the two read as one surface. */
.fade-out + .site-chrome-bottom .footer,
.fade-out ~ div .footer { border-top: 0; }

/* ==========================================================================
   19. SLIDE-UP STACK

   Between the sugar quiz and "Pick your size". The quiz pins itself to the
   viewport while the next section rises over the top of it, so the join
   reads as a panel sliding up from the bottom rather than two sections
   scrolling past together.

   Pure sticky — no scroll listener, no library. The one prerequisite is
   that nothing between here and the viewport is a scroll container, which
   is why body uses overflow-x: clip rather than hidden (see section 2).
   ========================================================================== */
.slide-stack { position: relative; }

/* There is no runway wrapper any more.
   It existed to give the sticky quiz something to hold against, but its
   padding is empty page: you scrolled through a band of nothing between the
   quiz and the panel. The panel is a sibling inside this same block, so the
   panel's own height is the distance the quiz stays pinned for — which is
   exactly as long as it takes to cover it, and not a pixel more. */

.slide-stack__under {
  /* Sticky from the TOP, with a negative offset.
     -------------------------------------------
     `bottom: 0` looks like the right answer for "hold this until the next
     thing covers it" and is not: sticky-bottom holds an element as you
     scroll UP toward it, and does nothing at all as you scroll DOWN past
     it. Measured mid-scroll, the section simply left the screen.

     The technique that does work for a section taller than the viewport is
     a NEGATIVE top offset of (viewportHeight - sectionHeight). The section
     scrolls normally until its bottom reaches the bottom of the screen,
     then holds there while the panel below rises over it.

     That offset depends on two things this file cannot know — the height of
     the viewport and the height of the quiz, which changes between the
     question and the result. So app.js measures both and writes the value
     into --stack-top. The 0 fallback keeps the section merely un-pinned,
     never broken, if that script does not run. */
  position: sticky;
  top: var(--stack-top, 0px);
  /* Sits below the rising panel in the stacking order. */
  z-index: 0;
}

/* The quiz keeps its normal size in here. Nothing is trimmed, because
   sticking to the bottom means the section never has to fit one screen. */

/* Tells you the pause is deliberate and there is more below. Fades out as
   soon as the panel starts to arrive would be nicer, but that needs a
   scroll listener for what is a one-line hint. */
.slide-stack__cue {
  margin: clamp(8px, 1.6vh, 20px) 0 0; text-align: center;
  font-family: var(--font-mono);
  font-size: clamp(.64rem, 1.3vh, .78rem);
  letter-spacing: .16em; text-transform: uppercase;
  color: rgba(247, 245, 238, .45);
  animation: cue-bob 2.4s var(--ease-out) infinite;
}
@keyframes cue-bob { 0%, 100% { translate: 0 0; } 50% { translate: 0 6px; } }

.slide-stack__over {
  position: relative; z-index: 1;
  /* Must be opaque, or the pinned section shows through it. The lighter lip
     at the top edge is what makes it read as a lit leading edge. */
  background: linear-gradient(180deg, #fdfcf7 0%, var(--bg) 42%);
  border-radius: 44px 44px 0 0;
  /* The shadow is cast upward onto the section being covered, so the quiz
     darkens as the panel arrives without any scroll-driven maths. */
  box-shadow: 0 -30px 60px rgba(20, 26, 8, .34), 0 -1px 0 rgba(20, 26, 8, .16);
  /* Just enough to clear the grip bar. The old value left a dead band of
     empty panel between its leading edge and the first line of type. */
  padding-top: var(--s-7);
}

/* A short bar at the top of the panel, the way a sheet that can be dragged
   is usually marked. Decorative — the panel is not draggable, but it reads
   as "this thing moves". */
.slide-stack__grip {
  position: absolute; top: 18px; left: 50%;
  width: 54px; height: 5px; margin-left: -27px;
  border-radius: var(--r-full);
  background: var(--ink); opacity: .18;
}

/* --- Cross-page transitions ---
   Progressive enhancement: browsers without the View Transitions API simply
   navigate the way they always did.

   @view-transition is declared at the top level rather than inside a media
   query, because that is where it is unambiguously valid — reduced motion is
   honoured below by removing the animations instead of the rule. */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: vt-out .18s var(--ease) both; }
::view-transition-new(root) { animation: vt-in .28s var(--ease-out) both; }
@keyframes vt-out { to { opacity: 0; } }
@keyframes vt-in { from { opacity: 0; transform: translateY(8px); } }

@media (prefers-reduced-motion: reduce) {
  .reveal { transition-delay: 0ms !important; }
  .hero__art img { transform: none !important; }
  .showdown-hint svg { animation: none; }
  /* Page transitions become instant rather than a faster fade. */
  ::view-transition-old(root), ::view-transition-new(root) { animation: none !important; }
}
/* ==========================================================================
   Track your order (track.html)
   ========================================================================== */
/* .track-page, not .track: .track is already the order-progress layout above. */
.track-page { max-width: 720px; }
.track__form { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--s-3); align-items: start; }
.track__form .field { margin: 0; }
/* The button sits level with the input, not with its label. */
.track__form .btn { margin-top: 26px; }
.track__form .input { text-transform: uppercase; letter-spacing: .04em; }
.track__form .input::placeholder { text-transform: none; letter-spacing: 0; }
@media (max-width: 560px) {
  .track__form { grid-template-columns: 1fr; }
  .track__form .btn { margin-top: 0; width: 100%; }
}

.track__card { margin-top: var(--s-5); }
.track__head { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--s-3); flex-wrap: wrap; }
.track__title { font-family: var(--font-ui); font-size: 1.25rem; margin: var(--s-2) 0 var(--s-3); }
.track__note { margin: var(--s-4) 0; color: var(--ink-2); }
.track__items { font-size: .86rem; color: var(--text-muted); margin: var(--s-3) 0 0; }
.track__help { font-size: .86rem; color: var(--text-muted); margin: var(--s-4) 0 0; }
.track__actions { display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: center; margin-top: var(--s-4); }

/* Four stages on a line: filled olive dots up to the current one, outlined
   after it, joined by a rule that is olive as far as the order has got. */
.track-steps { list-style: none; padding: 0; margin: var(--s-5) 0 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
.track-steps__step { position: relative; display: grid; justify-items: center; gap: 8px; text-align: center; }
.track-steps__step + .track-steps__step::before {
  content: ""; position: absolute; top: 15px; right: 50%; width: 100%; height: 2px;
  background: var(--line); z-index: 0;
}
.track-steps__step.is-done + .track-steps__step::before,
.track-steps__step.is-done + .track-steps__step.is-current::before { background: var(--olive); }
.track-steps__dot {
  position: relative; z-index: 1;
  width: 32px; height: 32px; border-radius: var(--r-full);
  display: grid; place-items: center;
  border: 2px solid var(--line); background: var(--white); color: var(--butter);
}
.track-steps__dot svg { width: 16px; height: 16px; }
.track-steps__step.is-done .track-steps__dot,
.track-steps__step.is-current .track-steps__dot { background: var(--olive); border-color: var(--olive); }
.track-steps__step.is-current .track-steps__dot { box-shadow: 0 0 0 4px var(--lime); }
.track-steps__label { font-size: .8rem; font-weight: 700; color: var(--text-muted); }
.track-steps__step.is-done .track-steps__label,
.track-steps__step.is-current .track-steps__label { color: var(--ink); }

/* ==========================================================================
   Privacy policy (privacy.html)
   ========================================================================== */
.privacy__summary {
  background: var(--white); border: 2px solid var(--ink); border-radius: var(--r-lg);
  padding: var(--s-5) var(--s-6); margin-bottom: var(--s-6);
}
.privacy__summary h2 { margin-top: 0; font-size: 1.3rem; }
.privacy__summary ul { margin-bottom: 0; }
/* Two-column tables scroll sideways inside themselves on a narrow phone
   rather than pushing the page wider. */
.privacy__table { overflow-x: auto; }
.privacy__table table { min-width: 460px; }
.privacy__table th, .privacy__table td { padding-right: var(--s-4); vertical-align: top; }
.privacy__table td:first-child { width: 45%; font-weight: 600; color: var(--ink); }
/* A details table set out like the nutrition card: a rounded card, a tinted
   header row, a rule between rows. */
.info-card {
  background: var(--white); border: 2px solid var(--ink); border-radius: var(--r-lg);
  overflow: hidden; margin-block: var(--s-4);
}
.prose .info-card table { margin: 0; font-size: .95rem; }
.info-card th, .info-card td { padding: 13px var(--s-5); border-bottom: 1px solid var(--line); vertical-align: top; }
.info-card tbody tr:last-child th, .info-card tbody tr:last-child td { border-bottom: 0; }
.info-card thead th { background: var(--butter-deep); border-bottom: 2px solid var(--ink); }
.info-card tbody th {
  width: 28%; font-size: .9rem; font-weight: 700; letter-spacing: 0; text-transform: none;
  color: var(--text-muted);
}
.info-card tbody td { font-weight: 600; color: var(--ink); }
@media (max-width: 560px) {
  .info-card th, .info-card td { padding: 11px var(--s-3); }
  .info-card tbody th { width: 34%; }
}
