:root{
  /* Palette matched EXACTLY to the booking site (tyl-booking-web) — not just
     "the same family," the identical hex values — so the two sites read as
     one property. Values are copied straight from that site's :root
     (ink/pine/sage/linen→paper/mist/ember). If the booking site's palette
     ever changes, mirror the change here too. Variable NAMES are kept from
     this file's own history so every existing rule keeps working. */
  --ink:#161616;
  --pine:#1F1F1F;
  --sage:#A6A6A0;
  --linen:#FFFDF7;        /* = booking's --paper (page background) */
  --sand:#ECECE8;         /* = booking's --mist (card/band tint) */
  --amber:#B4632F;        /* = booking's --ember — now the shared brand accent */
  --amber-dark:#904F26;
  --nuyu:#E4589B;         /* NUYU's own brand pink — a third-party mark, not part of site theming, left as-is */
  /* Signals the Tropic partner section. Chosen to read as botanical next to
     our amber, NOT lifted from Tropic's brand assets — we aren't licensed to
     reproduce their marks, and a section that merely links out doesn't need to. */
  --leaf:#4A6B45;
  --leaf-dark:#3A5537;
  --white:#FFFFFF;

  /* Text tones, semantic rather than hardcoded, so the next theme change is
     one edit here instead of a hunt through the stylesheet. */
  --muted:#5C5C56;        /* body copy on light surfaces */
  --muted-2:#70706A;      /* small print on light surfaces */
  --muted-3:#6F6F69;      /* struck-through prices — was #9E9E97, only 2.70:1
                             on white, which made the old price of every
                             discounted item the least readable text on the
                             page. Now 5.0:1. */
  --on-dark:#F6F3EA;      /* = booking's --linen, its own on-dark text colour */
  --on-dark-soft:#A6A6A0; /* = booking's --sage, reused for quieter on-dark text */

  --radius:20px;          /* = booking's --r-lg */
  --shadow:0 18px 40px -18px rgba(20,20,20,.35); /* = booking's shadow formula */
}
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth}
body{font-family:'Inter',sans-serif;color:var(--ink);background:var(--linen);line-height:1.6}
img{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
h1,h2,h3,.serif{font-family:'Inter',sans-serif;font-weight:700;line-height:1.15}
/* 1180, not 1120: buys ~60px across every section — a longer measure for the
   body copy and wider product cards — without pushing line length past a
   comfortable reading width. */
.wrap{max-width:1180px;margin:0 auto;padding:0 22px}
.eyebrow{display:inline-block;font-size:.72rem;letter-spacing:.16em;text-transform:uppercase;font-weight:700;color:var(--pine);background:rgba(26,26,26,.07);padding:5px 12px;border-radius:999px;margin-bottom:14px}
.btn{display:inline-block;background:var(--pine);color:#fff;font-weight:700;font-size:.95rem;padding:13px 24px;border-radius:999px;border:none;cursor:pointer;transition:transform .15s ease, background .15s ease}
.btn:hover{background:var(--ink);transform:translateY(-1px)}
.btn.ghost{background:transparent;color:var(--pine);border:1.5px solid var(--pine)}
.btn.ghost:hover{background:var(--pine);color:#fff}
/* --amber-dark as the resting fill, not --amber: white on --amber is 4.41:1,
   just under the 4.5:1 these labels need at 15px. The --amber TOKEN is left
   alone (it is matched to the booking site and still used for icons, borders
   and the breathing dot, none of which are text) — only this button, which
   carries white type, is darkened. */
.btn.amber{background:var(--amber-dark)}
.btn.amber:hover{background:#7A421F}
:focus-visible{outline:3px solid var(--amber);outline-offset:2px}

/* ---- ticker -------------------------------------------------------------
   Replaces the old static announcement bar with a scrolling marquee, the
   same component (and the exact same seamless-loop technique — write the
   items once, duplicate the markup, animate -50%) already used on the
   booking site's ticker. Kept on a dark bar rather than the booking site's
   light one, so it still reads as a bold offers strip, closer to the
   reference designs, rather than disappearing into the page background. */
.ticker{background:var(--ink);overflow:hidden;white-space:nowrap;position:relative}
.ticker-track{display:inline-flex;gap:0;animation:ticker-scroll 34s linear infinite;will-change:transform}
.ticker:hover .ticker-track,.ticker:focus-within .ticker-track{animation-play-state:paused}
.ticker span{display:inline-flex;align-items:center;gap:.6rem;padding:.55rem 1.4rem;font-size:.78rem;font-weight:700;letter-spacing:.03em;color:var(--on-dark)}
.ticker span b{color:var(--amber)}
.ticker span::after{content:"✦";color:var(--amber);font-size:.65rem}
@keyframes ticker-scroll{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@media(prefers-reduced-motion:reduce){.ticker-track{animation:none;flex-wrap:wrap}}

/* ---- trust strip ----------------------------------------------------
   A row of icon + claim, directly under the hero. Every claim here is one
   already made elsewhere on the site (ribbon, hero note, help page) — this
   is a second, more scannable presentation of the same facts, not new ones. */
/* padding:0 overrides the global `section{padding:64px 0}`. This is a claim
   bar continuous with the hero panel above it, not a section of its own — the
   inherited padding was adding 128px of dead space and floating it away from
   the hero it is supposed to be joined to. */
.trust-strip{background:var(--ink);border-radius:0 0 var(--radius) var(--radius);
  margin:0 14px 32px;padding:0}
/* Four across since Tropic joined the strip. Was a hard repeat(3), which left
   the fourth item orphaned on a row of its own. */
.trust-strip .wrap{display:grid;grid-template-columns:repeat(4,1fr);gap:0}
.trust-strip li{list-style:none;display:flex;align-items:center;gap:10px;padding:16px 14px;font-size:.8rem;font-weight:600;color:var(--on-dark);border-left:1px solid rgba(255,255,255,.1)}
.trust-strip li:first-child{border-left:none}
.trust-strip svg{width:20px;height:20px;flex:0 0 auto;stroke:var(--amber);fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
@media(max-width:820px){
  .trust-strip .wrap{grid-template-columns:repeat(2,1fr)}
  .trust-strip li{border-left:none;border-top:1px solid rgba(255,255,255,.1)}
  .trust-strip li:nth-child(-n+2){border-top:none}
}

/* ---- product trust tags -----------------------------------------------
   Small honest claims ("Studio favourite" etc.) drawn from copy already
   written for that product — never a fabricated review count or rating. */
.trust-tag{position:absolute;top:12px;left:12px;background:var(--ink);color:var(--on-dark);font-size:.68rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;border-radius:999px;padding:5px 11px;z-index:1}
.product-hero .trust-tag{position:static;display:inline-block;margin-bottom:12px}

/* header */
header{position:sticky;top:0;z-index:50;background:rgba(255,253,247,.92);backdrop-filter:blur(8px);border-bottom:1px solid var(--sand)}
.nav{display:flex;align-items:center;gap:26px;padding:14px 0}
.brand{display:flex;align-items:center;gap:10px;font-family:'Inter',sans-serif;font-size:1.15rem;font-weight:800}
.brand img{height:40px;width:auto;border-radius:8px}
.brand small{display:block;font-family:'Inter',sans-serif;font-size:.68rem;letter-spacing:.22em;text-transform:uppercase;color:var(--amber);font-weight:700}
.nav ul{display:flex;gap:22px;list-style:none;margin-left:auto;font-size:.92rem;font-weight:600}
.nav ul a:hover{color:var(--pine)}
.basket-btn{position:relative;background:var(--pine);color:#fff;border:none;border-radius:999px;padding:10px 18px;font-weight:700;cursor:pointer;font-family:'Inter',sans-serif}
.basket-btn span{background:var(--amber);border-radius:999px;padding:1px 8px;margin-left:6px;font-size:.8rem}
.menu-toggle{display:none;background:none;border:none;width:42px;height:42px;cursor:pointer;place-items:center;color:var(--ink)}
.menu-toggle svg{width:26px;height:26px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round}
@media(max-width:820px){
  .nav{gap:12px}
  /* Absolute, not fixed: the header's backdrop-filter makes it the containing
     block for fixed descendants, so top:100% under the sticky header is both
     simpler and correct. */
  .nav ul{position:absolute;top:100%;left:0;right:0;flex-direction:column;gap:0;background:var(--linen);border-bottom:1px solid var(--sand);padding:6px 22px 18px;margin-left:0;display:none;z-index:55;box-shadow:var(--shadow)}
  .nav ul.open{display:flex}
  .nav ul li{width:100%}
  .nav ul a{display:block;padding:14px 0;border-bottom:1px solid var(--sand)}
  .nav ul li:last-child a{border-bottom:none}
  .menu-toggle{display:grid}
  .brand span{font-size:1rem}
}

/* ---- hero -----------------------------------------------------------
   A floating dark panel, matching the same treatment #supplements/.banner
   already use elsewhere on this page (round corners, inset margin) — so the
   hero is consistent with the rest of the page rather than the one exception.
   The hero and the trust strip below it share one continuous rounded panel:
   hero owns the top corners, trust-strip the bottom ones.

   The hero's job here is to MERCHANDISE, not decorate. See js/hero.js for
   why the old auto-rotating photo carousel was replaced by a real shelf of
   named, priced products. */
.hero{position:relative;overflow:hidden;color:var(--on-dark);padding:0;
  background:linear-gradient(160deg, #2a2a2a, var(--pine) 55%, var(--ink));
  border-radius:var(--radius) var(--radius) 0 0;
  border:3px solid var(--ink);border-bottom:none;
  margin:22px 14px 0}

/* Ambient colour, taken from whichever product is currently featured (its own
   sampled accent, set on the panel as --glow by js/hero.js). The dark wash on
   the left is permanent and load-bearing: it guarantees the white heading
   stays readable no matter which product's colour is currently glowing. */
/* Registering --glow as a real colour lets the browser INTERPOLATE it, so
   changing product cross-fades the whole backdrop instead of snapping to the
   next colour. Unregistered (older browsers) it simply jumps — no worse than
   before. */
@property --glow{syntax:'<color>';inherits:true;initial-value:#B4632F}
.hero{transition:--glow .55s ease}
.hero-aura{position:absolute;inset:0;z-index:0;pointer-events:none;
  background:
    /* Two stacked washes of the featured product's own colour: a tight, strong
       core right behind the card and a broad soft one filling the panel, so
       the range on screen actually tints the hero rather than just tinging it. */
    radial-gradient(560px 400px at 72% 48%, color-mix(in srgb, var(--glow) 60%, transparent), transparent 72%),
    radial-gradient(1000px 660px at 78% 42%, color-mix(in srgb, var(--glow) 32%, transparent), transparent 76%),
    radial-gradient(900px 480px at 82% 6%, rgba(255,255,255,.05), transparent 60%),
    /* Left-hand scrim stays, and stays last: it is what guarantees the white
       heading survives whichever colour is currently glowing behind it. */
    linear-gradient(100deg, rgba(0,0,0,.62) 0%, rgba(0,0,0,.26) 46%, transparent 66%)}
@supports not (background: color-mix(in srgb, red 50%, transparent)){
  .hero-aura{background:linear-gradient(100deg, rgba(0,0,0,.58) 0%, rgba(0,0,0,.24) 46%, transparent 68%)}
}

.hero h1{font-size:clamp(2rem,3.4vw,3rem);letter-spacing:-.015em;color:#fff}
.hero h1 em{font-style:italic;color:var(--brand,var(--amber))}
.hero p.lead{margin:12px 0 22px;font-size:.98rem;color:var(--on-dark);max-width:46ch}
.hero .eyebrow{background:rgba(255,255,255,.14);color:#fff}
.hero-cta{display:flex;gap:14px;flex-wrap:wrap}
.hero-note{margin-top:18px;font-size:.8rem;color:var(--on-dark-soft)}
/* ---- Hero: range tabs -------------------------------------------------
   What the old carousel's dots were doing, made explicit and handed to the
   visitor. Nothing rotates on a timer any more, so this is the only thing
   that ever changes what the hero shows. */
.hero-head{position:relative;z-index:3;padding-top:16px}
.hero-tabs{display:inline-flex;gap:4px;padding:4px;border-radius:999px;
  background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.11)}
.hero-tabs button{display:inline-flex;align-items:center;gap:9px;cursor:pointer;
  font:inherit;color:var(--on-dark-soft);background:none;border:none;
  padding:8px 17px;border-radius:999px;
  transition:background .22s ease, color .22s ease}
.hero-tabs button::before{content:"";width:7px;height:7px;border-radius:50%;
  background:var(--brand);flex:0 0 auto}
.hero-tabs button b{font-size:.92rem;font-weight:800;letter-spacing:.01em}
.hero-tabs button em{font-style:normal;font-size:.68rem;letter-spacing:.13em;
  text-transform:uppercase;opacity:.75}
/* :not(selected) is load-bearing. A bare :hover has the same specificity as
   the [aria-selected] rule below and, coming first, would still have won the
   cascade on the selected tab — painting white text onto the cream pill and
   making the label of the tab you are pointing at disappear. */
.hero-tabs button:not([aria-selected="true"]):hover{color:#fff;background:rgba(255,255,255,.07)}
/* Cream-on-ink for the selected tab rather than brand-on-white: the two brand
   colours need opposite text colours to stay legible (see onAccent in
   js/brands.js), and this sidesteps that entirely with one safe pairing. */
.hero-tabs button[aria-selected="true"]{background:var(--on-dark);color:var(--ink)}

/* ---- Hero: panels -----------------------------------------------------
   One per range. Only the selected one is in the DOM flow ([hidden]), so
   there is no stacking, no cross-fade and no hidden-but-focusable content. */
.hero-stage{position:relative;z-index:1}
.hero-panel{position:relative}
.hero-panel[hidden]{display:none}
.hero-panel:focus{outline:none}
.hero-panel .wrap{position:relative;z-index:1;display:grid;
  grid-template-columns:1.38fr 1fr;gap:46px;align-items:center;
  padding-top:28px;padding-bottom:34px}
/* align-items:flex-start is load-bearing: without it the flex column stretches
   every child to full width, which turned the eyebrow pill into a 535px bar. */
.hero-say{display:flex;flex-direction:column;justify-content:center;align-items:flex-start}
.hero-standfirst{background:rgba(255,255,255,.14);color:#fff}
.hero-panel .breathe{background:var(--brand)}
/* .btn.ghost is two classes, so a bare .hero-alt lost the specificity fight
   and the button kept ghost's dark-on-dark text — it read as disabled. */
.btn.ghost.hero-alt{background:rgba(255,253,247,.10);border-color:rgba(246,243,234,.8);color:#fff}
.btn.ghost.hero-alt:hover{background:var(--on-dark);border-color:var(--on-dark);color:var(--ink)}

/* ---- Hero: the shelf --------------------------------------------------
   THE BLEND, which is the whole idea here. Not one product photo we have has
   a transparent background — every one is opaque with its own colour baked in
   (see SHOTS in js/data.js). So a card is painted with that photo's OWN
   corner colours, and the photo's edges dissolve into it: the letterboxing
   either side of a square image in a wider card is genuinely invisible, and
   product + card read as one object rather than a picture stuck on a tile.
   Change --shot-a/--shot-b and the mount follows the photograph. */
.hero-shop{display:flex;flex-direction:column;gap:10px;min-width:0}

.hero-feature{position:relative;display:block;border-radius:22px;overflow:hidden;
  background:linear-gradient(135deg, var(--shot-a,#e9e9e4), var(--shot-b,#dcdcd6));
  box-shadow:0 30px 64px -24px rgba(0,0,0,.8), 0 0 0 1px rgba(255,255,255,.09);
  transition:transform .35s cubic-bezier(.2,.8,.2,1), box-shadow .35s ease;
  -webkit-tap-highlight-color:transparent}
.hero-feature:hover{transform:translateY(-3px);
  box-shadow:0 38px 74px -24px rgba(0,0,0,.85), 0 0 0 1px rgba(255,255,255,.14)}
/* The padding is what stops the photo reading as a raw square. Every shot is
   a square image in a wider box, so it was painting a hard-edged block that
   ran the full height of the stage and butted straight into the meta strip —
   and because the stage gradient spans the whole box while the photo's own
   spans only the square, the two gradients run at different rates and the
   edge showed. Inset and rounded, it becomes a deliberate floating tile
   instead: nothing to line up, nothing to give away. */
/* The photo fills the card edge to edge, and the CARD's radius rounds it.
   There is deliberately no inner tile any more: an inset photo always draws a
   visible rectangle, because a rounded tile sitting on a stage can only ever
   approximate the colour behind it — and the hairline ring that was here made
   that edge explicit. Filling the card removes the boundary entirely rather
   than trying to disguise it, and the corners are then genuinely round
   because they are the card's own.

   1/0.95 rather than a square keeps the card from getting tall, and the 5%
   it crops is safe on every shot we carry: the tightest product bounding box
   in the catalogue still leaves ~9% clear top and bottom (see the bbox
   sampling behind SHOTS in js/data.js). Do not flatten this much further
   without re-checking those — past ~10% the taller bottles start to clip. */
.hero-feature-shot{display:block;position:relative;aspect-ratio:1/0.95}
.hero-feature-shot img{width:100%;height:100%;object-fit:cover;display:block}

.hero-feature-tag{position:absolute;top:13px;left:13px;z-index:2;
  background:var(--ink);color:var(--on-dark);font-size:.66rem;font-weight:700;
  letter-spacing:.08em;text-transform:uppercase;padding:5px 11px;border-radius:999px}
.hero-feature-tag[hidden]{display:none}

/* The meta strip carries its own near-opaque surface rather than sitting on
   the photo. Backdrops range from white to mid-grey across the catalogue, so
   nothing else guarantees the type stays legible on every product. */
/* The backdrop-filter below makes this a containing block for absolutely
   positioned descendants (filter and backdrop-filter both do). That is why
   .hero-feature-tag is a sibling of this strip rather than a child — inside
   it, it anchored here and landed on top of the product name. */
.hero-feature-meta{display:flex;flex-direction:column;gap:1px;
  padding:12px 16px 13px;background:rgba(255,253,247,.95);color:var(--ink);
  -webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);
  border-top:1px solid rgba(22,22,22,.07)}
.hero-feature-sub{font-size:.68rem;font-weight:700;letter-spacing:.13em;
  text-transform:uppercase;color:var(--muted-2)}
.hero-feature-name{font-size:1.1rem;font-weight:800;line-height:1.25;letter-spacing:-.005em}
.hero-feature-buy{display:flex;align-items:baseline;justify-content:space-between;
  gap:14px;margin-top:7px}
.hero-feature-price{font-size:1.16rem;font-weight:800;white-space:nowrap}
.hero-feature-price s{color:var(--muted-3);font-weight:500;font-size:.84rem;margin-left:7px}
/* --amber-dark, not --amber: on this near-white strip the lighter amber is
   4.30:1, just under the 4.5:1 needed at this size. The darker one is 6.15:1. */
.hero-feature-go{font-size:.84rem;font-weight:700;color:var(--amber-dark);white-space:nowrap}
.hero-feature:hover .hero-feature-go{color:var(--ink)}

@keyframes hero-swap{from{opacity:.25;transform:scale(.985)}to{opacity:1;transform:none}}
.hero-feature.is-swap .hero-feature-shot img{animation:hero-swap .34s ease}

/* The rail. Six products, all visible at once — which is what makes the
   timer unnecessary: there is nothing hidden to rotate into view. They are
   real links, so the hero still works as a browsable row with JS off. */
.hero-rail{display:grid;grid-template-columns:repeat(6,1fr);gap:8px}
/* Scroll affordance. When the strip is scrollable the edge with more product
   beyond it fades out, so a half-cut thumbnail reads as "keep going this way"
   rather than as a layout that has broken. A mask rather than an overlay
   element, because a mask is painted against the element's own box and so
   stays put while the contents scroll underneath it. The classes are added by
   js/hero.js only when there is genuinely something to scroll to. */
.hero-rail.can-scroll-right{
  -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 44px),transparent);
          mask-image:linear-gradient(90deg,#000 calc(100% - 44px),transparent)}
.hero-rail.can-scroll-left{
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 44px);
          mask-image:linear-gradient(90deg,transparent,#000 44px)}
.hero-rail.can-scroll-left.can-scroll-right{
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 44px,#000 calc(100% - 44px),transparent);
          mask-image:linear-gradient(90deg,transparent,#000 44px,#000 calc(100% - 44px),transparent)}
.hero-thumb{position:relative;display:block;border-radius:12px;overflow:hidden;
  background:linear-gradient(135deg, var(--shot-a,#e9e9e4), var(--shot-b,#dcdcd6));
  box-shadow:0 10px 20px -12px rgba(0,0,0,.7);
  outline:2px solid transparent;outline-offset:-2px;
  transition:transform .25s ease, outline-color .25s ease, box-shadow .25s ease;
  -webkit-tap-highlight-color:transparent}
.hero-thumb-shot{display:block;aspect-ratio:1/1}
.hero-thumb-shot img{width:100%;height:100%;object-fit:contain;display:block}
.hero-thumb.is-cover .hero-thumb-shot img{object-fit:cover}
.hero-thumb-price{display:block;text-align:center;font-size:.71rem;font-weight:800;
  padding:5px 2px 6px;background:rgba(255,253,247,.95);color:var(--ink)}
.hero-thumb:hover{transform:translateY(-3px)}
.hero-thumb.is-on{outline-color:var(--brand);transform:translateY(-3px);
  box-shadow:0 16px 26px -14px rgba(0,0,0,.75)}

@media(prefers-reduced-motion:reduce){
  .hero-feature,.hero-thumb{transition:none}
  .hero-feature:hover,.hero-thumb:hover,.hero-thumb.is-on{transform:none}
  .hero-feature.is-swap .hero-feature-shot img{animation:none}
}

@media(max-width:980px){
  .hero-panel .wrap{grid-template-columns:1fr;gap:32px;
    padding-top:30px;padding-bottom:38px}
  .hero-shop{width:100%;max-width:540px}
  /* Stacked, the text no longer sits to the left of the imagery, so the
     left-weighted scrim would darken nothing useful. */
  .hero-aura{background:
    radial-gradient(620px 420px at 70% 62%, color-mix(in srgb, var(--glow) 34%, transparent), transparent 70%),
    linear-gradient(180deg, rgba(0,0,0,.55) 0%, rgba(0,0,0,.30) 46%, rgba(0,0,0,.42) 100%)}
  .hero p.lead{max-width:52ch}
}
@media(max-width:620px){
  .hero-head{padding-top:18px}
  .hero-tabs button{padding:7px 13px}
  .hero-tabs button em{display:none}
  /* Deliberately NOT flattened on mobile. This was 16/12, which with
     object-fit:cover cropped 12.5% off the top AND bottom of every shot — well
     past the ~5% margin the tightest products in the catalogue have, so the
     taller bottles were losing their caps and bases on phones. Any change here
     has to be checked against the bounding boxes behind SHOTS in js/data.js. */
  .hero-feature-shot{aspect-ratio:1/0.95}
  /* Everything here buys vertical space for one reason: on a phone the shelf
     is what has to get near the fold. Left as the desktop rhythm, the first
     product sat the better part of a screen below it — a merchandising hero
     whose merchandise you had to go looking for. */
  .hero-panel .wrap{gap:24px;padding-top:22px;padding-bottom:30px}
  .hero h1{font-size:1.9rem}
  .hero p.lead{font-size:.94rem;margin:10px 0 18px;max-width:40ch}
  .hero .eyebrow{margin-bottom:10px}
  .hero-note{margin-top:14px}
  /* Side by side rather than stacked — two full-width buttons cost a whole
     extra row of height for no added clarity. */
  .hero-cta{gap:10px;flex-wrap:nowrap}
  .hero-cta .btn{flex:1 1 auto;text-align:center;padding:12px 14px;font-size:.9rem}
  /* Six thumbs across a phone would be ~46px each. Scrolled instead, so they
     stay big enough to actually see the product. */
  /* padding-top is load-bearing: overflow-x:auto forces overflow-y to auto
     too, so without room above, the -3px lift on the hovered/selected
     thumbnail was being clipped off the top of the strip. */
  .hero-rail{display:flex;gap:8px;overflow-x:auto;scroll-snap-type:x proximity;
    -webkit-overflow-scrolling:touch;scrollbar-width:none;padding:5px 0 4px}
  .hero-rail::-webkit-scrollbar{display:none}
  .hero-thumb{flex:0 0 88px;scroll-snap-align:start}
}

/* ---- product wheel ----------------------------------------------------
   A slow rolling strip of the NUYU range, directly under the hero. The only
   moving thing on the landing page besides the breathing dot, and it moves
   products — so the eye is drawn to stock rather than away from it. */
.wheel-sec{padding:30px 0 6px}
.wheel-head{display:flex;align-items:baseline;justify-content:space-between;gap:16px;margin-bottom:16px}
.wheel-head .eyebrow{margin-bottom:0}
.wheel-head a{font-size:.85rem;font-weight:700;color:var(--pine);white-space:nowrap}
.wheel-head a:hover{color:var(--ink)}
.wheel{overflow:hidden;position:relative}
/* fade the two ends so the strip reads as continuous rather than cut off */
.wheel::before,.wheel::after{content:"";position:absolute;top:0;bottom:0;width:64px;z-index:2;pointer-events:none}
.wheel::before{left:0;background:linear-gradient(90deg,var(--linen),rgba(255,253,247,0))}
.wheel::after{right:0;background:linear-gradient(270deg,var(--linen),rgba(255,253,247,0))}
.wheel-track{display:flex;width:max-content}
.wheel-item{--accent:var(--sage);flex:0 0 auto;width:186px;margin-right:16px;background:#fff;border-radius:var(--radius);
  box-shadow:var(--shadow);padding:12px;display:flex;flex-direction:column;gap:6px;
  transition:transform .28s cubic-bezier(.22,1,.36,1),box-shadow .28s ease}
/* Two background layers: the photo, and beneath it a gradient painted with
   that photo's OWN sampled corner colours (SHOTS in js/data.js) so the image
   edges dissolve into the tile. Same technique as the hero shelf — see the
   ".hero-feature" comment there for why every shot needs it. */
/* Fills and is rounded by its own radius — same reasoning as the cards and the
   hero. Contained, each square photo left an 18px flat band down either side
   of a 162x126 tile, on all 38 of them. */
.wheel-shot{display:block;height:auto;aspect-ratio:1/0.95;border-radius:12px;
  background-image:var(--shot-img),linear-gradient(135deg,var(--shot-a,#fff),var(--shot-b,#fff));
  background-size:cover,cover;background-repeat:no-repeat,no-repeat;
  background-position:center,center}
.wheel-name{font-family:'Inter',sans-serif;font-weight:600;font-size:.92rem;line-height:1.25;color:var(--ink)}
.wheel-price{font-weight:700;font-size:.9rem;color:var(--ink)}
.wheel-price s{color:var(--muted-3);font-weight:400;font-size:.78rem;margin-left:5px}
/* Tropic entries in the wheel. They leave the site and can't be basketed, so
   they carry an outbound arrow and say whose price it is — the shopper should
   know that before the click. Tinted edge keeps them legible as a set without
   shouting over our own stock. */
.wheel-item.is-tropic{border:1px solid #DBE5D5;background:#FBFCFA}
.wheel-out{color:var(--leaf);font-weight:700}
.wheel-price em{display:block;font-style:normal;font-weight:500;font-size:.72rem;
  color:var(--leaf);letter-spacing:.02em;margin-top:2px}
.wheel-item:hover{transform:translateY(-4px)}
@supports (background:color-mix(in srgb,red 10%,white)){
  /* The tile no longer takes an accent wash — it is painted with the photo's
     own colours instead (see .wheel-shot). Only the hover glow still uses the
     accent, where it sits outside the image and can't clash with it. */
  .wheel-item:hover{box-shadow:0 16px 32px color-mix(in srgb,var(--accent) 26%,transparent)}
}
@media(prefers-reduced-motion:no-preference){
  .wheel-track{animation:roll 64s linear infinite}
  /* let people stop it to read or click, by mouse or by keyboard */
  .wheel:hover .wheel-track,.wheel:focus-within .wheel-track{animation-play-state:paused}
  @keyframes roll{from{transform:translateX(0)}to{transform:translateX(-50%)}}
}
@media(prefers-reduced-motion:reduce){
  /* no auto-roll — make it a normal swipeable strip instead */
  .wheel{overflow-x:auto}
  .wheel-item:hover{transform:none}
}
@media(max-width:600px){
  .wheel-item{width:150px}
  .wheel-shot{height:104px}
  .wheel::before,.wheel::after{width:28px}
}

/* breathing dot signature */
.breathe{width:10px;height:10px;border-radius:50%;background:var(--amber);display:inline-block;margin-right:9px;animation:breathe 4s ease-in-out infinite;vertical-align:middle}
@keyframes breathe{0%,100%{transform:scale(1);opacity:.7}50%{transform:scale(1.7);opacity:1}}
@media(prefers-reduced-motion:reduce){.breathe{animation:none}.btn:hover{transform:none}}

/* sections */
section{padding:64px 0}
.sec-head{max-width:60ch;margin-bottom:34px}
.sec-head h2{font-size:clamp(1.9rem,3.6vw,2.6rem);letter-spacing:-.01em}
.sec-head p{margin-top:10px;color:var(--muted)}

/* product grid */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:20px}
/* The supplements grid specifically uses a fixed column count (not the
   auto-fill above) because js/data.js deliberately orders that list so
   same-family products (Muscle & Joint solo/combo, Everyday solo/combo,
   the two collagens) land in the same column 4 apart — auto-fill would
   pick a column count based on available width and silently break that
   pairing. See the ordering comment in js/data.js. */
#grid-supplements{grid-template-columns:repeat(4,1fr)}
/* Below 4 columns the "same family, same column" trick above no longer
   applies — at 2 columns it would pair unrelated products in a row, and at
   1 column the family pairs end up 4 cards apart instead of next to each
   other. Rather than a second product order for smaller screens, reflow
   with `order` (nth-child position matches the DOM order in js/data.js):
   this turns each family into one row at 2 columns, and into two adjacent
   cards at 1 column — same underlying order, no JS or markup needed. */
@media(max-width:980px){
  #grid-supplements{grid-template-columns:repeat(2,1fr)}
  #grid-supplements > *:nth-child(1){order:1}  /* Sleep */
  #grid-supplements > *:nth-child(5){order:2}  /* Hydrate */
  #grid-supplements > *:nth-child(2){order:3}  /* Muscle & Joint solo */
  #grid-supplements > *:nth-child(6){order:4}  /* Muscle & Joint combo */
  #grid-supplements > *:nth-child(3){order:5}  /* Everyday solo */
  #grid-supplements > *:nth-child(7){order:6}  /* Everyday combo */
  #grid-supplements > *:nth-child(4){order:7}  /* Cogniskin */
  #grid-supplements > *:nth-child(8){order:8}  /* Marine Collagen + Bamboo */
}
@media(max-width:560px){#grid-supplements{grid-template-columns:1fr}}
/* --accent is set per card from the product's own photography (see js/data.js).
   Falls back to sage so a card is never unstyled. */
.card{--accent:var(--sage);background:#fff;border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow);display:flex;flex-direction:column;transition:transform .28s cubic-bezier(.22,1,.36,1),box-shadow .28s ease}
.card:hover{transform:translateY(-6px);box-shadow:0 18px 38px rgba(20,20,20,.16)}
@supports (background:color-mix(in srgb,red 10%,white)){
  /* hover glow picks up the product's own hue */
  .card:hover{box-shadow:0 18px 38px color-mix(in srgb,var(--accent) 26%,transparent)}
}
.swatch{height:150px;display:flex;align-items:flex-end;padding:14px;position:relative}
.swatch .tag{background:rgba(255,255,255,.9);border-radius:999px;font-size:.7rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;padding:4px 11px;color:var(--pine)}
/* The photo sits on a gradient of its OWN sampled corner colours (SHOTS in
   js/data.js), not on a wash of the product's accent. The accent is the
   dominant colour IN the product; the photo's backdrop is a different colour
   again, so tinting the surround with the accent drew a visible mismatched
   ring around all eleven photos. Matching the photo instead makes its edges
   disappear — the same blend the hero shelf uses. */
/* The photo FILLS the tile and the card's own radius rounds it — the same
   reasoning as the hero card. Contained, a square photo in a 269x230 box left
   a 20px band of flat gradient down each side, and no gradient ever matches a
   photograph exactly, so all eleven cards carried a faint visible rectangle.
   Filling removes the boundary rather than trying to disguise it.

   1/0.95 crops ~5% of the height, which every product in the catalogue can
   afford — the tightest still keeps ~5.3% clear (see the bounding boxes
   behind SHOTS in js/data.js). Flatten this further and the taller bottles
   start losing their caps. The gradient stays underneath as the fallback for
   a photo that fails to load. */
.swatch.photo{
  background-image:var(--shot-img),linear-gradient(135deg,var(--shot-a,#fff),var(--shot-b,#fff));
  background-size:cover,cover;background-repeat:no-repeat,no-repeat;
  background-position:center,center;
  height:auto;aspect-ratio:1/0.95;border-bottom:1px solid var(--sand)}
/* Both were the raw NUYU pink and both failed: the tag at 3.06:1 (pink on its
   own pale tint) and the badge at 3.40:1 (white on pink). The darkened pink
   fixes each — as type in the first case, as a fill in the second. */
.swatch .tag.nuyu{color:#A03C6C;background:#FDEFF6}
.save-badge{position:absolute;top:12px;right:12px;background:#A03C6C;color:#fff;font-size:.7rem;font-weight:700;letter-spacing:.06em;border-radius:999px;padding:4px 11px}
.card-body{padding:18px;display:flex;flex-direction:column;gap:8px;flex:1}
.card-body h3{font-size:1.12rem}
.card-body p{font-size:.88rem;color:var(--muted);flex:1}
.card-foot{display:flex;justify-content:space-between;align-items:center;margin-top:6px}
.price{font-weight:700;font-size:1.05rem}
.price s{color:var(--muted-3);font-weight:400;font-size:.85rem;margin-left:6px}
.add{background:var(--sand);border:none;border-radius:999px;padding:9px 16px;font-weight:700;font-family:'Inter',sans-serif;cursor:pointer;font-size:.85rem;transition:background .15s}
.add:hover{background:var(--amber-dark);color:#fff}

/* supplement band */
#supplements{background:var(--sand);border-radius:32px;margin:0 14px}
.compliance{margin-top:26px;font-size:.78rem;color:var(--muted-2);max-width:78ch}

/* split banner */
.banner{background:var(--pine);color:var(--linen);border-radius:32px;margin:0 14px;padding:54px 0}
.banner .wrap{display:grid;grid-template-columns:1fr 1fr;gap:40px;align-items:center}
.banner h2{color:#fff;font-size:clamp(1.6rem,3vw,2.2rem)}
/* The base .eyebrow is dark type on a barely-there dark tint, which is fine on
   the page's light surfaces but rendered "From the studio" invisible here —
   --pine text sitting on the --pine banner, a contrast ratio of exactly 1.
   Same inversion the hero already applies to its own eyebrow. */
.banner .eyebrow{background:rgba(255,255,255,.14);color:#fff}
.banner img{border-radius:var(--radius);box-shadow:var(--shadow)}
@media(max-width:820px){.banner .wrap{grid-template-columns:1fr}}

/* ======================================================================
   BRAND SYSTEM
   ----------------------------------------------------------------------
   Every range we carry gets its own room on the page. Each .brand-sec sets
   four custom properties from js/brands.js — --brand, --brand-tint,
   --brand-surface, --brand-edge — and everything below inherits from those.
   Nothing here names NUYU or Tropic, so a third brand needs no new CSS.

   The studio palette still holds the page together: ink type, the same
   radius, the same shadow, the same grid. A brand colours its own room; it
   never repaints the building.
   ====================================================================== */

/* ---- Sticky switcher --------------------------------------------------
   Sits under the site header. With two brands the page is long, and someone
   who came for skincare shouldn't scroll through supplements to find it.
   The active pill is set by an IntersectionObserver as you scroll. */
.brand-switch{position:sticky;top:var(--header-h,72px);z-index:40;background:rgba(255,253,247,.93);
  backdrop-filter:blur(8px);border-bottom:1px solid var(--sand)}
.brand-switch .wrap{display:flex;align-items:center;gap:8px;padding:9px 22px}
.switch-label{font-size:.68rem;font-weight:800;letter-spacing:.16em;text-transform:uppercase;
  color:var(--muted-3);margin-right:4px}
.brand-switch a{font-size:.85rem;font-weight:700;padding:6px 15px;border-radius:999px;
  border:1.5px solid transparent;color:var(--muted);transition:background .18s,color .18s,border-color .18s}
.brand-switch a:hover{color:var(--brand);border-color:var(--brand)}
/* --on-brand, not #fff — same measured reason as .btn.brand-btn above: white
   on NUYU's pink is 3.41:1 and fails. See onAccent in js/brands.js. */
.brand-switch a[aria-current="true"]{background:var(--brand);color:var(--on-brand,#fff);border-color:var(--brand)}
@media(max-width:820px){
  .brand-switch .wrap{overflow-x:auto;scrollbar-width:none}
  .brand-switch .wrap::-webkit-scrollbar{display:none}
  .switch-label{display:none}
}

/* ---- The room ---------------------------------------------------------- */
.brand-sec{background:var(--brand-surface);border-radius:32px;margin:0 14px 22px;
  padding:0 0 46px;overflow:hidden;scroll-margin-top:calc(var(--header-h,72px) + 74px)}

/* ---- Masthead ---------------------------------------------------------
   Identity on the left, the case for the brand on the right. The wordmark is
   set large and in the brand's own colour — this is the moment a visitor
   registers whose shelf they're looking at, so it gets the room to land. */
.brand-masthead{background:var(--brand-tint);padding:46px 0 42px;margin-bottom:40px}
.brand-masthead .wrap{display:grid;grid-template-columns:.9fr 1.1fr;gap:44px;align-items:start}
/* --brand-ink, not --brand: these are the brand colour set as TYPE on the
   brand's own tint, where the raw accent is too light to read (NUYU was
   2.81:1 for both). See brandInk in js/brands.js. */
.brand-kicker{display:inline-block;font-size:.7rem;font-weight:800;letter-spacing:.18em;
  text-transform:uppercase;color:var(--brand-ink,var(--brand));margin-bottom:10px}
.brand-wordmark{font-size:clamp(2.4rem,6vw,4rem);font-weight:800;letter-spacing:-.03em;
  line-height:.95;color:var(--brand-ink,var(--brand));margin:0}
.brand-tagline{margin-top:14px;font-size:1.02rem;font-weight:600;color:var(--ink);max-width:26ch}
.brand-blurb{font-size:.95rem;color:var(--muted);max-width:62ch}

.brand-facts{list-style:none;display:flex;flex-wrap:wrap;gap:7px;margin:18px 0 0;padding:0}
/* --brand-ink again: the raw accent as 12px type left these fact pills at
   2.81:1 on NUYU's shelf. */
.brand-facts li{font-size:.78rem;font-weight:600;color:var(--brand-ink,var(--brand));
  background:rgba(255,255,255,.72);border:1px solid var(--brand-edge);
  border-radius:999px;padding:5px 12px}

/* How you buy it. The distinction is the single most useful thing on the
   page — one range checks out here, the other doesn't — so it is stated as a
   control, not buried in prose. */
.brand-how{margin-top:22px;display:flex;flex-wrap:wrap;align-items:center;gap:10px}
.brand-chip{display:inline-flex;align-items:center;gap:8px;font-size:.85rem;font-weight:700;
  border-radius:999px;padding:10px 18px}
.brand-chip.is-here{background:rgba(255,255,255,.8);color:var(--ink);border:1px solid var(--brand-edge)}
.brand-chip.is-out{background:var(--brand);color:#fff;transition:transform .15s ease,filter .15s ease}
.brand-chip.is-out:hover{transform:translateY(-1px);filter:brightness(1.08)}
/* --muted, not --muted-2: this sits on the brand tint rather than on white,
   where the lighter grey fell to 4.03:1. */
.brand-chip-note{font-size:.78rem;color:var(--muted)}

@media(max-width:820px){
  .brand-masthead{padding:34px 0 30px;margin-bottom:28px}
  .brand-masthead .wrap{grid-template-columns:1fr;gap:22px}
}

/* ---- Groups within a brand -------------------------------------------- */
.brand-group-head{margin:0 0 18px}
.brand-group-head h3{font-size:1.15rem;font-weight:700;color:var(--ink);
  display:flex;align-items:center;gap:12px}
.brand-group-head h3::after{content:"";flex:1;height:1px;background:var(--brand-edge)}
.brand-sec .grid+.brand-group-head{margin-top:44px}
.brand-empty{font-size:.95rem;color:var(--muted)}

/* ---- Small print ------------------------------------------------------
   Compliance for what we sell, the commercial disclosure for what we don't.
   Deliberately readable rather than hidden — both are there to be read. */
.brand-note{margin-top:34px;padding-top:20px;border-top:1px solid var(--brand-edge);
  font-size:.79rem;line-height:1.65;color:var(--muted-2);max-width:88ch}

/* ---- Partner product cards --------------------------------------------
   Same shape as our own cards so the page reads as one shop, but the price
   says whose it is and the button leaves the site. No Add — nothing here can
   reach our basket, and a shopper should see that before clicking. */
.card.is-partner .price{display:flex;flex-direction:column;line-height:1.2}
.card.is-partner .price em{font-style:normal;font-weight:500;font-size:.72rem;
  color:var(--brand);margin-top:2px}
.tag.is-brand{background:var(--brand);color:#fff}
.add.is-out{background:var(--brand);color:#fff;border:none;display:inline-flex;
  align-items:center;justify-content:center;text-decoration:none}
.add.is-out:hover{filter:brightness(1.08)}

.search-out{color:var(--leaf);font-weight:700}

/* ---- Hero buttons, one per brand -------------------------------------- */
/* --on-brand is measured per brand, not chosen — white on NUYU's pink is
   3.41:1 (fails) while ink on it is 5.31:1; Tropic's green is the exact
   inverse. See onAccent in js/brands.js. */
.btn.brand-btn{background:var(--brand);color:var(--on-brand,#fff)}
.btn.brand-btn:hover{background:var(--brand);filter:brightness(1.06)}

/* basket drawer */
.drawer{position:fixed;inset:0 0 0 auto;width:min(420px,100%);background:#fff;z-index:100;box-shadow:-14px 0 40px rgba(20,20,20,.25);transform:translateX(100%);transition:transform .25s ease;display:flex;flex-direction:column}
.drawer.open{transform:none}
.drawer header{position:static;display:flex;justify-content:space-between;align-items:center;padding:20px 22px;border-bottom:1px solid var(--sand);background:#fff}
.drawer h3{font-size:1.3rem}
.drawer .close{background:none;border:none;font-size:1.5rem;cursor:pointer;color:var(--ink)}
.drawer .items{flex:1;overflow:auto;padding:18px 22px;display:flex;flex-direction:column;gap:14px}
.line{display:flex;justify-content:space-between;gap:12px;align-items:center;background:var(--linen);border-radius:12px;padding:12px 14px}
.line b{font-size:.95rem}
.line small{color:var(--muted)}
.qty{display:flex;align-items:center;gap:8px}
.qty button{width:26px;height:26px;border-radius:50%;border:1.5px solid var(--sand);background:#fff;cursor:pointer;font-weight:700}
.drawer footer{padding:20px 22px;border-top:1px solid var(--sand)}
.total{display:flex;justify-content:space-between;font-weight:700;font-size:1.1rem;margin-bottom:14px}
.drawer .note{font-size:.78rem;color:var(--muted-2);margin-top:10px}
.empty{text-align:center;color:var(--muted-2);margin-top:40px}
.overlay,.search-overlay{position:fixed;inset:0;background:rgba(20,20,20,.4);z-index:90;opacity:0;pointer-events:none;transition:opacity .25s}
.overlay.show,.search-overlay.show{opacity:1;pointer-events:auto}

/* header search */
.search-btn{background:none;border:none;width:40px;height:40px;display:grid;place-items:center;cursor:pointer;color:var(--pine);border-radius:999px;transition:background .15s}
.search-btn:hover{background:var(--sand)}
.search-btn svg{width:20px;height:20px}
.search-panel{position:fixed;top:0;left:0;right:0;background:var(--linen);z-index:100;
  transform:translateY(-100%);transition:transform .25s ease;box-shadow:0 14px 40px rgba(20,20,20,.18);
  max-height:80vh;overflow:auto}
.search-panel.open{transform:none}
.search-panel-bar{display:flex;align-items:center;gap:12px;padding:20px 0;border-bottom:1px solid var(--sand)}
.search-panel-bar svg{width:20px;height:20px;color:var(--sage);flex:0 0 auto}
.search-panel-bar input{flex:1;border:none;background:none;font-family:inherit;font-size:1.1rem;color:var(--ink);outline:none}
.search-panel-bar .close{background:none;border:none;font-size:1.6rem;cursor:pointer;color:var(--ink);flex:0 0 auto}
#searchResults{padding:18px 0 28px;display:flex;flex-direction:column;gap:4px}
.search-hint{color:var(--muted-2);font-size:.9rem;padding:8px 0}
.search-result{display:flex;align-items:center;gap:14px;padding:10px 8px;border-radius:12px;text-decoration:none;transition:background .15s}
.search-result:hover{background:var(--sand)}
.search-result img{width:48px;height:48px;object-fit:contain;background:#fff;border-radius:10px;flex:0 0 auto}
.search-result b{display:block;font-size:.92rem;color:var(--ink)}
.search-result small{display:block;font-size:.78rem;color:var(--muted-2)}
.search-result-price{margin-left:auto;font-weight:700;color:var(--amber-dark);white-space:nowrap}
.toast{position:fixed;bottom:24px;left:50%;transform:translateX(-50%) translateY(80px);background:var(--ink);color:#fff;padding:12px 22px;border-radius:999px;font-weight:600;z-index:120;transition:transform .3s ease;font-size:.9rem}
.toast.show{transform:translateX(-50%)}

/* footer */
.foot{background:var(--ink);color:var(--on-dark-soft);padding:54px 0 30px;margin-top:70px}
.foot .wrap{display:grid;grid-template-columns:2fr 1fr 1fr;gap:36px}
.foot h4{color:#fff;font-family:'Inter',sans-serif;font-weight:700;margin-bottom:12px}
.foot ul{list-style:none;display:grid;gap:8px;font-size:.9rem}
.foot a:hover{color:var(--amber)}
.foot .fine{grid-column:1/-1;border-top:1px solid rgba(255,255,255,.12);padding-top:18px;font-size:.75rem;color:var(--sage)}
@media(max-width:820px){.foot .wrap{grid-template-columns:1fr}}

/* ---- product detail page ----------------------------------------------
   Previously the <img> carried hardcoded width/height="900x900" attributes
   used only to reserve layout space, but the real NUYU photos are NOT
   square — browsers use those attributes to set an implicit 1:1
   aspect-ratio, then stretch/squash the real (non-square) image to fill it,
   which is what caused the visible vertical distortion. Fixed below by
   giving the image a dedicated, fixed-aspect-ratio box (.product-shot) with
   object-fit:contain, which can never distort the photo — at most it
   letterboxes, the same safe technique the card grid already uses via
   background-size:contain.
   Layout redesigned wide/horizontal with a bold accent-colour panel
   (inspired by premium PDP references), replacing the previous plain-white
   two-column layout. */
.crumbs{font-size:.82rem;color:var(--muted-2);margin:22px 0 8px}
.crumbs a:hover{color:var(--pine)}

/* Background is the product's own accent, softly tinted — set inline via
   color-mix() on <body>, so the whole page (not just a boxed panel) reads as
   "this product's colour", the way a DTC PDP's bottle-matched background
   does. The hero panel itself stays transparent so it blends straight into
   that page tint instead of sitting on top of it as a separate dark block. */
.product-hero{--accent:var(--sage);
  border-radius:var(--radius);margin:20px 14px 0;overflow:hidden;position:relative}
.product-hero .wrap.pdp-grid{display:grid;grid-template-columns:minmax(260px,420px) 1fr;gap:48px;
  align-items:start;padding-top:36px;padding-bottom:44px}

.product-shot{aspect-ratio:1/1;background:#fff;border-radius:var(--radius);
  display:flex;align-items:center;justify-content:center;padding:30px;
  box-shadow:var(--shadow);position:sticky;top:90px}
.product-shot img{width:100%;height:100%;object-fit:contain}

/* Bundles we curated ourselves (not an official NUYU bundle shot) show the
   three real product photos fanned together, instead of faking one combined
   image — honest about what's actually in the box. Sized to fill most of
   the frame (previously 38% wide with light overlap, which left a lot of
   visible white space around a small cluster) while keeping enough overlap
   and rotation that it still reads as one composed group, not three
   separate stamps. */
.product-shot.multi{gap:0;padding:18px}
.product-shot.multi img{width:50%;height:auto;max-height:96%;object-fit:contain;
  filter:drop-shadow(0 14px 22px rgba(20,20,20,.2));
  transition:transform .3s cubic-bezier(.2,.8,.2,1),filter .3s ease,z-index 0s}
.product-shot.multi img:nth-child(1){transform:rotate(-7deg) translateY(6%);margin-right:-19%;z-index:1}
.product-shot.multi img:nth-child(2){transform:translateY(-4%) scale(1.1);z-index:2}
.product-shot.multi img:nth-child(3){transform:rotate(7deg) translateY(6%);margin-left:-19%;z-index:1}
/* Hover-only, and only on devices that actually have real hover (a mouse) —
   without this, tapping a bottle on touch devices triggers the :hover style
   with nothing to tap "away" from, so it visibly gets stuck enlarged until
   the visitor taps elsewhere. Desktop keeps the interactive pop; mobile just
   shows the static, already-legible fan arrangement. */
@media (hover:hover) and (pointer:fine){
  .product-shot.multi img:hover{transform:translateY(-8%) scale(1.28) rotate(0deg);z-index:5;
    filter:drop-shadow(0 22px 34px rgba(20,20,20,.32))}
}
@media(prefers-reduced-motion:reduce){.product-shot.multi img{transition:none}}

.product-hero .eyebrow{background:color-mix(in srgb, var(--accent) 22%, #fff);color:var(--ink)}
.product-hero h1{color:var(--ink);margin:6px 0 10px}
.product-hero .trust-tag{background:var(--ink);color:#fff}
.product-hero .desc{margin:0 0 14px;color:var(--muted);max-width:52ch}
.see-inside{display:inline-block;font-size:.85rem;font-weight:700;color:var(--amber-dark);
  text-decoration:underline;margin-bottom:22px}
.product-hero .badges{display:flex;flex-wrap:wrap;gap:10px;margin:26px 0 0;font-size:.8rem;font-weight:600;color:var(--ink);list-style:none}
.product-hero .badges li{display:flex;align-items:center;gap:7px;background:#fff;border:1px solid var(--line, rgba(20,20,20,.12));border-radius:999px;padding:7px 14px 7px 11px}
.product-hero .badges li::before{content:"✓";display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border-radius:50%;background:var(--amber);color:#fff;font-size:.62rem;font-weight:900;flex:0 0 auto}

/* ---- pack size + subscribe/one-time purchase panel ---- */
.pdp-block{margin-bottom:20px}
.pdp-label{display:block;font-size:.78rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted-2, var(--sage));margin-bottom:10px}
.pack-pills{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
.pack-pill{position:relative;background:#fff;border:1.5px solid rgba(20,20,20,.14);border-radius:14px;
  padding:12px 10px;font-family:inherit;font-size:.86rem;font-weight:700;color:var(--ink);cursor:pointer;
  transition:border-color .15s,box-shadow .15s;text-align:center}
.pack-pill:hover{border-color:var(--amber)}
.pack-pill.selected{border-color:var(--amber);box-shadow:0 0 0 2px color-mix(in srgb, var(--amber) 30%, transparent)}
.pack-pill:disabled{opacity:.45;cursor:not-allowed}
.pack-pill-label{display:block}
.pack-pill-price{display:block;margin-top:3px;font-size:.74rem;font-weight:700;color:var(--muted)}
.pack-pill-price s{opacity:.65;font-weight:600;margin-right:3px}
.pack-tag{display:block;margin-top:4px;font-size:.66rem;font-weight:800;letter-spacing:.03em;color:var(--amber-dark)}
.pack-tag.best{color:#1F7A4D}

/* ---- product wheel (products/complete-monthly-bundle.html): switch
   between several products in one hero instead of one fixed product, with
   the pack-size tiers restyled as a vertical list of ribbon cards to match
   the requested reference layout. Scoped to .wheel-pdp so the other, single-
   product pages keep their existing compact horizontal pill row. ---- */
.wheel-shot-wrap{position:relative}
.wheel-nav{position:absolute;top:50%;transform:translateY(-50%);width:44px;height:44px;
  border-radius:50%;border:1.5px solid var(--line, rgba(20,20,20,.14));background:#fff;
  color:var(--ink);font-size:1.3rem;line-height:1;cursor:pointer;display:grid;place-items:center;
  box-shadow:0 10px 24px -14px rgba(20,20,20,.4);transition:transform .15s,border-color .15s;z-index:2}
.wheel-nav:hover{border-color:var(--accent, var(--amber));transform:translateY(-50%) scale(1.08)}
.wheel-nav.prev{left:-18px}
.wheel-nav.next{right:-18px}
@media(max-width:760px){.wheel-nav.prev{left:-8px}.wheel-nav.next{right:-8px}}
.wheel-dots{display:flex;justify-content:center;gap:8px;margin-top:16px}
.wheel-dot{width:9px;height:9px;border-radius:50%;border:none;background:rgba(20,20,20,.18);
  cursor:pointer;padding:0;transition:background .15s,transform .15s}
.wheel-dot.active{background:var(--accent, var(--amber));transform:scale(1.25)}
.wheel-pdp .product-shot img{transition:opacity .2s ease}

.wheel-pdp .pack-pills{display:flex;flex-direction:column;gap:16px;grid-template-columns:none}
.wheel-pdp .pack-pill{display:flex;align-items:center;justify-content:space-between;gap:14px;
  text-align:left;padding:16px 20px;border-radius:16px;border-width:2px}
.wheel-pdp .pack-pill::before{content:"";width:20px;height:20px;border-radius:50%;flex:0 0 auto;
  border:2px solid rgba(20,20,20,.25);transition:border-color .15s,background .15s}
.wheel-pdp .pack-pill.selected::before{border-color:var(--accent, var(--amber));
  background:radial-gradient(circle,var(--accent, var(--amber)) 0 40%,transparent 44%)}
.wheel-pdp .pack-pill-label{font-size:1rem}
.wheel-pdp .pack-pill-price{margin-left:auto;text-align:right;font-size:.95rem;font-weight:800;color:var(--ink)}
.wheel-pdp .pack-pill-price s{font-size:.82rem}
.wheel-pdp .pack-tag{position:absolute;top:-11px;left:50%;transform:translateX(-50%);margin:0;
  background:var(--ink);color:#fff;padding:4px 16px;border-radius:999px;font-size:.66rem;white-space:nowrap}
.wheel-pdp .pack-tag.best{background:#1F7A4D;color:#fff}

.purchase-options{display:flex;flex-direction:column;gap:10px}
.purchase-opt{position:relative;display:flex;align-items:flex-start;gap:12px;background:#fff;
  border:1.5px solid rgba(20,20,20,.14);border-radius:14px;padding:14px 16px;cursor:pointer;transition:border-color .15s}
.purchase-opt input{margin-top:3px;accent-color:var(--amber)}
.purchase-opt b{display:block;font-size:.92rem;color:var(--ink)}
.purchase-opt .opt-price{display:block;font-size:.86rem;font-weight:700;color:var(--amber-dark);margin-top:2px}
.purchase-opt.selected{border-color:var(--amber);box-shadow:0 0 0 2px color-mix(in srgb, var(--amber) 22%, transparent)}
.opt-perks{list-style:none;margin:8px 0 0;padding:0;font-size:.78rem;color:var(--muted);display:flex;flex-direction:column;gap:3px}
.opt-perks li::before{content:"✓ ";color:#1F7A4D;font-weight:800}
.opt-badge{position:absolute;top:-10px;right:14px;background:#1F7A4D;color:#fff;font-size:.64rem;
  font-weight:800;letter-spacing:.04em;text-transform:uppercase;border-radius:999px;padding:4px 10px}

.pdp-buy{display:flex;align-items:center;gap:16px;margin-top:4px}
.pdp-buy .btn{flex:1 1 auto}
.pdp-total{font-size:1.2rem;font-weight:800;color:var(--ink);white-space:nowrap}
.klarna-note{font-size:.8rem;color:var(--muted);margin:12px 0 0}
.klarna-note b{color:var(--ink)}

@media(max-width:820px){
  .product-hero .wrap.pdp-grid{grid-template-columns:1fr;gap:24px;padding-top:28px;padding-bottom:32px}
  .product-shot{max-width:320px;margin:0 auto;position:static}
  .product-hero .badges{justify-content:center}
}

/* product story panels — "why we stock it" etc. */
.story{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:20px;margin-top:6px}
.story article{background:var(--sand);border-radius:var(--radius);padding:24px}
@supports (background:color-mix(in srgb,red 10%,white)){
  .story article{background:color-mix(in srgb,var(--accent) 7%,var(--sand))}
}
.story h3{font-size:1.05rem;margin-bottom:8px}
.story p{font-size:.92rem;color:var(--muted)}
.story .eyebrow{margin-bottom:10px}

/* honest "check the label" note on supplement pages */
.label-note{display:flex;gap:14px;align-items:flex-start;background:#fff;border-left:4px solid var(--amber);border-radius:12px;padding:18px 20px;margin-top:26px;font-size:.88rem;color:var(--muted);max-width:78ch}
.label-note b{color:var(--ink);display:block;margin-bottom:3px;font-family:'Inter',sans-serif;font-weight:700;font-size:1rem}

/* NUYU's own free companion app — factual callout, only on spray/patch
   products the scan feature actually applies to */
.audio-mag{background:#fff;border-radius:var(--radius);padding:32px;box-shadow:var(--shadow)}
.audio-mag h2{font-size:1.5rem;margin-bottom:10px}
.audio-mag p{font-size:.95rem;color:var(--muted);max-width:68ch}
.audio-mag-steps{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin:22px 0 20px}
.audio-mag-steps div{background:var(--linen);border-radius:14px;padding:16px}
.audio-mag-steps b{display:block;font-size:.95rem;color:var(--ink);margin-bottom:4px}
.audio-mag-steps span{display:block;font-size:.84rem;color:var(--muted)}
@media(max-width:820px){.audio-mag-steps{grid-template-columns:1fr}}

/* Fallback when a product image fails to load. Product photography is served
   from NUYU's CDN, so a URL change there must not leave a blank hole. */
.img-fallback{background:linear-gradient(150deg,var(--sage),var(--sand));min-height:260px;padding:20px;color:var(--ink);font-weight:700}

/* collect-at-studio option in the basket drawer */
.collect{display:flex;align-items:center;gap:10px;font-size:.86rem;font-weight:700;color:var(--pine);background:var(--linen);border-radius:12px;padding:11px 14px;margin-bottom:12px;cursor:pointer}
.collect input{width:17px;height:17px;accent-color:var(--pine);cursor:pointer}

/* free-delivery progress in the basket drawer */
.ship-progress{margin-bottom:14px;font-size:.82rem;color:var(--muted)}
.ship-progress .track{height:7px;border-radius:999px;background:var(--sand);overflow:hidden;margin-top:8px}
.ship-progress .fill{height:100%;background:var(--amber);border-radius:999px;transition:width .3s ease}
.ship-progress.done{color:var(--pine);font-weight:700}
.ship-progress.done .fill{background:var(--pine)}

/* ---- motion ----------------------------------------------------------
   Deliberately slow and small. This is a spa brand, so movement should
   feel like breathing rather than like a carousel. Everything below is
   opt-out via prefers-reduced-motion at the end of this block. */
@media(prefers-reduced-motion:no-preference){
  /* Scroll reveal. Scoped to html.reveal-ready, which js/reveal.js only adds
     once it has confirmed IntersectionObserver works — so if the script never
     runs, .reveal content simply stays visible instead of disappearing. */
  .reveal-ready .reveal{opacity:0;transform:translateY(20px);
    transition:opacity .7s cubic-bezier(.22,1,.36,1),transform .7s cubic-bezier(.22,1,.36,1)}
  .reveal-ready .reveal.in{opacity:1;transform:none}

  /* the product photo drifts, echoing the breathing dot in the hero */
  .product-shot{animation:drift 11s ease-in-out infinite}
  @keyframes drift{0%,100%{transform:translateY(0)}50%{transform:translateY(-9px)}}

  /* add-to-basket presses in slightly */
  .add:active,.btn:active{transform:translateY(1px)}

  /* Product photos ease in slightly on hover. Deliberately hover-only: it
     reacts to a buyer who is already looking, so it never competes for
     attention the way ambient motion would. .card clips the overflow. */
  .swatch.photo{transition:background-color .3s ease,transform .4s cubic-bezier(.22,1,.36,1)}
  .card:hover .swatch.photo{transform:scale(1.04)}
  .wheel-shot{transition:background-color .3s ease,transform .4s cubic-bezier(.22,1,.36,1)}
  .wheel-item:hover .wheel-shot{transform:scale(1.05)}
}

/* Honour the OS setting: no reveal, no drift, no lift. Content must still
   be visible, so .reveal resets to its final state rather than staying at
   opacity 0. */
@media(prefers-reduced-motion:reduce){
  /* .reveal needs no reset here — it is only ever hidden under .reveal-ready,
     and reveal.js does not add that class when reduced motion is requested. */
  .product-shot{animation:none}
  .card:hover{transform:none}
}

/* 404 */
.notfound{text-align:center;padding:90px 0 40px;max-width:52ch;margin:0 auto}
.notfound h1{font-size:clamp(2rem,5vw,3rem);margin-bottom:14px}
.notfound p{color:var(--muted);margin-bottom:26px}

/* ---- Delivery terms at the point of purchase -------------------------
   Shown in the basket (folded) and on every product page (open). The copy
   lives in js/delivery.js; this is only its presentation.

   Deliberately legible rather than tucked away in 10px grey: these are the
   terms a purchase is made on, and someone should be able to read them
   without hunting. */
.delivery-fold{border:1px solid var(--sand);border-radius:12px;margin:0 0 12px;
  background:#fff}
.delivery-fold summary{cursor:pointer;list-style:none;padding:11px 14px;
  font-size:.82rem;font-weight:700;color:var(--ink);display:flex;align-items:center;gap:8px}
.delivery-fold summary::-webkit-details-marker{display:none}
.delivery-fold summary::before{content:"+";color:var(--amber);font-weight:800;flex:none}
.delivery-fold[open] summary::before{content:"–"}
.delivery-body{padding:0 14px 14px;border-top:1px solid var(--sand);margin-top:2px;padding-top:12px}

.delivery-block{border-top:1px solid var(--sand);margin-top:26px;padding-top:22px}
.delivery-block h3{font-size:1.05rem;margin-bottom:16px}

.delivery-points,.delivery-links{list-style:none;padding:0;margin:0}
.delivery-points{display:grid;gap:6px}
.delivery-points li{font-size:.84rem;line-height:1.5;color:var(--muted);display:flex;gap:8px}
.delivery-points li::before{content:"";width:4px;height:4px;border-radius:50%;background:var(--amber);flex:none;margin-top:8px;opacity:.65}
.delivery-foot{margin-top:12px;font-size:.79rem;line-height:1.55;color:var(--muted-2)}
.delivery-links{margin-top:10px;display:flex;flex-wrap:wrap;gap:16px}
.delivery-links a{font-size:.8rem;font-weight:700;color:var(--amber);text-decoration:underline;text-underline-offset:3px}
.delivery-links a:hover{color:var(--amber-dark)}
