*,
*::before,
*::after { box-sizing: border-box; }

html {
  background: var(--void);
  color-scheme: dark;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  min-width: 0;
  overflow-x: clip;
  background: var(--void);
  color: var(--moon);
  font-family: var(--font-ui);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: geometricPrecision;
}

body.is-locked { overflow: hidden; }
button, input { font: inherit; }
button, a { -webkit-tap-highlight-color: transparent; }
button { color: inherit; }
a { color: inherit; text-decoration: none; }
img, svg, canvas { display: block; max-width: 100%; }
::selection { background: var(--signal); color: white; }

:focus-visible {
  outline: 2px solid var(--chlorophyll);
  outline-offset: 4px;
}

.skip-link {
  position: fixed;
  z-index: 1000;
  top: 0.75rem;
  left: 0.75rem;
  padding: 0.75rem 1rem;
  border-radius: 0.25rem;
  background: var(--moon);
  color: var(--void);
  transform: translateY(-160%);
  transition: transform 180ms var(--ease-out);
}

.skip-link:focus { transform: translateY(0); }


/* Floating navigation: compact, legible over both fluid and bone surfaces. */
.site-header {
  position: fixed;
  z-index: 240;
  /* viewport-fit=cover means the viewport starts behind the status bar on a
     notched iPhone, so the resting offset has to clear it. */
  top: calc(0.75rem + var(--safe-top, 0px));
  right: var(--gutter-right, var(--gutter));
  left: var(--gutter-left, var(--gutter));
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  height: 4.4rem;
  padding: 0.35rem 0.45rem 0.35rem 1.1rem;
  border: 1px solid rgba(255,255,255,0.14);
  border-radius: 0.35rem;
  background: rgba(7, 7, 10, 0.42);
  box-shadow: 0 1rem 3rem rgba(0,0,0,0.08);
  backdrop-filter: blur(18px) saturate(145%);
  transition: transform 280ms var(--ease-out), background-color 280ms var(--ease-out), border-color 280ms var(--ease-out);
}

.site-header.is-scrolled {
  border-color: rgba(255,255,255,0.2);
  background: rgba(7, 7, 10, 0.82);
  transform: translateY(-0.2rem);
}

.wordmark { justify-self: start; width: 7.9rem; }
.wordmark img { width: 100%; height: 2rem; object-fit: contain; object-position: left center; }

.desktop-nav {
  display: flex;
  align-items: center;
  gap: clamp(1.5rem, 3vw, 3rem);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
}

.desktop-nav a { position: relative; padding: 0.6rem 0; color: rgba(255,255,255,0.78); }
.desktop-nav a::after { position: absolute; right: 0; bottom: 0.25rem; left: 0; height: 1px; background: var(--chlorophyll); content: ""; transform: scaleX(0); transform-origin: right; transition: transform 220ms var(--ease-out); }

.header-actions { display: flex; justify-self: end; align-items: center; gap: 0.5rem; }
.bag-button, .menu-button { min-height: 3.2rem; border: 1px solid rgba(255,255,255,0.15); border-radius: 0.22rem; background: rgba(255,255,255,0.04); cursor: pointer; font-size: 0.7rem; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; transition: transform 120ms var(--ease-out), background-color 180ms ease, border-color 180ms ease; }
.bag-button { display: flex; align-items: center; gap: 0.75rem; padding: 0.2rem 0.25rem 0.2rem 0.9rem; }
.bag-button span { display: grid; place-items: center; width: 2.55rem; height: 2.55rem; border-radius: 0.15rem; background: var(--chlorophyll); color: var(--void); font-variant-numeric: tabular-nums; }
/* The count's reaction to being added to. landing.js adds .is-bumped only when
   the number goes UP and drops it on animationend. One beat, not a loop: this
   is a receipt, not an attention-getter, and it sits in the fixed header where
   anything repeating would become wallpaper. */
@keyframes bag-bump {
  0%   { transform: scale(1); }
  38%  { transform: scale(1.18); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: no-preference) {
  .bag-button span.is-bumped { animation: bag-bump 420ms var(--ease-out); }
}
.menu-button { display: none; align-items: center; gap: 0.65rem; padding: 0 0.9rem; }
.menu-button i { position: relative; width: 0.9rem; height: 0.7rem; }
.menu-button i::before, .menu-button i::after { position: absolute; left: 0; width: 100%; height: 1px; background: currentColor; content: ""; }
.menu-button i::before { top: 0.15rem; }
.menu-button i::after { bottom: 0.15rem; }
.bag-button:active, .menu-button:active, .hero-product:active, .sku-switcher button:active, .nutrition-button:active, .add-button:active, .range-card:active, .batch-form button:active { transform: scale(0.97); }

@media (hover: hover) and (pointer: fine) {
  .desktop-nav a:hover::after { transform: scaleX(1); transform-origin: left; }
  .bag-button:hover, .menu-button:hover { border-color: rgba(255,255,255,0.35); background: rgba(255,255,255,0.09); }
}

/* Hero — live WebGL field + interruptible three-SKU composition. */
.hero {
  --hero-accent: var(--sku-jamun-bright);
  position: relative;
  min-height: 100svh;
  overflow: hidden;
  padding: 7rem var(--gutter) 1.6rem;
  background: #07070a;
  isolation: isolate;
}

body[data-sku="mango"] .hero { --hero-accent: var(--nectar); }
body[data-sku="pineapple"] .hero { --hero-accent: var(--chlorophyll); }

.hero__fluid, .hero__particles, .hero__fallback, .hero__grid, .hero__noise { position: absolute; inset: 0; width: 100%; height: 100%; }
.hero__fallback { z-index: -5; background: conic-gradient(from 118deg at 78% 42%, transparent 0 21%, color-mix(in srgb, var(--nova) 19%, transparent) 25%, transparent 34% 65%, color-mix(in srgb, var(--electric-ice) 18%, transparent) 72%, transparent 82%), linear-gradient(138deg, #07070a 0 49%, color-mix(in srgb, var(--hero-accent) 18%, #07070a) 68%, #07070a 88%); transition: background 600ms var(--ease-in-out); }
.hero__fluid { z-index: -4; opacity: 0.9; filter: saturate(1.35) contrast(1.12); }
.hero__particles { z-index: -1; pointer-events: none; }
.hero__grid { z-index: -2; opacity: 0.34; background-image: linear-gradient(rgba(255,255,255,0.04) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.04) 1px, transparent 1px); background-size: 4.5rem 4.5rem; mask-image: linear-gradient(90deg, black, rgba(0,0,0,0.15) 66%, transparent); }
.hero__noise { z-index: -1; opacity: 0.12; pointer-events: none; background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 180 180' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.32'/%3E%3C/svg%3E"); mix-blend-mode: soft-light; }
.hero::after { position: absolute; z-index: -1; inset: 0; background: linear-gradient(90deg, rgba(7,7,10,0.8) 0, rgba(7,7,10,0.25) 48%, rgba(7,7,10,0.08) 70%, rgba(7,7,10,0.5)), linear-gradient(180deg, rgba(7,7,10,0.2), transparent 55%, rgba(7,7,10,0.82)); content: ""; pointer-events: none; }

.hero__register { position: relative; z-index: 10; display: flex; justify-content: space-between; padding-bottom: 0.85rem; border-bottom: 1px solid rgba(255,255,255,0.2); color: rgba(255,255,255,0.6); font-size: 0.62rem; letter-spacing: 0.15em; text-transform: uppercase; }

.hero__copy { position: relative; z-index: 12; width: min(48rem, 49vw); margin-top: clamp(4rem, 10svh, 8rem); pointer-events: none; }
.hero__eyebrow { margin: 0 0 1.1rem; color: var(--hero-accent); font-size: 0.72rem; font-weight: 500; letter-spacing: 0.13em; text-transform: uppercase; transition: color 400ms ease; }
.hero__title { margin: 0; font-family: var(--font-ui); font-size: clamp(4.6rem, 9.4vw, 11.2rem); font-weight: 500; line-height: 0.73; letter-spacing: -0.075em; text-transform: uppercase; }
.hero__title > span { display: block; overflow: hidden; padding: 0.09em 0.02em 0.13em; }
.hero__dek { max-width: 28rem; margin: 1.7rem 0 1.2rem; color: rgba(255,255,255,0.72); font-size: clamp(0.92rem, 1.1vw, 1.08rem); line-height: 1.55; }
.hero__cta { display: flex; align-items: center; justify-content: space-between; width: min(19rem, 100%); padding: 0.9rem 0; border-top: 1px solid rgba(255,255,255,0.35); border-bottom: 1px solid rgba(255,255,255,0.35); pointer-events: auto; font-size: 0.7rem; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; }

.hero-products { position: absolute; z-index: 8; top: 17%; right: 1.5%; bottom: 7rem; width: 62%; pointer-events: none; }
.hero-product { position: absolute; top: 0; bottom: 0; width: clamp(13rem, 18.5vw, 23rem); padding: 0; border: 0; background: transparent; cursor: pointer; pointer-events: auto; transition: left 680ms var(--ease-in-out), right 680ms var(--ease-in-out), transform 680ms var(--ease-in-out), opacity 400ms ease, filter 400ms ease; }
.hero-product[data-slot="active"] { z-index: 4; left: 49%; opacity: 1; filter: saturate(1.05) brightness(1); transform: translateX(-50%) translateY(-1.8%) scale(1.08); }
.hero-product[data-slot="left"] { z-index: 2; left: 4%; opacity: 0.62; filter: saturate(0.78) brightness(0.72); transform: translateX(0) translateY(10%) scale(0.78); }
.hero-product[data-slot="right"] { z-index: 2; right: 2%; left: auto; opacity: 0.62; filter: saturate(0.78) brightness(0.72); transform: translateX(0) translateY(10%) scale(0.78); }
.hero-product::before { position: absolute; z-index: 1; top: 48%; left: 50%; width: 84%; aspect-ratio: 0.92; border: 1px solid color-mix(in srgb, currentColor 58%, transparent); background: linear-gradient(148deg, color-mix(in srgb, currentColor 55%, white 8%), color-mix(in srgb, currentColor 35%, transparent) 48%, transparent 74%); box-shadow: 0 0 6rem color-mix(in srgb, currentColor 28%, transparent); clip-path: polygon(47% 0, 78% 8%, 100% 34%, 91% 73%, 62% 100%, 24% 91%, 0 58%, 9% 18%); content: ""; opacity: 0.76; transform: translate(-50%, -50%) scale(0.86); transition: transform 680ms var(--ease-in-out), opacity 400ms ease; }
.hero-product[data-slot="active"]::before { opacity: 1; transform: translate(-50%, -50%) scale(1.13); }
.hero-product--jamun { color: var(--sku-jamun-bright); }
.hero-product--mango { color: var(--nectar); }
.hero-product--pineapple { color: var(--chlorophyll); }
.hero-product__image { position: absolute; z-index: 2; top: 3.5%; right: 0; bottom: 4.6rem; left: 0; display: grid; place-items: center; }
.hero-product__image img { width: 100%; height: 100%; object-fit: contain; filter: drop-shadow(0 2.2rem 2.5rem rgba(0,0,0,0.54)); }
.hero-product__index { position: absolute; z-index: 4; top: 5%; left: 0; width: 2.3rem; height: 2.3rem; display: grid; place-items: center; border: 1px solid currentColor; background: rgba(7,7,10,0.64); color: white; clip-path: polygon(50% 0,100% 50%,50% 100%,0 50%); font-size: 0.62rem; font-variant-numeric: tabular-nums; }
.hero-product__label { position: absolute; z-index: 3; right: 0; bottom: 0; left: 0; display: grid; grid-template-columns: 1fr auto; align-items: end; gap: 0.5rem; padding-top: 0.75rem; border-top: 1px solid rgba(255,255,255,0.25); color: white; text-align: left; }
.hero-product__label b { font-family: var(--font-display); font-size: clamp(1.15rem, 1.9vw, 2rem); font-weight: 400; line-height: 1; }
.hero-product__label small { color: rgba(255,255,255,0.55); font-size: 0.52rem; letter-spacing: 0.08em; text-transform: uppercase; }

.hero__active-label { position: absolute; z-index: 12; right: var(--gutter); bottom: 1.75rem; margin: 0; color: rgba(255,255,255,0.5); font-size: 0.58rem; letter-spacing: 0.12em; text-transform: uppercase; }
.hero__active-label b { color: var(--hero-accent); font-weight: 500; transition: color 400ms ease; }

@media (hover: hover) and (pointer: fine) {
    .hero-product:not([data-slot="active"]):hover { opacity: 0.9; filter: saturate(0.96) brightness(0.84); }
}

.velocity-strip { overflow: hidden; border-top: 1px solid rgba(7,7,10,0.9); border-bottom: 1px solid rgba(7,7,10,0.9); background: var(--chlorophyll); color: var(--void); }
.velocity-strip div { display: flex; width: max-content; align-items: center; gap: 2.4rem; padding: 0.8rem 0; animation: velocity-run 20s linear infinite; font-size: 0.66rem; font-weight: 500; letter-spacing: 0.14em; }
.velocity-strip i { font-style: normal; }
@keyframes velocity-run { to { transform: translateX(-50%); } }

.section { position: relative; padding: var(--section-space) var(--gutter); }
.section-register { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding-bottom: 1rem; border-bottom: 1px solid rgba(255,255,255,0.16); color: rgba(255,255,255,0.5); font-size: 0.62rem; letter-spacing: 0.14em; text-transform: uppercase; }

.proof { position: relative; overflow: hidden; background: var(--store-canvas, #070709); }
/* The orrery stays as generated linework but stops being colour: desaturated
   and dropped to a barely-there graphite tracing, which is the "technical
   drafting layer" the section can carry without competing with the type.
   Repainted in CSS rather than in Orrery.js so the art itself is untouched. */
/* 01 no longer hosts an orrery. It is the Cosmic Garden instrument and 05
   still carries it; behind a section whose whole argument is that nothing
   is added, a decorative diagram was the one thing on screen that had been
   added. */
/* The clipped poster shape is gone. It was violet into green with a cyan
   inner glow — three decorative hues in one object, and the loudest thing in
   the section after the lime headline. */
.proof::after { display: none; }

.proof h2, .range h2, .process h2 { margin: 0; font-family: var(--font-ui); font-size: clamp(3.9rem, 7.7vw, 9rem); font-weight: 500; line-height: 0.8; letter-spacing: -0.065em; }
.proof h2 em, .range h2 em, .process h2 em { color: var(--chlorophyll); font-family: var(--font-display); font-weight: 400; }






/* Product observatory. Controls stay above the visual plane and remain clickable. */

.nutrition-button, .add-button { display: flex; align-items: center; justify-content: space-between; width: 100%; border: 1px solid var(--void); background: transparent; color: var(--void); cursor: pointer; text-transform: uppercase; transition: transform 120ms var(--ease-out), background-color 200ms ease, color 200ms ease; }
.add-button { min-height: 3.6rem; padding: 0 0.45rem 0 1rem; background: var(--void); color: var(--moon); font-size: 0.67rem; font-weight: 500; letter-spacing: 0.1em; }
.add-button i { display: grid; place-items: center; width: 2.65rem; height: 2.65rem; background: var(--chlorophyll); color: var(--void); font-size: 1.2rem; font-style: normal; }


@media (hover: hover) and (pointer: fine) {
    .add-button:hover { background: color-mix(in srgb, var(--sku-accent) 75%, var(--void)); }
}.philosophy__layout { position: relative; z-index: 1; display: grid; grid-template-columns: 0.22fr 1.25fr 0.36fr; gap: clamp(2rem, 5vw, 6rem); align-items: end; padding-top: clamp(1.4rem, 2.2vw, 2.4rem); }
.philosophy__index { margin: 0; font-size: 0.62rem; line-height: 1.75; letter-spacing: 0.12em; }.philosophy__note { margin: 0 0 0.7rem; font-size: 0.8rem; line-height: 1.55; }.orrery { display: block; width: 100%; height: 100%; overflow: visible; }
@keyframes sigil-drift { to { transform: translateY(-50%) rotate(360deg); } }


@media (max-width: 900px) {  @keyframes sigil-drift { to { transform: rotate(360deg); } }
}

/* 94svh held 752px around ~358px of content — 48% ink, the emptiest section
   on the page. It is a closing CTA, not a viewport-filling statement. */
/* --- FIRST HARVEST ----------------------------------------------------------
   Museum Black, the same ground the PDP bays, the Trade Desk and the Service
   Archive stand on, so the last commerce moment before the footer belongs to
   the same house as the rest of the site. What it replaced was a conic
   teal/cyan/orange field with two rotated polygons over a grid -- a campaign
   poster that shared no language with anything around it.

   Asymmetric, not a centred stack: the argument holds the left, the capture
   sits right and drops below the headline baseline so the form reads as the
   answer to "GET THERE FIRST." rather than as a widget underneath it. */
.first-batch {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  align-items: start;
  gap: clamp(2.5rem, 6vw, 6rem);
  align-content: center;
  overflow: hidden;
  /* No min-height. The section is exactly as tall as the proposition needs;
     68svh of forced air on a short message is dead scroll, not luxury. */
  padding: var(--section-space) var(--gutter-right) var(--section-space) var(--gutter-left);
  background: linear-gradient(136deg, #0a0a0d, #070709 58%, #050506);
  color: var(--moon);
}

/* ONE light, not a palette. Saffron because that is the action colour this
   section resolves into, held at 9% so the eye reads depth and finds the
   headline first. If the colour is the first thing noticed it is too strong. */
.first-batch__light {
  position: absolute;
  z-index: 0;
  inset: 0;
  background: radial-gradient(118% 76% at 6% -12%, rgba(255, 182, 24, .09), transparent 56%);
}

.first-batch__lede,
.first-batch__capture { position: relative; z-index: 1; min-width: 0; }

/* The capture is the LOWER counterweight. Top-aligned it left a large void
   under the form while the headline column ran on, which reads as a gap
   rather than as composition. Ending it against the body copy draws a
   diagonal from the headline down to the button and closes the frame. */
.first-batch__capture { align-self: end; padding-bottom: .4rem; }

.first-batch__eyebrow {
  margin: 0 0 clamp(1.4rem, 3vw, 2.2rem);
  color: rgba(255, 255, 255, .42);
  font-family: var(--font-ui);
  font-size: .58rem;
  letter-spacing: .24em;
}

/* One statement, carried by scale and silence. No italic, no second family,
   no accent word -- the old headline set FIRST HARVEST. in display serif lime
   and the decoration did the work the sentence should have been doing. */
.first-batch h2 {
  margin: 0;
  font-family: var(--font-ui);
  font-size: clamp(2.9rem, 7.4vw, 6.2rem);
  font-weight: 500;
  line-height: .92;
  letter-spacing: -.02em;
  text-transform: uppercase;
}

.first-batch__body {
  max-width: 34rem;
  margin: clamp(1.6rem, 3vw, 2.4rem) 0 0;
  color: rgba(255, 255, 255, .68);
  font-size: .95rem;
  line-height: 1.65;
}
.first-batch__body p { margin: 0 0 .9rem; }
.first-batch__body p:last-child { margin-bottom: 0; }

/* Reassurance, not a headline: dimmer and smaller than the body it follows. */
.first-batch__assure {
  margin: 1.4rem 0 0;
  color: rgba(255, 255, 255, .4);
  font-size: .74rem;
  line-height: 1.6;
}
/* The privacy affordance that arrived with the capture API. The email is now
   actually stored by an email service provider, so the point of collection has
   to offer a way out and a way to read the policy. It is underlined because the
   global `a { color: inherit; text-decoration: none }` would otherwise render
   it identical to the sentence around it -- an affordance nobody can see is not
   an affordance. Same muted colour, same type; only the underline is new. */
.first-batch__assure a { text-decoration: underline; text-underline-offset: .18em; }
.first-batch__assure a:hover,
.first-batch__assure a:focus-visible { color: rgba(255, 255, 255, .72); }
/* The field is a ruled baseline, not a bordered box. The old treatment was a
   1px rectangle with a backdrop-filter holding an input and the button side by
   side -- a generic newsletter widget. Label, rule, then a separate CTA reads
   as architecture and gives the button room to be the strongest thing here. */
.batch-form { width: 100%; max-width: 27rem; }
.batch-form > label {
  display: block;
  margin-bottom: .55rem;
  color: rgba(255, 255, 255, .5);
  font-size: .58rem;
  letter-spacing: .16em;
  text-transform: uppercase;
}
.batch-form__field { border-bottom: 1px solid rgba(255, 255, 255, .22); transition: border-color 220ms var(--ease-out); }
.batch-form__field:focus-within { border-bottom-color: var(--nectar); }
.batch-form[data-state="invalid"] .batch-form__field { border-bottom-color: var(--danger); }
.batch-form input {
  width: 100%;
  min-width: 0;
  padding: .65rem 0;
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--moon);
  font-family: var(--font-ui);
  /* 16px, not rem. Below 16 iOS Safari zooms the viewport on focus and throws
     the customer sideways out of the form. */
  font-size: 16px;
  line-height: 1.5;
}
.batch-form input::placeholder { color: rgba(255, 255, 255, .26); }
/* Autofill repaints the field white in WebKit; the inset shadow is the only
   way to hold the dark ground. */
.batch-form input:-webkit-autofill {
  -webkit-text-fill-color: var(--moon);
  -webkit-box-shadow: 0 0 0 100px #08080b inset;
}
.batch-form button {
  display: inline-flex;
  min-height: 3.2rem;
  margin-top: 1.4rem;
  padding: 0 2rem;
  align-items: center;
  justify-content: center;
  border: 0;
  /* --nectar is the existing ASTROVE action colour: .c-btn--primary and the
     buy rail already use it. No second action colour is invented here. */
  background: var(--nectar);
  color: var(--ink);
  cursor: pointer;
  font-family: var(--font-ui);
  font-size: .68rem;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
}
.batch-form button:hover { background: var(--moon); }
.batch-form button:focus-visible { outline: 2px solid var(--nectar); outline-offset: 3px; }
.batch-form button[disabled] { opacity: .62; cursor: default; }
.batch-form[data-state="invalid"] .batch-form__status,
.batch-form[data-state="error"] .batch-form__status { color: #ff8a6b; }
.batch-form[data-state="success"] .batch-form__status { color: var(--nectar); }
/* pending == "your mail client is opening". Neutral, not the success gold:
   nothing has been subscribed at this point and the colour must not imply it. */
.batch-form[data-state="pending"] .batch-form__status { color: var(--moon-soft); }


/* Shared material drawers. */
.site-menu, .bag { position: fixed; z-index: 420; inset: 0; }
.site-menu[hidden], .bag[hidden] { display: none; }
.site-menu__scrim, .bag__scrim { position: absolute; inset: 0; background: rgba(5,5,7,0.72); opacity: 0; backdrop-filter: blur(6px); transition: opacity 240ms var(--ease-out); }
.site-menu__panel, .bag__panel { position: absolute; top: 0.65rem; right: 0.65rem; bottom: 0.65rem; display: flex; flex-direction: column; width: min(34rem, calc(100% - 1.3rem)); overflow: hidden; border: 1px solid rgba(255,255,255,0.16); border-radius: 0.4rem; background: rgba(21,10,40,0.94); box-shadow: 0 2.4rem 7rem rgba(0,0,0,0.6); opacity: 0; transform: translateX(5%) scale(0.98); transform-origin: top right; backdrop-filter: blur(28px) saturate(145%); transition: opacity 240ms var(--ease-out), transform 420ms var(--ease-drawer); }
.site-menu.is-open .site-menu__scrim, .bag.is-open .bag__scrim { opacity: 1; }
.site-menu.is-open .site-menu__panel, .bag.is-open .bag__panel { opacity: 1; transform: translateX(0) scale(1); }
.site-menu__top, .bag__header { display: flex; min-height: 5rem; align-items: center; justify-content: space-between; padding: 0 1.4rem; border-bottom: 1px solid rgba(255,255,255,0.15); }
.site-menu__top span { color: var(--moon-dim); font-size: 0.64rem; letter-spacing: 0.12em; }
.site-menu__top button, .bag__header button { border: 0; background: transparent; cursor: pointer; font-size: 0.66rem; letter-spacing: 0.1em; text-transform: uppercase; }
.site-menu nav { display: flex; flex: 1; flex-direction: column; justify-content: center; padding: 1.5rem; }
.site-menu nav a { display: flex; align-items: baseline; gap: 1rem; padding: 0.75rem 0; border-bottom: 1px solid rgba(255,255,255,0.15); font-family: var(--font-display); font-size: clamp(2.8rem, 9vw, 5.8rem); line-height: 0.92; letter-spacing: -0.04em; }
.site-menu nav a span { color: var(--chlorophyll); font-family: var(--font-ui); font-size: 0.62rem; letter-spacing: 0.1em; }
.site-menu__panel > p { margin: 0; padding: 1.4rem; color: var(--moon-dim); font-size: 0.62rem; letter-spacing: 0.1em; text-transform: uppercase; }
.origin-archive { position: fixed; z-index: 620; inset: 0; }
.origin-archive[hidden] { display: none; }
.origin-archive__scrim {
  position: absolute; inset: 0;
  background: rgba(3, 3, 5, .82);
  opacity: 0;
  transition: opacity 260ms var(--ease-out);
}
.origin-archive.is-open .origin-archive__scrim { opacity: 1; }

/* Full-viewport takeover on phones -- 100dvh, not vh, so Safari chrome cannot
   trap the foot of the document. Never a floating rounded rectangle. */
.origin-archive__sheet {
  --origin-surface: #130B1A;
  --origin-surface-raised: #1A1023;
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  height: 100dvh;
  overflow: hidden;
  background:
    radial-gradient(128% 74% at 8% -4%, var(--sku-bloom, transparent), transparent 52%),
    var(--origin-surface);
  opacity: 0;
  transition: opacity 300ms var(--ease-out);
}
.origin-archive[data-sku] .origin-archive__sheet { --origin-surface: var(--sku-surface); --origin-surface-raised: var(--sku-surface-raised); }
/* Archive accent = the DOSSIER accent: the same colour nutrition, taste and
   rituals resolve for each SKU, so the four records read as one. It used to
   borrow the specimen-story accent from pdp-media.css instead, which diverged
   for jamun (rose #c2506f against violet) and pineapple (muted #9fb840 against
   lime) -- invisible while the archive was a separate door, obvious once the
   dossier rail put the records side by side. Pineapple takes the lime the other
   three pineapple records already carry; keeping it out of this one record no
   longer kept it out of the dossier. Used only on the archive number, region
   label, close control and rules -- never on body copy. */
.origin-archive { --origin-accent: var(--nectar); }
.origin-archive[data-sku="mango"]     { --origin-accent: #ffa50a; }
/* JAMUN FOLLOWS THE DOSSIER. The archive once carried its own rose (#c2506f)
   while nutrition, taste and rituals all resolve jamun to --sku-jamun-bright.
   That went unseen while the archive was a separate door; once the dossier rail
   joined the four records, switching to Origin turned the close control, the
   archive number, the region label and the lit rail tab from violet to rose
   under the same violet bloom. One fruit, one accent, across every record. */
.origin-archive[data-sku="jamun"]     { --origin-accent: var(--sku-jamun-bright); }
.origin-archive[data-sku="pineapple"] { --origin-accent: #c9ff2b; }
.origin-archive.is-open .origin-archive__sheet { opacity: 1; }

.origin-archive__bar {
  display: flex; flex: 0 0 auto;
  align-items: center; justify-content: space-between;
  min-height: 3.5rem; padding: 0 1.1rem;
  border-bottom: 1px solid rgba(255, 247, 230, .14);
}
.origin-archive__mark {
  color: rgba(255, 247, 230, .5);
  font-family: var(--font-ui); font-size: .58rem;
  letter-spacing: .18em; text-transform: uppercase;
}
.origin-archive__mark b { color: var(--origin-accent); font-weight: 500; }
.origin-archive__close {
  min-height: 2.75rem; padding: 0 .2rem 0 1rem;
  border: 0; background: transparent; cursor: pointer;
  color: var(--origin-accent);
  font-family: var(--font-ui); font-size: .6rem; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase;
}
.origin-archive__close:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }

.origin-archive__doc { flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch; }
/* Museum Black for the image bay: the photograph is the strongest colour event
   and the chromatic surface stays out of its way. */
.origin-archive__plate {
  margin: 0; padding: 1.6rem 1.1rem;
  background: #070709;
  border-bottom: 1px solid rgba(255, 247, 230, .12);
}
.origin-archive__plate img {
  display: block; width: 100%; max-width: 22rem; margin: 0 auto;
  height: auto; object-fit: contain;
  transform: scale(.985);
  opacity: 0;
  transition: transform 420ms var(--ease-out), opacity 360ms var(--ease-out);
}
.origin-archive.is-open .origin-archive__plate img { transform: scale(1); opacity: 1; }

.origin-archive__text { padding: 1.5rem 1.1rem 3rem; }
.origin-archive__text > * { opacity: 0; transform: translateY(.5rem); transition: opacity 320ms var(--ease-out), transform 320ms var(--ease-out); }
.origin-archive.is-open .origin-archive__text > * { opacity: 1; transform: none; }
.origin-archive.is-open .origin-archive__region { transition-delay: 60ms }
.origin-archive.is-open .origin-archive__text h2 { transition-delay: 90ms }
.origin-archive.is-open .origin-archive__body { transition-delay: 130ms }
.origin-archive.is-open .origin-archive__ledger { transition-delay: 180ms }
.origin-archive.is-open .origin-archive__source { transition-delay: 220ms }

.origin-archive__region {
  display: flex; flex-wrap: wrap; gap: .5rem .9rem; margin: 0 0 .9rem;
  font-family: var(--font-ui); font-size: .56rem;
  letter-spacing: .16em; text-transform: uppercase;
}
.origin-archive__region span { color: var(--origin-accent); }
.origin-archive__region em { color: rgba(255, 247, 230, .45); font-style: italic; letter-spacing: .06em; text-transform: none; }
.origin-archive__text h2 {
  margin: 0 0 1.1rem;
  color: var(--moon);
  font-family: var(--font-ui); font-size: clamp(2rem, 8.4vw, 2.9rem);
  font-weight: 500; line-height: .98; letter-spacing: -.03em;
}
.origin-archive__body p { margin: 0 0 .85rem; max-width: 34rem; color: rgba(255, 247, 230, .74); font-size: .92rem; line-height: 1.62; }
.origin-archive__body p:last-child { margin-bottom: 0; }

/* Archival ledger: thin rules, small caps label, restrained value. No pills,
   no cards, no shadows. */
.origin-archive__ledger { margin: 1.8rem 0 0; border-top: 1px solid rgba(255, 247, 230, .16); }
.origin-archive__ledger div {
  display: grid; gap: .25rem;
  padding: .85rem 0;
  border-bottom: 1px solid rgba(255, 247, 230, .12);
}
.origin-archive__ledger span {
  color: rgba(255, 247, 230, .42);
  font-family: var(--font-ui); font-size: .53rem;
  letter-spacing: .17em; text-transform: uppercase;
}
.origin-archive__ledger strong { color: var(--moon); font-family: var(--font-display); font-size: 1.35rem; font-weight: 400; line-height: 1.15; }
.origin-archive__source {
  margin: 1.5rem 0 0; color: rgba(255, 247, 230, .38);
  font-family: var(--font-ui); font-size: .53rem;
  letter-spacing: .14em; text-transform: uppercase;
}

/* Desktop: editorial split. Plate left, document right, capped so the text
   never stretches across the screen. */
@media (min-width: 901px) {
  /* minmax(0, 1fr) on the row, min-height:0 on the columns: without them the
   plate's intrinsic height set the row and both columns rendered 10px taller
   than the box that clips them, so the foot of the text column sat outside a
   container that cannot scroll. */
  .origin-archive__doc { display: grid; grid-template-columns: 46fr 54fr; grid-template-rows: minmax(0, 1fr); overflow: hidden; }
  .origin-archive__plate, .origin-archive__text { min-height: 0; }
  .origin-archive__plate { display: grid; place-items: center; padding: clamp(2rem, 4vw, 4rem); border-right: 1px solid rgba(255, 247, 230, .12); border-bottom: 0; }
  .origin-archive__plate img { max-width: min(30rem, 80%); }
  .origin-archive__text { max-width: 40rem; padding: clamp(2.6rem, 4.4vw, 4.4rem) clamp(2rem, 3.4vw, 3.6rem); overflow-y: auto; }
  .origin-archive__text h2 { font-size: clamp(2.6rem, 3.4vw, 4rem); }
  .origin-archive__bar { padding: 0 clamp(2rem, 3.4vw, 3.6rem); min-height: 4rem; }

  /* One page here too. The text column is the archive's scroller at this width
     (the doc itself is overflow:hidden), and it ran +100 to +148 at 1366x768 --
     jamun and pineapple worst, because their dossiers carry the longest body.
     Most of it was a 60px band of padding above and below the text and a ledger
     spending .85rem on every one of four rows. */
  .origin-archive__text { padding: clamp(1.5rem, 2.4vw, 2.4rem) clamp(2rem, 3.4vw, 3.6rem); }
  .origin-archive__text h2 { font-size: clamp(2.2rem, 2.9vw, 3.3rem); }
  .origin-archive__body p { margin: 0 0 .6rem; font-size: .86rem; line-height: 1.52; }
  .origin-archive__ledger { margin: 1.1rem 0 0; }
  .origin-archive__ledger div { padding: .5rem 0; }
  .origin-archive__ledger strong { font-size: 1.2rem; }
}

@media (prefers-reduced-motion: reduce) {
  .origin-archive__scrim,
  .origin-archive__sheet,
  .origin-archive__plate img,
  .origin-archive__text > * { transition: opacity 140ms linear; transform: none; }
}

/* --- anchor landing clearance ----------------------------------------------
   The header is sticky and nothing declared scroll-margin, so a CTA landing on
   its target parked the target top at viewport 0 and the header sat on it:
   measured 20px of the Mango media stage and 46px of the first single-fruit
   card clipped at 390. Headings were clear; the product image was not.

   Scoped to the destinations the corrected CTAs actually use, so this fixes
   the landing without changing how any section is laid out. */
.specimen-story,
#single-fruits,
#discovery { scroll-margin-top: clamp(3.9rem, 4.4vw, 4.6rem); }

/* --- MOBILE INDEX -----------------------------------------------------------
   The panel was a purple poster: rgba(21,10,40,.94) ground with seven display-
   serif rows at up to 5.8rem, vertically centred, so every destination was a
   headline and the list needed most of the screen to say six words.

   It is now an index. Obsidian ground from the same family as the PDP bays,
   UI type instead of display, the numeral demoted to a quiet gold tick and the
   NAME promoted to the scanning target. Rows are hairline-separated and sized
   for a thumb rather than for a poster.

   Every selector here is scoped to .site-menu on purpose: .site-menu__panel and
   .site-menu__top share their base rules with .bag__panel / .bag__header, and
   the bag is not in scope. */
.site-menu__panel {
  /* The shared rule pins top AND bottom, which stretched the panel to the full
     viewport. With six compact rows that left ~374px of empty panel under the
     last item at 390. Release the bottom edge so the index is exactly as tall
     as it needs to be, and cap it so it can still scroll on a short screen.
     The bag keeps the full-height treatment; this is scoped to the menu. */
  bottom: auto;
  max-height: calc(100% - 1.3rem);
  background: rgba(9, 9, 12, 0.96);
  border-color: rgba(255, 247, 230, 0.14);
}
.site-menu__top {
  min-height: 3.5rem;
  padding: 0 1.1rem;
  border-bottom-color: rgba(255, 247, 230, 0.14);
}
.site-menu__top span {
  color: rgba(255, 247, 230, 0.5);
  font-family: var(--font-ui);
  font-size: 0.6rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
/* Close is the one control that must never be hunted for. */
.site-menu__top button {
  min-height: 2.75rem;
  padding: 0 0.2rem 0 1rem;
  color: var(--nectar);
  font-family: var(--font-ui);
  font-weight: 500;
}
.site-menu__top button:focus-visible { outline: 2px solid var(--nectar); outline-offset: 3px; }

/* flex-start, not centre: centring a short list in a tall panel is what left
   the dead region under the rows. */
.site-menu nav {
  flex: 0 1 auto;
  justify-content: flex-start;
  padding: 0.9rem 1.1rem 0.4rem;
}
.site-menu nav a {
  align-items: center;
  gap: 0.95rem;
  min-height: 3.4rem;
  padding: 0.55rem 0;
  border-bottom-color: rgba(255, 247, 230, 0.12);
  color: var(--moon);
  font-family: var(--font-ui);
  font-size: 1.32rem;
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.012em;
  transition: color 200ms var(--ease-out), border-color 200ms var(--ease-out);
}
.site-menu nav a:last-child { border-bottom: 0; }
.site-menu nav a span {
  min-width: 1.5rem;
  color: rgba(255, 165, 10, 0.68);
  font-size: 0.58rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.14em;
}
.site-menu nav a:focus-visible { outline: 2px solid var(--nectar); outline-offset: 3px; }
@media (hover: hover) and (pointer: fine) {
  .site-menu nav a:hover { color: var(--nectar); border-bottom-color: rgba(255, 165, 10, 0.4); }
}
.site-menu nav a:active { color: var(--nectar); }
.site-menu__panel > p {
  padding: 0.9rem 1.1rem 1.1rem;
  color: rgba(255, 247, 230, 0.38);
  font-size: 0.58rem;
  letter-spacing: 0.16em;
}

.bag__header h2 { margin: 0; font-family: var(--font-display); font-size: 2rem; font-weight: 400; }
.bag__items { flex: 1; overflow-y: auto; padding: 1rem 1.4rem; }
.bag__empty { display: grid; min-height: 100%; place-items: center; align-content: center; gap: 0.8rem; color: var(--moon-dim); text-align: center; }
.bag__empty span { color: var(--chlorophyll); }
.bag__empty p { margin: 0; font-family: var(--font-display); font-size: 1.8rem; }
.bag__empty a { border-bottom: 1px solid currentColor; font-size: 0.66rem; letter-spacing: 0.08em; text-transform: uppercase; }
.bag-item { display: grid; grid-template-columns: 5rem 1fr auto; gap: 1rem; align-items: center; padding: 1rem 0; border-bottom: 1px solid rgba(255,255,255,0.14); }
.bag-item__image { display: grid; height: 6.5rem; place-items: center; background: rgba(255,255,255,0.05); }
.bag-item__image img { width: 80%; height: 90%; object-fit: contain; }
.bag-item__content h3 { margin: 0 0 0.2rem; font-family: var(--font-display); font-size: 1.35rem; font-weight: 400; }
.bag-item__content p { margin: 0; color: var(--moon-dim); font-size: 0.62rem; }
.bag-item__qty { display: inline-flex; margin-top: 0.7rem; border: 1px solid rgba(255,255,255,0.18); }
.bag-item__qty button, .bag-item__qty span { display: grid; width: 2rem; height: 2rem; place-items: center; border: 0; background: transparent; }
.bag-item__qty button { cursor: pointer; }
.bag-item__price { align-self: start; font-size: 0.72rem; }
.bag__summary { padding: 1.2rem 1.4rem; border-top: 1px solid rgba(255,255,255,0.16); }
.bag__summary p { display: flex; justify-content: space-between; margin: 0 0 1rem; }
/* space-between existed to push a trailing arrow to the far edge. That arrow
   was a bare U+2197, which iOS renders as the blue emoji glyph, and it is gone;
   centre the label so it is optically balanced rather than left-hugging. Only
   the alignment changed -- lime, height, padding and type are untouched. */
.bag__summary button { display: flex; width: 100%; min-height: 3.7rem; align-items: center; justify-content: center; padding: 0 1rem; border: 0; background: var(--chlorophyll); color: var(--void); cursor: pointer; font-size: 0.68rem; letter-spacing: 0.08em; text-transform: uppercase; }
.bag__summary small { display: block; margin-top: 0.6rem; color: var(--moon-dim); font-size: 0.6rem; text-align: center; }

/* --- BAG DRAWER, aligned to the menu ----------------------------------------
   The index above was refreshed to obsidian and the bag was left behind, which
   is exactly what the note on that block predicted: the two drawers SHARE their
   base rules, so the menu's override moved one of them and not the other. Side
   by side the bag still read as the old build -- measured rgba(21, 10, 40, .94),
   H262 with 30 points of channel spread, against the menu's rgba(9, 9, 12, .96)
   at H240 and spread 3. One is violet, the other is black.

   This is colour only. Every value below is taken from the approved menu block
   above rather than invented: the same obsidian ground, the same ivory-tinted
   hairlines instead of pure white, the same gold close, and ivory alphas in
   place of --moon-dim (#a396b4, itself a violet-grey at H266) for secondary
   copy. Structure, spacing, type and behaviour are untouched.

   THE CHECKOUT BUTTON IS DELIBERATELY NOT HERE. --chlorophyll is the commerce
   action colour, carried by the header bag badge as well, and the note beside
   that rule records it as already approved. A dark drawer is not a reason to
   restyle the one control the customer is meant to press. */
.bag__panel {
  background: rgba(9, 9, 12, 0.96);
  border-color: rgba(255, 247, 230, 0.14);
}
.bag__header { border-bottom-color: rgba(255, 247, 230, 0.14); }
/* Close matches the index: gold, and never hunted for. */
.bag__header button { color: var(--nectar); font-family: var(--font-ui); font-weight: 500; }
.bag__header button:focus-visible { outline: 2px solid var(--nectar); outline-offset: 3px; }
.bag__empty { color: rgba(255, 247, 230, 0.5); }
.bag__empty a:focus-visible { outline: 2px solid var(--nectar); outline-offset: 3px; }
.bag-item { border-bottom-color: rgba(255, 247, 230, 0.12); }
.bag-item__image { background: rgba(255, 247, 230, 0.05); }
.bag-item__content p { color: rgba(255, 247, 230, 0.5); }
.bag-item__qty { border-color: rgba(255, 247, 230, 0.18); }
.bag__summary { border-top-color: rgba(255, 247, 230, 0.14); }
.bag__summary small { color: rgba(255, 247, 230, 0.38); }
/* The last legacy value in the drawer. The lime CTA is untouched -- background,
   size, type and label all stay -- but its LABEL was painted with --void
   (#0a0417), a violet-black, so the bag still carried one purple token even
   after the surfaces were neutral. tokens.css has no neutral dark token to
   reach for: --void-deep, --void, --void-lift and --ink measure H255 to H268,
   every one of them violet. #0a0a0d is the Museum Black the menu panel, the
   PDP bays, the Trade Desk and the Service Archive already stand on, so this
   borrows the approved neutral rather than inventing a token for one label.
   Contrast against the lime is effectively unchanged: 17.09:1 -> 16.78:1. */
.bag__summary button { color: #0a0a0d; }

/* Nutrition telemetry takeover: exact value racing bars. */
/* --- NUTRITION CHROMATIC SURFACE --------------------------------------------
   One system, three surfaces. The panel used to paint var(--void) (#0a0417, a
   purple-black) and the shell painted a HARD-CODED plum gradient further down
   the file, identical for every SKU -- which is why Alphonso Mango read as a
   Jamun-family panel no matter what the accent was set to.

   Surface = mood, bar = identity, type = information. Each surface is the SKU
   hue crushed to a chromatic black at roughly equal perceptual lightness, so
   the three read as one component; the bright accent stays in the data layer
   and is never used as a field. */
.nutrition {
  --nutrition-accent: var(--sku-jamun-bright);
  --nutrition-surface: #130B1A;
  --nutrition-surface-raised: #1A1023;
  position: fixed; z-index: 600; inset: 0; overflow: hidden;
  background: var(--nutrition-surface);
  clip-path: circle(0% at var(--ox, 50%) var(--oy, 100%));
  visibility: hidden;
  transition: clip-path 420ms var(--ease-garden), visibility 0s linear 420ms;
}
/* ONE SKU SURFACE MAP. Declared once, consumed by every SKU-scoped overlay, so
   a surface can never drift between the nutrition and insight environments. */
/* EACH SURFACE IS A BLACKENED VERSION OF ITS OWN PACK COLOUR. Jamun sits 4deg
   off its violet accent, pineapple in its green; mango now sits at H37,
   one degree off #ffa50a, so all three follow one rule instead of two.

   Mango has been wrong twice, in opposite directions, and the measurements
   say why. #160F0B read as MUD: its channel spread is 11, against 15 for
   jamun and 16 for pineapple, so there was not enough chroma left to read as
   a hue at all and it resolved to grey-brown. The correction overshot into
   #0B1020, which is simply NAVY -- H226 against an H38 accent, a surface
   belonging to no fruit ASTROVE sells.

   Mango cannot be solved the way the other two were, and #191002 was the
   proof: on a real iPhone it read unmistakably BROWN. That is not a tuning
   failure, it is a naming one. Dark purple is still purple and dark green is
   still green, but dark orange has its own colour name -- brown. Any flat
   field at jamun's luminance with an orange hue lands in that category, so
   two attempts (#160F0B, #191002) both arrived there from opposite sides.

   Escaping brown needs luminance, and luminance alone would make mango the
   loud panel of three. Measured: a flat orange bright enough to read orange
   (#A55000) is 13.7% relative luminance against jamun 0.71% and pineapple
   1.25% -- eleven times the brightest reference.

   So mango does not get a brighter SURFACE. It gets a near-neutral black
   surface, like the others, plus a small saturated bloom in one corner --
   light rather than pigment. Brown is a percept of a dark orange SURFACE; a
   concentrated glow over black is read as illumination and keeps its hue.
   Measured mean field luminance: mango 1.17%, pineapple 1.16%, jamun 0.74%.
   Mango is now visibly saffron WITHOUT being brighter than the references. */
[data-sku="mango"]     { --sku-surface: #090706; --sku-surface-raised: #1F1505; }
[data-sku="jamun"]     { --sku-surface: #080610; --sku-surface-raised: #170E22; }
[data-sku="pineapple"] { --sku-surface: #060806; --sku-surface-raised: #0E1C10; }
/* THE BLOOM CARRIES THE IDENTITY, NOT THE SURFACE.

   All three substrates are now near-black and the fruit arrives as coloured
   LIGHT entering a dark room. That started as the only way to give mango an
   orange identity without it collapsing into brown, and it turned out to be
   the better grammar for all three, so jamun and pineapple use it too rather
   than staying on the older painted-surface treatment.

   Geometry is shared and deliberately identical -- same origin, same radius,
   same falloff, same layer order. COLOUR IS THE ONLY VARIABLE. Each bloom is
   derived from that SKU's own pack identity, not from its UI accent: jamun
   comes off --sku-jamun #6a17c4 rather than the neon --sku-jamun-bright, and
   pineapple off --sku-pineapple #14c274 leaned chartreuse rather than the
   commerce lime. Borrowing the interface accents would have given a candy
   violet and a lime spotlight.

   ALPHA IS NOT SHARED, and that is the one deliberate asymmetry. A violet
   carries far less luminance than an orange at the same alpha, and saturated
   colour reads brighter than its measured luminance (Helmholtz-Kohlrausch),
   so equal alpha gives a jamun that both measures dark AND looks garish.
   Alpha is tuned per hue against rendered means: mango 1.26%, pineapple
   1.07%, jamun 0.75% -- jamun sits lowest by measurement and matches the
   other two by eye. Measured on screenshots, not calculated. */
[data-sku]             { --sku-bloom: transparent; }
[data-sku="mango"]     { --sku-bloom: rgba(255, 182, 24, .72); }
[data-sku="jamun"]     { --sku-bloom: rgba(126, 44, 168, 1); }
[data-sku="pineapple"] { --sku-bloom: rgba(105, 190, 68, .58); }
.nutrition[data-sku] { --nutrition-surface: var(--sku-surface); --nutrition-surface-raised: var(--sku-surface-raised); }
.nutrition[hidden] { display: none; }
.nutrition.is-open { clip-path: circle(150% at var(--ox, 50%) var(--oy, 100%)); visibility: visible; transition: clip-path 420ms var(--ease-garden), visibility 0s; }
/* Background deliberately absent here: one rule further down owns the surface.
   Two competing declarations for the same field is what hid the defect. */
.nutrition__shell { height: 100%; overflow-y: auto; }
.nutrition__header { position: sticky; z-index: 3; top: 0; display: flex; align-items: center; justify-content: space-between; min-height: 5.5rem; padding: 0 var(--gutter); border-bottom: 1px solid rgba(255,255,255,0.17); background: color-mix(in srgb, var(--nutrition-surface) 84%, transparent); backdrop-filter: blur(20px) saturate(1.4); }
.nutrition__header > div { display: flex; align-items: baseline; gap: 1rem; }
.nutrition__header span { color: var(--moon-dim); font-size: 0.58rem; letter-spacing: 0.12em; }
.nutrition__header strong { color: var(--nutrition-accent); font-size: 0.7rem; font-weight: 500; letter-spacing: 0.12em; }
.nutrition__header button { display: flex; align-items: center; gap: 1rem; min-height: 3rem; padding: 0 0.4rem 0 0.9rem; border: 1px solid rgba(255,255,255,0.2); background: transparent; cursor: pointer; font-size: 0.62rem; letter-spacing: 0.1em; text-transform: uppercase; }
.nutrition__header button i { display: grid; width: 2.1rem; height: 2.1rem; place-items: center; background: var(--nutrition-accent); color: var(--void); font-size: 1.2rem; font-style: normal; }
.nutrition__intro { display: grid; grid-template-columns: 0.85fr 0.55fr; gap: 3rem; align-items: end; padding: clamp(1.4rem, 3vw, 2.5rem) var(--gutter) 1.2rem; }
.nutrition__intro > div:first-child > span { color: var(--nutrition-accent); font-size: 0.58rem; letter-spacing: 0.13em; }
.nutrition__intro h2 { margin: 0.4rem 0 0; font-family: var(--font-ui); font-size: clamp(2.6rem, 4.5vw, 5rem); font-weight: 500; line-height: 0.84; letter-spacing: -0.05em; text-transform: uppercase; }
.nutrition__intro > p { max-width: 30rem; margin: 0; color: var(--moon-dim); font-size: 0.76rem; line-height: 1.55; }
/* ---------------------------------------------------------------------------
   NUTRITION — OBSIDIAN GROUND, SKU IN THE ACCENTS

   The bar system this replaces drew every row against 100% of the daily
   reference. On a 20g pouch that is arithmetic, not design: zero of twelve
   bars reached a quarter of their track, eight rendered visually empty, and
   fourteen near-empty tracks told a reader the product contained almost
   nothing. It also flattened the two figures that carry weight -- jamun's
   Vitamin C at 35.30% and Iron at 19.53% sat at the same visual weight as
   Vitamin D at 0.

   A per-SKU field wash was tried here and reverted: it turned the panel into a
   coloured room and made the fruit the wallpaper. The ground is near-black for
   every fruit; --nutrition-accent does the work, in more places rather than
   fewer.
   --------------------------------------------------------------------------- */
.nutrition__grid { display: block; padding: 0 var(--gutter) 2.5rem; }

/* 1 -- WHAT THE POUCH IS */
.nutrition-pouch { display: grid; grid-template-columns: auto 1fr; align-items: center; gap: 0.4rem 2.8rem; margin: 1.6rem 0 0; }
.nutrition-pouch__fig { margin: 0; color: var(--nutrition-accent); font-size: clamp(3rem, 8vw, 6.4rem); font-weight: 500; line-height: 0.82; letter-spacing: -0.06em; }
.nutrition-pouch__fig i { font-style: normal; font-size: 0.34em; letter-spacing: 0; }
.nutrition-pouch__say { max-width: 24ch; margin: 0; font-family: var(--font-display); font-size: clamp(1rem, 1.8vw, 1.4rem); line-height: 1.25; }
.nutrition-pouch__sub { grid-column: 1 / -1; margin: 1rem 0 0; color: var(--moon-dim); font-size: 0.58rem; letter-spacing: 0.14em; text-transform: uppercase; }
.nutrition-pouch__sub i { margin: 0 0.35em; font-style: normal; opacity: 0.55; }

/* 2 -- WHAT IT MEANINGFULLY CONTAINS. The card stays black and the fruit marks
   it with a lit edge, rather than the card taking a tinted fill. */
.nutrition-present { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr)); gap: 1px; margin: 1.7rem 0 0; background: rgba(255,255,255,0.16); min-height: 7.4rem; }
.nutrition-present article { position: relative; padding: 1.4rem 1.3rem 1.4rem 1.5rem; background: var(--nutrition-surface, #0c0c11); }
.nutrition-present article::before { position: absolute; inset: 0 auto 0 0; width: 2px; background: var(--nutrition-accent); content: ""; }
/* An unused notable slot is removed, not dimmed. The old .is-void put opacity
   0.5 on the article, which let the section's own 1px-gap background composite
   through as a solid grey slab where a reader expects a second figure. */
.nutrition-present article[hidden] { display: none; }
/* Held so a rank change never jumps the layout, but it must read as EMPTY. */
.nutrition-present h3 { margin: 0; color: var(--moon-dim); font-size: 0.58rem; font-weight: 500; letter-spacing: 0.16em; text-transform: uppercase; }
.nutrition-present b { display: block; margin: 0.65rem 0 0.2rem; color: var(--nutrition-accent); font-size: clamp(1.8rem, 4vw, 2.9rem); font-weight: 500; line-height: 0.9; letter-spacing: -0.04em; }
.nutrition-present b i { color: var(--moon-dim); font-style: normal; font-size: 0.36em; letter-spacing: 0.02em; }
.nutrition-present small { display: block; color: var(--nutrition-accent); font-size: 0.6rem; letter-spacing: 0.1em; text-transform: uppercase; }

/* 3 -- WHAT IT DELIBERATELY DOES NOT CONTAIN */
.nutrition-absence { display: grid; grid-template-columns: repeat(3, minmax(0, max-content)); gap: 0.9rem 3.4rem; margin: 1.7rem 0 0; padding-top: 1.2rem; border-top: 1px solid color-mix(in srgb, var(--nutrition-accent) 30%, rgba(255,255,255,0.16)); }
.nutrition-absence div span { display: block; color: var(--nutrition-accent); font-family: var(--font-display); font-size: clamp(1.5rem, 2.8vw, 2.2rem); line-height: 1; }
.nutrition-absence div em { display: block; margin-top: 0.3rem; color: var(--moon-dim); font-size: 0.54rem; font-style: normal; letter-spacing: 0.16em; text-transform: uppercase; }
.nutrition-absence__note { grid-column: 1 / -1; margin: 0.3rem 0 0; color: var(--moon-dim); font-size: 0.54rem; letter-spacing: 0.14em; text-transform: uppercase; opacity: 0.75; }
.nutrition-absence__note i { margin: 0 0.35em; font-style: normal; }

/* 4 -- THE FULL DECLARATION, as a record. Colour is information here: a row
   clearing the promotion cut is marked and lit, so the two or three that carry
   weight are findable by eye instead of flattened into fourteen. */
.nutrition-record { margin: 1.7rem 0 0; }
.nutrition-record__head { display: flex; justify-content: space-between; padding-bottom: 0.65rem; border-bottom: 1px solid color-mix(in srgb, var(--nutrition-accent) 30%, rgba(255,255,255,0.16)); color: var(--moon-dim); font-size: 0.54rem; letter-spacing: 0.16em; text-transform: uppercase; }
.nutrition-record ol { margin: 0; padding: 0; list-style: none; }
.nutrition-record li { position: relative; display: grid; grid-template-columns: 2.1rem 1fr auto auto; gap: 1rem; align-items: baseline; padding: 0.58rem 0; border-bottom: 1px solid rgba(255,255,255,0.07); font-size: 0.8rem; }
.nutrition-record li::before { position: absolute; top: 50%; left: -0.9rem; width: 4px; height: 4px; border-radius: 50%; background: var(--nutrition-accent); content: ""; transform: translateY(-50%) scale(0); }
.nutrition-record li.is-notable::before { transform: translateY(-50%) scale(1); }
.nutrition-record li > span:first-child { color: rgba(255,255,255,0.28); font-size: 0.54rem; letter-spacing: 0.1em; font-variant-numeric: tabular-nums; }
.nutrition-record li.is-notable > span:first-child { color: var(--nutrition-accent); }
.nutrition-record__name { color: rgba(255,255,255,0.72); }
.nutrition-record li.is-notable .nutrition-record__name { color: var(--store-ivory, #f2e9db); }
.nutrition-record li b { font-weight: 500; }
.nutrition-record li b i { color: var(--moon-dim); font-style: normal; }
.nutrition-record li em { min-width: 8rem; color: var(--moon-dim); font-size: 0.6rem; font-style: normal; letter-spacing: 0.08em; text-align: right; }
.nutrition-record li.is-notable em { color: var(--nutrition-accent); }
.nutrition-num { font-variant-numeric: tabular-nums; }
.nutrition__footer { display: flex; justify-content: space-between; gap: 2rem; padding: 1rem var(--gutter) 2rem; border-top: 1px solid rgba(255,255,255,0.16); color: var(--moon-dim); font-size: 0.54rem; letter-spacing: 0.08em; }
.nutrition__footer p { max-width: 38rem; margin: 0; line-height: 1.45; text-align: right; }

/* ============================================================================
   THE PANEL IS ONE PAGE, NOT A DOCUMENT

   Measured before touching anything: at 1440x900 the nutrition panel ran 1414px
   of content in a 900px box (+514), and at 1280x800 it ran +606. The reader
   opened a nutrition panel and was shown a title. Everything that answers the
   question they clicked to ask was below the fold.

   THE WASTE WAS HORIZONTAL, NOT VERTICAL. Fourteen declaration rows were stacked
   down the left while the right half of a 1440px panel held nothing, and each
   row stretched its four cells across the full width so the name and its value
   sat a third of a metre apart. Two columns of seven inside a right-hand column
   costs no legibility -- the type does not shrink -- and removes ~265px of the
   overflow on its own. The rest comes from the left column, which now carries
   the specimen material it always should have.

   Column-wise flow, not row-wise: the declaration is a NUMBERED list, so 01-07
   must read down the left and 08-14 down the right. Row-wise auto-flow would
   put 01 02 on one line and number the panel wrongly. */
@media (min-width: 1100px) {
  /* Once the panel stopped scrolling it stopped filling the viewport, and the
     footer floated with a band of dead space beneath it that read as a page cut
     short. Column layout puts the rule at the base of the screen, so the slack
     sits between the record and the footer as deliberate negative space. */
  .nutrition__shell, .insight__shell { display: flex; flex-direction: column; }
  .nutrition__grid, .insight__body { flex: 1 1 auto; }
  .nutrition__footer, .insight__footer { margin-top: auto; }

  .nutrition__intro { align-items: start; padding-top: clamp(1rem, 2vw, 1.6rem); padding-bottom: 0.2rem; }
  .nutrition__intro > p { max-width: 34rem; }

  .nutrition__grid { display: grid; grid-template-columns: minmax(0, 0.78fr) minmax(0, 1.22fr); column-gap: clamp(2rem, 3.4vw, 3.6rem); align-content: start; padding-bottom: 0.9rem; }
  .nutrition-pouch { grid-column: 1; margin-top: 0.2rem; }
  /* auto-fit could not keep two cards on one line once the column narrowed to
     0.78fr -- at 1280 they stacked and cost jamun, the only two-notable SKU,
     an extra 130px that mango and pineapple never paid. Explicit column flow
     keeps them side by side, and with one notable it yields one full-width
     track rather than an empty one showing the section background. */
  .nutrition-present { grid-column: 1; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); grid-template-columns: none; }
  .nutrition-absence { grid-column: 1; }
  .nutrition-record { grid-column: 2; grid-row: 1 / span 3; margin-top: 0.2rem; }

  .nutrition-record ol { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: repeat(7, auto); grid-auto-flow: column; column-gap: clamp(1.6rem, 2.6vw, 2.8rem); }
  /* The reference cell reserved 8rem so a full-width row could right-align its
     percentage against the panel edge. In a half-width column that reservation
     is a third of the track. */
  .nutrition-record li em { min-width: 0; }
  .nutrition-record li { grid-template-columns: 1.6rem 1fr auto auto; gap: 0.7rem; padding: 0.4rem 0; font-size: 0.76rem; }
  .nutrition__footer { padding: 0.75rem var(--gutter) 1.1rem; }
}

[data-reveal] { opacity: 0; transform: translateY(18px); transition: opacity 620ms var(--ease-out), transform 620ms var(--ease-out); }
/* The state landing.js has been adding all along, which no stylesheet ever
   defined. [data-reveal] starts at opacity 0 and the only rule that lifted it
   was inside @supports (animation-timeline: view()) — which is itself gated on
   prefers-reduced-motion: no-preference. So on the reduced-motion path every
   revealed element on the page stayed invisible: registers, headlines and body
   copy in 01, 02, 03 and 05 alike. Verified at 390 with reducedMotion forced —
   the section rendered as a bare hairline and four facts, because those are the
   only elements in it without the attribute.

   Placed immediately after the base rule so the scroll-driven path below still
   wins where it applies; animations outrank author declarations regardless. */
[data-reveal].is-visible { opacity: 1; transform: none; }

/* Progressive enhancement: browsers with native scroll-driven animations get a
   compositor-only entrance that scrubs with scroll position (and reverses on
   scroll-up) instead of the one-shot IntersectionObserver fade above. Zero JS,
   zero main-thread cost. Falls back cleanly to the rule above everywhere else. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    [data-reveal] {
      opacity: 1;
      transform: none;
      transition: none;
      animation: reveal-rise both;
      animation-timing-function: var(--ease-garden);
      animation-timeline: view();
      animation-range: entry 0% entry 55%;
    }
  }
}

@keyframes reveal-rise { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: translateY(0); } }


/* ---------------------------------------------------------------------------
   01 / THE PRINCIPLE — THE ARRIVAL

   The section next to this one scrubs: its transformation is tied to scroll
   position because it is describing a change. This section is not describing a
   change, it is stating a position, so it ARRIVES and then holds. Nothing here
   scrubs back and forth.

   It had three reveals -- register, headline, note -- all firing on the same
   range, so the whole section landed in one lump and then sat still, and its
   most particular content, the four-part ledger, had no reveal at all. That is
   what read as a static slide beside a neighbour that moves.

   Order: register, headline, the qualifying note, the rule drawn across, the
   four facts one after another, the disclaimer last. A claim, then its
   evidence, in the order a reader would want them.

   Reuses --step, the stagger the Gateway already uses, rather than inventing a
   second mechanism. 4% per step keeps nine beats inside the entry phase.
   --------------------------------------------------------------------------- */
.proof .section-register { --step: 0; }
.proof #proof-title      { --step: 1; }
.principle__note         { --step: 2; }
.principle__proof li:nth-child(1) { --step: 4; }
.principle__proof li:nth-child(2) { --step: 5; }
.principle__proof li:nth-child(3) { --step: 6; }
.principle__proof li:nth-child(4) { --step: 7; }
.principle__foot         { --step: 8; }

/* ONE TIMELINE FOR THE WHOLE SECTION, not one per element.

   Measured on the first cut: staggering against each element's OWN entry
   phase put 20px of scroll between the first fact and the fourth, and the
   whole ledger arrived inside 40px -- the four facts effectively popped
   together, which is not a sequence. The cause is that `entry` for a 60px
   list item is about 60px of scroll, so 4% of it is 2.4px. The row is also
   horizontal, so all four items enter at the same moment and the offset was
   the only thing separating them.

   Naming the section's own timeline gives every step the same, much longer
   ruler -- the section's pass is its height plus a viewport -- so a step is
   tens of pixels instead of two. */
.proof.section { view-timeline-name: --principle-view; }

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .proof [data-reveal] {
      animation-timeline: --principle-view;
      animation-range: cover calc(6% + var(--step, 0) * 3.4%) cover calc(30% + var(--step, 0) * 3.4%);
    }
    /* --step 3's slot: after the note, before the first fact. */
    .principle__proof::before {
      animation: principle-rule both;
      animation-timing-function: var(--ease-garden);
      animation-timeline: --principle-view;
      animation-range: cover 14% cover 34%;
    }
  }
}

@keyframes principle-rule { from { transform: scaleX(0); } to { transform: scaleX(1); } }


@media (max-width: 1180px) {
  .hero__copy { width: 55vw; }
  .hero-products { right: 0; width: 61%; }
  .hero-product { width: clamp(12rem, 18vw, 19rem); }
      }

@media (min-width: 721px) and (max-height: 840px) {
  .hero { padding-top: 6.25rem; }
  .hero__copy { margin-top: 2.1rem; }
  .hero__title { font-size: clamp(4.6rem, 8.2vw, 7.2rem); line-height: 0.74; }
  .hero__dek { margin: 1rem 0 0.75rem; }
  .hero__cta { padding: 0.65rem 0; }
  .hero-products { top: 15%; bottom: 5.5rem; }
  .hero-product__label b { font-size: clamp(1.05rem, 1.6vw, 1.65rem); }
                      .add-button { min-height: 3rem; }
  }

@media (max-width: 900px) {
  .site-header { grid-template-columns: 1fr auto; }
  .desktop-nav { display: none; }
  .menu-button { display: flex; }
  .hero { min-height: 58rem; padding-top: 6.6rem; }
  .hero__copy { width: 67vw; margin-top: 3.2rem; }
  .hero__title { font-size: clamp(4rem, 12vw, 7.6rem); }
  .hero-products { top: 27%; right: -7%; bottom: 6.5rem; width: 68%; }
  .hero-product { width: clamp(10rem, 21vw, 15rem); }
  .hero-product__label small { display: none; }
  .range__heading, .process__layout, .philosophy__layout { grid-template-columns: 1fr; }

            .philosophy__layout { align-items: start; }
  .philosophy__note { max-width: 28rem; }
}

@media (min-width: 721px) and (max-width: 900px) {
  .hero { min-height: max(70rem, 100svh); }
  .hero__copy { width: 100%; margin-top: 2.8rem; }
  .hero__title { width: 80%; font-size: clamp(4.8rem, 12.5vw, 6rem); }
  .hero__dek { max-width: 25rem; margin-top: 1.1rem; }
  .hero__cta { padding: 0.7rem 0; }
  .hero-products { top: auto; right: 0; bottom: 5rem; left: 0; width: 100%; height: 30%; }
  .hero-product { width: clamp(12rem, 27vw, 15rem); }
  .hero-product[data-slot="active"] { left: 50%; transform: translateX(-50%) translateY(-3%) scale(1.1); }
  .hero-product[data-slot="left"] { left: 1.5rem; transform: translateX(0) translateY(12%) scale(0.76); }
  .hero-product[data-slot="right"] { right: 1.5rem; transform: translateX(0) translateY(12%) scale(0.76); }
    .hero__active-label { display: none; }
}

@media (max-width: 720px) {
  .site-header { top: 0.5rem; right: 0.5rem; left: 0.5rem; height: 3.9rem; padding-left: 0.75rem; }
  .wordmark { width: 6.6rem; }
  .bag-button { min-height: 2.8rem; padding-left: 0.7rem; }
  .bag-button span { width: 2.25rem; height: 2.25rem; }
  .menu-button { min-height: 2.8rem; }
  .hero { display: flex; min-height: max(48rem, 100svh); flex-direction: column; padding: 5.6rem 1rem 1rem; }
  .hero__register span:last-child { display: none; }
  .hero__copy { width: 100%; margin-top: 2.4rem; }
  .hero__eyebrow { margin-bottom: 0.8rem; }
  .hero__title { font-size: clamp(3.5rem, 18vw, 6rem); line-height: 0.76; }
  .hero__dek { max-width: 20rem; margin-top: 1rem; font-size: 0.8rem; }
  .hero__cta { width: 14rem; padding: 0.65rem 0; }
  .hero-products { top: auto; right: 0; bottom: 6.4rem; left: 0; width: 100%; height: 28%; }
  .hero-product { width: min(9.5rem, 35vw); }
  .hero-product[data-slot="active"] { left: 50%; transform: translateX(-50%) translateY(-4%) scale(1.15); }
  .hero-product[data-slot="left"] { left: -0.6rem; transform: translateX(0) translateY(14%) scale(0.73); }
  .hero-product[data-slot="right"] { right: -0.6rem; transform: translateX(0) translateY(14%) scale(0.73); }
  .hero-product__index { top: 9%; width: 1.8rem; height: 1.8rem; }
  .hero-product__label { display: block; padding-top: 0.45rem; text-align: center; }
  .hero-product__label b { font-size: 1rem; }
          .hero__active-label { display: none; }
  .velocity-strip div { animation-duration: 14s; }
  .section-register { font-size: 0.52rem; }

  .proof h2, .range h2, .process h2 { font-size: clamp(3rem, 15vw, 5.5rem); }



                                        .philosophy__layout { padding-top: 4rem; }  /* One column, argument then capture, in reading order. */
  .first-batch { grid-template-columns: 1fr; gap: 2.4rem; }
  .batch-form { max-width: none; }
  .batch-form button { width: 100%; min-height: 3.4rem; }
  .nutrition__header { min-height: 4.5rem; }
  .nutrition__header > div span { display: none; }
  .nutrition__intro { grid-template-columns: 1fr; gap: 1.4rem; }
  .nutrition__intro h2 { font-size: clamp(2.8rem, 14vw, 5rem); }
  .nutrition-pouch { grid-template-columns: 1fr; }
  .nutrition-absence { grid-template-columns: repeat(2, minmax(0, max-content)); gap: 0.9rem 2.2rem; }
  .nutrition-record li { grid-template-columns: 1.8rem 1fr auto; gap: 0.6rem; }
  .nutrition-record li em { grid-column: 1 / -1; min-width: 0; text-align: left; padding-left: 2.4rem; }
  .nutrition__footer { display: block; }
  .nutrition__footer p { margin-top: 0.6rem; text-align: left; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .hero__fluid, .hero__particles { display: none; }
  .velocity-strip div { animation: none; }
    .hero-product, .hero-product::before, .collection__pack, [data-reveal], .nutrition, .nutrition.is-open, .insight, .insight.is-open { transition-duration: 180ms !important; }
  [data-reveal] { transform: none; }  }

@media (prefers-reduced-transparency: reduce) {
  .site-header, .site-menu__panel, .bag__panel { background: var(--void-deep); backdrop-filter: none; }
  /* --void-deep is #04020a, violet-black. Opaque Museum Black for the bag so
     the transparency fallback does not reintroduce the tint this pass removed.
     Scoped to .bag__panel: the header and the menu keep their current rule. */
  .bag__panel { background: #0a0a0d; }
  .nutrition__header { background: var(--nutrition-surface); backdrop-filter: none; }
}

@media (prefers-contrast: more) {
  .site-header { border-color: white; background: #07070a; }
  .hero::after { background: rgba(7,7,10,0.58); }
}

/* Product intelligence and low-amplitude package selection. */
.site-header.is-hidden { transform: translateY(calc(-100% - 1.2rem)); }

.hero::before { display: none; }

.hero-products { touch-action: pan-y; user-select: none; }

.product-lenses { display: grid; grid-template-columns: repeat(4, 1fr); border-top: 1px solid var(--void); border-left: 1px solid var(--void); }
.product-lenses button { display: grid; min-height: 4rem; align-content: space-between; padding: 0.58rem; border: 0; border-right: 1px solid var(--void); border-bottom: 1px solid var(--void); background: transparent; color: var(--void); cursor: pointer; text-align: left; transition: background-color 180ms ease, color 180ms ease, transform 120ms var(--ease-out); }
.product-lenses button span { font-size: 0.56rem; font-weight: 500; letter-spacing: 0.09em; text-transform: uppercase; }
.product-lenses button:active { transform: scale(0.97); }
@media (hover: hover) and (pointer: fine) {
  .product-lenses button:hover { background: var(--void); color: var(--moon); }
  }

@keyframes process-scan-sweep { 0%,8% { left: 2%; opacity: 0; } 18% { opacity: 1; } 82% { opacity: 1; } 92%,100% { left: 98%; opacity: 0; } }

.insight {
  --insight-accent: var(--sku-jamun-bright);
  --insight-surface: #130B1A;
  --insight-surface-raised: #1A1023;
  position: fixed; z-index: 610; inset: 0; overflow: hidden;
  background: var(--insight-surface);
  clip-path: circle(0% at var(--ox, 50%) var(--oy, 100%));
  visibility: hidden;
  transition: clip-path 420ms var(--ease-garden), visibility 0s linear 420ms;
}
.insight[data-sku] { --insight-surface: var(--sku-surface); --insight-surface-raised: var(--sku-surface-raised); }
.insight[hidden] { display: none; }
.insight.is-open { clip-path: circle(150% at var(--ox, 50%) var(--oy, 100%)); visibility: visible; transition: clip-path 420ms var(--ease-garden), visibility 0s; }
/* Background owned by the single rule further down, as with the nutrition shell. */
.insight__shell { height: 100%; overflow-y: auto; }
.insight__header { position: sticky; z-index: 4; top: 0; display: flex; min-height: 5.5rem; align-items: center; justify-content: space-between; padding: 0 var(--gutter); border-bottom: 1px solid rgba(255,255,255,0.17); background: color-mix(in srgb, var(--insight-surface) 84%, transparent); backdrop-filter: blur(20px) saturate(1.4); }
.insight__header > div { display: flex; align-items: baseline; gap: 1rem; }
.insight__header span { color: var(--moon-dim); font-size: 0.56rem; letter-spacing: 0.12em; }
.insight__header strong { color: var(--insight-accent); font-size: 0.68rem; font-weight: 500; letter-spacing: 0.12em; }
.insight__header button { display: flex; min-height: 3rem; align-items: center; gap: 1rem; padding: 0 0.4rem 0 0.9rem; border: 1px solid rgba(255,255,255,0.2); background: transparent; cursor: pointer; font-size: 0.62rem; letter-spacing: 0.1em; text-transform: uppercase; }
.insight__header button i { display: grid; width: 2.1rem; height: 2.1rem; place-items: center; background: var(--insight-accent); color: var(--void); font-size: 1.1rem; font-style: normal; }
.insight__hero { display: grid; grid-template-columns: 1.1fr 0.55fr; gap: clamp(2rem,7vw,8rem); align-items: end; padding: clamp(3rem,7vw,7rem) var(--gutter) clamp(2.5rem,5vw,4rem); }
.insight__hero span { color: var(--insight-accent); font-size: 0.58rem; letter-spacing: 0.13em; }
.insight__hero h2 { max-width: 70rem; margin: 0.8rem 0 0; font-family: var(--font-ui); font-size: clamp(3.5rem,7.6vw,9rem); font-weight: 500; line-height: 0.78; letter-spacing: -0.07em; }
.insight__hero > p { max-width: 31rem; margin: 0 0 0.7rem; color: var(--moon-soft); font-family: var(--font-display); font-size: clamp(1.2rem,2.2vw,2.25rem); line-height: 1.08; }
.insight__body { padding: 0 var(--gutter) 3rem; }
.insight-facts, .ritual-grid { display: grid; grid-template-columns: repeat(3,1fr); border-top: 1px solid rgba(255,255,255,0.18); border-left: 1px solid rgba(255,255,255,0.18); }
.insight-facts article, .ritual-grid article { position: relative; display: flex; min-height: 13rem; flex-direction: column; padding: 1.2rem; border-right: 1px solid var(--store-hairline, #26212a); border-bottom: 1px solid var(--store-hairline, #26212a); }
.insight-facts span, .ritual-grid span { color: var(--insight-accent); font-size: 0.52rem; letter-spacing: 0.11em; }
.insight-facts strong, .ritual-grid strong { margin: auto 0 0.8rem; font-family: var(--font-display); font-size: clamp(1.6rem,2.6vw,3rem); font-weight: 400; line-height: 0.95; }
.insight-facts p, .ritual-grid p { max-width: 23rem; margin: 0; color: var(--moon-dim); font-size: 0.7rem; line-height: 1.5; }
.ritual-grid { grid-template-columns: repeat(4,1fr); }
.ritual-grid article { min-height: 16rem; }
.taste-map { border-top: 1px solid rgba(255,255,255,0.18); }
.taste-map article { display: grid; min-height: 6rem; grid-template-columns: 2.5rem minmax(8rem,.45fr) 1fr minmax(7rem,.25fr); gap: 1.2rem; align-items: center; border-bottom: 1px solid rgba(255,255,255,0.18); }
.taste-map article > span { color: var(--insight-accent); font-size: 0.52rem; }
.taste-map article > strong { font-family: var(--font-display); font-size: clamp(1.35rem,2vw,2.2rem); font-weight: 400; }
.taste-map article > div { position: relative; height: 1rem; background: repeating-linear-gradient(90deg,rgba(255,255,255,0.13) 0 1px,transparent 1px 20%); }
.taste-map article > div::after { position: absolute; right: 0; bottom: 0; left: 0; height: 2px; background: rgba(255,255,255,0.15); content: ""; }
.taste-map article i { position: absolute; z-index: 2; right: 0; bottom: 0; left: 0; height: 2px; background: var(--insight-accent); box-shadow: 0 0 .8rem var(--insight-accent); transform: scaleX(var(--level)); transform-origin: left; }
.taste-map article b { position: absolute; z-index: 3; bottom: -.18rem; left: calc(var(--level) * 100%); width: .42rem; height: .42rem; border-radius: 50%; background: white; box-shadow: 0 0 .8rem var(--insight-accent); transform: translateX(-50%); }
.taste-map article > em { color: var(--moon-soft); font-size: 0.58rem; font-style: normal; letter-spacing: 0.1em; text-align: right; }

/* The insight shell answers to the same rule as the nutrition panel: what the
   reader clicked to see must be on the screen they land on. Measured at
   1280x800 before this block -- taste +253 (mango, which carries the most
   metrics), rituals +125. The overspend was almost entirely the hero: ~90px of
   top padding above a display line set at 7.6vw, then 64px beneath it, before
   a single fact. The type still steps down from the page hero; it just stops
   claiming a full screen of its own inside a panel. */
@media (min-width: 1100px) {
  .insight__hero { align-items: end; padding: clamp(1.4rem, 2.6vw, 2.4rem) var(--gutter) clamp(1.2rem, 2vw, 1.8rem); }
  .insight__hero h2 { margin: 0.5rem 0 0; font-size: clamp(2.6rem, 4.6vw, 5.2rem); line-height: 0.84; }
  .insight__hero > p { margin: 0 0 0.35rem; font-size: clamp(1.05rem, 1.6vw, 1.6rem); line-height: 1.16; }
  .insight__body { padding-bottom: 1.2rem; }

  /* min-height was doing the spacing work in both grids. Padding does it now,
     so a card is as tall as it needs to be instead of as tall as it was told. */
  .insight-facts article, .ritual-grid article { min-height: 0; padding: 1rem 1.1rem 1.15rem; }
  .insight-facts strong, .ritual-grid strong { margin: 1.1rem 0 0.5rem; font-size: clamp(1.4rem, 2vw, 2.2rem); }
  .ritual-grid article { min-height: 0; }
  .taste-map article { min-height: 0; padding: 0.75rem 0; }
}

/* SHORT DESKTOP VIEWPORTS. A 1440x900 display gives a browser roughly 760px of
   viewport once its own chrome is subtracted, and a 1280x800 laptop gives ~700.
   Those are the real heights this panel opens into, not the nominal ones, so the
   last 60px is reclaimed only where it is actually needed instead of being taken
   from every desktop. Nothing here changes the type size of the declaration. */
@media (min-width: 1100px) and (max-height: 790px) {
  .nutrition__intro { padding-top: 0.8rem; }
  .nutrition__intro h2 { font-size: clamp(2.2rem, 3.4vw, 3.4rem); }
  .nutrition-pouch__fig { font-size: clamp(2.6rem, 5.4vw, 4.4rem); }
  .nutrition-present { min-height: 0; margin-top: 1.1rem; }
  .nutrition-present article { padding: 1rem 1.1rem 1.05rem 1.2rem; }
  .nutrition-absence { margin-top: 1.1rem; padding-top: 0.85rem; }
  .nutrition-record li { padding: 0.3rem 0; }
  .nutrition__footer { padding: 0.55rem var(--gutter) 0.8rem; }

  .origin-archive__text { padding: clamp(1.1rem, 1.8vw, 1.8rem) clamp(1.8rem, 3vw, 3rem); }
  .origin-archive__body p { margin: 0 0 .48rem; line-height: 1.46; }
  .origin-archive__ledger { margin: .8rem 0 0; }
  .origin-archive__ledger div { padding: .38rem 0; }
}
.insight__footer { display: flex; justify-content: space-between; gap: 2rem; padding: 1rem var(--gutter) 2rem; border-top: 1px solid rgba(255,255,255,0.16); color: var(--moon-dim); font-size: 0.54rem; letter-spacing: 0.08em; }
.insight__footer p { max-width: 40rem; margin: 0; line-height: 1.45; text-align: right; }

@media (max-width: 900px) {
        .insight__hero { grid-template-columns: 1fr; }
}

@media (max-width: 720px) {
  .site-header { height: 3.55rem; }
  .site-header.is-scrolled { transform: none; }
  .site-header.is-hidden { transform: translateY(calc(-100% - 1rem)); }
  .wordmark { width: 6.15rem; }
  .bag-button, .menu-button { min-height: 2.55rem; }
  .bag-button span { width: 2.05rem; height: 2.05rem; }
  .hero { min-height: max(50rem,100svh); padding-top: 5.15rem; }
  .hero__copy { margin-top: 1.7rem; }
  .hero__title { font-size: clamp(3.35rem,17vw,5.25rem); }
  .hero-products { bottom: 8.1rem; height: 30%; }
          .hero-product { width: min(10.5rem,38vw); }
  .section.proof { padding-top: 5rem; padding-bottom: 5rem; }



  .proof__metrics article { min-height: 6.25rem; padding: .6rem; }
  .proof__metrics span, .proof__metrics small { font-size: .42rem; letter-spacing: .07em; }
  .proof__metrics strong { font-size: clamp(1.55rem,8vw,2rem); }
                            .product-lenses { grid-template-columns: repeat(2,1fr); }
  .product-lenses button { min-height: 3.35rem; }
                            .insight__header { min-height: 4.5rem; }
  .insight__header > div span { display: none; }
  .insight__hero { gap: 1.4rem; padding-top: 2.4rem; }
  .insight__hero h2 { font-size: clamp(3rem,14vw,5.3rem); }
  .insight__hero > p { font-size: 1.25rem; }
  .insight-facts, .ritual-grid { grid-template-columns: 1fr; }
  .insight-facts article, .ritual-grid article { min-height: 9.5rem; }
  .taste-map article { min-height: 7.1rem; grid-template-columns: 1.8rem 1fr auto; gap: .7rem; padding: .7rem 0; }
  .taste-map article > div { grid-column: 2 / -1; grid-row: 2; }
  .taste-map article > em { grid-column: 3; grid-row: 1; }
  .insight__footer { display: block; }
  .insight__footer p { margin-top: .6rem; text-align: left; }
}

@media (prefers-reduced-motion: reduce) {
  html.site-entered .hero::before, .process__scanline { animation: none; }
    }

/* Refinement 03 — appetite-first celestial cartography. */
.hero__noise { display: none; }
.hero__copy { width: min(49rem, 47vw); margin-top: clamp(3.2rem, 8svh, 6.8rem); }
.hero__title { font-size: clamp(4.7rem, 9.1vw, 10.8rem); }
.hero__dek { max-width: 31rem; color: rgba(255,255,255,0.78); }
.hero__cta {
  width: min(22rem, 100%);
  padding: 0.85rem 0.35rem 0.85rem 1rem;
  border: 1px solid rgba(255,255,255,0.28);
  background: rgba(255,255,255,0.035);
  backdrop-filter: blur(12px);
  transition: transform 120ms var(--ease-out), border-color 200ms ease, background-color 200ms ease;
}
.hero__cta:active { transform: scale(0.98); }
.hero-products { top: 14%; right: 0; bottom: 6.1rem; width: 62%; }
.hero-product::before {
  z-index: 0;
  width: 77%;
  border: 1px solid color-mix(in srgb, currentColor 58%, transparent);
  background: linear-gradient(148deg, color-mix(in srgb, currentColor 58%, white 7%), color-mix(in srgb, currentColor 31%, transparent) 48%, transparent 74%);
  box-shadow: 0 0 6rem color-mix(in srgb, currentColor 28%, transparent);
}
.hero-product__image { top: 0; bottom: 4.4rem; }
.hero-product__image img { filter: drop-shadow(0 2.4rem 2.8rem rgba(0,0,0,0.6)); }
.hero-product__label { z-index: 5; }
.hero-product__index { z-index: 6; background: rgba(7,7,10,0.64); backdrop-filter: blur(8px); }



@media (hover: hover) and (pointer: fine) {
  .hero__cta:hover { border-color: var(--hero-accent); background: color-mix(in srgb, var(--hero-accent) 9%, transparent); }
  }

@media (max-width: 1180px) {
  .hero__copy { width: 54vw; }
  .hero-products { width: 59%; }
  }


@media (min-width: 901px) and (max-height: 780px) {
  .hero__copy { margin-top: 2.1rem; }
  .hero__title { font-size: clamp(4.1rem,7.4vw,6.5rem); line-height: 0.75; }
  .hero__dek { margin: 0.9rem 0 0.7rem; }
  .hero__cta { padding: 0.52rem 0.25rem 0.52rem 0.8rem; }
  .hero-products { top: 13%; bottom: 5.4rem; }
}

@media (max-width: 720px) {
  .hero { min-height: max(58rem,100svh); padding-top: 5.35rem; }
  .hero__copy { width: 100%; margin-top: 1.55rem; }
  .hero__title { font-size: clamp(3.65rem,18.2vw,5.7rem); }
  .hero__dek { max-width: 21.5rem; margin-top: 0.85rem; font-size: 0.83rem; }
  .hero__cta { width: min(20rem,100%); padding: 0.55rem 0.25rem 0.55rem 0.8rem; }
      .hero-products { top: auto; right: 0; bottom: 6.4rem; left: 0; width: 100%; height: 34%; }
    .hero-product { width: min(11.4rem,42vw); }
    .hero-product__label b { font-size: 1.05rem; }
                          }


/* Process Film 04 — the supplied ASTROVE master, staged as a precision instrument. */




@keyframes process-scan {
  0%, 100% { opacity: 0.18; transform: scaleX(0.18); }
  50% { opacity: 0.54; transform: scaleX(1); }
}
@keyframes process-glint {
  0% { left: -25%; opacity: 0; }
  18% { opacity: 1; }
  82% { opacity: 0.86; }
  100% { left: 87%; opacity: 0; }
}
@keyframes process-crystal {
  0% { opacity: 0; transform: translate(-50%,-50%) rotate(-28deg) scale(0.62); }
  50% { opacity: 0.66; }
  100% { opacity: 0.22; transform: translate(-50%,-50%) rotate(0deg) scale(1); }
}
@keyframes process-pressure {
  0% { opacity: 0; transform: translate(-50%,-50%) scaleY(1.35) scaleX(0.7); }
  45% { opacity: 0.74; }
  100% { opacity: 0.28; transform: translate(-50%,-50%) scaleY(1) scaleX(1); }
}
@keyframes process-stamp {
  0% { opacity: 0; transform: translate(-50%,-50%) rotate(-18deg) scale(1.32); }
  58% { opacity: 0.64; }
  100% { opacity: 0.22; transform: translate(-50%,-50%) rotate(0deg) scale(1); }
}





/* Refinement 05 — Nocturne Orchard, a collision-free product stage. */
.hero {
  min-height: max(100svh, 54rem);
  background: var(--void);
}
.hero__fallback {
  background:
    linear-gradient(126deg, transparent 0 48%, color-mix(in srgb, var(--hero-accent) 12%, transparent) 48.2% 72%, transparent 72.2%),
    radial-gradient(ellipse at 78% 42%, color-mix(in srgb, var(--electric-ice) 10%, transparent), transparent 29rem),
    linear-gradient(138deg, var(--void-deep) 0 48%, color-mix(in srgb, var(--hero-accent) 12%, var(--void)) 68%, var(--void) 92%);
  transition: background 360ms var(--ease-in-out);
}
.hero__fluid { opacity: 0.72; filter: saturate(1.08) contrast(1.08); }
.hero__grid {
  opacity: 0.24;
  background-size: 6rem 6rem;
  -webkit-mask-image: linear-gradient(90deg, #000, rgba(0,0,0,0.35) 42%, transparent 72%);
  mask-image: linear-gradient(90deg, #000, rgba(0,0,0,0.35) 42%, transparent 72%);
}
.hero::after {
  background:
    linear-gradient(90deg, rgba(1,3,2,0.94) 0 35%, rgba(1,3,2,0.7) 48%, rgba(1,3,2,0.08) 68%, rgba(1,3,2,0.44)),
    linear-gradient(180deg, rgba(1,3,2,0.16), transparent 52%, rgba(1,3,2,0.88));
}
.hero__copy { width: min(43rem, 42vw); margin-top: clamp(2.25rem, 5.5svh, 4.7rem); }
.hero__eyebrow { margin-bottom: 0.8rem; font-size: 0.62rem; letter-spacing: 0.17em; }
.hero__title { font-size: clamp(4.15rem, 6.55vw, 8.4rem); line-height: 0.735; letter-spacing: -0.068em; }
.hero__dek { max-width: 32rem; margin: 1.15rem 0 0.9rem; color: rgba(244,239,228,0.76); font-size: clamp(0.82rem, 0.95vw, 0.98rem); }
.hero__cta { width: min(21rem, 100%); background: rgba(5,8,7,0.4); }

.hero-products {
  top: 9.5rem;
  right: var(--gutter);
  bottom: 5.8rem;
  width: 50.5%;
  overflow: hidden;
  border-right: 1px solid rgba(244,239,228,0.1);
  border-left: 1px solid rgba(244,239,228,0.1);
  clip-path: polygon(4% 0, 100% 0, 100% 93%, 95% 100%, 0 100%, 0 8%);
}
.hero-products::before,
.hero-products::after {
  position: absolute;
  z-index: 0;
  content: "";
  pointer-events: none;
}
.hero-products::before {
  inset: 0;
  background:
    linear-gradient(116deg, transparent 0 29%, color-mix(in srgb, var(--hero-accent) 13%, transparent) 29.2% 49%, transparent 49.2%),
    linear-gradient(67deg, transparent 0 61%, rgba(139,214,223,0.09) 61.2% 76%, transparent 76.2%),
    linear-gradient(90deg, rgba(255,255,255,0.018), transparent 28% 72%, rgba(255,255,255,0.018));
}
.hero-products::after {
  top: 7%;
  right: 8%;
  bottom: 9%;
  left: 7%;
  border: 1px solid color-mix(in srgb, var(--hero-accent) 30%, transparent);
  background: linear-gradient(138deg, rgba(255,255,255,0.025), transparent 36% 72%, color-mix(in srgb, var(--hero-accent) 7%, transparent));
  clip-path: polygon(7% 0, 88% 0, 100% 16%, 96% 86%, 84% 100%, 4% 100%, 0 77%, 2% 12%);
}
.hero-product {
  top: 1.2%;
  bottom: 2.2rem;
  left: 50% !important;
  right: auto !important;
  width: clamp(13.5rem, 17.4vw, 21rem);
  transform-origin: 50% 60%;
  transition: transform 520ms var(--ease-garden), opacity 240ms ease, filter 240ms ease;
}
.hero-product[data-slot="active"] {
  z-index: 4;
  opacity: 1;
  filter: saturate(1.02) brightness(1.02);
  transform: translate3d(-50%, -1.5%, 0) scale(1);
}
.hero-product[data-slot="left"] {
  z-index: 2;
  opacity: 0.48;
  filter: saturate(0.72) brightness(0.68);
  transform: translate3d(-145%, 12%, 0) scale(0.68);
}
.hero-product[data-slot="right"] {
  z-index: 2;
  opacity: 0.48;
  filter: saturate(0.72) brightness(0.68);
  transform: translate3d(45%, 12%, 0) scale(0.68);
}
.hero-product::before {
  top: 49%;
  width: 82%;
  aspect-ratio: 0.73;
  border-color: color-mix(in srgb, currentColor 38%, transparent);
  background:
    linear-gradient(139deg, color-mix(in srgb, currentColor 28%, white 4%), transparent 36% 71%, color-mix(in srgb, currentColor 10%, transparent));
  box-shadow: 0 1.8rem 6rem color-mix(in srgb, currentColor 16%, transparent);
  clip-path: polygon(11% 0, 87% 4%, 100% 21%, 93% 89%, 76% 100%, 3% 92%, 0 18%);
  opacity: 0.6;
  transform: translate(-50%, -50%);
  transition: opacity 220ms ease, transform 420ms var(--ease-out);
}
.hero-product[data-slot="active"]::before { opacity: 0.92; transform: translate(-50%, -50%) scale(1.02); }
.hero-product__image { top: 0; bottom: 3.5rem; }
.hero-product__image img { filter: drop-shadow(0 2rem 2.2rem rgba(0,0,0,0.62)); }
.hero-product__label { bottom: 0.2rem; min-height: 2.7rem; align-items: center; padding: 0.55rem 0.15rem 0; }
.hero-product__label b { font-size: clamp(1rem, 1.35vw, 1.5rem); }
.hero-product__label small { font-size: 0.45rem; }
.hero-product[data-slot="left"] .hero-product__label,
.hero-product[data-slot="right"] .hero-product__label { opacity: 0.72; }
.hero-product__index { top: 8%; background: rgba(5,8,7,0.78); backdrop-filter: blur(10px); }
.hero__active-label { display: none; }

.hero__active-label.sr-only {
  display: block;
}

.taste-map article b {
  border-radius: 0;
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
}

@media (max-width: 1180px) and (min-width: 721px) {
  .hero__copy { width: 43.5vw; }
  .hero__title { font-size: clamp(4rem, 6.35vw, 6.2rem); }
  .hero-products { width: 51%; }
  .hero-product { width: clamp(12rem, 17.2vw, 17rem); }
  .hero-product__label small { display: none; }
}

@media (min-width: 721px) and (max-height: 820px) {
  .hero { min-height: 49rem; }
  .hero__copy { margin-top: 1.6rem; }
  .hero__title { font-size: clamp(3.75rem, 5.85vw, 5.7rem); }
  .hero__dek { margin-top: 0.75rem; line-height: 1.42; }
  .hero__cta { padding-top: 0.45rem; padding-bottom: 0.45rem; }
  .hero-products { top: 8.9rem; bottom: 5.1rem; }
}

@media (max-width: 720px) {
  .hero { min-height: max(68rem, 100svh); padding-top: 5.25rem; }
  .hero__register span:last-child { max-width: 11rem; text-align: right; }
  .hero__copy { width: 100%; margin-top: 1.55rem; }
  .hero__eyebrow { margin-bottom: 0.75rem; font-size: 0.48rem; }
  .hero__title { max-width: 23rem; font-size: clamp(3.25rem, 15.25vw, 4.65rem); line-height: 0.75; }
  .hero__dek { max-width: 22rem; margin-top: 0.85rem; font-size: 0.78rem; line-height: 1.46; }
  .hero__cta { width: min(18rem, 86vw); }
  .hero-products {
    top: 34rem;
    right: 1rem;
    bottom: 5.1rem;
    left: 1rem;
    width: auto;
    height: auto;
  }
  .hero-products::after { top: 4%; right: 3%; bottom: 8%; left: 3%; }
  .hero-product { top: 0; bottom: 1.8rem; width: min(11.2rem, 43vw); }
  .hero-product[data-slot="active"] { transform: translate3d(-50%, -1%, 0) scale(1); }
  .hero-product[data-slot="left"] { transform: translate3d(-142%, 13%, 0) scale(0.64); }
  .hero-product[data-slot="right"] { transform: translate3d(42%, 13%, 0) scale(0.64); }
  .hero-product__image { bottom: 3rem; }
  .hero-product__label { min-height: 2.25rem; }
  .hero-product__label b { font-size: 0.92rem; }
  .hero-product__label small { display: none; }
  .hero-product__index { top: 7%; width: 1.85rem; height: 1.85rem; font-size: 0.52rem; }
    }

@media (prefers-reduced-motion: reduce) {
  .hero-product { transition: opacity 180ms ease, filter 180ms ease; }
}

/* Refinement 06 — Celestial Conservatory. Colour is architecture, motion sells the range. */
body { background: #070709; }
.site-header {
  border-color: rgba(255,243,219,.22);
  background: linear-gradient(110deg, rgba(4,5,37,.88), rgba(40,16,87,.75));
  box-shadow: 0 1.2rem 4rem rgba(3,3,28,.22);
  backdrop-filter: blur(18px) saturate(1.35);
}
/* SCROLLED IS THE HERO'S BAR TOO. This was rgba(5,5,39,.9): a navy slab that took
   over the moment the page moved 24px, so every screen after the hero read under
   a coloured band while the hero itself got neutral glass (the rest state,
   .site-header:not(.is-scrolled), further down). Same surface, border tone and
   blur as that rest state, so the bar never changes colour as the page is read.
   Background, border and backdrop only -- box-shadow stays with the condensed
   rule in commerce.css, which drops it deliberately when the bar goes flush. */
.site-header.is-scrolled {
  border-color: rgba(242, 233, 219, 0.13);
  background: rgba(13, 13, 17, 0.88);
  backdrop-filter: blur(1.4rem) saturate(1.15);
}
.bag-button span { background: var(--chlorophyll); color: #071d24; }

.hero {
  --hero-accent: var(--sku-jamun-bright);
  color: var(--moon);
  background: var(--void);
}
.hero__fluid,
.hero__particles,
.hero__fallback,
.hero__grid,
.hero__noise { pointer-events: none; }
body[data-sku="mango"] .hero { --hero-accent: var(--nectar); }
body[data-sku="jamun"] .hero { --hero-accent: var(--sku-jamun-bright); }
body[data-sku="pineapple"] .hero { --hero-accent: var(--chlorophyll); }
.hero__fallback {
  background:
    radial-gradient(ellipse at 82% 20%, color-mix(in srgb, var(--hero-accent) 62%, transparent), transparent 32%),
    radial-gradient(ellipse at 70% 72%, rgba(46,233,255,.36), transparent 33%),
    radial-gradient(ellipse at 25% 84%, rgba(255,75,22,.3), transparent 34%),
    linear-gradient(132deg, #050733 0 34%, #1b1a9e 54%, #4a0d92 76%, #043352 100%);
}
.hero__fluid { opacity: .94; filter: saturate(1.48) contrast(1.12) brightness(1.13); mix-blend-mode: screen; }
.hero__particles { opacity: .72; mix-blend-mode: screen; }
.hero__grid { opacity: .12; background-size: 8rem 8rem; }
/* Legibility scrim behind the hero copy. Tinted to the field rather than a
   neutral black so it darkens without desaturating the fluid sim behind it. */
.hero::after {
  background:
    linear-gradient(90deg, rgba(7,2,26,.9) 0 24%, rgba(7,2,26,.66) 38%, rgba(7,2,26,.06) 61%, rgba(7,2,26,.32)),
    linear-gradient(180deg, rgba(7,2,26,.14), transparent 52%, rgba(4,2,10,.8));
}
.hero__eyebrow { color: var(--electric-ice); }
.hero__dek { color: rgba(255,243,219,.82); }
.hero__actions { display: grid; width: min(32rem,100%); grid-template-columns: minmax(12rem,1.3fr) minmax(9rem,1fr); gap: .65rem; pointer-events: auto; }
.hero__cta,
.hero__stir {
  min-height: 3.45rem;
  border: 1px solid color-mix(in srgb, var(--hero-accent) 68%, rgba(255,255,255,.2));
  color: var(--moon);
  background: rgba(9,8,58,.54);
  clip-path: polygon(0 0,92% 0,100% 28%,100% 100%,8% 100%,0 72%);
  backdrop-filter: blur(12px);
}
.hero__cta { width: auto; padding: .55rem .35rem .55rem .9rem; border-top: 1px solid color-mix(in srgb, var(--hero-accent) 68%, rgba(255,255,255,.2)); border-bottom: 1px solid color-mix(in srgb, var(--hero-accent) 68%, rgba(255,255,255,.2)); }
.hero__stir.is-stirring i { animation: stir-signal 680ms var(--ease-out); }
.hero__cta:active,
.hero__stir:active { transform: scale(.975); }

.hero-products {
  overflow: visible;
  border: 0;
  clip-path: none;
  isolation: isolate;
}
.hero-products::before {
  inset: 1% -4% 3%;
  background:
    radial-gradient(ellipse at 50% 45%, color-mix(in srgb, var(--hero-accent) 36%, transparent), transparent 47%),
    conic-gradient(from 28deg at 50% 50%, rgba(111,231,255,.13), transparent 18%, color-mix(in srgb, var(--hero-accent) 19%, transparent) 38%, transparent 56%, rgba(66,242,155,.12) 76%, transparent);
  clip-path: polygon(50% 0,63% 16%,91% 8%,83% 36%,100% 54%,79% 68%,86% 96%,57% 84%,42% 100%,30% 79%,2% 88%,15% 59%,0 42%,24% 31%,18% 6%,43% 17%);
  filter: blur(1rem);
  opacity: .9;
}
.hero-products::after {
  top: 5%; right: 4%; bottom: 9%; left: 4%;
  border: 0;
  background: linear-gradient(112deg, transparent 12%, rgba(111,231,255,.12) 12.2% 33%, transparent 33.2% 61%, color-mix(in srgb, var(--hero-accent) 16%, transparent) 61.2% 77%, transparent 77.2%);
  clip-path: polygon(8% 0,100% 13%,91% 87%,34% 100%,0 61%);
}
.hero-product { bottom: 5.2rem; }
.hero-product::before {
  border: 0;
  background: linear-gradient(145deg, color-mix(in srgb, currentColor 42%, white 8%), color-mix(in srgb, currentColor 5%, transparent) 58%, color-mix(in srgb, currentColor 31%, transparent));
  box-shadow: 0 2.6rem 8rem color-mix(in srgb, currentColor 28%, transparent);
  clip-path: polygon(50% 0,73% 12%,97% 8%,89% 38%,100% 61%,76% 73%,68% 100%,43% 85%,15% 96%,20% 66%,0 48%,25% 30%);
  filter: blur(.3px);
}
.hero-product[data-slot="active"] { filter: saturate(1.18) brightness(1.08); }
.hero-product[data-slot="left"],
.hero-product[data-slot="right"] { opacity: .58; filter: saturate(.84) brightness(.74); }
.hero-product__index { border: 1px solid currentColor; background: rgba(4,4,36,.6); clip-path: polygon(50% 0,100% 50%,50% 100%,0 50%); }
.hero-products__controls {
  position: absolute;
  z-index: 9;
  right: 9%;
  bottom: .45rem;
  left: 9%;
  display: grid;
  grid-template-columns: 3rem 1fr 3rem;
  height: 3rem;
  gap: .4rem;
  pointer-events: auto;
}
.hero-products__controls button {
  border: 1px solid rgba(255,243,219,.34);
  color: var(--moon);
  background: rgba(4,4,38,.72);
  clip-path: polygon(0 0,88% 0,100% 25%,100% 100%,12% 100%,0 75%);
  backdrop-filter: blur(10px);
  cursor: pointer;
  pointer-events: auto;
  transition: color 180ms ease, background 180ms ease, transform 120ms var(--ease-out);
}
.hero-products__controls button:nth-child(2) { display: flex; align-items: center; justify-content: space-between; padding: 0 1rem; font: 500 .53rem/1 var(--font-ui); letter-spacing: .14em; text-transform: uppercase; }
.hero-products__controls button:hover { color: var(--void-deep); background: var(--hero-accent); }
.hero-products__controls button:active { transform: scale(.95); }
.hero-products__progress { position: absolute; z-index: 10; right: 9%; bottom: 3.75rem; left: 9%; height: 2px; overflow: hidden; background: rgba(255,255,255,.14); }
.hero-products__progress i { display: block; width: 100%; height: 100%; background: linear-gradient(90deg,var(--electric-ice),var(--hero-accent),var(--chlorophyll)); transform: scaleX(0); transform-origin: left; }
.hero-products__proof { position: absolute; z-index: 6; top: 1.3rem; right: 6%; display: flex; gap: 1.1rem; margin: 0; color: rgba(255,243,219,.76); font-size: .48rem; letter-spacing: .12em; text-transform: uppercase; }
.hero-products__proof span { padding-left: .65rem; border-left: 2px solid var(--hero-accent); }

.velocity-strip { color: var(--void-deep); background: var(--chlorophyll); }
/* Proof was a light `--signal` band in Refinement 06 and was later flipped to a
   dark field (see the v2 block near the end of this file) — but these ink-on-
   light overrides were never flipped with it, leaving the register and metric
   labels as near-black text on a near-black background. Corrected to the field. */
.proof .section-register { border-color: rgba(10, 4, 23, 0.3); color: rgba(10, 4, 23, 0.78); }
.proof__metrics article span,
.proof__metrics article small { color: rgba(255,247,230,.6); }
/* .philosophy / .first-batch fields are defined once, in the v2 block at the
   end of this file. */

@keyframes stir-signal {
  0% { transform: scale(.7) rotate(-40deg); }
  58% { transform: scale(1.25) rotate(150deg); }
  100% { transform: scale(1) rotate(180deg); }
}

@media (max-width: 720px) {
  .hero { min-height: max(72rem,100svh); }
  .hero__actions { width: 100%; grid-template-columns: 1.15fr .9fr; }
  .hero__cta,
  .hero__stir { min-height: 3.1rem; }
      .hero-products { top: 37.1rem; bottom: 5.1rem; }
  .hero-product { bottom: 4.9rem; }
  .hero-products__controls { right: 3%; left: 3%; grid-template-columns: 2.75rem 1fr 2.75rem; }
  .hero-products__progress { right: 3%; left: 3%; }
  .hero-products__proof { top: .55rem; right: 3%; left: 3%; justify-content: center; gap: .7rem; font-size: .39rem; }
  .hero-products__proof span { padding-left: .42rem; }
  }

@media (prefers-reduced-motion: reduce) {
  .hero-products__progress { display: none; }
  .hero__stir.is-stirring i { animation: none; }
}

/* Refinement 07 — Editorial commerce, intrinsic layout and native tactility. */
.sr-only {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important;
  border: 0 !important;
}

.ui-icon {
  width: 1.15rem;
  height: 1.15rem;
  flex: 0 0 auto;
  overflow: visible;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.ui-icon .icon-play { fill: currentColor; stroke: none; }
[data-hero-autoplay][aria-pressed="true"] .icon-play,
[data-hero-autoplay][aria-pressed="false"] .icon-pause { display: none; }

.site-header {
  border: 0;
  border-radius: 0;
  box-shadow: 0 .75rem 2.5rem rgba(2,3,28,.16);
}
.wordmark,
.bag-button,
.menu-button { min-height: 44px; }

.hero {
  container: astrove-hero / inline-size;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 31rem), 1fr));
  grid-template-rows: auto 1fr;
  gap: clamp(1.5rem, 3.5vw, 4.5rem);
  align-items: center;
  min-height: max(100svh, 48rem);
  padding: clamp(6.6rem, 9vw, 8rem) var(--gutter) clamp(2rem, 4vw, 4.5rem);
  overflow: clip;
}
.hero__register { grid-column: 1 / -1; align-self: start; }
.hero__copy {
  align-self: center;
  width: auto;
  max-width: 47rem;
  margin: clamp(1.25rem, 3svh, 3rem) 0;
  pointer-events: none;
}
.hero__eyebrow { margin-bottom: clamp(.85rem, 1.5vw, 1.4rem); }
.hero__title {
  max-width: 9.2ch;
  font-family: var(--font-display);
  font-size: clamp(4.3rem, 7vw, 8.8rem);
  font-weight: 400;
  line-height: .78;
  letter-spacing: -.058em;
  text-transform: none;
  text-wrap: balance;
}
.hero__title-line { display: block; overflow: hidden; padding: .04em .05em .12em 0; }
.hero__title-word { display: block; }
.hero__title-line:nth-child(2) .hero__title-word,
.hero__title-line:nth-child(4) .hero__title-word {
  color: var(--hero-accent);
  font-style: italic;
  text-shadow: 0 0 2.8rem color-mix(in srgb, var(--hero-accent) 24%, transparent);
  transition: color 240ms ease;
}
.hero__dek {
  max-width: 58ch;
  margin: clamp(1.35rem, 2.4vw, 2.25rem) 0 1.1rem;
  color: rgba(255,243,219,.78);
  font-size: clamp(.92rem, 1.05vw, 1.05rem);
  line-height: 1.65;
  text-wrap: pretty;
}
.hero__actions {
  display: flex;
  width: min(34rem,100%);
  grid-template-columns: none;
  flex-wrap: wrap;
  gap: .9rem 1.25rem;
  align-items: center;
}
.hero__cta {
  display: flex;
  width: min(19rem,100%);
  min-height: 3.65rem;
  align-items: center;
  justify-content: space-between;
  padding: .7rem .8rem .7rem 1.1rem;
  border: 0;
  color: var(--void-deep);
  background: var(--hero-accent);
  box-shadow:
    0 .2rem .3rem rgba(2,3,28,.18),
    0 1.1rem 3rem color-mix(in srgb, var(--hero-accent) 22%, transparent),
    inset 0 1px rgba(255,255,255,.36);
  clip-path: polygon(0 0,94% 0,100% 28%,100% 100%,6% 100%,0 72%);
  font-size: .68rem;
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
  transition: transform 160ms var(--ease-out), background-color 200ms ease, box-shadow 200ms ease;
}
.hero__process-link {
  display: inline-flex;
  min-height: 2.75rem;
  align-items: center;
  gap: .55rem;
  pointer-events: auto;
  color: rgba(255,243,219,.72);
  font-size: .64rem;
  letter-spacing: .08em;
  transition: color 180ms ease, transform 160ms var(--ease-out);
}
.hero__process-link .ui-icon { width: .85rem; height: .85rem; }
.hero__cta:active,
.hero__process-link:active { transform: scale(.97); }

.hero-products {
  position: relative;
  inset: auto;
  align-self: stretch;
  width: auto;
  min-width: 0;
  min-height: clamp(33rem, 67svh, 46rem);
  overflow: visible;
}
.hero-products::before {
  inset: 4% -2% 6%;
  opacity: .72;
  filter: blur(1.35rem);
}
.hero-products::after { opacity: .38; }
.hero-product {
  top: 0;
  bottom: 5.4rem;
  width: clamp(12.25rem, 17vw, 20.5rem);
  transition: transform 560ms var(--ease-in-out), opacity 240ms ease, filter 240ms ease;
}
.hero-product::before { opacity: .36; filter: blur(.2px); }
.hero-product[data-slot="active"]::before { opacity: .62; }
.hero-product__image {
  transition: transform 120ms linear;
  will-change: transform;
}
.hero-product__image::after {
  position: absolute;
  z-index: 0;
  right: 20%;
  bottom: 2.5%;
  left: 20%;
  height: 8%;
  content: "";
  border-radius: 50%;
  background: radial-gradient(ellipse, rgba(2,3,28,.76), rgba(2,3,28,.24) 52%, transparent 74%);
  filter: blur(.65rem);
  transform: scaleX(.92);
  transition: opacity 240ms ease, transform 560ms var(--ease-in-out);
}
.hero-product__image img {
  position: relative;
  z-index: 1;
  filter:
    drop-shadow(0 .35rem .25rem rgba(2,3,28,.22))
    drop-shadow(0 1.8rem 1.45rem rgba(2,3,28,.42))
    drop-shadow(0 4rem 5rem rgba(2,3,28,.3));
}
.hero-product[data-slot="active"] .hero-product__image::after { opacity: 1; transform: scaleX(1.08); }
.hero-product[data-slot="left"] .hero-product__image::after,
.hero-product[data-slot="right"] .hero-product__image::after { opacity: .42; transform: scaleX(.7); }
.hero-product__label { border-top: 0; }
.hero-products__controls {
  right: 8%;
  left: 8%;
  height: 3.15rem;
  grid-template-columns: 3.15rem minmax(8rem,1fr) 3.15rem;
}
.hero-products__controls button {
  min-width: 44px;
  min-height: 44px;
  border: 0;
  color: rgba(255,243,219,.84);
  background: rgba(4,4,38,.56);
  box-shadow: inset 0 1px rgba(255,255,255,.14), 0 .8rem 2.4rem rgba(2,3,28,.18);
  clip-path: none;
  transition: transform 160ms var(--ease-out), color 180ms ease, background-color 180ms ease;
}
.hero-products__controls button:active { transform: scale(.97); }
.hero-products__controls button:nth-child(2) { justify-content: center; gap: .75rem; }
.hero-products__controls button:nth-child(2) .ui-icon { width: .8rem; height: .8rem; }
.hero-products__progress { right: 8%; left: 8%; }
.hero-products__proof { right: 4%; gap: .85rem; }

.product-lenses .ui-icon { width: 1rem; height: 1rem; }
.product-lenses button,
.add-button,
.batch-form button,
.bag__summary button {
  transition: transform 160ms var(--ease-out), color 180ms ease, background-color 180ms ease, box-shadow 180ms ease;
}
.product-lenses button:active,
.add-button:active,
.batch-form button:active,
.bag__summary button:active { transform: scale(.97); }

@media (hover: hover) and (pointer: fine) {
  .hero__cta:hover { transform: translate3d(0,-2px,0); box-shadow: 0 .25rem .35rem rgba(2,3,28,.2), 0 1.5rem 3.6rem color-mix(in srgb,var(--hero-accent) 29%,transparent), inset 0 1px rgba(255,255,255,.42); }
  .hero__process-link:hover { color: var(--moon); transform: translate3d(.18rem,0,0); }
  .hero-products__controls button:hover { color: var(--void-deep); background: var(--hero-accent); }
}

@container astrove-hero (max-width: 64rem) {
  .hero__title { max-width: 10ch; font-size: clamp(3.65rem, 13.5cqi, 7rem); }
  .hero__copy { max-width: 42rem; margin-bottom: 0; }
  .hero-products { min-height: clamp(35rem, 72svh, 44rem); }
  .hero-product { width: min(12rem,43cqi); }
  .hero-products__proof { top: .5rem; right: 3%; left: 3%; justify-content: center; font-size: .4rem; }
  .hero__register span:last-child { max-width: 12rem; text-align: right; }
}

@media (max-width: 38rem) {
  .hero { gap: 1rem; padding-top: 5.8rem; }
  .hero__title { font-size: clamp(3.2rem, 16.4vw, 4.9rem); }
  .hero__dek { font-size: .84rem; line-height: 1.58; }
  .hero__actions { align-items: stretch; }
  .hero__cta { width: 100%; }
  .hero__process-link { justify-content: center; }
  .hero-products { min-height: clamp(32rem, 68svh, 37rem); }
  .hero-product { bottom: 5.1rem; width: min(11rem,44vw); }
  .hero-products__controls { right: 2%; left: 2%; grid-template-columns: 3rem 1fr 3rem; }
  .hero-products__progress { right: 2%; left: 2%; }
}

@media (prefers-reduced-motion: reduce) {
  .hero-product__image { transition: none; will-change: auto; }
  .hero__cta,
  .hero__process-link,
  .hero-products__controls button { transition: color 180ms ease, background-color 180ms ease, opacity 180ms ease; }
}

/* Refinement 08 — Cosmic Garden Prime / independent provenance chapters */
/* PAGE CANVAS - neutral, and free of decorative paint.

   The body carried two full-bleed radials under every section: an ultraviolet
   at 13% and an acid lime at 6%. They are the page itself tinted violet and
   green, which is exactly the themed feeling this pass removes, and neither
   communicated anything. #070709 matches the hero ground, so the hero no
   longer steps down into a different-coloured page when it ends.

   Every section below paints its own ground, so this shows only in the seams
   between them. */
html { background: #070709; }
body {
  color: var(--moon);
  background: #070709;
}

.site-header {
  border-color: rgba(255,247,230,.15);
  background: linear-gradient(112deg, rgba(4,2,10,.94), rgba(37,5,68,.86) 60%, rgba(4,16,46,.88));
  box-shadow: 0 1.25rem 4rem rgba(0,0,0,.32), inset 0 1px rgba(255,255,255,.05);
  backdrop-filter: blur(1.25rem) saturate(1.6);
}

/* --------------------------------------------------------------------------
   THE NAV AT REST IS THE HERO'S NAV.

   `.is-scrolled` is added at scrollTop > 24, so the resting state is precisely
   and only the state the bar is in while it floats over the hero photograph.
   The scrolled state now carries the same surface (.site-header.is-scrolled,
   above), so the bar holds one treatment from the hero to the footer.

   What it replaces: a 112deg three-stop gradient running rgba(4,2,10,.94) into
   rgba(37,5,68,.86) into rgba(4,16,46,.88) — near-black to violet to navy,
   across a 1300px bar, sitting over a photograph that is itself changing colour
   every five seconds. Two colour systems moving at once, and the wordmark
   living in whichever one happened to be behind it.

   A near-black translucent surface instead. The bar stops being a coloured
   object and becomes a stable instrument the fruit moves behind, which is the
   only way one wordmark can hold authority across three plates. The backdrop
   blur and saturate still let the photograph register through it, so it reads
   as glass over the image rather than as a black band across it.

   0.88, not 0.82. Measured across all three plates: the old gradient held the
   wordmark at 20.3:1 and a first attempt at 0.82 gave back 19.9:1 — the
   gradient's near-black END happened to sit exactly under the wordmark, so
   flattening it to one tone cost a little there even while it gained
   everywhere else. 0.88 restores it. The point of this change was never a
   contrast number; it is that the bar now holds ONE tone across its width
   instead of three, so nothing in it shifts as the fruit changes.
   -------------------------------------------------------------------------- */
.site-header:not(.is-scrolled) {
  border-color: rgba(242, 233, 219, 0.13);
  /* #0d0d11 - the hero system's surface black, at 0.88 so the photograph
     still registers through it. It was rgba(6,3,12), which was near-black
     but violet-cast; the bar is the one chrome element the customer sees
     against every plate, so it is the last place a hue should hide. */
  background: rgba(13, 13, 17, 0.88);
  box-shadow: 0 1.25rem 4rem rgba(0, 0, 0, 0.38), inset 0 1px rgba(255, 255, 255, 0.045);
  backdrop-filter: blur(1.4rem) saturate(1.15);
}

/* --------------------------------------------------------------------------
   BRAND ANCHOR

   The wordmark is the ONLY place ASTROVE is stated above the fold — the hero's
   second "ASTROVE" eyebrow is gone, and it is not coming back as a patch for a
   legibility problem. So the mark itself has to hold against every plate the
   hero can rotate to, including the pale-stone ground of the pineapple and
   mango masters, and it has to hold at the top of a phone where the photograph
   runs full-bleed directly under the bar.

   Three layers, cheapest first:

   1. A local scrim under the mark only. The header gradient is already dark at
      its left end, but it is one flat ramp across a 1300px bar; this deepens
      just the ~13rem the wordmark occupies, which is the only region that has
      to carry a logo rather than 0.72rem nav labels.
   2. A drop-shadow on the glyphs, so the white letterforms keep a dark edge of
      their own. This is the layer that survives if the backdrop-filter is
      unsupported or the bar is ever made more transparent.
   3. Enough contrast that the mark does not depend on either — the scrim floor
      is opaque enough to read as intentional shading rather than a haze.
   -------------------------------------------------------------------------- */
.wordmark {
  position: relative;
  isolation: isolate;
}
.wordmark::before {
  content: "";
  position: absolute;
  /* Beyond the mark's own box on every side, so the falloff happens off the
     glyphs rather than across them — but never past the bar itself. The left
     inset is the header's own padding-left and the vertical insets stop short
     of a 4.4rem bar containing a 44px target, so this cannot draw a dark
     rectangle hanging outside the chrome. */
  inset: -0.72rem -2.4rem -0.72rem -1.1rem;
  z-index: -1;
  background: linear-gradient(96deg, rgba(7, 7, 9, 0.82) 8%, rgba(7, 7, 9, 0.62) 52%, rgba(7, 7, 9, 0) 100%);
  pointer-events: none;
}
.wordmark img {
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.72)) drop-shadow(0 0 10px rgba(0, 0, 0, 0.45));
}
/* The phone bar is shorter (3.9rem, then 3.55rem) and has less padding-left
   (0.75rem), so the scrim has to be re-cut to the smaller box — the desktop
   insets would hang outside it on all three sides. This is also the breakpoint
   where the scrim matters most: the macro plate runs full-bleed to the top of
   the viewport directly beneath the bar. */
@media (max-width: 860px) {
  .wordmark::before { inset: -0.34rem -1.9rem -0.34rem -0.75rem; }
}

/* The TOP STRIP that lived here is retired. It went through two corrections --
   a six-stop rainbow on the track, then all three fruit accents across the
   fill -- and the remaining version was still a bar that filled: graphite for
   88% of its length, warming to the active SKU in the last 12%. What replaced
   it is ASTROVE / TRACE in styles/trace.css and js/trace.js, which reports the
   reader's position with a moving head rather than a filled ribbon. */

/* Hero — deep space that is actually violet-blue, not black. The fluid sim
   sits on top in `screen` blend, so a hued floor lets the dye read as light
   emitted INTO a colour rather than smeared over a void. */
.hero {
  background:
    linear-gradient(105deg, rgba(4,2,10,.22), transparent 44%),
    radial-gradient(ellipse at 78% 24%, rgba(255,31,125,.3), transparent 34%),
    radial-gradient(ellipse at 84% 72%, rgba(46,233,255,.22), transparent 38%),
    radial-gradient(ellipse at 16% 78%, rgba(122,45,255,.26), transparent 40%),
    linear-gradient(145deg, #0c0330 0%, var(--field-nebula) 44%, #041f3c 100%);
}
.hero__grid { opacity: .14; mix-blend-mode: screen; }
.hero__eyebrow { color: var(--hero-accent, var(--chlorophyll)); }
.hero__cta {
  color: var(--void-deep);
  background: linear-gradient(110deg, var(--moon), var(--moon-soft));
  box-shadow: 0 .25rem .35rem rgba(0,0,0,.3), 0 1.5rem 4rem color-mix(in srgb, var(--hero-accent) 18%, transparent), inset 0 1px rgba(255,255,255,.72);
}
.hero-products::before {
  background: conic-gradient(from 210deg, transparent, color-mix(in srgb,var(--hero-accent) 26%,transparent), rgba(120,231,255,.14), transparent 68%);
}
.hero-products__controls button { background: rgba(21,10,40,.72); }
/* Livery ribbon. Every stop is held above ~0.26 relative luminance so the
   void-deep type clears 5:1 across the whole sweep — magenta sits out here
   and gets its own moments further down the page. */
/* VELOCITY STRIP — the last loud surface between the hero and the engine.

   It was a five-stop ribbon (acid lime -> cyan -> ivory -> amber -> blaze)
   with a lime glow above it and an orange glow below, running full width
   between the SKU cards and THE FORMULA. Against a storefront that is now
   near-black end to end it was the single brightest object on the page, and
   it sat exactly on the seam this whole sequence of passes has been closing.

   Same strip, same marquee, same copy — repainted onto the storefront system:
   a graphite band, ivory type, hairlines top and bottom. It still separates
   the two sections; it just stops shouting to do it. */
.velocity-strip {
  color: var(--store-secondary, #b8b0a3);
  background: var(--store-surface, #0d0d11);
  border-top: 1px solid var(--store-hairline, #26212a);
  border-bottom: 1px solid var(--store-hairline, #26212a);
  box-shadow: none;
}
/* The diamond separators were carrying the accent; one quiet gold cue is
   enough to keep the rhythm readable. */
.velocity-strip i { color: var(--store-gold, #e7a51a); opacity: 0.65; }

/* Proof — the first committed colour field after the hero. Indigo, and loud
   enough to read as a block of violet-blue rather than another dark stretch. */
/* Was ultraviolet at 42% over an indigo field, with a lime and a magenta
   radial on top. Neutral canvas now; the section carries no colour of its
   own. */
/* THE ONE LIGHT BAND ON THE PAGE. Measured before this change every section
   of the document sat between luminance 3 and 14 out of 255 across 17.9
   screens -- one tonal value end to end, which is what made a page this long
   feel undifferentiated. --signal is 67: a 6.01:1 step against the #070709 on
   either side of it, and the only place the reader can feel they arrived
   somewhere. The band belongs to the section that states a belief, not to the
   one that captions a transformation.

   EVERY COLOUR INSIDE IT INVERTS. White on --signal is only 3.35:1, which is
   fine for a display headline and a fail for anything smaller, so the headline
   is the one white thing here and all supporting type is --void based. */
.proof {
  color: #fff;
  background: var(--signal);
}
/* Grain: a flat saturated field is exactly what bands on an OLED phone. */
.proof::before {
  position: absolute;
  inset: 0;
  z-index: 0;
  content: "";
  pointer-events: none;
  opacity: 0.5;
  background-image: radial-gradient(rgba(10, 4, 23, 0.05) 1px, transparent 1px);
  background-size: 3px 3px;
}
/* "OUR BEST AD." was acid lime. Ivory: the line is already the largest type
   on the page and does not need a second way of shouting. */
.proof h2 em { color: var(--store-ivory, #f2e9db); }
.proof__copy > p { color: rgba(255,247,230,.82); }
/* The lime haze behind the three figures is gone; they sit on the section
   surface with graphite hairlines and nothing else. */
.proof__metrics article {
  border-color: var(--store-hairline, #26212a);
  background: var(--store-surface, #0d0d11);
  box-shadow: none;
}

.cosmic-collection {
  position: relative;
  overflow: clip;
  color: var(--moon);
  background: var(--void-deep);
  isolation: isolate;
}
.cosmic-collection::before {
  position: absolute;
  z-index: -1;
  inset: 0;
  content: "";
  pointer-events: none;
  background:
    linear-gradient(rgba(255,255,255,.027) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.027) 1px, transparent 1px);
  background-size: clamp(5rem,9vw,9.5rem) clamp(5rem,9vw,9.5rem);
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(to bottom, transparent, #000 6%, #000 94%, transparent);
}
.collection-manifesto {
  position: relative;
  /* The rail used to be absolute at top:8vw while the grid was centred in a
     58svh box: two independent vertical systems, so the headline floated far
     below its own section number. The rail is now in flow and the content
     stacks straight after it, which is what locks rail + headline + copy to
     one grid. min-height only guards the short-content case now. */
  min-height: min(44svh, 32rem);
  display: grid;
  align-content: start;
  padding-top: clamp(3.4rem, 5vw, 5.4rem);
  padding-bottom: clamp(3.4rem, 5vw, 5.4rem);
  background:
    radial-gradient(circle at 13% 22%, rgba(255,31,125,.4), transparent 26rem),
    radial-gradient(circle at 88% 68%, rgba(46,233,255,.22), transparent 30rem),
    linear-gradient(135deg, #0b0320, var(--field-orchid) 52%, #04203a);
}
.collection-manifesto::after {
  position: absolute;
  right: -8vw;
  bottom: -1px;
  width: min(49vw,46rem);
  height: min(38vw,31rem);
  content: "";
  opacity: .72;
  background: linear-gradient(135deg, rgba(201,255,43,.34), rgba(46,233,255,.08) 38%, rgba(255,31,125,.34));
  clip-path: polygon(8% 22%, 46% 0, 100% 15%, 82% 74%, 42% 100%, 0 68%);
}
.collection-manifesto .section-register { margin-bottom: clamp(1.4rem, 2.4vw, 2.4rem); }
.collection-manifesto__grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0,1.45fr) minmax(16rem,.55fr);
  gap: clamp(3rem,8vw,10rem);
  align-items: end;
}
.collection-manifesto h2 {
  max-width: 12ch;
  margin: 0;
  font: 400 clamp(3.8rem,8.7vw,9.6rem)/.82 var(--font-ui);
  letter-spacing: -.072em;
}
.collection-manifesto h2 em {
  display: inline-block;
  color: var(--chlorophyll);
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: -.04em;
}
.collection-manifesto__grid > p {
  max-width: 31rem;
  margin: 0 0 .65rem;
  color: rgba(255,247,230,.73);
  font-size: clamp(1rem,1.35vw,1.3rem);
  line-height: 1.58;
}

.specimen-story {
  --specimen-accent: var(--sku-jamun-bright);
  --specimen-secondary: var(--nova);
  --specimen-deep: var(--field-orchid);
  position: relative;
  display: grid;
  grid-template-columns: minmax(22rem,.92fr) minmax(28rem,1.08fr);
  gap: clamp(2rem,6vw,8rem);
  align-items: center;
  min-height: max(100svh,58rem);
  padding: clamp(3.6rem,5.4vw,6.4rem) var(--gutter);
  border-top: 1px solid rgba(255,247,230,.13);
  /* Each bay is tinted by its own fruit and falls off to that fruit's deep
     tone — not to a shared black. Accent washes are ~2x v1 so the hue is
     legible on a phone in daylight, which is where the mud showed worst. */
  background:
    radial-gradient(circle at 15% 28%, color-mix(in srgb,var(--specimen-accent) 34%,transparent), transparent 30rem),
    radial-gradient(circle at 88% 72%, color-mix(in srgb,var(--specimen-secondary) 26%,transparent), transparent 33rem),
    linear-gradient(136deg, var(--specimen-deep), color-mix(in srgb,var(--specimen-deep) 55%, var(--void-deep)) 64%, var(--void-deep));
  isolation: isolate;
}
.specimen-story--mango { --specimen-accent: var(--nectar); --specimen-secondary: var(--signal); --specimen-deep: var(--field-ember); }
.specimen-story--pineapple { --specimen-accent: var(--chlorophyll); --specimen-secondary: var(--verdant); --specimen-deep: var(--field-forest); }
/* The second bay reverses, whichever fruit is in it. Mango now leads the
   collection, so the flip moved from --mango to --jamun. */
.specimen-story--jamun .specimen-story__visual { order: 2; }
.specimen-story--jamun .specimen-story__content { order: 1; }
.specimen-story::after {
  position: absolute;
  z-index: -1;
  inset: 0;
  content: "";
  pointer-events: none;
  opacity: .55;
  background: linear-gradient(115deg, transparent 0 47%, color-mix(in srgb,var(--specimen-accent) 52%,transparent) 47.08%, transparent 47.18% 100%);
}
.specimen-story__visual {
  position: relative;
  /* Was clamp(38rem,58vw,57rem) = 742px at 1280 — a floor tall enough to hold
     the broken image box. With the pouch sizing correctly this only needs to
     hold the product plus its botanical plate. */
  min-height: clamp(26rem,40vw,38rem);
  display: grid;
  place-items: center;
  perspective: 80rem;
}
/* SPECIMEN SLIDE — replaces the arbitrary six-point polygon.
   The polygon failed on figure/ground: a saturated SKU field behind a pack of
   the same hue, so the product had nothing to read against and the plate labels
   washed out. Here the ground stays dark and the SKU colour lives in line-work
   and in ONE saturated disc behind the pack — the pack becomes the brightest,
   most saturated thing in its own container, which is the whole point.
   Vocabulary (graticule, reticle, crosshair, scale) matches BotanicalPlate,
   SpecimenPlate and Orrery, so the bays belong to the same authored system. */
.specimen-story__shape {
  position: absolute;
  z-index: 0;
  inset: 3% 1%;
  overflow: hidden;
  background:
    linear-gradient(var(--slide-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--slide-line) 1px, transparent 1px);
  background-size: clamp(2rem, 3.4vw, 2.9rem) clamp(2rem, 3.4vw, 2.9rem);
  /* Falls off toward the edges so the grid never reads as wallpaper. */
  -webkit-mask-image: radial-gradient(ellipse at 50% 44%, #000 26%, transparent 76%);
  mask-image: radial-gradient(ellipse at 50% 44%, #000 26%, transparent 76%);
  clip-path: none;
}
.specimen-story {
  --slide-line: color-mix(in srgb, var(--specimen-accent) 22%, transparent);
}
/* Anchored to .specimen-story__visual now (see BotanicalPlate.js) — outside
   the shape's clip-path, so nothing on the plate gets sliced. z-index sits
   between the shape and the pouch render (which is z-index 2). */
.botanical-plate {
  position: absolute;
  z-index: 1;
  right: 2%;
  /* Clears the taste-tag row, which is absolutely positioned at bottom: 7%.
     At bottom: 1.5% the plate's binomial and scale bar sat underneath it. */
  bottom: 13%;
  left: 2%;
  width: 96%;
  height: auto;
  opacity: .88;
  mix-blend-mode: normal;
  pointer-events: none;
}
.botanical-plate .plate-frame { opacity: .8; }
@media (max-width: 760px) {
  .botanical-plate { right: 2%; bottom: 2%; left: 2%; width: 96%; opacity: .7; }
}
/* Crosshair through the reticle centre. Two hairlines, masked so they fade
   before the edge rather than stopping dead. */
.specimen-story__shape::after {
  position: absolute;
  inset: 0;
  content: "";
  background:
    linear-gradient(90deg, transparent, color-mix(in srgb,var(--specimen-accent) 46%,transparent) 18% 82%, transparent),
    linear-gradient(color-mix(in srgb,var(--specimen-accent) 46%,transparent), color-mix(in srgb,var(--specimen-accent) 46%,transparent));
  background-size: 100% 1px, 1px 100%;
  background-position: 50% 44%, 50% 50%;
  background-repeat: no-repeat;
  -webkit-mask-image: radial-gradient(ellipse at 50% 44%, #000 30%, transparent 82%);
  mask-image: radial-gradient(ellipse at 50% 44%, #000 30%, transparent 82%);
}
/* Reticle: outer ring plus a dashed inner ring. Circles read as decisions
   where the old six-point polygon read as an accident. */
.specimen-story__shape i {
  position: absolute;
  top: 44%;
  left: 50%;
  width: min(88%, 35rem);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border: 1px solid color-mix(in srgb, var(--specimen-accent) 52%, transparent);
  border-radius: 50%;
  clip-path: none;
}
.specimen-story__shape i::after {
  position: absolute;
  inset: 13%;
  content: "";
  border: 1px dashed color-mix(in srgb, var(--specimen-accent) 30%, transparent);
  border-radius: 50%;
}
/* THE DISC. One saturated moment per bay, sitting behind the pack as a light
   source. This is what rescues Jamun — a dark pack needs something luminous
   behind it or it has no silhouette on a dark ground. */
.specimen-story__shape b {
  position: absolute;
  top: 44%;
  left: 50%;
  /* 58% put the disc at 291px behind a 276px pack — the glow cleared the
     silhouette by 7px a side and read as a rim, not a light source. At 72% it
     lands ~1.3x the pack across all three SKUs, so the light genuinely sits
     behind the object, and the reticle at 88% still rings clear of it. */
  width: min(72%, 28.5rem);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background:
    radial-gradient(circle at 50% 50%,
      var(--specimen-accent) 0 42%,
      color-mix(in srgb, var(--specimen-secondary) 82%, transparent) 68%,
      transparent 78%);
  clip-path: none;
  filter: saturate(1.12) blur(.4px);
  opacity: .9;
}
/* <picture> wrappers are layout-transparent: the product images are wrapped for
   AVIF/WebP negotiation, and display:contents keeps the <img> as the direct
   layout child so the sizing rules below are unaffected. */
/* FoilPass canvas. Sits above the product renders but below the controls, and
   is fully inert to input — the <button> nodes underneath must keep receiving
   every pointer and keyboard event. Decoration only. */
.foil-pass {
  position: absolute;
  z-index: 7;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  mix-blend-mode: screen;
}
@media (prefers-reduced-motion: reduce) { .foil-pass { display: none; } }

.specimen-story__visual picture,
.hero-product__image picture,
.process-engine__pin picture { display: contents; }

.specimen-story__visual img {
  position: relative;
  z-index: 2;
  /* Sized against the VIEWPORT, not the column. `.specimen-story--mango` swaps
     order, which puts its visual in the wider 1.08fr track — so a percentage
     width rendered mango 324px against 276px for the other two. Same asset,
     same box, different size. A viewport-relative width locks all three. */
  width: clamp(13rem, 22vw, 19.5rem);
  /* height:auto lets the intrinsic aspect drive the box.
     Previously only `max-height: 82%` was set, which resolves against a parent
     whose height this element is simultaneously determining — the box stretched
     to 276x1099 while the pouch drew at 276x425 inside it, leaving ~337px of
     letterboxed emptiness ABOVE AND BELOW the product and inflating the whole
     bay. The cap is now viewport-relative, so there is no circular dependency. */
  height: auto;
  max-height: min(58svh, 32rem);
  object-fit: contain;
  filter: drop-shadow(0 .6rem .3rem rgba(0,0,0,.22)) drop-shadow(0 2.3rem 2.8rem rgba(0,0,0,.48)) drop-shadow(0 5rem 6rem color-mix(in srgb,var(--specimen-accent) 19%,transparent));
  transform: translateZ(0);
  transition: transform 500ms var(--ease-out), filter 500ms ease;
}
.specimen-story__coordinate {
  position: absolute;
  z-index: 3;
  top: 9%;
  left: 0;
  color: rgba(255,247,230,.7);
  font-size: .6rem;
  letter-spacing: .19em;
  writing-mode: vertical-rl;
}
.specimen-story__visual > p {
  position: absolute;
  z-index: 3;
  right: 4%;
  bottom: 7%;
  left: 5%;
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin: 0;
  color: rgba(255,247,230,.76);
  font-size: .56rem;
  letter-spacing: .15em;
}
/* Flex column purely so the running order can be set here rather than in
   markup. The bay reads: name -> lead -> the numbers -> price and add-to-bag,
   and only then the provenance depth. Previously the commerce block sat below
   271px of Kew/ICAR citation, so on a 720px viewport you could see the product
   or you could see the price, never both. */
.specimen-story__content {
  position: relative;
  z-index: 2;
  max-width: 51rem;
  display: flex;
  flex-direction: column;
}
/* This column is a flex stack with explicit order on every child, so a child
   without one sorts to 0 and jumps above the product name. The recovery
   blocks are numbered here for that reason — without these four lines the
   price, the assurances and the buy button all render above the h3, which is
   exactly what shipped for one build and is why this comment exists. */
.specimen-story__index      { order: 1; }
.specimen-story__content h3 { order: 2; }
.specimen-story__lead       { order: 3; }
.specimen-story__spec       { order: 4; }
.specimen-story__assurance  { order: 5; }
.specimen-story__commerce   { order: 6; }
.product-lenses            { order: 7; }
.specimen-story__index {
  display: block;
  margin-bottom: clamp(1.1rem,2vw,1.8rem);
  color: var(--specimen-accent);
  font-size: .68rem;
  font-weight: 500;
  letter-spacing: .19em;
}
.specimen-story__content h3 {
  /* 6.5vw resolved to 83px at 1280 and set three lines at 211px, which pushed
     the price and add-to-bag 21px past the fold. 5.4vw keeps it unmistakably
     editorial at ~69px while bringing the whole commerce block inside one
     viewport — the point of the bay. */
  max-width: 12ch;
  margin: 0;
  color: var(--moon);
  font: 400 clamp(3rem,5.4vw,5.9rem)/.86 var(--font-ui);
  letter-spacing: -.068em;
}
.specimen-story__content h3 em {
  display: inline-block;
  color: var(--specimen-accent);
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: -.045em;
  text-shadow: 0 0 2.8rem color-mix(in srgb,var(--specimen-accent) 15%,transparent);
}
.specimen-story__lead {
  max-width: 42rem;
  margin: clamp(1.1rem,1.9vw,1.7rem) 0 clamp(1.4rem,2.4vw,2.1rem);
  color: rgba(255,247,230,.76);
  font-size: clamp(1.03rem,1.28vw,1.28rem);
  line-height: 1.62;
}
.provenance-ledger {
  display: grid;
  grid-template-columns: repeat(2,minmax(0,1fr));
  border-top: 1px solid rgba(255,247,230,.18);
  border-bottom: 1px solid rgba(255,247,230,.18);
}
.provenance-ledger article {
  /* was min-height: 14rem — a 224px floor against ~189px of real content, so
     every record carried ~35px of dead space it never needed. */
  min-height: 0;
  padding: 1.35rem 1.25rem 1.35rem 0;
  background: linear-gradient(135deg, rgba(255,255,255,.035), transparent 62%);
}
.provenance-ledger article + article { padding-left: 1.25rem; border-left: 1px solid rgba(255,247,230,.15); }
.provenance-ledger span,
.provenance-ledger a,
.specimen-story__facts dt {
  color: var(--specimen-accent);
  font-size: .58rem;
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
}
.provenance-ledger strong { display: block; margin: .55rem 0 .4rem; font: 400 1.6rem/1.02 var(--font-display); }
.provenance-ledger p { min-height: 0; margin: 0 0 .7rem; color: rgba(255,247,230,.58); font-size: .8rem; line-height: 1.5; }
.provenance-ledger a { display: inline-flex; min-height: 44px; align-items: center; gap: .5rem; color: rgba(255,247,230,.8); transition: color 180ms ease, transform 160ms var(--ease-out); }
.specimen-story__facts {
  display: grid;
  grid-template-columns: repeat(2,minmax(0,1fr));
  margin: 1.2rem 0 0;
}
.specimen-story__facts div { display: flex; justify-content: space-between; gap: 1rem; padding: .95rem 0; border-bottom: 1px solid rgba(255,247,230,.12); }
.specimen-story__facts div:nth-child(odd) { padding-right: 1.25rem; }
.specimen-story__facts div:nth-child(even) { padding-left: 1.25rem; border-left: 1px solid rgba(255,247,230,.12); }
.specimen-story__facts dd { margin: 0; color: var(--moon); font-size: .72rem; text-align: right; }
.specimen-story__actions { margin-top: 1.35rem; }
.specimen-story .product-lenses {
  display: grid;
  grid-template-columns: repeat(4,minmax(0,1fr));
  border: 1px solid rgba(255,247,230,.19);
  background: rgba(255,255,255,.025);
}
.specimen-story .product-lenses button {
  min-height: 3.7rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .6rem;
  padding: 0 .8rem;
  border: 0;
  border-right: 1px solid rgba(255,247,230,.14);
  color: rgba(255,247,230,.76);
  background: transparent;
  font-size: .6rem;
  letter-spacing: .09em;
  text-transform: uppercase;
}
.specimen-story .product-lenses button:last-child { border-right: 0; }
.specimen-story__commerce { display: grid; grid-template-columns: minmax(7rem,.28fr) minmax(14rem,.72fr); align-items: stretch; margin-top: .9rem; }
.specimen-story__commerce > span { display: grid; align-items: center; font: 400 clamp(2.4rem,4vw,4rem)/1 var(--font-display); }
.specimen-story .add-button {
  min-height: 4.4rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 0 0 1.3rem;
  border: 0;
  color: var(--void-deep);
  /* THE BUY CONTROL IS THE BRIGHTEST THING IN THE BAY. It was a gradient from
     warm ivory (#fff7e6) into a cream mixed 48% with the fruit accent, which read
     as a faded, tinted panel -- for mango it ended near rgb(255,208,124). Pure
     white, so the one action on the page is the highest-luminance surface on it. */
  background: #fff;
  box-shadow: 0 1.4rem 4rem color-mix(in srgb,var(--specimen-accent) 13%,transparent), inset 0 1px rgba(255,255,255,.7);
  font-size: .67rem;
  font-weight: 500;
  letter-spacing: .11em;
  text-transform: uppercase;
}
.specimen-story .add-button i { width: 4.4rem; height: 4.4rem; display: grid; place-items: center; border-left: 1px solid rgba(3,2,7,.25); font-style: normal; font-size: 1.1rem; }
/* The tile states its lime outright rather than inheriting it, so no future
   bay or state rule can tint the one colour this control is recognised by. */
.specimen-story .add-button i { background: var(--chlorophyll); color: var(--void-deep); }
.cosmic-collection__source-note {
  margin: 0;
  padding-top: 1.25rem;
  padding-bottom: 1.25rem;
  color: rgba(255,247,230,.43);
  background: var(--void-deep);
  font-size: .58rem;
  letter-spacing: .06em;
  line-height: 1.5;
}

/* ============================================================================
   Chromatic System v2 — section rhythm.

   v1 ran hero → proof → collection → journey → process → philosophy →
   first-batch → footer as eight consecutive near-blacks. Nothing to breathe
   against, so nothing read as premium — just dim.

   v2 assigns each zone a distinct hue family and puts one saturated LIGHT band
   at the emotional peak of the scroll. That band used to sit at the doctrine;
   the doctrine section is gone and the band moved up to the principle with the
   line it belongs to:

     hero        deep violet-blue space
     velocity    full-chroma livery ribbon
     proof       BLAZE — full-bleed --signal, dark ink (the one light band)
     collection  orchid ink → three fruit-tinted bays
     journey     neutral charcoal archive (deliberate palette cleanser)
     process     ABYSS — cold technical blue, the only cool-only zone
     philosophy  COLD — near-colourless, so the specimen is the only hue
     first-batch FOREST — deep emerald
     footer      floor
   ============================================================================ */

.process-engine { background: var(--store-canvas, #070709); }
.process-engine__intro h2 em { color: var(--store-gold, #e7a51a); }.philosophy__index,
.philosophy__note { color: var(--ink-soft); }
/* THE DEFECT. `linear-gradient(145deg,#0a0320,#1a072e ...)` is a hard-coded
   plum and was the whole nutrition field for all three SKUs. Replaced with the
   SKU surface tokens. No radial accent bloom: a mostly solid chromatic surface
   reads as depth, a coloured glow reads as decoration and competes with the
   bars. The lift runs raised -> base within one hue; nothing crosses hue.

   .insight__shell gets the identical treatment a few rules below, off the same
   shared SKU surface map, so the two overlays cannot drift apart. */
.nutrition__shell {
  background:
    radial-gradient(128% 74% at 8% -4%, var(--sku-bloom, transparent), transparent 52%),
    linear-gradient(152deg, var(--nutrition-surface-raised), var(--nutrition-surface) 52%);
}
/* The last hard-coded plum in the SKU experience. Same treatment as the
   nutrition shell: in-hue lift, no accent bloom, surface dominant. */
.insight__shell {
  background:
    radial-gradient(128% 74% at 8% -4%, var(--sku-bloom, transparent), transparent 52%),
    linear-gradient(152deg, var(--insight-surface-raised), var(--insight-surface) 52%);
}

@media (hover: hover) and (pointer: fine) {
  .specimen-story:hover .specimen-story__visual img { transform: translate3d(0,-.5rem,0) scale(1.012); filter: drop-shadow(0 .6rem .3rem rgba(0,0,0,.2)) drop-shadow(0 2.8rem 3rem rgba(0,0,0,.5)) drop-shadow(0 6rem 7rem color-mix(in srgb,var(--specimen-accent) 25%,transparent)); }
  .provenance-ledger a:hover { color: var(--specimen-accent); transform: translateX(.2rem); }
  .specimen-story .product-lenses button:hover { color: var(--void-deep); background: var(--specimen-accent); }
  .specimen-story .add-button:hover { transform: translateY(-2px); box-shadow: 0 1.8rem 5rem color-mix(in srgb,var(--specimen-accent) 25%,transparent), inset 0 1px rgba(255,255,255,.85); }
}

@media (max-width: 68rem) {
  .collection-manifesto__grid { grid-template-columns: 1fr; gap: 2rem; }
  .collection-manifesto h2 { font-size: clamp(3.7rem,10vw,7rem); }
  .specimen-story { grid-template-columns: minmax(18rem,.85fr) minmax(22rem,1.15fr); gap: 2rem; }
  .specimen-story__visual { min-height: clamp(34rem,58vw,46rem); }
  .specimen-story__content h3 { font-size: clamp(3.3rem,6.7vw,5.4rem); }
  .provenance-ledger { grid-template-columns: 1fr; }
  .provenance-ledger article { min-height: 0; }
  .provenance-ledger article + article { padding-left: 0; border-top: 1px solid rgba(255,247,230,.14); border-left: 0; }
  .provenance-ledger p { min-height: 0; }
}

@media (max-width: 760px) {
  .collection-manifesto { min-height: 0; padding-top: 3rem; padding-bottom: 3.4rem; }
  .collection-manifesto h2 { font-size: clamp(3.15rem,15vw,5.2rem); }
  .collection-manifesto::after { width: 78vw; height: 60vw; opacity: .35; }
  /* Measured at 390 before this pass: 1125px block, of which 104px was bottom
     padding and 222px was inter-row gap, with the pouch at 21% of the
     composition. The space is recomposed rather than shrunk — the pouch takes
     what the padding gives up. */
  .specimen-story,
  .specimen-story--mango { grid-template-columns: 1fr; gap: 0.8rem; min-height: 0; padding-top: 1.6rem; padding-bottom: 2rem; }
  /* The desktop flip moved from --mango to --jamun (see the comment beside
     those order rules), but this mobile reset was left pointing at --mango.
     Result: jamun kept order:2/1 in the single-column layout, so its copy
     came before its media while mango and pineapple led with media. */
  .specimen-story--jamun .specimen-story__visual,
  .specimen-story--jamun .specimen-story__content { order: initial; }
  /* 128vw = 480px at 375 for a 278px image — the same letterboxed dead space the
     desktop rule had. */
  .specimen-story__visual { min-height: min(84vw,27rem); }
  /* The pouch is the hero object. object-fit keeps it undistorted and the
     packaging untouched; only the box it is allowed to occupy changes. */
  .specimen-story__visual img { width: min(68vw,20rem); max-height: 92%; }
  .specimen-story__shape { inset: 2% 0 1%; }
  .specimen-story__visual > p { right: 7%; bottom: 5%; left: 7%; font-size: .46rem; letter-spacing: .1em; }
  .specimen-story__coordinate { top: 6%; font-size: .48rem; }
  .specimen-story__content h3 { font-size: clamp(3.15rem,15vw,5.1rem); }
  .specimen-story__lead { font-size: .94rem; line-height: 1.5; }
  .provenance-ledger strong { font-size: 1.45rem; }
  .specimen-story__facts { grid-template-columns: 1fr; }
  .specimen-story__facts div:nth-child(n) { padding-right: 0; padding-left: 0; border-left: 0; }
  .specimen-story .product-lenses { grid-template-columns: repeat(2,1fr); }
  .specimen-story .product-lenses button:nth-child(2) { border-right: 0; }
  .specimen-story .product-lenses button:nth-child(-n+2) { border-bottom: 1px solid rgba(255,247,230,.14); }
  .specimen-story__commerce { grid-template-columns: 1fr; gap: .75rem; }
  .specimen-story__commerce > span { min-height: 3.3rem; }
}

@media (prefers-reduced-motion: reduce) {
  .specimen-story__visual img,
  .provenance-ledger a { transition: none; }
}

/* Magnetic pull on primary CTAs: JS (setupMagneticButtons in landing.js) writes
   --mx/--my as it tracks the pointer within each button's bounds. Placed last so
   it wins the cascade over the earlier hover-lift rules above without touching
   them. Falls back to a no-op transform (var(...,0)) wherever JS doesn't run. */
@media (hover: hover) and (pointer: fine) {
  .hero__cta, .add-button {
    transform: translate3d(var(--mx, 0), var(--my, 0), 0);
  }
}

/* ============================================================================
   01 / THE PRINCIPLE

   Typography carries this. A claim, a qualification, and four facts on one
   rule — nothing else in the section, and nothing that needs to be scrolled
   through to be understood. It is the shortest section on the page on purpose:
   a statement of belief that takes a screen and a half to deliver is not a
   belief, it is a brochure.
   ========================================================================== */
.proof { overflow: visible; }
.section.proof { padding-bottom: clamp(3.2rem, 5.5vw, 5.5rem); }

/* The headline runs the full measure rather than sitting in a column.
   "THE INGREDIENT LIST" is 8.65em wide in this face at its tracking, so a
   1.1fr column could only carry it at 64px before wrapping — which stranded
   the word LIST on a line of its own at every breakpoint. Full width takes
   156px at 1440, so the line fits with room to spare and the size is chosen
   for the composition instead of by the wrap. */
.principle {
  position: relative;
  z-index: 2;
  padding-top: clamp(2.6rem, 5.5vw, 5.5rem);
}

/* The em keeps the display-serif italic the page uses for the second half of
   every headline; the gold is the commerce accent, not a new colour. */
.principle h2 {
  margin: 0;
  font-family: var(--font-ui);
  font-size: clamp(3.1rem, 6.4vw, 7.6rem);
  font-weight: 500;
  line-height: 0.84;
  letter-spacing: -0.06em;
}
/* 6.01:1. Emphasis on this band is weight and darkness -- gold on blaze
   orange is a hue collision and reads muddy. */
.principle h2 em { color: var(--void, #0a0417); font-family: var(--font-display); font-weight: 400; font-style: italic; }

/* Set under the headline and pushed right: the claim is made, then
   qualified, in the reading order the sentence already implies. */
.principle__note {
  max-width: 30rem;
  margin: clamp(1.5rem, 3vw, 2.6rem) 0 0 46%;
  color: rgba(10, 4, 23, 0.82);
  font-family: var(--font-display);
  font-size: clamp(1.15rem, 1.65vw, 1.72rem);
  line-height: 1.32;
  letter-spacing: -0.015em;
}

/* Four facts, one rule, read in a single pass. Bordered cells were tried here
   and read as a feature comparison — the wrong genre for four plain truths. */
/* max-content columns rather than four equal fifths of the page: spread
   across the full measure the facts read as four unrelated points, grouped
   they read as one specification. The rule still runs the full width. */
.principle__proof {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, max-content));
  gap: clamp(1rem, 4.6vw, 5.5rem);
  justify-content: start;
  margin: clamp(2.6rem, 5vw, 5rem) 0 0;
  padding: 1.15rem 0 0;
  list-style: none;
}
/* THE RULE IS DRAWN, not just present. It was a border-top, which cannot be
   animated without animating the box. As a pseudo-element it can sweep from the
   left before the facts land underneath it -- the instrument ruling a line and
   then reporting under it, which is the section's own register voice.
   scaleX(1) is the resting state, so with no scroll-driven support and under
   reduced motion the rule is simply there. */
.principle__proof::before {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  height: 1px;
  background: rgba(10, 4, 23, 0.28);
  transform: scaleX(1);
  transform-origin: left center;
  content: "";
}
.principle__proof li { display: grid; gap: 0.3rem; align-content: start; }
.principle__proof strong {
  color: var(--void, #0a0417);
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 2.6vw, 2.9rem);
  font-weight: 400;
  line-height: 0.94;
}
.principle__proof span {
  color: rgba(10, 4, 23, 0.78);
  font-size: 0.55rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.principle__foot {
  margin: 1.5rem 0 0;
  /* A disclaimer that cannot be read is not a disclaimer. 0.78 on --signal is
     4.6:1 -- louder than the 0.32 ivory it replaces, and correctly so. */
  color: rgba(10, 4, 23, 0.78);
  font-size: 0.55rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

@media (max-width: 900px) {
  .principle { padding-top: 3.1rem; }
  .principle__note { max-width: 34rem; margin: 1.6rem 0 0; }
  /* Two by two rather than four across: at 360 a four-column row puts
     "Added preservatives" on three lines and the rule stops being readable. */
  .principle__proof { grid-template-columns: repeat(2, 1fr); gap: 1.75rem 1.4rem; margin-top: 2.7rem; padding-top: 1.5rem; justify-content: stretch; }
}

@media (max-width: 520px) {
  /* Measured ceiling, not a guess: the phrase needs 8.65em, so 360px of
     viewport (328px usable) tops out at 37px and 390 at 41px. 9.9vw sits
     just under both and keeps the line whole. */
  .principle h2 { font-size: clamp(2rem, 9.9vw, 3.2rem); }
  .principle__note { font-size: 1.1rem; }
  .principle__proof strong { font-size: 1.5rem; }
  .principle__proof span { font-size: 0.46rem; letter-spacing: 0.12em; }
  .principle__foot { font-size: 0.46rem; }
}

/* ============================================================================
   03 / THE COLLECTION — THE SET

   The bundle, sitting where the decision is made. Deliberately NOT a fourth
   product bay: it carries no fruit colour field of its own, because it is not
   a fourth fruit — it is the three already shown, taken together. Obsidian
   ground, one gold rule, the arithmetic stated plainly.
   ========================================================================== */
.collection-set {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(1.6rem, 5vw, 5rem);
  align-items: end;
  padding: clamp(1.9rem, 2.9vw, 2.9rem) var(--gutter) clamp(2rem, 3.1vw, 3.1rem);
  border-top: 1px solid rgba(255, 247, 230, 0.13);
  background: var(--store-canvas, #070709);
}

.collection-set__label {
  margin: 0 0 0.9rem;
  color: var(--store-gold, #e7a51a);
  font-size: 0.55rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.collection-set h3 {
  margin: 0;
  font-family: var(--font-ui);
  font-size: clamp(2.2rem, 4.6vw, 4.4rem);
  font-weight: 500;
  line-height: 0.9;
  letter-spacing: -0.05em;
}
.collection-set__note {
  max-width: 26rem;
  margin: 0.9rem 0 0;
  color: var(--store-secondary, #b8b0a3);
  font-size: clamp(0.88rem, 1vw, 1rem);
  line-height: 1.5;
}

.collection-set__buy { display: grid; gap: 0.9rem; justify-items: end; }
.collection-set__price { display: grid; gap: 0.25rem; margin: 0; justify-items: end; text-align: right; }
.collection-set__price span {
  color: #f8f3ec;
  font-family: var(--font-display);
  font-size: clamp(2rem, 3.2vw, 3rem);
  line-height: 1;
}
.collection-set__price small {
  color: rgba(255, 255, 255, 0.44);
  font-size: 0.55rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
}
/* Inherits the .add-button the product bays already use, so the primary buy
   control is one component across the whole collection. */
/* The decisive commerce moment on the page, so it stops borrowing the quiet
   dark treatment the per-SKU buttons use. Alphonso gold, ink type, and the
   chevron block carries the deeper gold: the existing warm language at full
   strength, not a new colour and not a pill. */
.add-button--set {
  min-width: 16rem;
  min-height: 3.9rem;
  background: var(--nectar);
  color: var(--void);
  font-size: .72rem;
  letter-spacing: .12em;
}
.add-button--set i { background: var(--store-gold, #e7a51a); color: var(--void); }
@media (hover: hover) and (pointer: fine) {
  .add-button--set:hover { background: #ffb733; }
}

@media (max-width: 760px) {
  .collection-set {
    grid-template-columns: 1fr;
    gap: 1.6rem;
    align-items: start;
    padding-top: 2.4rem;
    padding-bottom: 2.6rem;
  }
  .collection-set__buy { justify-items: stretch; gap: 1rem; }
  .collection-set__price { justify-items: start; text-align: left; }
  .add-button--set { width: 100%; min-width: 0; }
}

/* ============================================================================
   THE GATEWAY — warm to cold

   The only saffron field on the page, and it exists to be left. The ground
   runs orchard-warm at the top and resolves to exactly var(--store-canvas) at
   the bottom edge, which is the colour the reactor opens on, so 02 is entered
   rather than bumped into. Nothing here animates: a held colour is the
   transition.
   ========================================================================== */
.gateway {
  position: relative;
  z-index: 1;
  display: grid;
  align-content: center;
  /* justify-items:start, not the default stretch: the figures must sit against
     the same left edge as the headline rather than spreading across the gutter.

     A full-measure span was tried here and reverted. It filled the width, but
     two quantities 600px apart cannot be compared in one glance -- the reader
     has to travel to the second one instead of seeing both at once. The empty
     width was never the real fault; the weak relationship between the two
     figures was, and spreading them made it weaker. */
  justify-items: start;
  gap: clamp(1.5rem, 3.4vw, 2.4rem);
  /* A REVELATION, NOT A CHAPTER. This briefly ran at 82svh, which bought the
     transformation room but made the bridge compete with the Process engine for
     scroll duration -- and scroll duration belongs to the Process. Compressed to
     the minimum the four beats need: headline, the named source, the gap, the
     resolution. The warm-to-cold gradient still resolves to
     --store-canvas at the bottom edge; it simply travels faster. */
  min-height: 56svh;
  --gw-pad: clamp(3.2rem, 6.5vw, 6.4rem);
  padding: var(--gw-pad) var(--gutter);
  /* CLIP, NOT HIDDEN. `overflow: hidden` makes an element a scroll container,
     so every view() timeline inside it -- the staggered reveals below --
     would resolve against THIS box, which never scrolls. It froze the old
     scroll-scrubbed drain at 32% at every position for exactly that reason. `clip` clips identically without creating a scrollport.
     .process-engine already uses clip for its own reasons; same rule. */
  overflow: clip;
  /* Warm, but dark. The blaze band moved up to 01 / THE PRINCIPLE with the
     line it belongs to -- this section is a caption for a transformation, not
     a statement of belief, and two consecutive full-strength claims read as
     the same argument made twice. Here the gradient resolves into
     --store-canvas at the bottom edge so the section hands off into the
     engine rather than cutting against it. */
  /* THE TOP EDGE IS DARK ON PURPOSE. This ramp used to open at #2b1503 with
     both glows sitting at 8% and 22% -- fine under obsidian, but directly
     under the blaze band that reads as the orange going dirty rather than as
     one section ending and another starting. Warm-under-warm is a smear, not
     a cut. The base now opens at the page canvas and the glows are pushed to
     the middle third, where the figures are: the boundary above is a clean
     edge, the warmth still blooms where the transformation happens, and the
     ramp still resolves cold into the engine below. */
  /* COLD, AND ALMOST COLOURLESS. This was a warm amber field while the
     specimen was a gold mango. With jamun in it the warm ground fought the
     fruit: magenta against amber, with commerce gold on top, and all three
     went muddy -- desaturated complements on a dark field do not pop, they
     cancel.

     The field now carries almost no hue of its own, so the fruit is the only
     colour in the frame. What little it does carry is the page's own reading
     of this fruit: CRYOSTAT files jamun as DEEP COLD at --electric-ice, and
     this section is the hinge between the blaze Principle above it and the
     cold Process below. Warm to cold happens here. */
  background:
    radial-gradient(72% 40% at 18% 34%, color-mix(in srgb, var(--electric-ice) 10%, transparent), transparent 72%),
    radial-gradient(64% 38% at 84% 66%, color-mix(in srgb, var(--sku-jamun, #6a17c4) 20%, transparent), transparent 74%),
    linear-gradient(170deg, var(--void-deep, #04020a) 0%, #0a0716 46%, var(--store-canvas, #070709) 100%);

  /* THE SHAPED RUN. --drain is scrubbed 0 -> 1 by scroll. --t is the part of
     that run where anything changes: it holds at 0 through the first 30%,
     crosses over the next 50%, and leaves 20% to settle -- so the fresh fruit
     is seen still before the drying front moves, and 20G has landed before the
     specimen starts to leave. The ranges that place that run on the page, and
     the drift that gives it room, are in SCRUBBED, WITH A SLOW DRIFT. */
  --t: clamp(0, calc((var(--drain, 1) - 0.30) / 0.50), 1);
}
/* One grain pass so a large flat warm field does not band on an OLED phone. */
.gateway::after {
  position: absolute;
  inset: 0;
  content: "";
  pointer-events: none;
  opacity: 0.5;
  background-image: radial-gradient(rgba(255, 255, 255, 0.035) 1px, transparent 1px);
  background-size: 3px 3px;
}

.gateway__line {
  position: relative;
  z-index: 1;
  max-width: 22ch;
  margin: 0;
  color: #fdf3e2;
  font-family: var(--font-ui);
  font-size: clamp(2.4rem, 6.4vw, 6.6rem);
  font-weight: 500;
  line-height: 0.92;
  letter-spacing: -0.055em;
  text-wrap: balance;
}
.gateway__line em {
  /* Ivory. Gold here was a third colour competing with the fruit for the same
     attention, and it lost. */
  color: #fdf3e2;
  font-family: var(--font-display);
  font-weight: 400;
  font-style: italic;
}

/* ---------------------------------------------------------------------------
   THE TRANSFORMATION — ~200-225G in, 20G out.

   Typography and space carry this, not a card. The two states sit on one
   hairline axis, at different weights, and land in sequence: source, then
   result. That sequence is the arrow. An actual arrow was tried in an earlier
   pass and retired -- it was diagram language in a typographic composition,
   and it renders as an emoji on several platforms.

   FULL AGAINST DISTILLED. The source is the larger footprint, looser tracked
   and dimmed: mass that is mostly water and occupies area without weight. The
   result is smaller, tighter, heavier and gold -- less area, more density,
   which IS the physics, so the type performs it rather than captioning it.
   The outcome is the brighter of the two and never the smaller in weight,
   because reading it as loss would be exactly the wrong story.

   Both sit under the headline's maximum at every width. Measured at 1440: the
   headline is 92px, the source figure 89.6px. The sentence still leads. */
.gateway__transform {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: auto auto auto;
  justify-content: start;
  /* Baseline, not centre. Centre-aligning two boxes of different height put
     the two unit labels 7px out of step at 1440 -- a visible stagger under a
     pair that is supposed to read as one measurement. Baseline puts the
     numerals on one line and the labels follow them. */
  align-items: baseline;
  gap: clamp(1.4rem, 5vw, 3.6rem);
}

/* ---------------------------------------------------------------------------
   THE DRAIN.

   This section describes a transformation and used to render it as two numbers
   that sat there. Every other moving part of this page -- the fluid hero, the
   four-stage engine, the thermal instrument -- earns its place by doing
   something; this one did not, and next to them it read as a slide.

   ONE PROPERTY DRIVES EVERYTHING. --drain scrubs 0 -> 1 with scroll position,
   and the source contracts and dims, the rule fills, and the result grows and
   ignites off that single number. Scrubbed, not triggered: scroll back up and
   the water returns, which is what makes it an instrument rather than a stat
   counter that fires once and is spent.

   THE DIGITS NEVER MOVE. Interpolating 225 -> 20 would put unapproved figures
   like "143G FRESH FRUIT" on screen for most of the scroll, and fresh input
   mass is the one claim this repo withdrew, guarded in scripts/build.js and
   recorded in section 9 of the canonical file as an owner decision with
   documentation still outstanding. So mass is expressed as SIZE, not as a
   number: the page states the two approved figures and nothing between them.

   Compositor-only. No JS, no library, no WebGL, and nothing here touches the
   process engine. */
@property --drain {
  syntax: "<number>";
  inherits: true;
  initial-value: 1;
}

/* initial-value is 1, not 0: without scroll-driven animation support, or under
   reduced motion, every consumer below must resolve to the FINISHED state --
   drained, filled, ignited. A 0 default would leave those readers looking at a
   half-transformed composition that never completes. */
/* The drain is scrubbed by scroll, with a slow drift that keeps the specimen in
   view while it runs: see SCRUBBED, WITH A SLOW DRIFT at the end of the Gateway
   rules. It sits after the phone rules so its padding wins the cascade. */

/* THE AXIS. A track and a fill -- the line the transformation happens along.
   It is a rule, not a graphic: no head, no arrow, no icon. */
.gateway__axis {
  display: block;
  /* Baseline alignment gives an empty element nothing to sit on, so the axis
     is placed against the source figure's own box instead: pushed down from
     the row top by a third of the numeral height, which is its optical middle.
     Tuned by measurement, not by eye -- see the numbers in the QA report. */
  align-self: start;
  font-size: clamp(2.15rem, 6.2vw, 5.6rem);
  margin-top: 0.46em;
  position: relative;
  width: clamp(2rem, 7vw, 6rem);
  height: 1px;
  background: rgba(253, 243, 226, 0.14);
}
/* The fill, and the bright head at its leading edge -- the same vocabulary the
   CRYOSTAT rail uses, so the page has one idea of what a filling line is. */
.gateway__axis-fill {
  position: absolute;
  inset: 0 auto 0 0;
  display: block;
  width: calc(var(--drain, 1) * 100%);
  background: linear-gradient(90deg, rgba(253, 243, 226, 0.18), var(--electric-ice));
  box-shadow:
    0 0 0.6rem color-mix(in srgb, var(--electric-ice) calc(var(--drain, 1) * 60%), transparent),
    0 0 1.6rem color-mix(in srgb, var(--electric-ice) calc(var(--drain, 1) * 28%), transparent);
}

.gateway__state { margin: 0; }


/* A QUANTITY IS ONE WORD. Left wrappable, the source figure can break after
   its dash and read as two numbers on two lines. Short enough that nowrap
   cannot overflow: 180px at the 2.15rem clamp floor, inside a 320px column
   at 360. */
.gateway__figure {
  white-space: nowrap;
  display: block;
  font-family: var(--font-ui);
  font-weight: 500;
  line-height: 0.86;
  /* Lining tabular figures so 200-225 and 20 sit on a common baseline and
     neither the almost-equal sign nor the en dash rides above the numerals. */
  font-variant-numeric: lining-nums tabular-nums;
}
/* The unit is a unit, not part of the number. */
.gateway__figure i {
  font-size: 0.4em;
  font-style: normal;
  letter-spacing: 0.01em;
}

/* The two labels cross over with the drain: the source's name fades as its
   mass goes, the result's arrives with it. */
.gateway__state--in .gateway__label {
  opacity: calc(1 - var(--t, 1) * 0.72);
  transform: translateY(calc(var(--t, 1) * -0.3em));
}
.gateway__state--out .gateway__label {
  opacity: calc(0.06 + var(--t, 1) * 0.94);
  transform: translateY(calc((1 - var(--t, 1)) * 0.5em));
}

.gateway__label {
  display: block;
  margin-top: clamp(0.5rem, 1.3vw, 0.8rem);
  color: rgba(253, 243, 226, 0.5);
  font-family: var(--font-ui);
  font-size: clamp(0.54rem, 0.95vw, 0.64rem);
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}

/* SOURCE — abundant. */
.gateway__state--in .gateway__figure {
  /* THE MASS LEAVES, and it has to LOOK like ten to one. The first cut of this
     ran scale 1 -> 0.84 with a light fade, which on a page that opens with a
     WebGL fluid simulation read as nothing at all. It now collapses to HALF,
     lifts as it goes and blurs out of focus -- water leaving, not a number
     politely dimming.

     It stops at 0.4 opacity rather than 0. The figure is an approved published
     claim and has to stay legible at the end of the run, not just at the
     start; the drama is carried by what it loses RELATIVE to the result, which
     ends nearly three times its size. */
  transform-origin: left center;
  transform:
    scale(calc(1 - var(--t, 1) * 0.5))
    translateY(calc(var(--t, 1) * -0.34em));
  opacity: calc(1 - var(--t, 1) * 0.6);
  /* 1.5px, not 2.4. The softening is what sells evaporation, but this figure
     is a published claim and has to stay plainly readable at the end of the
     run, not merely present in the DOM. */
  filter: blur(calc(var(--t, 1) * 1.5px));
  color: rgba(253, 243, 226, 0.56);
  /* The floor is 2.15rem, not 2.5: at 2.5 the source figure computed 40px
     against a 38.4px headline at 390 and inverted the hierarchy the slide
     depends on. Measured at every width now -- the sentence always leads. */
  font-size: clamp(2.15rem, 6.2vw, 5.6rem);
  letter-spacing: -0.01em;
}
/* RESULT — distilled. */
.gateway__state--out .gateway__figure {
  /* WHAT IS LEFT, AND IT ARRIVES. Comes in at a third of its size, unlit and
     nearly transparent, and grows into the largest thing in the section --
     bigger at the end than the source ever was at the start. The reduction is
     told by the SWAP in dominance, which is the part a reader actually feels,
     rather than by two numbers politely trading a few percent.

     The bloom is the landing. It is keyed off --t squared so it stays dark
     for most of the run and then ignites at the end instead of glowing the
     whole way down. */
  transform-origin: left center;
  transform: scale(calc(0.32 + var(--t, 1) * 0.68));
  opacity: calc(0.18 + var(--t, 1) * 0.82);
  /* --electric-ice, which is exactly the colour CRYOSTAT already assigns this
     fruit. 12:1 on this field, and cyan sits WITH magenta where gold fought
     it. It is also the right meaning: what arrives at the end of this scroll
     arrived by being frozen. */
  color: color-mix(in srgb, var(--electric-ice) calc(var(--t, 1) * 100%), rgba(253, 243, 226, 0.42));
  text-shadow: 0 0 calc(var(--t, 1) * var(--t, 1) * 2.4rem) color-mix(in srgb, var(--electric-ice) calc(var(--t, 1) * var(--t, 1) * 45%), transparent);
  font-size: clamp(2.6rem, 8.4vw, 7.6rem);
  font-weight: 600;
  letter-spacing: -0.06em;
}

/* THE PROOF FOOTER. Three truths, one line, the smallest type in the section.
   Subordinate to the transformation by roughly 6x at every width. */
.gateway__proof {
  position: relative;
  z-index: 1;
  margin: 0;
  color: rgba(253, 243, 226, 0.5);
  font-family: var(--font-ui);
  font-size: clamp(0.6rem, 1.05vw, 0.72rem);
  font-weight: 500;
  letter-spacing: 0.2em;
  line-height: 1.9;
  text-transform: uppercase;
}
.gateway__proof i { font-style: normal; color: rgba(253, 243, 226, 0.32); margin: 0 0.35em; }

/* ---- staggered arrival -----------------------------------------------------
   EXTENDS the existing reveal system rather than adding a second one. The
   headline carries no --step and therefore establishes first; the source lands
   before the result, so the pair is read as a sequence and not as a diagram. */
.gateway__state--in { --step: 1; }
.gateway__state--out { --step: 2; }
.gateway__proof { --step: 3; }

/* ONE mechanism, not two. An earlier attempt also laddered transition-delay so
   the IntersectionObserver fallback would stagger as well. It did not apply --
   measured, the elements all began transitioning together and the computed
   delays did not match the declared ones -- so it is gone rather than shipped
   unexplained. The fallback now does exactly what every other section on this
   page does on that path: one 620ms fade for the group. The sequence below is
   the enhancement, and it is the only thing sequencing this slide. */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .gateway [data-reveal] {
      animation-range: entry calc(var(--step, 0) * 5%) entry calc(52% + var(--step, 0) * 5%);
    }
  }
}

/* THE STAGE. Right-hand column on desktop, and the reason the composition no
   longer leaves 31-56% of the section empty -- that space is now carrying the
   product rather than a stretched hairline. */
.gateway__stage {
  position: relative;
  z-index: 1;
  display: block;
  /* Sized to the masters: both are 1122x1402, exactly 4:5. Bigger than the
     first pass -- at 22rem the specimen was an illustration beside the type
     rather than the thing the section is about. */
  width: min(100%, 28rem);
  margin: 0;
  aspect-ratio: 4 / 5;

  /* THE DRYING FRONT. Position of the edge sweeping across the fruit, from
     just off one corner to just off the other. The soft band is wide on
     purpose: these two frames are not camera-matched -- an oval seen flat and
     a cube seen at three-quarters -- so a hard edge would read as a torn
     collage. Wide, it reads as a directional dissolve, and the important part
     survives either way: whatever the front has not reached is still at full
     opacity and full sharpness, instead of a crossfade degrading the whole
     photograph from the first pixel of scroll. */
  --front: calc(var(--t, 1) * 152% - 52%);
  --front-soft: 52%;
}
.gateway__specimen {
  position: absolute;
  inset: 0;
  display: block;
  transform-origin: 50% 58%;
  /* NO BLEND, NO MASK. Both of those were workarounds for a rectangular frame
     with a black ground in it. These masters are keyed to real alpha by the
     `gateway` category in scripts/images.js, so there is no ground to hide.

     Screen in particular had to go: it takes black to nothing, which is fine
     for a bright gold mango and destroys a dark one. Jamun flesh is nearly
     black in its shadows, so half the subject would have dissolved into the
     warm field behind it. Keying at build time is the correct place to solve
     this -- the page just composites. */
}
.gateway__specimen img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* THE CUT FACE IS ANGLED, NOT UPRIGHT.

   Shot portrait, it read as anatomy rather than as fruit -- a vertical long
   axis, bilateral symmetry, a dark central cleft where the stone was, and a wet
   sheen, which together are a well-known hazard with halved fruit. Every one of
   those cues is orientation-dependent, so turning the frame removes the read
   without touching the photograph.

   -24 and not 90: ninety fixes it too but looks like a portrait photograph that
   has been turned on its side, because the key light ends up raking in from the
   edge. At -24 it reads as a cut disc lying at an angle, the lighting still
   makes sense, and it sits with the diagonal of the dried piece beside it.

   ON THE IMG, not the specimen: the specimen element carries the --drain scale
   and drift, and mixing a constant rotation into an animated transform means
   every frame recomputes both. Separate elements, separate jobs.

   IF THE PAIR IS EVER RE-SHOT, this comes off -- compose the angle in camera
   and put the stone cavity off-centre, which removes the symmetry doing most
   of the work here. */
.gateway__specimen--fresh img { transform: rotate(-24deg); }
/* FRESH: full, lit, and then it goes -- contracting toward the point the dried
   piece arrives at, lifting and softening as the water leaves it. */
.gateway__specimen--fresh {
  /* NOT FADED AND NOT BLURRED ANY MORE. Both of those degraded the whole frame
     evenly the instant the run started, which is why the detail never got a
     chance to be looked at. The front removes it progressively instead: ahead
     of the edge this photograph is untouched. */
  -webkit-mask-image: linear-gradient(115deg, transparent var(--front), #000 calc(var(--front) + var(--front-soft)));
  mask-image: linear-gradient(115deg, transparent var(--front), #000 calc(var(--front) + var(--front-soft)));
  transform:
    scale(calc(1 - var(--t, 1) * 0.2))
    translateY(calc(var(--t, 1) * -4%));
}
.gateway__specimen--dried {
  /* The complement of the fresh mask -- opaque where the front has passed,
     clear where it has not, so the two alphas sum to one across the band and
     nothing dips or doubles through the handover. */
  -webkit-mask-image: linear-gradient(115deg, #000 var(--front), transparent calc(var(--front) + var(--front-soft)));
  mask-image: linear-gradient(115deg, #000 var(--front), transparent calc(var(--front) + var(--front-soft)));
  /* Resolves to 0.86: what survives is smaller than what went in. The frames
     fill their boxes differently -- the halved fruit sits at about 60%, the
     dried piece at about 85% -- so this is matched by apparent size, not by
     transform. Not to scale; 10:1 would be a speck. */
  transform: scale(calc(0.62 + var(--t, 1) * 0.24));
}

@media (min-width: 861px) {
  .gateway {
    grid-template-columns: minmax(0, 1fr) clamp(17rem, 32vw, 30rem);
    grid-template-areas:
      "line  stage"
      "trans stage"
      "proof stage";
    column-gap: clamp(2rem, 5vw, 5rem);
    align-items: center;
  }
  .gateway__line { grid-area: line; }
  .gateway__transform { grid-area: trans; }
  .gateway__proof { grid-area: proof; }
  .gateway__stage { grid-area: stage; justify-self: end; align-self: center; }
}

@media (max-width: 860px) {
  /* Stacked, it reads headline -> specimen -> figures, which is the order the
     sentence above it already implies. */
  /* CENTRED, NOT LEFT-ALIGNED. Both photographs are centred in their own frames
     (visual centre measured at 49-50%), but the stage inherited justify-items:
     start, so the fruit sat 42-53px left of the screen's centre at 360-430px. The
     type keeps its left edge; the specimen is the object, and it is centred. */
  .gateway__stage { width: min(68vw, 20rem); justify-self: center; }
}

@media (max-width: 760px) {
  .gateway {
    /* Composed for the narrow column, not inherited from the wide one. Height
       comes from the content plus its air, with only a modest floor -- a narrow
       column reaches the same three beats in less vertical distance. */
    min-height: 0;
    /* Tighter than it was (3-5rem, 1.4-2.2rem gaps): the specimen and its
       figures have to fit inside the band between the header and the buy rail
       for the transformation to be watched whole, and on a 390x844 phone that
       band is 726px. */
    --gw-pad: clamp(2.2rem, 8vw, 3.5rem);
    padding-top: var(--gw-pad);
    padding-bottom: var(--gw-pad);
    gap: clamp(1.1rem, 4vw, 1.8rem);
  }
  /* ONE ROW, NOT A COLUMN. Stacked, the two figures and a vertical stem cost
     ~165px of a phone's height; side by side over a full-width track they cost
     ~90px, which is most of what lets the section fit the visible band. The
     axis keeps its job -- the same track, filling left to right with the
     drain -- and now runs under BOTH figures, so it reads as the line between
     them. "Freeze-dried fruit" may wrap to two lines; the figures may not. */
  .gateway__transform {
    grid-template-columns: auto auto;
    grid-template-areas: "in out" "axis axis";
    justify-content: start;
    align-items: end;
    column-gap: clamp(1.4rem, 7vw, 2.4rem);
    row-gap: 0.85rem;
  }
  .gateway__state--in { grid-area: in; }
  .gateway__state--out { grid-area: out; }
  .gateway__axis { grid-area: axis; width: 100%; margin-top: 0; }
}

/* SHORT PHONES. On a 375x667 screen the band between the header and the buy rail
   is 549px and the section measured 637px, so the figures sat 68px under the
   rail while the specimen was framed. Only phones this short pay for it: a smaller specimen and
   less air. 390x844 and taller keep the composition above. */
@media (max-width: 760px) and (max-height: 700px) {
  .gateway { --gw-pad: 1.6rem; padding-top: var(--gw-pad); padding-bottom: var(--gw-pad); gap: 0.9rem; }
  .gateway__stage { width: min(50vw, 15rem); }
}


/* ============================================================================
   SCRUBBED, WITH A SLOW DRIFT

   The transformation follows scroll -- the reader's own hand drives it, and that
   is what makes it worth watching. Played on a timer instead, it read as cheap.

   The problem it had was geometric, not a matter of ranges: a scrubbed change can
   only be watched whole while the specimen is fully on screen, and at the page's
   own scroll speed that window was a few hundred pixels (at 390x844 headline,
   fruit and figures shared the screen for 16% of the run; at 1440x900 the stage
   was half off-screen when 20G landed). Pinning the section fixed it and was
   declined: the page already holds the reader in the Process engine.

   So nothing stops. The specimen DRIFTS: across the middle of the pass it travels
   at roughly half the page's speed, which about doubles the scroll it spends
   fully framed, and the drain is laid across that framed stretch with a still
   moment either side. On phones the headline keeps full speed and only the
   specimen, figures and proof line drift; on desktop they share rows with the
   headline, so the whole composition drifts together.

   --drift-max is both how far the group travels and the room added under it, so
   it can never be clipped by the section's overflow: clip. Ranges are fractions
   of the section's cover pass, derived from the measured layout at 390x844 and
   1440x900 and checked across 360-1920 wide.

   Compositor-only, no JS. No scroll-driven support or reduced motion: no drift,
   no added room, and --drain rests at its registered 1, the finished state.
   ========================================================================== */
@property --drift {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .gateway {
      --drift-max: 30svh;
      --drain-range: cover 25.1% cover 67%;
      --drift-range: cover 34.5% cover 62.8%;
      padding-bottom: calc(var(--gw-pad) + var(--drift-max));
      animation: gateway-drain linear both, gateway-drift linear both;
      animation-timeline: view(), view();
      animation-range: var(--drain-range), var(--drift-range);
    }
    .gateway__line,
    .gateway__stage,
    .gateway__transform,
    .gateway__proof {
      translate: 0 calc(var(--drift, 0) * var(--drift-max));
    }
    @media (max-width: 860px) {
      .gateway {
        --drain-range: cover 30.4% cover 72%;
        --drift-range: cover 39.5% cover 68.3%;
      }
      .gateway__line { translate: none; }
    }
    /* SHORT PHONES START THE DISSOLVE LATER. The ranges are fractions of the cover
       pass, and on a shorter screen the specimen fills more of the band, so it
       becomes fully framed later in that pass. At 360x740 the dissolve began the
       instant the specimen was framed (no still moment before it) and at 375x667
       after 16px. Starting it later and shortening it slightly restores the pause
       on both. 390x844 and taller keep the range above. */
    @media (max-width: 860px) and (max-height: 780px) {
      .gateway { --drain-range: cover 36.5% cover 70.5%; }
    }
  }
}
@keyframes gateway-drain { from { --drain: 0; } to { --drain: 1; } }
@keyframes gateway-drift { from { --drift: 0; } to { --drift: 1; } }


/* ============================================================================
   03 — THE FRUIT IMAGE IS THE DOOR

   A labelled button laid over the photograph rather than a click handler on the
   article: the bays already contain links and add-to-bag buttons, so making the
   whole card interactive would nest controls inside a control. This is
   keyboard-reachable, screen-reader-labelled, and invisible until focused or
   hovered.
   ========================================================================== */
.specimen-story__visual { position: relative; }
.specimen-story__enter {
  position: absolute;
  z-index: 4;
  inset: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.specimen-story__enter span {
  position: absolute;
  right: 0;
  bottom: 0;
  padding: 0.62rem 0.95rem;
  border: 1px solid color-mix(in srgb, var(--specimen-accent) 48%, transparent);
  color: #fff;
  font-family: var(--font-ui);
  font-size: 0.55rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  background: rgba(7, 7, 10, 0.62);
  backdrop-filter: blur(6px);
  opacity: 0;
  transform: translateY(0.4rem);
  transition: opacity 260ms ease, transform 260ms cubic-bezier(0.23, 1, 0.32, 1);
}
.specimen-story:hover .specimen-story__enter span,
.specimen-story__enter:focus-visible span { opacity: 1; transform: translateY(0); }
.specimen-story__enter:focus-visible { outline: 1px solid var(--specimen-accent); outline-offset: -0.5rem; }
/* No hover on a phone, so the affordance is simply always present there. */
@media (hover: none) {
  .specimen-story__enter span { opacity: 1; transform: none; }
}

/* ============================================================================
   THE ORIGIN DOSSIER

   Full-bleed, per-fruit colour world, revealed from whatever was tapped using
   the same clip-path origin the insight panel established. Not a dialog box on
   a scrim — the fruit's world replaces the page.
   ========================================================================== */
.dossier {
  --dossier-accent: var(--sku-mango, #ff9f00);
  --dossier-deep: #2a1604;
  position: fixed;
  z-index: 640;
  inset: 0;
  overflow: hidden;
  clip-path: circle(0% at var(--ox, 50%) var(--oy, 50%));
  visibility: hidden;
  transition: clip-path 520ms var(--ease-garden, cubic-bezier(0.23, 1, 0.32, 1)), visibility 0s linear 520ms;
}
.dossier.is-open { clip-path: circle(150% at var(--ox, 50%) var(--oy, 50%)); visibility: visible; transition-delay: 0s; }
.dossier[data-fruit="jamun"] { --dossier-accent: var(--sku-jamun-bright, #bb55ff); --dossier-deep: #1b0a2b; }
.dossier[data-fruit="pineapple"] { --dossier-accent: var(--sku-pineapple, #14c274); --dossier-deep: #04201a; }

.dossier__ground {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(90% 70% at 16% 12%, color-mix(in srgb, var(--dossier-accent) 30%, transparent), transparent 64%),
    linear-gradient(158deg, var(--dossier-deep), #06060a 72%);
}

.dossier__close {
  position: absolute;
  z-index: 3;
  top: clamp(1rem, 3vw, 2.2rem);
  right: clamp(1rem, 3vw, 2.2rem);
  width: 2.85rem;
  height: 2.85rem;
  display: grid;
  place-items: center;
  border: 1px solid rgba(255, 255, 255, 0.24);
  border-radius: 50%;
  background: rgba(7, 7, 10, 0.55);
  cursor: pointer;
  backdrop-filter: blur(8px);
}
.dossier__close i { position: relative; display: block; width: 0.85rem; height: 1px; background: #fff; transform: rotate(45deg); }
.dossier__close i::after { position: absolute; inset: 0; background: #fff; content: ""; transform: rotate(-90deg); }
.dossier__close:focus-visible { outline: 1px solid var(--dossier-accent); outline-offset: 3px; }

.dossier__shell {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
  gap: clamp(1.5rem, 5vw, 5rem);
  align-items: center;
  height: 100%;
  padding: clamp(3.4rem, 6vw, 5rem) var(--gutter);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

.dossier__plate { position: relative; margin: 0; }
.dossier__plate img { display: block; width: 100%; height: auto; max-height: 62svh; object-fit: contain; }
.dossier__plate figcaption {
  display: flex;
  gap: 0.8rem;
  align-items: baseline;
  margin-top: 0.9rem;
  color: rgba(255, 255, 255, 0.5);
  font-size: 0.55rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.dossier__plate figcaption em { font-family: var(--font-display); font-style: italic; letter-spacing: 0.04em; text-transform: none; }

.dossier__region {
  margin: 0 0 0.8rem;
  color: var(--dossier-accent);
  font-size: 0.55rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.dossier__body h2 {
  margin: 0 0 clamp(1.4rem, 3vw, 2.4rem);
  font-family: var(--font-ui);
  font-size: clamp(2.3rem, 4.6vw, 4.6rem);
  font-weight: 500;
  line-height: 0.9;
  letter-spacing: -0.05em;
}

.dossier__fields { display: grid; gap: clamp(1rem, 2vw, 1.5rem); margin: 0 0 clamp(1.6rem, 3vw, 2.6rem); }
.dossier__fields > div { display: grid; grid-template-columns: 6.5rem minmax(0, 1fr); gap: 1rem; align-items: baseline; padding-top: 0.85rem; border-top: 1px solid rgba(255, 255, 255, 0.14); }
.dossier__fields dt { color: rgba(255, 255, 255, 0.44); font-size: 0.52rem; letter-spacing: 0.18em; text-transform: uppercase; }
.dossier__fields dd { margin: 0; max-width: 42ch; color: rgba(255, 255, 255, 0.8); font-size: clamp(0.86rem, 1vw, 0.98rem); line-height: 1.56; }
/* An unverified field is labelled, not hidden. */
.dossier__fields > div[data-pending] dd { color: rgba(255, 255, 255, 0.4); font-style: italic; }

.dossier__buy { display: flex; gap: clamp(1rem, 2.5vw, 2rem); align-items: center; flex-wrap: wrap; }
.dossier__price { display: grid; gap: 0.2rem; margin: 0; }
.dossier__price span { color: #fff; font-family: var(--font-display); font-size: clamp(1.6rem, 2.4vw, 2.2rem); line-height: 1; }
.dossier__price small { color: rgba(255, 255, 255, 0.44); font-size: 0.52rem; letter-spacing: 0.14em; text-transform: uppercase; }
.dossier__note { margin: clamp(1.2rem, 2.4vw, 2rem) 0 0; max-width: 46ch; color: rgba(255, 255, 255, 0.34); font-size: 0.55rem; line-height: 1.55; letter-spacing: 0.05em; }

@media (max-width: 900px) {
  .dossier__shell {
    grid-template-columns: 1fr;
    gap: 1.4rem;
    align-items: start;
    align-content: start;
    padding-top: clamp(4.2rem, 14vw, 6rem);
    padding-bottom: clamp(2.4rem, 8vw, 4rem);
  }
  .dossier__plate img { max-height: 30svh; }
  .dossier__fields > div { grid-template-columns: 1fr; gap: 0.3rem; }
  .dossier__buy { gap: 1rem; }
  .dossier__buy .add-button { flex: 1 1 12rem; }
}

@media (prefers-reduced-motion: reduce) {
  .dossier { transition: visibility 0s linear 120ms, clip-path 120ms linear; }
  .specimen-story__enter span { transition: none; }
}

/* Each bay carries its own fruit again. A single saffron ground was tried on
   instruction and is superseded: mango saffron, jamun aubergine, pineapple
   forest, from the --specimen-* variables that were never removed. */


/* ============================================================================
   THE STORY SHELL — empty slots

   A slot awaiting approved copy keeps its heading and its measure and prints
   nothing. The rule below it is what holds the composition, so the page reads
   as a designed frame waiting for words rather than as something broken.
   ========================================================================== */
.dossier__eyebrow {
  display: flex;
  gap: 0.9rem;
  align-items: baseline;
  margin: 0 0 0.7rem;
  color: rgba(255, 255, 255, 0.5);
  font-size: 0.55rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.dossier__eyebrow em { color: var(--dossier-accent); font-family: var(--font-display); font-style: italic; letter-spacing: 0.04em; text-transform: none; }

.dossier__fields > div[data-awaiting] dd { min-height: 1.5rem; }
.dossier__fields > div[data-awaiting] dd::after {
  display: block;
  width: min(14rem, 40%);
  height: 1px;
  margin-top: 0.6rem;
  background: rgba(255, 255, 255, 0.14);
  content: "";
}

/* ============================================================================
   PRODUCT BLOCKS — commerce recovery pass

   The three fruit blocks are the product pages this site actually has: there
   is one HTML document, and #alphonso-mango-chunks / #jamun-chunks /
   #pineapple-slices are anchors into these. So they are built as purchase
   fields, not as editorial spreads.

   Order of attention, deliberately: name, sensory line, price and weight,
   what is and is not in it, then one button that outweighs everything near it.
   Everything optional sits below that button as a plainly-worded row.
   ========================================================================== */
.specimen-story__spec {
  display: flex;
  gap: clamp(1.6rem, 4vw, 3rem);
  margin: clamp(1.4rem, 3vw, 2.2rem) 0 0;
  padding: 0;
  list-style: none;
}
.specimen-story__spec li { display: grid; gap: 0.2rem; }
.specimen-story__spec strong {
  color: #fff;
  font-family: var(--font-display);
  /* OPTICAL LEFT ALIGNMENT, and only after measuring that the boxes were
     already correct. Every box in this column shares left: 43.19px at 1440 --
     index, h3, spec row, both values, both captions, the assurance list. The
     drift is ink, not layout: PP Editorial New at ~32px carries a larger left
     side bearing than PP Neue Montreal at ~8px, so the value printed 0.75-0.88px
     right of the caption directly beneath it. Measured at 8x sampling on all
     three PDPs; the same offset appears on every one, which is why this belongs
     on the shared component and not on the SKU that happened to be screenshotted.

     -0.025em, not a pixel: the value is clamp()ed across the breakpoints, so a
     fixed nudge would over-correct as the type shrinks. Measured after: 0.125px
     and 0.000px at 1440, -0.125px at 390 -- inside the sampling floor at both. */
  margin-left: -0.025em;
  font-size: clamp(1.5rem, 2.2vw, 2.05rem);
  font-weight: 400;
  line-height: 1;
}
.specimen-story__spec span { color: rgba(255,255,255,.5); font-size: .52rem; letter-spacing: .16em; text-transform: uppercase; }

.specimen-story__assurance {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem 1.4rem;
  margin: clamp(1rem, 2vw, 1.4rem) 0 0;
  padding: 0;
  list-style: none;
  color: rgba(255,255,255,.66);
  font-size: .58rem;
  letter-spacing: .13em;
  text-transform: uppercase;
}
.specimen-story__assurance li { position: relative; padding-left: .85rem; }
.specimen-story__assurance li::before {
  position: absolute;
  top: .48em;
  left: 0;
  width: .34rem;
  height: .34rem;
  border-radius: 50%;
  background: var(--specimen-accent);
  content: "";
}

/* Nothing beside this competes with it. */
.specimen-story__commerce { display: block; margin: clamp(1.6rem, 3vw, 2.4rem) 0 0; }
.add-button--buy { width: 100%; max-width: 26rem; }


/* The product name is a product name now, not a campaign line. */
.specimen-story__content h3 {
  font-size: clamp(1.9rem, 3.1vw, 3rem);
  line-height: 1.02;
  letter-spacing: -0.035em;
}
.specimen-story__content h3 em { font-style: normal; }

@media (max-width: 760px) {
  .add-button--buy { max-width: none; }
  .specimen-story__spec { gap: 2rem; }
}

/* ============================================================================
   THE DISCOVERY MATRIX — affordance only

   The information architecture was already right; the four cells simply did
   not read as interactive. Everything below is about that and nothing else —
   the buttons, icons, labels, destinations and hover inversion are the
   original implementation restored from HEAD.

   The cue is a faint fruit-colour edge luminance, present at rest so it does
   not depend on hover, and lifted a step on hover and focus. No pulse, no
   neon, nothing that moves on its own.
   ========================================================================== */
/* Scoped to .specimen-story to match the 0,2,0 rule above that sets a 4-up
   row; at 0,1,0 this lost the cascade on desktop no matter where it sat. */
.specimen-story .product-lenses {
  grid-template-columns: repeat(2, 1fr);
  max-width: 26rem;
  margin: clamp(1.6rem, 3vw, 2.2rem) 0 0;
}

.product-lenses button {
  position: relative;
  min-height: 5.4rem;
  align-content: space-between;
  padding: 0.8rem;
  /* At rest: a hairline of the fruit's own colour just inside the tile. Enough
     to say "this is a control" from across the page, not enough to compete
     with the buy button directly above it. */
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--specimen-accent) 26%, transparent),
              0 0 1.1rem -0.55rem color-mix(in srgb, var(--specimen-accent) 60%, transparent);
  transition: background-color 180ms ease, color 180ms ease, box-shadow 220ms ease, transform 120ms var(--ease-out);
}
.product-lenses button .ui-icon { width: 1.05rem; height: 1.05rem; opacity: 0.72; transition: opacity 180ms ease; }

@media (hover: hover) and (pointer: fine) {
  .product-lenses button:hover {
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--specimen-accent) 62%, transparent),
                0 0 1.8rem -0.5rem color-mix(in srgb, var(--specimen-accent) 85%, transparent);
  }
  .product-lenses button:hover .ui-icon { opacity: 1; }
}

.product-lenses button:focus-visible {
  outline: 1px solid var(--specimen-accent);
  outline-offset: -3px;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--specimen-accent) 70%, transparent),
              0 0 2rem -0.5rem color-mix(in srgb, var(--specimen-accent) 90%, transparent);
}
/* Energised on the way into its destination. */
.product-lenses button:active {
  transform: scale(0.985);
  background: var(--specimen-accent);
  color: var(--void);
  box-shadow: inset 0 0 0 1px var(--specimen-accent), 0 0 2.4rem -0.4rem var(--specimen-accent);
}
.product-lenses button:active .ui-icon { opacity: 1; }

/* No hover on a phone, so the cue is simply always there and the whole tile is
   the tap target. */
@media (hover: none) {
  .product-lenses button {
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--specimen-accent) 38%, transparent),
                0 0 1.3rem -0.5rem color-mix(in srgb, var(--specimen-accent) 70%, transparent);
  }
  .product-lenses button .ui-icon { opacity: 0.9; }
}

@media (max-width: 760px) {
  .product-lenses { max-width: none; }
  .product-lenses button { min-height: 4.8rem; }
}

/* Static states only; nothing here animates on its own in any case. */
@media (prefers-reduced-motion: reduce) {
  .product-lenses button { transition: none; }
}

/* ============================================================================
   PHASE 02A — CANONICAL PDP SHELL

   Purchase hierarchy as one continuous read: name, proposition, weight+price,
   assurances, one CTA, then the intelligence matrix. The rows below only
   tighten the rhythm between those blocks — no child is added or reordered,
   because .specimen-story__content is a flex column with explicit order on
   every child and an unordered child sorts to 0, above the product name.
   ========================================================================== */
@media (max-width: 760px) {
  /* Buying information should arrive right after the media, not a screen
     later. Only the gaps move; type sizes, copy and hierarchy are untouched. */
  .specimen-story__index { margin-bottom: 0.32rem; }
  .specimen-story__lead { margin-top: 0.32rem; margin-bottom: 0.38rem; }
  .specimen-story__spec { margin-top: 0.44rem; }
  .specimen-story__assurance { margin-top: 0.4rem; }
  .specimen-story__commerce { margin-top: 0.58rem; }
  .specimen-story .product-lenses { margin-top: 0.7rem; }
}

/* --- desktop pointer luminance -------------------------------------------
   The Spotlight-card mechanic, reproduced natively: pointermove writes two
   custom properties, CSS paints a radial in the fruit accent. No library, no
   React, no per-frame JS work beyond two style writes. Gated to real pointers
   so a phone never pays for it, and disabled entirely under reduced motion. */
@media (hover: hover) and (pointer: fine) {
  .specimen-story .product-lenses button::before {
    position: absolute;
    inset: 0;
    content: "";
    pointer-events: none;
    opacity: 0;
    background: radial-gradient(9rem circle at var(--sx, 50%) var(--sy, 50%),
      color-mix(in srgb, var(--specimen-accent) 26%, transparent), transparent 62%);
    transition: opacity 260ms ease;
  }
  .specimen-story .product-lenses button:hover::before { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .specimen-story .product-lenses button::before { display: none; }
}

/* --- sticky rail: the fruit in view is the primary action ---------------- */
.buy-rail__primary--add { border: 0; font: inherit; cursor: pointer; }
.buy-rail[data-rail-mode="product"] .buy-rail__secondary { white-space: nowrap; }

/* ============================================================================
   DOSSIER RAIL

   The 2x2 matrix under each specimen story opens four records. Until this rail
   existed it could only open them ONE AT A TIME, from the outside: every switch
   was close, scroll back to the product, press the next tile. Four doors and no
   corridor. The rail is the corridor -- the same four destinations, rendered
   identically inside all three shells, so a reader who is already inside the
   dossier never has to leave it to change subject.

   IT IS THE SAME COMPONENT IN ALL THREE, deliberately. The shells stay distinct
   -- a data panel, an editorial spread, a catalogue sheet -- and the rail is the
   one element that does not change between them, which is what makes them read
   as three views of one specimen rather than three separate modals.

   Instrument tabs, not a pill group: hairline dividers, micro-caps, a single
   lit rule under the record you are reading. Colour comes from the SKU, through
   whichever accent the host shell already owns -- one variable in, so the rail
   never has to know which fruit it is showing.
   ========================================================================== */
.nutrition      { --dossier-accent: var(--nutrition-accent); }
.insight        { --dossier-accent: var(--insight-accent); }
.origin-archive { --dossier-accent: var(--origin-accent); }

.dossier-rail { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; border-bottom: 1px solid rgba(255,255,255,0.14); }
.dossier-rail button {
  position: relative;
  display: flex; min-height: 3.2rem; align-items: center; justify-content: center; gap: 0.55rem;
  padding: 0 0.5rem;
  border: 0; border-left: 1px solid rgba(255,255,255,0.09);
  background: transparent;
  color: var(--moon-dim);
  cursor: pointer;
  font: inherit; font-size: 0.58rem; letter-spacing: 0.14em; text-transform: uppercase;
  transition: color 220ms var(--ease-out), background-color 220ms var(--ease-out);
}
.dossier-rail button:first-child { border-left: 0; }
/* The dot is the state, the rule underneath is the confirmation. Two marks
   rather than one because colour alone cannot carry current-ness for a reader
   who cannot separate the accent from the dim ivory beside it. */
.dossier-rail button i { width: 4px; height: 4px; background: currentColor; opacity: 0.32; transition: opacity 220ms var(--ease-out); }
.dossier-rail button::after {
  content: ""; position: absolute; right: 0; bottom: -1px; left: 0; height: 1px;
  background: var(--dossier-accent, var(--nectar));
  transform: scaleX(0);
  transition: transform 340ms var(--ease-garden);
}
.dossier-rail button:hover:not(:disabled) { color: var(--moon); background: rgba(255,255,255,0.03); }
.dossier-rail button:focus-visible { outline: 2px solid var(--dossier-accent, var(--nectar)); outline-offset: -4px; }
/* The record already on screen is disabled, not merely styled: there is nowhere
   for it to go, and the focus traps in all three overlays skip disabled nodes. */
.dossier-rail button:disabled { cursor: default; }
.dossier-rail button.is-current { color: var(--dossier-accent, var(--nectar)); background: rgba(255,255,255,0.045); }
.dossier-rail button.is-current i { opacity: 1; }
.dossier-rail button.is-current::after { transform: scaleX(1); }

/* The nutrition and insight shells scroll as one column under a sticky header,
   so the rail pins beneath it. --dossier-top is the header's MEASURED height
   (js/dossier.js): 5.5rem is its declared minimum, not what it resolves to when
   the SKU line wraps, and a guessed offset leaves either a gap or an overlap. */
.nutrition .dossier-rail,
.insight .dossier-rail { position: sticky; z-index: 2; top: var(--dossier-top, 5.5rem); backdrop-filter: blur(20px) saturate(1.4); }
.nutrition .dossier-rail { background: color-mix(in srgb, var(--nutrition-surface) 92%, transparent); }
.insight .dossier-rail { background: color-mix(in srgb, var(--insight-surface) 92%, transparent); }
/* The archive needs none of that: its bar is a flex item outside the scroller,
   so the rail below it is already held in place by the layout. It does need the
   same surface tint, though: the sheet carries its bloom at full strength right
   where the rail sits, so without this the one component that is supposed to be
   identical in all three shells arrived as a bright violet band in this one. */
.origin-archive .dossier-rail { flex: 0 0 auto; background: color-mix(in srgb, var(--origin-surface) 82%, transparent); backdrop-filter: blur(20px) saturate(1.4); }

/* Four labels across 360px. The tracking that makes the rail read as
   instrumentation at desktop is the first thing to spend at phone widths. */
@media (max-width: 520px) {
  .dossier-rail button { min-height: 3rem; gap: 0.4rem; padding: 0 0.3rem; font-size: 0.54rem; letter-spacing: 0.07em; }
}

/* The shell arriving from a handoff is lifted over the one it replaces. Base
   stacking is nutrition 600, insight 610, archive 620, so any switch DOWN that
   order would otherwise reveal behind the panel it is meant to be covering. */
.nutrition.is-arriving,
.insight.is-arriving,
.origin-archive.is-arriving { z-index: 700; }

@media (prefers-reduced-motion: reduce) {
  .dossier-rail button,
  .dossier-rail button i,
  .dossier-rail button::after { transition-duration: 1ms; }
}

/* ============================================================================
   FIT TO PAGE

   Stopping the overflow was half the job. Once every panel fitted, its content
   stayed a fixed stack pinned to the top and the footer was pinned to the base,
   so every pixel of slack pooled in ONE band between them. Measured with mango:
   at 1512x945 the panels left 233-361px dead beneath their last element, and at
   1920x1080 up to 497px. That is not negative space, it is a page that stops.

   So the slack is DISTRIBUTED into the composition instead of collected under
   it. Every rule below only spends space that is already free: minimum sizes
   are untouched, so a short viewport with no slack renders exactly as before
   and the zero-scroll sweep still holds.
   ========================================================================== */
@media (min-width: 1100px) {
  /* NUTRITION. The pouch figure and the notable card stay together at the head
     of the left column and the absence band drops to its foot, level with the
     end of the declaration. Spreading all three evenly (space-between, the first
     attempt) left the notable card floating between two voids. The declaration
     spans all three tracks and its rows share the column -- 1fr, not
     minmax(0, 1fr), so a row can never be squeezed below its content. */
  .nutrition__grid { grid-template-rows: auto auto 1fr; align-content: stretch; }
  .nutrition-absence { align-self: end; }
  .nutrition-record { display: flex; flex-direction: column; align-self: stretch; }
  .nutrition-record ol { flex: 1 1 auto; grid-template-rows: repeat(7, 1fr); }
  .nutrition-record li { align-content: center; }

  /* TASTE AND RITUALS. One flex column from hero to footer; the body's single
     child takes the rest. Taste rows grow evenly and keep their bars centred.
     Ritual cards grow as columns, and the title goes back to the foot of the
     card with the occasion label at its head -- the composition the cards had
     when min-height was doing this job. */
  .insight__body { display: flex; flex-direction: column; }
  .insight__body > * { flex: 1 1 auto; }
  .taste-map { display: flex; flex-direction: column; }
  .taste-map article { flex: 1 1 0; }
  .ritual-grid { align-content: stretch; }
  .insight-facts strong, .ritual-grid strong { margin-top: auto; }
}

/* ORIGIN. The plate is already centred in its half; the text sat at the top of
   the other half with 210-365px beneath it. Auto margins on the first and last
   child centre the dossier against the plate -- and unlike justify-content:
   center they collapse to zero when the column is full, so the first line can
   never be pushed above a scroller's reach on a short screen. */
@media (min-width: 901px) {
  .origin-archive__text { display: flex; flex-direction: column; }
  .origin-archive__text > :first-child { margin-top: auto; }
  .origin-archive__text > :last-child { margin-bottom: auto; }
}

/* DENSITY FOLLOWS HEIGHT. Two columns of seven exist to fit the declaration on
   a short screen. On a tall one they are the wrong density: filling 620px with
   seven rows each made every row ~86px, a double-spaced spreadsheet. Past 860px
   of height there is room for all fourteen in one column at ~44px, which is how
   a printed declaration reads anyway. Below it, the two-column layout returns.

   Rituals is the same problem in a different shape: four cards stretched to
   ~500px tall held a label at the top, a title at the bottom and nothing in
   between -- the dead space moved inside the cards rather than going away. Two
   by two gives each card a wide, short proportion that its content can fill. */
/* The figures grow with the height they are given -- but only where there is
   height to give. Declared unguarded, these came later in the file than the
   short-viewport block and overrode its sizes: at 1100x700 the pouch figure
   went from ~59px to ~88px and the panel scrolled by 3px. */
@media (min-width: 1100px) and (min-height: 791px) {
  .nutrition-pouch__fig { font-size: clamp(3rem, min(8vw, 12.5vh), 8rem); }
  .nutrition-present b { font-size: clamp(1.8rem, min(4vw, 6.2vh), 4.2rem); }
}
@media (min-width: 1100px) and (min-height: 861px) {
  .nutrition-record ol { grid-template-columns: 1fr; grid-template-rows: repeat(14, 1fr); grid-auto-flow: row; }
  .nutrition-record li { font-size: clamp(0.76rem, 1.5vh, 0.95rem); }
  .nutrition-record li em { min-width: 8rem; }
}
@media (min-width: 1100px) and (min-height: 821px) {
  .ritual-grid { grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(2, 1fr); }
  .ritual-grid article { padding: clamp(1.1rem, 2.4vh, 1.9rem) clamp(1.2rem, 2vw, 2rem); }
  .ritual-grid strong { font-size: clamp(1.6rem, min(2.4vw, 4.2vh), 3rem); }
  .ritual-grid p { max-width: 34rem; font-size: clamp(0.7rem, 1.45vh, 0.9rem); }
}
