/* ============================================================
   BRITISH MIST — design system
   2 fonts: Montserrat (display) + Inter (body)
     — Space Grotesk was the original display face; replaced by Montserrat
       (9 Jul 2026, Mark's pick). See --font-display below, the single token.
   Palette: black + warm bone + red (red is the only accent)
     — this REPLACED the original union-jack navy/red/white + KURO pink
       (#ff2d95) set, which shipped in the initial build at 3a7d28b (8 Jul 2026).
       The replacement commit is 62acc09, 9 Jul 2026 (session 2, Mark's pick);
       62acc09 removed the union-jack set, it did not introduce it.
       Do not reintroduce it.
     — d0cfc7c (21 Jul 2026) is a SEPARATE, later event and did not touch the
       above: it reverted only the five :root accent tokens from an unapproved
       ink-navy preview back to house red #d62828 (client prefers red).
   Voice: sophisticated, modern, premium British
   ============================================================ */

:root {
  /* colour tokens */
  /* Black · warm bone · red */
  --navy-950: #100e0c;
  --navy-black: #0c0b0a;
  --navy-900: #17150f;
  --navy-800: #221e17;
  --navy-700: #2e2921;
  --navy-100: #e6ded0;
  /* Accent — the whole site's accent is these five values. Swapping them re-skins
     header, hero glows, deal badges, KURO band, buttons and the PDP together.
     Ink navy (preview, not approved): #1d3557 / #2a4a75 / #e8edf4 / 29, 53, 87 / #8fb4e8
     Currently: house red (client prefers red over the navy preview — Mark, session 31) */
  --red-600: #d62828;
  --red-500: #e5342b;
  --red-050: #f8e7e2;
  --red-rgb: 214, 40, 40;         /* same as --red-600, for rgba() tints and glows */
  /* The same house red, darkened just enough to clear 4.5:1 as SMALL text on the
     cream grounds (--paper 4.76, --paper-warm 5.01, --red-050 4.80). --red-600 is
     the SURFACE red — buttons, rules, fills, anything large — and does not move:
     the whole site is bound to it. Reach for this one only for red type under
     18.66px bold / 24px regular, where --red-600 measures 4.14-4.37 and fails. */
  --red-ink: #c62222;
  /* accent text on the dark bands */
  --accent-on-dark: #e5342b;
  --blue-300: #c9b79c;
  --blue-100: #efe7db;
  --pink-500: var(--red-500);
  --green-500: #3a9d78;
  --ink: #14120e;
  --ink-soft: #4a443b;
  --ink-mute: #7a756b;
  --paper: #efe9de;
  --paper-warm: #f4efe4;
  --line: #ded6c8;
  --on-dark: #f3efe7;
  --on-dark-soft: #a8a296;

  /* How many wells the deal tray puts in a row. Declared here because every var the
     builder paints with has to be — a var() nothing declares is invalid at
     computed-value time and the property falls back to its initial, which for
     grid-template-columns means one column and a stack. renderPack overrides it per
     tray from the pool's own base tier; this is only the value before it does. */
  --tray-cols: 5;

  /* type scale */
  --font-display: 'Montserrat', system-ui, -apple-system, sans-serif;
  --font-body: 'Inter', system-ui, -apple-system, sans-serif;
  --fs-hero: clamp(2.1rem, 4.6vw, 3.6rem);
  --fs-h2: clamp(1.5rem, 2.6vw, 2.2rem);
  --fs-h3: 1.15rem;
  --fs-body: 1rem;
  --fs-small: .875rem;
  --fs-tiny: .78rem;

  /* spacing / misc */
  --sp-1: 8px; --sp-2: 16px; --sp-3: 24px; --sp-4: 32px;
  --sp-5: 48px; --sp-6: 64px; --sp-7: 96px;
  /* Soft geometry — smooth, sleek surfaces. Buttons are pills (see .btn);
     cards, inputs, chips and tags use the rounded tokens below. */
  --radius: 14px;
  --radius-sm: 8px;
  --shadow-card: 0 1px 2px rgba(15,12,8,.06), 0 8px 24px -12px rgba(15,12,8,.18);
  --shadow-lift: 0 4px 8px rgba(15,12,8,.08), 0 18px 40px -16px rgba(15,12,8,.3);
  --maxw: 1200px;
}

/* The brand marketing bands opt back out of the site accent and keep the house red —
   navy reads cold against the fruit and flavour photography, red reads fruity.
   Re-declaring the accent here cascades to every button, chip and glow inside them.
   (.kuro-sellers nests inside .kuro-band, so it's covered.) */
.kuro-band,
.bj-band {
  --red-600: #d62828;
  --red-500: #e5342b;
  --red-050: #f8e7e2;
  --red-rgb: 214, 40, 40;
  --accent-on-dark: #e5342b;
  /* --pink-500 is declared as var(--red-500) on :root, which resolves there — so the
     override above can't reach it. It has to be restated for the bands. */
  --pink-500: #e5342b;
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  font-family: var(--font-body);
  font-size: var(--fs-body);
  color: var(--ink);
  background: var(--paper);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}
img, svg { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; }
.container { max-width: var(--maxw); margin-inline: auto; padding-inline: var(--sp-3); }

h1, h2, h3 .display { text-wrap: balance; }
.display { font-family: var(--font-display); font-weight: 700; letter-spacing: -.015em; line-height: 1.08; }

.eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: var(--fs-tiny); font-weight: 600; letter-spacing: .14em;
  text-transform: uppercase; color: var(--red-600);
}
.eyebrow::before { content: ""; width: 22px; height: 2px; background: var(--red-600); }
.on-dark .eyebrow, .eyebrow.light { color: var(--blue-300); }
.on-dark .eyebrow::before, .eyebrow.light::before { background: var(--blue-300); }

/* ---------- buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  padding: 14px 28px; border-radius: 999px; border: 2px solid transparent;
  font-weight: 600; font-size: var(--fs-small); line-height: 1;
  transition: transform .22s ease, box-shadow .22s ease, background .22s ease, color .22s ease;
  white-space: nowrap;
}
/* a lift, not a bounce on hover */
.btn:hover { transform: translateY(-1px); box-shadow: var(--shadow-lift); }
.btn:active { transform: translateY(0); }
.btn-red { background: var(--red-600); color: #fff; }
.btn-red:hover { background: var(--red-500); }
.btn-navy { background: var(--navy-900); color: #fff; }
.btn-ghost { border-color: rgba(255,255,255,.45); color: #fff; background: transparent; }
.btn-ghost:hover { background: rgba(255,255,255,.1); }
.btn-ghost-dark { border-color: var(--navy-900); color: var(--navy-900); background: transparent; }
.btn-ghost-dark:hover { background: var(--navy-900); color: #fff; }
.btn-sm { padding: 10px 18px; font-size: var(--fs-tiny); }
.strength-section .section-head .btn { margin-top: var(--sp-3); }

/* ---------- compliance bar ---------- */
/* One strip above the header now, not two. The red free-delivery/dispatch-countdown bar
   that stacked under this one went on 17 Aug 2026 (Mark); this statutory nicotine warning
   stays. Anything that reserves vertical space for what sits above the hero counts THIS
   plus the header — see .hero-v2's min-height, which was wrong by exactly the missing bar. */
.compliance-bar {
  background: var(--navy-black); color: var(--on-dark-soft);
  font-size: var(--fs-tiny); text-align: center; padding: 8px var(--sp-2);
}
.compliance-bar strong { color: var(--on-dark); font-weight: 600; }

/* ---------- header ---------- */
.site-header {
  position: sticky; top: 0; z-index: 90;
  background: rgba(255,255,255,.92);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line);
  transition: box-shadow .25s ease;
}
.site-header.scrolled { box-shadow: 0 6px 24px -12px rgba(8,20,32,.25); }
.header-inner {
  display: flex; align-items: center; gap: var(--sp-4);
  padding-block: 14px;
}
.logo { display: flex; align-items: center; gap: 12px; flex-shrink: 0; }
.logo-crest {
  height: 44px; width: auto; display: block;
  object-fit: contain;
}
.logo-word { line-height: 1.05; display: flex; flex-direction: column; }
.logo-word .top { font-family: var(--font-display); font-weight: 700; font-size: 1.05rem; color: var(--red-600); letter-spacing: .01em; }
.logo-word .sub { font-size: .62rem; letter-spacing: .32em; text-transform: uppercase; color: var(--navy-900); font-weight: 600; }

.main-nav { display: flex; gap: 4px; margin-inline: auto; }
.main-nav a {
  padding: 10px 14px; border-radius: var(--radius-sm); white-space: nowrap;
  font-size: var(--fs-small); font-weight: 500; color: var(--ink-soft);
  transition: background .2s ease, color .2s ease;
}
.main-nav a:hover { background: var(--paper-warm); color: var(--navy-900); }
.main-nav a.active { color: var(--red-600); font-weight: 600; }
.nav-account { display: none; } /* mobile-drawer only — desktop uses the header account icon */

.header-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.icon-btn {
  width: 42px; height: 42px; border-radius: 50%;
  display: grid; place-items: center;
  background: transparent; border: none; color: var(--navy-900);
  position: relative; transition: background .2s ease;
}
.icon-btn:hover { background: var(--paper-warm); }
.icon-btn .badge {
  position: absolute; top: 2px; right: 0;
  min-width: 17px; height: 17px; padding-inline: 4px; border-radius: var(--radius-sm);
  background: var(--red-600); color: #fff;
  font-size: .62rem; font-weight: 700; display: grid; place-items: center;
}
/* An author `display` BEATS the UA's [hidden]{display:none}, so the wishlist
   badge painted a red "0" while its hidden property was true — measured in the
   browser, 23 Aug 2026. Fourth instance of this trap in this repo. The basket
   badge is never hidden (it always shows a count), so it never exposed it. */
.icon-btn .badge[hidden] { display: none; }
.nav-toggle { display: none; }

/* ---------- hero ---------- */
.hero {
  position: relative; overflow: hidden;
  background:
    radial-gradient(1100px 520px at 85% -10%, rgba(201,183,156,.16), transparent 60%),
    radial-gradient(760px 440px at -8% 112%, rgba(var(--red-rgb),.30), transparent 55%),
    linear-gradient(160deg, var(--navy-black) 0%, var(--navy-950) 55%, var(--navy-900) 100%);
  color: var(--on-dark);
}
.hero::before { /* tone-on-tone union jack — geometry only, reads on second glance */
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image:
    linear-gradient(100deg, rgba(12,11,10,.96) 10%, rgba(12,11,10,.78) 38%, rgba(12,11,10,.5) 60%, rgba(12,11,10,.38) 78%, rgba(12,11,10,.52) 100%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1200' height='600' viewBox='0 0 1200 600'%3E%3Crect width='1200' height='600' fill='%23012169'/%3E%3Cpath d='M0 0 1200 600 M1200 0 0 600' stroke='%23f3efe7' stroke-width='120'/%3E%3Cpath d='M0 0 1200 600 M1200 0 0 600' stroke='%23C8102E' stroke-width='40'/%3E%3Cpath d='M600 0 600 600 M0 300 1200 300' stroke='%23f3efe7' stroke-width='200'/%3E%3Cpath d='M600 0 600 600 M0 300 1200 300' stroke='%23C8102E' stroke-width='120'/%3E%3C/svg%3E");
  background-position: center, center 42%;
  background-size: cover, cover;
  opacity: .95;
  filter: saturate(1.28) brightness(.92);
  transform: rotate(-3.5deg) scale(1.12);
  -webkit-mask-image: radial-gradient(160% 175% at 50% 40%, #000 50%, transparent 94%);
  mask-image: radial-gradient(160% 175% at 50% 40%, #000 50%, transparent 94%);
}
.hero::after { /* subtle grain */
  content: ""; position: absolute; inset: 0; pointer-events: none; opacity: .5;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120' viewBox='0 0 120 120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .035 0'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)'/%3E%3C/svg%3E");
}
.hero-inner {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
  gap: var(--sp-6); align-items: center;
  padding-block: var(--sp-7) var(--sp-6);
}
.hero h1 { font-size: var(--fs-hero); margin: var(--sp-3) 0 var(--sp-3); color: #fff; }
.hero h1 .accent { color: var(--blue-300); display: block; }
.hero p.lede { color: var(--on-dark-soft); max-width: 52ch; margin-bottom: var(--sp-4); }
.hero-ctas { display: flex; gap: var(--sp-2); flex-wrap: wrap; margin-bottom: var(--sp-4); }
.hero-proof { display: flex; gap: var(--sp-3); flex-wrap: wrap; font-size: var(--fs-tiny); color: var(--on-dark-soft); }
.hero-proof span { display: inline-flex; align-items: center; gap: 7px; }
.hero-proof svg { width: 15px; height: 15px; color: var(--green-500); flex-shrink: 0; }

/* hero product visual */
.hero-visual { position: relative; display: grid; place-items: center; min-height: 380px; }
.hero-visual .bottle-stack { display: flex; align-items: flex-end; gap: -10px; filter: drop-shadow(0 30px 40px rgba(0,0,0,.45)); }
.hero-visual .bottle-stack svg { width: 118px; height: auto; }
.hero-visual .bottle-stack svg:nth-child(1) { transform: rotate(-9deg) translateY(10px); }
.hero-visual .bottle-stack svg:nth-child(3) { transform: rotate(9deg) translateY(10px); }
.hero-visual .bottle-stack svg:nth-child(2) { width: 138px; z-index: 1; }
.hero-deal-chip {
  position: absolute; top: 8%; right: 4%;
  background: var(--red-600); color: #fff; border-radius: var(--radius-sm);
  font-family: var(--font-display); font-weight: 700; font-size: .95rem;
  padding: 12px 20px; transform: rotate(6deg);
  box-shadow: var(--shadow-lift);
  animation: bob 4.5s ease-in-out infinite;
}
.hero-local-chip {
  position: absolute; bottom: 6%; left: 0;
  background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.2);
  backdrop-filter: blur(8px);
  color: #fff; border-radius: var(--radius); padding: 12px 18px;
  font-size: var(--fs-tiny); display: flex; gap: 10px; align-items: center;
  animation: bob 5.5s ease-in-out infinite reverse;
}
.hero-local-chip strong { display: block; font-size: var(--fs-small); }
@keyframes bob { 0%,100% { transform: translateY(0) rotate(var(--rot, 0deg)); } 50% { transform: translateY(-10px) rotate(var(--rot, 0deg)); } }
.hero-deal-chip { --rot: 6deg; }

/* ---------- hero v2: Mist range flavour switcher ---------- */
.hero-v2 {
  display: flex; flex-direction: column; justify-content: center; gap: var(--sp-3);
  /* Whole hero (copy + bottles + quick-buy) targets one screen: viewport minus what sits
     above it. That was compliance bar + promo bar + header (~148px). On 17 Aug 2026 the red
     promo bar went and the black warning bar stayed, so it is now the 37px warning bar plus
     the 73px sticky header — both measured on the built page, not guessed. Whenever a strip
     above the hero is added or removed THIS number moves with it: left at 148 it was
     reserving room for a bar that no longer exists and cropping the hero short. */
  min-height: calc(100vh - 110px);
  min-height: calc(100svh - 110px);
}
/* The copy column ran flush to the top and bottom of its own band — 361px of stack in a
   377px box. Mark asked for room to breathe (17 Aug 2026); this is where it comes from. */
.hero-v2 .hero-inner {
  grid-template-columns: minmax(0, 1fr) minmax(0, .9fr);
  align-content: center; padding-block: var(--sp-5) var(--sp-4);
}
.hero-v2 h1 { font-size: clamp(2.6rem, 5vw, 4.5rem); }
.hero-v2 h1.hero-lockup { margin: var(--sp-2) 0 0; }
.hero-lockup img { display: block; width: min(284px, 100%); height: auto; }
.hero-v2 .lede { font-size: 1.08rem; max-width: 46ch; }
.hero-v2 h1 .accent { transition: color .6s ease; font-weight: 800; letter-spacing: -.02em; }
.hero-tagline {
  display: block; color: #fff; font-weight: 700;
  font-size: clamp(1.15rem, 2.1vw, 1.6rem); line-height: 1.2;
  letter-spacing: -.01em; margin-top: 14px;
}
.scroll-cue {
  position: absolute; left: 50%; bottom: 18px; transform: translateX(-50%);
  z-index: 2; color: var(--on-dark-soft); font-size: var(--fs-tiny);
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  letter-spacing: .14em; text-transform: uppercase; font-weight: 600;
  transition: color .2s ease;
}
.scroll-cue:hover { color: #fff; }
.scroll-cue svg { animation: cue-drop 2.2s ease-in-out infinite; }
@keyframes cue-drop { 0%,100% { transform: translateY(0); opacity: .7; } 50% { transform: translateY(6px); opacity: 1; } }
.fh-notes { display: flex; align-items: center; flex-wrap: wrap; gap: 0 2px; margin-bottom: var(--sp-4); }
.fh-notes span { font-size: var(--fs-small); font-weight: 500; color: var(--on-dark-soft); }
.fh-notes span:not(:last-child)::after { content: "·"; margin: 0 10px; color: var(--on-dark-soft); opacity: .55; }
.fh-visual {
  position: relative; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: var(--sp-3);
  min-height: 300px;
}
.fh-glow {
  position: absolute; top: 46%; left: 50%; transform: translate(-50%, -55%);
  width: clamp(320px, 40svh, 460px); height: clamp(320px, 40svh, 460px); border-radius: 50%;
  background-color: var(--red-600, #d62828); opacity: .3; filter: blur(85px);
  pointer-events: none;
}
.fh-trio { position: relative; z-index: 1; display: flex; align-items: flex-end; justify-content: center; }
/* single photographic trio shot; sized relative to viewport height (svh) so it scales with the screen.
   the cutout floats on the union-jack, so two stacked drop-shadows fake ground contact + separation
   (drop-shadow follows each bottle's alpha silhouette, so the shadow reads per-bottle, not as a box). */
.fh-trio img {
  height: clamp(230px, 32svh, 380px); width: auto;
  filter: drop-shadow(0 14px 18px rgba(0,0,0,.5)) drop-shadow(0 40px 40px rgba(0,0,0,.4));
}
/* quick-buy row lives inside the hero, on the union jack (frosted cards read over the flag) */
.hero-quickbuy { position: relative; z-index: 1; padding-block: 0 var(--sp-3); }
.qb-head { text-align: center; margin-bottom: 2px; }
.hero-quickbuy .bm-sellers-grid { margin-top: 4px; gap: var(--sp-2); }
/* Quick-buy cards show the full splash photograph, the same art the shop grid and the
   product page use. They used to show a bare bottle cutout floating over the card on a
   red radial glow; the glow and the drop-shadow existed only to seat a transparent PNG
   and read as grubby edges once the photo has its own background, so both are gone.
   The splashes are square with the bottle filling the frame top to bottom, so the tile
   stays 1:1 — a crop to any wider ratio takes the cap off. */
.hero-quickbuy .bm-card-media { position: relative; display: block; padding-top: 0; }
.hero-quickbuy .bm-card-media img {
  position: relative; display: block; width: 100%; height: auto;
  aspect-ratio: 1 / 1; object-fit: cover; filter: none;
}
/* Hold four across much further down than the generic .bm-sellers-grid does. That grid
   drops to two at 1024px, which was harmless for a 197x720 cutout scaled to a fixed
   158px height — the tile never grew. A square splash is sized by the COLUMN, so two
   columns at 1000px wide made each photo 452px tall and the band 1362px. Four columns
   keeps it near the height it replaced; two is reserved for phones. */
.hero-quickbuy .bm-sellers-grid { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 700px) { .hero-quickbuy .bm-sellers-grid { grid-template-columns: repeat(2, 1fr); } }

.hero-quickbuy .bm-card-body { padding: 9px 14px 12px; gap: 3px; }
.hero-quickbuy .bm-card-body h3 { font-size: 1rem; margin-top: 0; }
.hero-quickbuy .ks-notes { font-size: var(--fs-tiny); }
.hero-quickbuy .ks-price { margin-top: 0; }
/* Bottom-align the button. Four narrower columns let the tasting notes wrap onto two
   lines on some cards and one on others, which left the buttons on a ragged line. */
.hero-quickbuy .ks-btn { margin-top: auto; }
.hero-quickbuy .ks-cta { margin-top: var(--sp-2); }

/* The lede and the proof ticks are out at EVERY width, not just desktop. They were dropped
   on desktop to pack the house block into one screen, which left the phone — the narrower
   screen — carrying MORE copy than the desktop: a 114px paragraph and 65px of ticks between
   the logo and the offer. Measured at 386px on 17 Aug 2026, that pushed the bottle trio to
   760px, below the fold on every phone, and put "2 FOR £20" at 445px. Both are echoed
   below anyway — the trust strip repeats the ticks, and the range copy repeats the lede. */
.hero-v2 .lede, .hero-v2 .hero-proof { display: none; }

/* desktop only: the "shop full range" CTA under the quick-buy row is a duplicate of the
   hero's "Shop the collection" (same link). It stays on mobile, where they are far apart. */
@media (min-width: 1025px) {
  .hero-quickbuy .ks-cta { display: none; }
  .hero-v2 .hero-copy .kuro-deal { margin-top: var(--sp-2); }
  /* The trio was a 245px image centred in a 515px column — 135px of dead air either side,
     which read as the bottles drifting away from the copy rather than belonging to it
     (Mark, 17 Aug 2026). Two changes, and both are needed: hugging the column's left edge
     alone just moves the void to the right, and growing it alone barely closes the gap.
     DESKTOP ONLY — below 1025px the grid is one column, where flex-start would shove the
     bottles against the left margin instead of centring them under the copy. */
  .fh-visual { align-items: flex-start; }
  .fh-trio img { height: clamp(230px, 42svh, 440px); }
  /* No height cap on desktop. The old one (112px) worked because a cutout could simply
     be scaled down; a square splash in a shorter box is covered by CROPPING, and with
     the bottle filling the frame top to bottom that takes the cap and the base off.
     The tile stays 1:1 and the section is taller here than it used to be. */
}

@media (max-width: 1024px) {
  /* mobile: stacked block is taller than one screen — flag backs it all, natural scroll */
  .hero-v2 { min-height: 0; display: block; }
  .hero-v2 .hero-inner { min-height: 0; grid-template-columns: 1fr; }
  .hero-v2 h1 { font-size: clamp(2.2rem, 7vw, 3.2rem); }
  .fh-visual { min-height: 300px; }
  .fh-trio img { height: clamp(240px, 34vh, 320px); min-height: 0; }
  .scroll-cue { display: none; }
}

/* ---------- trust strip ---------- */
.trust-strip { background: var(--paper); border-bottom: 1px solid var(--line); }
.trust-strip-inner {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-2);
  padding-block: var(--sp-3);
}
.trust-item { display: flex; gap: 12px; align-items: center; }
.trust-item svg { width: 30px; height: 30px; color: var(--red-600); flex-shrink: 0; }
.trust-item b { display: block; font-size: var(--fs-small); color: var(--navy-900); }
.trust-item small { color: var(--ink-mute); font-size: var(--fs-tiny); line-height: 1.4; display: block; }

/* ---------- generic section ---------- */
.section { padding-block: var(--sp-7); }
.section.tight { padding-block: var(--sp-6); }
.section-head { max-width: 640px; margin-bottom: var(--sp-5); }
.section-head.center { margin-inline: auto; text-align: center; }
.section-head.center .eyebrow::before { display: none; }
.section-head h2 { font-size: var(--fs-h2); color: var(--navy-900); margin-top: var(--sp-2); }
.section-head p { color: var(--ink-mute); margin-top: var(--sp-2); }
.on-dark .section-head h2 { color: #fff; }
.on-dark .section-head p { color: var(--on-dark-soft); }

/* ---------- category tiles ---------- */
.cat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-3); }
.cat-tile {
  position: relative; border-radius: var(--radius); overflow: hidden;
  padding: var(--sp-4) var(--sp-3); min-height: 210px;
  display: flex; flex-direction: column; justify-content: flex-end;
  color: #fff; box-shadow: var(--shadow-card);
  transition: transform .25s ease, box-shadow .25s ease;
}
.cat-tile:hover { transform: translateY(-6px); box-shadow: var(--shadow-lift); }
.cat-tile h3 { font-family: var(--font-display); font-size: 1.15rem; font-weight: 700; }
.cat-tile p { font-size: var(--fs-tiny); opacity: .85; margin-top: 6px; }
.cat-tile .count {
  position: absolute; top: var(--sp-2); right: var(--sp-2);
  background: rgba(255,255,255,.16); backdrop-filter: blur(6px);
  border-radius: var(--radius-sm); padding: 4px 12px; font-size: var(--fs-tiny); font-weight: 600;
}
.cat-tile .tile-icon { width: 44px; height: 44px; margin-bottom: auto; opacity: .9; }
.cat-eliquids,
.cat-kits,
.cat-pods,
.cat-bigpuff { background: linear-gradient(150deg, var(--navy-800), var(--navy-black)); }

/* ---------- deals band — offer tickets ---------- */
.deals-band { background: var(--paper-warm); }
.deal-cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-3); }
.deal-card {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column;
  background: #fbf8f1; color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-card);
  transition: transform .25s ease, box-shadow .25s ease;
}
.deal-card:hover { transform: translateY(-6px); box-shadow: var(--shadow-lift); }
.deal-feature { border: 2px solid var(--red-600); }
.deal-save {
  position: absolute; top: var(--sp-3); left: var(--sp-3); z-index: 2;
  font-family: var(--font-display); font-weight: 800; font-size: var(--fs-tiny);
  letter-spacing: .08em; text-transform: uppercase;
  background: var(--red-600); color: #fff;
  padding: 7px 12px; border-radius: var(--radius-sm);
  box-shadow: 0 6px 14px -6px rgba(var(--red-rgb),.55);
}
.deal-media {
  position: relative; height: 210px; flex: none;
  display: flex; align-items: center; justify-content: center;
  padding: var(--sp-4);
  background: radial-gradient(90% 120% at 50% 50%, rgba(20,15,10,.05), rgba(20,15,10,0) 70%);
}
.deal-img {
  position: relative; z-index: 1;
  max-height: 178px; max-width: 82%; width: auto; height: auto; object-fit: contain;
  filter: drop-shadow(0 18px 26px rgba(15,12,8,.3));
  transition: transform .35s ease;
}
.deal-img--logo { max-height: 108px; max-width: 72%; filter: none; }
.deal-img--tall { max-height: 158px; }
.deal-card:hover .deal-img { transform: translateY(-6px) scale(1.03); }
/* voucher perforation: dashed rule + edge notches (card overflow clips the halves) */
.deal-perf { position: relative; margin-top: var(--sp-3); border-top: 2px dashed var(--line); }
.deal-perf::before, .deal-perf::after {
  content: ""; position: absolute; top: -10px; width: 18px; height: 18px;
  border-radius: 50%; background: var(--paper-warm); border: 1px solid var(--line);
}
.deal-perf::before { left: -10px; }
.deal-perf::after { right: -10px; }
.deal-body { display: flex; flex-direction: column; gap: 7px; padding: var(--sp-3) var(--sp-4) var(--sp-4); }
.deal-brand {
  font-size: var(--fs-tiny); font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--ink-mute);
}
.deal-price { font-family: var(--font-display); font-size: 2.1rem; font-weight: 800; line-height: 1.1; }
.deal-price em { font-style: normal; color: var(--red-600); }
.deal-math { font-size: var(--fs-small); font-weight: 600; color: var(--ink-soft); }
.deal-math s { font-weight: 400; color: var(--ink-mute); }
.deal-note { font-size: var(--fs-small); color: var(--ink-mute); }
.deal-body .btn { margin-top: auto; align-self: stretch; }
.deal-note { margin-bottom: var(--sp-2); }

/* ---------- vape tax banner ---------- */
.tax-section { position: relative; }
.tax-banner {
  background:
    radial-gradient(780px 400px at 85% -28%, rgba(var(--red-rgb),.6), transparent 60%),
    radial-gradient(560px 300px at 4% 128%, rgba(var(--red-rgb),.32), transparent 60%),
    linear-gradient(125deg, var(--navy-black), var(--navy-950) 56%, var(--navy-900));
  color: var(--on-dark);
  padding: var(--sp-6) 0;
  border: 4px solid var(--red-600);
  border-radius: var(--radius);
  margin-inline: clamp(16px, 5vw, 64px);
  position: relative; overflow: hidden;
}
.tax-banner::before {
  content: "£2.20"; position: absolute; right: -24px; bottom: -62px;
  font-family: var(--font-display); font-weight: 800; font-size: 14rem; line-height: 1;
  opacity: .05; color: #fff; pointer-events: none;
}
.tax-flag {
  display: inline-flex; align-items: center; gap: 9px;
  background: rgba(var(--red-rgb),.16); border: 1px solid rgba(var(--red-rgb),.45); color: #ff9a9a;
  border-radius: var(--radius-sm); padding: 7px 14px;
  font-size: var(--fs-tiny); font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
}
.tax-flag svg { width: 16px; height: 16px; flex-shrink: 0; }
.tax-banner h3 {
  font-family: var(--font-display); font-weight: 800; color: #fff;
  font-size: clamp(1.85rem, 3.4vw, 2.6rem); line-height: 1.08;
  margin-top: var(--sp-3); letter-spacing: -.01em;
}
.tax-banner p { color: var(--on-dark-soft); font-size: var(--fs-small); margin-top: var(--sp-2); max-width: 66ch; }
.tax-facts { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-2); margin-top: var(--sp-4); position: relative; }
.tax-fact {
  background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.12);
  border-radius: var(--radius-sm); padding: var(--sp-3);
}
.tax-fact b {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--font-display); font-weight: 800; color: #fff; font-size: 1.5rem;
  font-variant-numeric: tabular-nums; line-height: 1;
}
.tax-fact span { display: block; margin-top: 8px; font-size: var(--fs-tiny); color: var(--on-dark-soft); }
.tax-fact--jump b svg { width: 20px; height: 20px; color: #ff5a5a; flex-shrink: 0; }
.tax-foot {
  display: flex; align-items: flex-end; justify-content: space-between; gap: var(--sp-4);
  flex-wrap: wrap; margin-top: var(--sp-5); padding-top: var(--sp-4);
  border-top: 1px solid rgba(255,255,255,.1); position: relative;
}
.tax-count-label { display: block; font-size: var(--fs-tiny); letter-spacing: .12em; text-transform: uppercase; color: var(--on-dark-soft); margin-bottom: 10px; }
.tax-countdown { display: flex; gap: 10px; }
.tax-countdown div {
  background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.16);
  border-radius: var(--radius-sm); padding: 10px 14px; min-width: 70px; text-align: center;
}
.tax-countdown b { display: block; font-family: var(--font-display); font-weight: 800; font-size: 1.75rem; font-variant-numeric: tabular-nums; color: #fff; line-height: 1; }
.tax-countdown small { display: block; margin-top: 5px; font-size: .62rem; letter-spacing: .14em; text-transform: uppercase; color: var(--on-dark-soft); }

/* ---------- product cards ---------- */
.product-tabs { display: flex; gap: var(--sp-3); justify-content: center; margin-bottom: var(--sp-5); flex-wrap: wrap; }
.product-tabs button {
  border: none; background: none; border-radius: 0;
  padding: 6px 2px 8px; font-size: var(--fs-small); font-weight: 600; color: var(--ink-mute);
  border-bottom: 2px solid transparent; transition: color .2s ease, border-color .2s ease;
}
.product-tabs button:hover { color: var(--navy-900); }
.product-tabs button.active { color: var(--navy-900); border-bottom-color: var(--red-600); }

.product-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-3); }
.product-grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.product-card {
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  padding: var(--sp-2); display: flex; flex-direction: column;
  transition: transform .22s ease, box-shadow .22s ease, border-color .22s ease;
  position: relative;
}
.product-card:hover { transform: translateY(-5px); box-shadow: var(--shadow-lift); border-color: transparent; }
.product-media {
  border-radius: var(--radius-sm); background: var(--paper-warm);
  display: grid; place-items: center; padding: var(--sp-3); margin-bottom: var(--sp-2);
  aspect-ratio: 1 / 1.05; position: relative; overflow: hidden;
}
.product-media svg { height: 88%; width: auto; transition: transform .3s ease; }
/* absolute — % heights don't resolve against the media box's aspect-ratio-derived height */
.product-media img.product-photo { position: absolute; inset: 6%; width: 88%; height: 88%; margin: auto; object-fit: contain; transition: transform .3s ease; }
.product-card:hover .product-media svg,
.product-card:hover .product-media img.product-photo { transform: scale(1.06) rotate(-2deg); }
/* Full-bleed splash renders (square, opaque) fill the whole tile — no cream frame */
.product-media:has(img.product-photo.bleed) { padding: 0; }
.product-media img.product-photo.bleed { inset: 0; width: 100%; height: 100%; object-fit: cover; }
.product-card:hover .product-media img.product-photo.bleed { transform: scale(1.05); }
.deal-badge {
  position: absolute; top: 10px; left: 10px; z-index: 1;
  background: var(--red-600); color: #fff; border-radius: var(--radius-sm);
  font-size: .68rem; font-weight: 700; padding: 4px 11px; letter-spacing: .04em;
}
.deal-badge.pink { background: var(--pink-500); }
.deal-badge.navy { background: var(--navy-900); }
/* "#3 in June" — the monthly ranking. Bottom-left, because top-left is the deal
   badge and top-right the wishlist button, and the deal is the commercial
   message that must not be displaced. Navy rather than red so it reads as a
   statement of fact rather than a promotion. */
.rank-badge {
  position: absolute; bottom: 10px; left: 10px; z-index: 1;
  background: var(--navy-900); color: #fff; border-radius: var(--radius-sm);
  font-size: .68rem; font-weight: 700; padding: 4px 11px; letter-spacing: .04em;
}
/* Bare heart, no white disc — Mark's call, 23 Aug 2026, picked off rendered
   variants ("shockwave": pop + double ring, no debris). The unfilled treatment
   is translucent white with a drop shadow because it has to read on BOTH card
   kinds: an ink outline vanishes on the 73 full-bleed splash artworks, and the
   shadow is what carries white on the bone-mounted ones. */
.wish-btn {
  position: absolute; top: 4px; right: 4px; z-index: 1;
  width: 40px; height: 40px; border: none; background: none; padding: 0;
  display: grid; place-items: center; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.wish-btn svg { width: 23px; height: 23px; overflow: visible; transition: transform .2s ease; }
.wish-btn .heart {
  fill: rgba(255,255,255,.30); stroke: #fff; stroke-width: 2;
  filter: drop-shadow(0 1px 3px rgba(15,12,8,.45));
  transition: fill .18s ease, stroke .18s ease;
}
.wish-btn.is-wished .heart {
  fill: var(--red-600); stroke: var(--red-600);
  filter: drop-shadow(0 1px 3px rgba(15,12,8,.35));
}
.wish-btn:hover svg { transform: scale(1.1); }
.wish-btn.bursting svg { animation: wish-pop .6s cubic-bezier(.34,1.56,.64,1); }
.wish-btn.unpop svg { animation: wish-unpop .3s ease; }
@keyframes wish-pop {
  0% { transform: scale(1); }
  20% { transform: scale(.72); }
  48% { transform: scale(1.32); }
  72% { transform: scale(.94); }
  100% { transform: scale(1); }
}
@keyframes wish-unpop {
  0% { transform: scale(1); } 40% { transform: scale(.72); } 100% { transform: scale(1); }
}
/* The shockwave rings. Clipped by .product-media's overflow at the corner and
   that is fine — the picked variant was judged inside the same clipping. */
.wish-ring {
  position: absolute; left: 50%; top: 50%; width: 40px; height: 40px;
  margin: -20px 0 0 -20px; border-radius: 50%; border: 0 solid var(--red-600);
  opacity: 0; pointer-events: none;
}
.wish-btn.bursting .wish-ring { animation: wish-ring .55s cubic-bezier(.2,.7,.35,1) forwards; }
.wish-btn.bursting .wish-ring--2 { animation-delay: .1s; border-color: #f0655a; }
@keyframes wish-ring {
  0% { opacity: .85; border-width: 10px; transform: scale(.32); }
  100% { opacity: 0; border-width: 1px; transform: scale(1.55); }
}
@media (prefers-reduced-motion: reduce) {
  .wish-btn svg { transition: none; }
  .wish-btn.bursting svg, .wish-btn.unpop svg { animation: none; }
  .wish-btn.bursting .wish-ring { animation: none; opacity: 0; }
}
/* Wishlist panel on the account dash — the shop's own cards in a tighter grid. */
.wish-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px; }
.wish-empty { color: var(--ink-mute); margin-bottom: 12px; }
/* The standalone /wishlist page gets the shop's own card width. */
.wish-grid--page { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: var(--sp-3, 18px); }
.wish-link-note { color: var(--ink-mute); margin-bottom: 16px; }
.product-brand { font-size: var(--fs-tiny); font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--red-600); }
.product-name { font-weight: 600; font-size: var(--fs-small); color: var(--navy-900); line-height: 1.4; margin: 4px 0 6px; }
/* Below the money, not above it. Mark's call, 20 Aug 2026, off four rendered
   arrangements. The chips used to sit between the name and the price, so the eye
   crossed a row of metadata to get from the product to what it costs — on the one
   page whose job is getting people to products. Name → price → button now runs
   uninterrupted and the chips read as a footnote. Card height is unchanged.
   They were NOT deleted, which was the shortest option at 67px less per row: the
   nine flavour profiles are the same vocabulary the sidebar filters and the
   recommender use, and they are the fastest way to scan 298 products.
   Moved in the markup rather than with `order`, so the DOM reading order still
   matches the visual one. */
/* margin-top:auto lives HERE, not on .product-foot, and that is the whole point
   of the reorder. Cards in a row are stretched to equal height, so ONE element
   absorbs the slack. While the foot absorbed it, a card with two chips pushed its
   own price away from the name to fill the gap while its three-chip neighbour did
   not — reintroducing exactly the name-to-price distance this change removed, and
   only on some cards. On the last element instead, the price sits under the name
   on every card and the chip rows line up along the bottom of the row. */
.flavour-tags {
  display: flex; gap: 5px; flex-wrap: wrap; margin-top: auto; padding-top: 10px;
}
.flavour-tags span {
  font-size: .66rem; font-weight: 600; border-radius: var(--radius-sm); padding: 3px 9px;
  background: var(--blue-100); color: var(--navy-700);
}
/* A money column and a button column, not a flex row. The arithmetic that forces
   this: the foot is 231px at 1166px (3 columns), the button's label needs 132px
   and the multibuy note needs ~130px, so the two CANNOT share a line. Three ways
   out and only one reads well — the note full-width on its own row leaves it
   dangling under the button with dead space beside it, and shortening the label
   to "Choose" buys only ~12px, so the note still wraps AND the label goes vague.
   So: the note tucks under the price it explains, and the button spans both rows
   and centres against them.
   Before this the note lived INSIDE .price, which held that column at ~111px and
   left 113px for the button — it wrapped and drew 113x59 instead of 132x39. Only
   at 3 columns; at 2 there was room, which is why it read as "odd sometimes". */
.product-foot {
  display: grid;
  grid-template-columns: 1fr auto; grid-template-rows: auto auto;
  column-gap: 10px; row-gap: 1px;
}
/* The display face, not the body one. Mark's pick, 20 Aug 2026, from a ladder of
   1.05/700/Inter, 1.25/800/Inter, 1.35/800/Montserrat and the same in red.
   Montserrat is what .deal-price already uses for money on the deals page, so
   this is the house treatment rather than a new one. NOT red: the brand name is
   red directly above it and the deal badge is red on the photo, so a red price
   makes three stacked, and red money reads as a markdown — which £3.99 is not.
   The saving is the multibuy, and it has its own line underneath. */
.price {
  grid-column: 1; grid-row: 1;
  font-family: var(--font-display); font-weight: 800; letter-spacing: -.01em;
  color: var(--navy-900); font-size: 1.35rem;
}
.multibuy-note {
  grid-column: 1; grid-row: 2;
  font-weight: 500; color: var(--ink-mute); font-size: var(--fs-tiny);
}
/* Spans both rows so it centres on the money block rather than sitting against
   the price and leaving the note hanging below it. Cards with no deal have no
   note and no second row, so this still centres on a single line.
   padding-block is 12px rather than the base 9px because at 39px the button read
   thin beside a two-line money block. Mark's pick, 20 Aug 2026, off a rendered
   ladder of 39 / 45 / 51 / 63px: 45px. NOT stretched to fill — the full-height
   version is 63px, which is bigger than the 59px the wrapped button used to be
   and brings back the bulk this whole change was undoing. */
.product-foot .add-btn {
  grid-column: 2; grid-row: 1 / span 2; align-self: center; white-space: nowrap;
  padding-block: 12px;
}
.add-btn {
  border: none; background: var(--navy-900); color: #fff;
  border-radius: var(--radius-sm); padding: 9px 16px; font-size: var(--fs-tiny); font-weight: 600;
  transition: background .2s ease, transform .2s ease;
}
/* Cards for products that have a nicotine strength render the same control as a link to
   the product page rather than a button — see productCard() in js/main.js. */
a.add-btn { display: inline-block; text-align: center; text-decoration: none; }
.add-btn:hover { background: var(--red-600); transform: scale(1.04); }
.add-btn:disabled { background: var(--paper-warm); color: var(--ink-mute); border: 1px solid var(--line); cursor: not-allowed; }
.add-btn:disabled:hover { background: var(--paper-warm); transform: none; }

/* ---------- out of stock (LIVE unticked in the sheet) ---------- */
/* neutral badge replacing the deal chip; strong grey-out on the artwork, muted copy */
.stock-badge {
  position: absolute; top: 10px; left: 10px; z-index: 1;
  background: var(--ink-soft); color: #fff; border-radius: var(--radius-sm);
  font-size: .68rem; font-weight: 700; padding: 4px 11px; letter-spacing: .04em;
}
.product-card.is-oos .product-photo,
.product-card.is-oos .product-media svg { filter: grayscale(1); opacity: .5; }
.product-card.is-oos .product-brand,
.product-card.is-oos .product-name { color: var(--ink-mute); }
.product-card.is-oos .flavour-tags { opacity: .5; }
.price.price-oos { color: var(--ink-mute); font-weight: 600; font-size: .95rem; }
/* search results mirror the same out-of-stock treatment */
.search-result.is-oos .search-result-media img,
.search-result.is-oos .search-result-media svg { filter: grayscale(1); opacity: .55; }
.search-result-price.price-oos { color: var(--ink-mute); font-weight: 600; }

/* ---------- strength finder ---------- */
.strength-section { background: var(--navy-black); }
/* smoking → vaping strength scale */
.s-low  { --accent: var(--blue-300); }
.s-mid  { --accent: #e6b53a; }
.s-high { --accent: #ff5a5a; }
.strength-scale { margin-top: var(--sp-5); }
.scale-caps {
  display: flex; justify-content: space-between; padding: 0 6px; margin-bottom: 12px;
  font-size: var(--fs-tiny); font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: var(--on-dark-soft);
}
.scale-track { position: relative; height: 20px; margin-bottom: var(--sp-4); }
.scale-line {
  position: absolute; top: 50%; left: 16.66%; right: 16.66%; height: 3px; transform: translateY(-50%);
  background: linear-gradient(90deg, var(--blue-300), #e6b53a 50%, #ff5a5a); border-radius: 3px;
}
.scale-nodes { position: relative; display: grid; grid-template-columns: repeat(3, 1fr); height: 100%; align-items: center; }
.scale-nodes .node {
  justify-self: center; width: 16px; height: 16px; border-radius: 50%;
  background: var(--accent); border: 3px solid var(--navy-black); box-shadow: 0 0 0 3px var(--accent);
}
.scale-tiers { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-3); }
.tier {
  background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.1);
  border-top: 2px solid var(--accent); border-radius: var(--radius); padding: var(--sp-4);
  transition: transform .22s ease, background .22s ease;
}
.tier:hover { transform: translateY(-5px); background: rgba(255,255,255,.07); }
.tier-head small { display: block; font-size: var(--fs-tiny); letter-spacing: .1em; text-transform: uppercase; color: var(--on-dark-soft); margin-bottom: 6px; }
.tier-head b { font-family: var(--font-display); font-weight: 800; color: #fff; font-size: 2rem; line-height: 1; }
.tier-meter { display: flex; gap: 5px; margin: var(--sp-3) 0 var(--sp-4); }
.tier-meter i { flex: 1; height: 7px; border-radius: 4px; background: rgba(255,255,255,.14); }
.tier-meter i.on { background: var(--accent); }
.tier-recs { list-style: none; display: flex; flex-direction: column; gap: 10px; }
.tier-recs li { color: var(--on-dark-soft); font-size: var(--fs-small); }
.tier-recs b { color: #fff; font-weight: 700; }
.tier-recs li span { font-size: var(--fs-tiny); text-transform: uppercase; letter-spacing: .08em; color: var(--on-dark-soft); margin-right: 6px; }

/* ---------- KURO band ---------- */
.kuro-band {
  background: #0c0b0a;
  color: #fff; position: relative; overflow: hidden;
}
.kuro-inner { display: grid; grid-template-columns: 1.1fr .9fr; gap: var(--sp-5); align-items: center; padding-block: var(--sp-6) var(--sp-4); position: relative; z-index: 1; }
.kuro-video {
  /* cap at viewport height + centre — full-band cover zoomed the 720p frame
     ~2.5x; this shows the whole scene, edges fading into the band's black */
  position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 100%; height: min(100%, 100vh);
  object-fit: cover; filter: saturate(.35) brightness(.5);
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 16%, #000 84%, transparent);
  mask-image: linear-gradient(to bottom, transparent, #000 16%, #000 84%, transparent);
  pointer-events: none;
}
.kuro-video-shade {
  position: absolute; inset: 0;
  background: rgba(12,11,10,.55);
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce), (max-width: 768px) {
  /* no video download on mobile/reduced-motion — static poster frame instead,
     graded to match so the band doesn't fall back to flat black */
  .kuro-video { display: none; }
  .kuro-band::before {
    content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
    width: 100%; height: min(100%, 100vh);
    background: url("../videos/kuro-tokyo-poster.jpg") center / cover no-repeat;
    filter: saturate(.35) brightness(.5);
    -webkit-mask-image: linear-gradient(to bottom, transparent, #000 16%, #000 84%, transparent);
    mask-image: linear-gradient(to bottom, transparent, #000 16%, #000 84%, transparent);
    pointer-events: none;
  }
}
.kuro-band .glow {
  position: absolute; inset: 0;
  background: radial-gradient(120% 140% at 50% 50%, color-mix(in srgb, var(--kuro-accent) 30%, transparent), transparent 70%);
  mix-blend-mode: screen; /* tints the city lights the kanji colour */
  pointer-events: none;
  opacity: 0; transition: opacity 1s ease;
}
.kuro-band .glow.on { opacity: 1; }

/* ---------- Bar Juice 5000 band ---------- */
.bj-band {
  background: #fbf8f1 url("../images/barjuice/bj-fruit-bg.jpg") center / cover no-repeat;
  color: var(--ink);
  position: relative; overflow: hidden;
  /* min-height (not aspect-ratio) — overflow:hidden zeroes the auto content minimum,
     so a locked aspect clipped the cards/CTA when content ran taller than the artwork */
  min-height: calc(100vw * (1536 / 2752));
  display: grid; place-items: center;
  padding-block: var(--sp-4);
}
.bj-band .glow {
  position: absolute; inset: 0;
  background: radial-gradient(120% 140% at 50% 50%, color-mix(in srgb, var(--kuro-accent) 14%, transparent), transparent 70%);
  pointer-events: none;
  opacity: 0; transition: opacity 1.4s ease;
}
.bj-band .glow.on { opacity: 1; }
/* The one LIGHT surface this device lands on (#fbf8f1 + the fruit photograph), so the
   two type colours flip to their ink equivalents. The red rule is the same on both. */
.bj-band .kuro-deal b { color: var(--ink); }
.bj-band .kuro-deal small { color: var(--ink-mute); }
.bj-head {
  position: relative; z-index: 1; text-align: center;
  display: flex; flex-direction: column; align-items: center;
}
.bj-head p { color: var(--ink-mute); font-size: var(--fs-small); max-width: 52ch; margin-top: -6px; }
/* .bj-head centres its column, and a left rule with centred type either side of it
   reads as a mistake. The device keeps its own left alignment; the block itself
   still centres. */
.bj-head .kuro-deal { margin-block: var(--sp-2); text-align: left; }
.bj-logo { margin: 0 0 var(--sp-3); }
.bj-logo img { display: block; width: min(230px, 60%); height: auto; margin-inline: auto; }
.bj-ctas { justify-content: center; margin-top: var(--sp-5); }
.bjs-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-2);
  width: 100%; max-width: 860px; margin-top: var(--sp-3);
}
.bjs-card {
  background: rgba(255,255,255,.72); backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px);
  border: 1px solid rgba(23,21,15,.08); border-radius: var(--radius);
  padding: 16px 14px 14px; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  transition: transform .25s ease, border-color .25s ease, box-shadow .25s ease;
}
.bjs-card:hover {
  transform: translateY(-4px);
  border-color: var(--kuro-accent);
  box-shadow: 0 10px 34px color-mix(in srgb, var(--kuro-accent) 22%, transparent);
}
.bjs-card img { height: 110px; width: auto; filter: drop-shadow(0 10px 18px rgba(23,21,15,.2)); }
.bjs-card h3 { font-family: var(--font-display); font-weight: 700; font-size: .95rem; margin-top: 6px; }
.bjs-card .ks-price b { color: var(--ink); }
@media (max-width: 1024px) {
  .bjs-grid { grid-template-columns: repeat(2, 1fr); }
  .bj-band { min-height: 0; padding-block: var(--sp-6); } /* stacked content taller than the artwork — let it flow */
}
@media (max-width: 560px)  { .bjs-grid { grid-template-columns: 1fr; } }
.bj-marquee {
  /* .bj-band is display:grid; place-items:center, so a child centres and shrinks to
     its content. The mask below fades the two EDGES of the band, which only means
     anything if the strip actually reaches them — hence the stretch. */
  justify-self: stretch; width: 100%;
  position: relative; z-index: 1; margin-top: var(--sp-5);
  overflow: hidden;
  mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
}
.bj-track {
  display: flex; align-items: flex-end; gap: 34px; width: max-content;
  animation: bjScroll 150s linear infinite;
  padding-block: 10px;
}
/* 🚨 No :hover { animation-play-state: paused } here. It stopped a 52-bottle strip
   DEAD mid-glide and started it again just as hard — the whole point of a marquee is
   that it drifts. home-bands.js eases the animation's playbackRate to 0 and back to 1
   instead, so it coasts to a halt and picks back up. A play-state rule would win
   instantly over that ramp and undo the whole thing, which is why it is gone rather
   than merely overridden. */
@keyframes bjScroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .bj-track { animation: none; } }
.bj-cell { display: flex; flex-direction: column; align-items: center; gap: 10px; margin: 0; }
/* 🚨 NO drop-shadow here, and it is not an oversight. The note at .kuro-card says a
   drop-shadow "follows each bottle's alpha silhouette, so the shadow reads per-bottle,
   not as a box" — true of one bottle, false of fifty-two. At 55px wide with a 34px gap
   and a 22px blur, the shadows of neighbouring bottles overlap continuously and merge
   into a soft grey rectangle the width of the whole strip, with a visible top edge
   across the fruit photograph. Proved by toggling it off against the live page: the
   band vanishes and the fruit reads through. Spaced-out cards keep theirs
   (.bjs-card img, .ks-card img) — there the silhouettes never touch. */
.bj-cell img { height: 200px; width: auto; }
/* The .bj-cell figcaption rule is deliberately gone. It set 9.9px --ink-mute over the
   fruit photograph: measured against the real background pixels, contrast under a
   caption ran 1.00-3.06 against a 4.5 floor, and the strip scrolls so it never settles.
   See the note in home-bands.js. Bringing names back needs a scrim, not a colour. */

/* ---------- KURO best sellers ---------- */
.kuro-sellers { position: relative; z-index: 1; color: #fff; padding-block: var(--sp-2) var(--sp-5); }
.kuro-sellers .section-head { margin-bottom: var(--sp-3); }
.kuro-sellers .section-head h2 { color: #fff; }
.kuro-sellers .section-head p { color: var(--on-dark-soft); }
.kuro-sellers-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-2);
  margin-top: var(--sp-3);
}
.ks-card {
  --kuro-accent: var(--accent-on-dark);
  background: rgba(16,14,12,.78); backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px);
  border: 1px solid rgba(255,255,255,.08); border-radius: var(--radius);
  padding: 18px 16px 16px; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  transition: transform .25s ease, border-color .25s ease, box-shadow .25s ease;
}
.ks-card:hover {
  transform: translateY(-4px);
  border-color: var(--kuro-accent);
  box-shadow: 0 0 44px color-mix(in srgb, var(--kuro-accent) 25%, transparent);
}
.ks-card img { height: 148px; width: auto; filter: drop-shadow(0 12px 22px rgba(0,0,0,.5)); }
.ks-card h3 { font-family: var(--font-display); font-weight: 700; font-size: 1.05rem; margin-top: 4px; }
.ks-notes { color: var(--on-dark-soft); font-size: var(--fs-tiny); letter-spacing: .04em; }
.ks-price { display: flex; align-items: center; gap: 10px; margin-top: 2px; }
.ks-price b { font-family: var(--font-display); font-weight: 800; font-size: 1.15rem; }
/* An unpriced product states its status where the price would go. It must not wear
   the price's weight — it is longer than any price and would out-shout the name. */
.ks-price b.ks-soon {
  font-family: var(--font-body); font-weight: 600; font-size: .78rem;
  letter-spacing: .01em; color: var(--ink-mute); text-transform: none;
}
.kuro-sellers .ks-price b.ks-soon { color: color-mix(in srgb, #fff 62%, transparent); }
.ks-deal {
  font-family: var(--font-display); font-weight: 700; font-size: .62rem; letter-spacing: .12em;
  color: var(--kuro-accent); border: 1px solid color-mix(in srgb, var(--kuro-accent) 55%, transparent);
  border-radius: var(--radius-sm); padding: 4px 8px;
}
.ks-btn { margin-top: auto; width: 100%; }
.ks-cta { text-align: center; margin-top: var(--sp-3); }
@media (max-width: 1024px) { .kuro-sellers-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px)  { .kuro-sellers-grid { grid-template-columns: 1fr; } }

/* British Mist best-sellers band */
.bm-sellers { position: relative; overflow: hidden; background: var(--navy-950); color: #fff; padding-block: var(--sp-5); }
.bm-sellers::before { content: ""; position: absolute; inset: 0; background: radial-gradient(70% 90% at 50% -10%, rgba(var(--red-rgb),.28), transparent 65%); pointer-events: none; }
.bm-sellers .container { position: relative; z-index: 1; }
.bm-sellers .section-head { margin-bottom: var(--sp-3); }
.bm-sellers .section-head h2 { color: #fff; }
.bm-sellers .section-head p { color: var(--on-dark-soft); }
.bm-sellers-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-2); margin-top: var(--sp-3); }
.bm-card {
  --kuro-accent: var(--accent-on-dark);
  background: rgba(16,14,12,.78); backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px);
  border: 1px solid rgba(255,255,255,.08); border-radius: var(--radius); overflow: hidden;
  display: flex; flex-direction: column;
  transition: transform .25s ease, border-color .25s ease, box-shadow .25s ease;
}
.bm-card:hover { transform: translateY(-4px); border-color: var(--red-600); box-shadow: 0 0 44px color-mix(in srgb, var(--red-600) 25%, transparent); }
.bm-card-media { display: block; }
.bm-card-media img { width: 100%; height: 190px; object-fit: cover; display: block; }
.bm-card-body { padding: 14px 16px 16px; display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; flex: 1; }
.bm-card-body h3 { font-family: var(--font-display); font-weight: 700; font-size: 1.05rem; margin-top: 2px; }
@media (max-width: 1024px) { .bm-sellers-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px)  { .bm-sellers-grid { grid-template-columns: repeat(2, 1fr); } }
.kuro-kicker { color: var(--accent-on-dark); font-family: var(--font-display); font-weight: 700; font-size: var(--fs-tiny); letter-spacing: .2em; }
.kuro-band h2 { font-family: var(--font-display); font-weight: 700; font-size: var(--fs-h2); margin: var(--sp-2) 0; }
.kuro-band h2 .jp { color: var(--pink-500); }
.kuro-band h2.kuro-lockup { margin: var(--sp-3) 0; }
.kuro-band .kuro-lockup img { display: block; width: min(360px, 100%); height: auto; }
.kuro-band p { color: var(--on-dark-soft); font-size: var(--fs-small); max-width: 46ch; }
/* The site's standing deal device: a red rule and type, no container.
   Until 17 Aug 2026 this was a bordered, tinted chip with a red zap tile, and the
   offer itself was the smallest thing in it at 1.05rem. Mark reviewed four directions
   on localhost and picked this one. It carries the hero, the KURO band and the Bar
   Juice 5000 band. The British Mist BAR band keeps its own .mistbar-offer treatment
   on purpose — see the note above it — so a new range still reads as a launch and
   not as standing furniture. */
.kuro-deal {
  margin-block: var(--sp-3);
  border-left: 3px solid var(--red-500);
  padding-left: 20px;
}
.kuro-deal b {
  display: block;
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(1.7rem, 2.4vw, 2.15rem);
  line-height: 1; letter-spacing: -.015em;
  color: var(--on-dark);
}
.kuro-deal b em { color: var(--red-500); font-style: normal; }
.kuro-deal small {
  display: block; margin-top: 9px;
  color: var(--on-dark-soft);
  font-size: .68rem; letter-spacing: .14em; font-weight: 600;
}
.kuro-visual { display: grid; place-items: center; }
@property --kuro-accent {
  syntax: '<color>';
  inherits: true;
  initial-value: #e5342b;
}
.kuro-band { --kuro-accent: var(--accent-on-dark); }
.kuro-visual .kuro-card {
  width: min(460px, 96%);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px;
}
.kuro-visual .kuro-card .k-en { font-family: var(--font-display); font-weight: 700; letter-spacing: .3em; font-size: .9rem; }
.kuro-visual .kuro-card .k-kanji { width: 46%; height: auto; margin-top: var(--sp-5); }
.kuro-visual .kuro-card .k-bottle { height: 520px; width: auto; filter: drop-shadow(0 14px 28px rgba(0,0,0,.55)); }
.kuro-visual .kuro-card .k-sub { font-size: .65rem; letter-spacing: .38em; color: #8f8f9c; text-transform: uppercase; }

/* ---------- USP / why different ---------- */
/* Why British Mist — asymmetric bento */
.usp-bento {
  display: grid; gap: var(--sp-3);
  grid-template-columns: repeat(3, 1fr);
  grid-template-areas:
    "deliver deliver reward"
    "curated support reward";
}
.usp-a { grid-area: deliver; }
.usp-b { grid-area: reward; }
.usp-c { grid-area: curated; }
.usp-d { grid-area: support; }
.usp-tile {
  position: relative; overflow: hidden;
  border-radius: var(--radius); padding: var(--sp-4);
  display: flex; flex-direction: column; gap: var(--sp-3); min-height: 190px;
  transition: transform .25s ease, box-shadow .25s ease, border-color .25s ease;
}
.usp-tile:hover { transform: translateY(-5px); }
.usp-ic { width: 46px; height: 46px; border-radius: var(--radius); display: grid; place-items: center; flex-shrink: 0; }
.usp-ic svg { width: 23px; height: 23px; }
.usp-body { margin-top: auto; display: flex; flex-direction: column; gap: 7px; }
.usp-tile h3 { font-family: var(--font-display); font-weight: 700; font-size: 1.18rem; line-height: 1.2; }
.usp-tile p { font-size: var(--fs-small); line-height: 1.55; }
.usp-c, .usp-d { background: #fff; border: 1px solid var(--line); }
.usp-c:hover, .usp-d:hover { box-shadow: var(--shadow-lift); border-color: transparent; }
.usp-c .usp-ic, .usp-d .usp-ic { background: var(--red-050); color: var(--red-600); }
.usp-c h3, .usp-d h3 { color: var(--navy-900); }
.usp-c p, .usp-d p { color: var(--ink-mute); }
.usp-a { background: var(--navy-black); color: #fff; }
.usp-a .usp-ic { background: rgba(255,255,255,.1); color: #fff; }
.usp-a h3 { color: #fff; }
.usp-a p { color: var(--on-dark-soft); max-width: 48ch; }
.usp-tag { font-size: var(--fs-tiny); font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--blue-300); margin-top: 2px; }
.usp-b { background: linear-gradient(155deg, #d81020, var(--red-600) 55%, #8f0000); color: #fff; }
.usp-b .usp-ic { background: rgba(255,255,255,.18); color: #fff; }
.usp-b h3 { color: #fff; font-size: 1.28rem; }
.usp-b p { color: rgba(255,255,255,.86); }
.usp-stat { font-family: var(--font-display); font-weight: 800; font-size: 4.2rem; line-height: .85; letter-spacing: -.03em; color: #fff; }
.usp-stat em { display: block; font-style: normal; font-size: .9rem; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: rgba(255,255,255,.82); margin-top: 10px; }

/* ---------- reviews ---------- */
.review-band { background: var(--paper-warm); overflow: hidden; }
.review-marquee {
  position: relative; width: 100%;
  overflow: hidden; padding-block: var(--sp-2);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.review-track { display: flex; width: max-content; animation: review-scroll 62s linear infinite; }
.review-marquee:hover .review-track { animation-play-state: paused; }
@keyframes review-scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.review-card {
  background: #fff; border-radius: var(--radius); padding: var(--sp-4); box-shadow: var(--shadow-card);
  display: flex; flex-direction: column; gap: var(--sp-2);
  width: 360px; flex-shrink: 0; margin-right: var(--sp-3);
}
@media (prefers-reduced-motion: reduce) {
  .review-track { animation: none; }
  .review-marquee { overflow-x: auto; }
}
.stars { display: flex; gap: 3px; color: #00b67a; }
.stars svg { width: 18px; height: 18px; }
.review-card blockquote { font-size: var(--fs-small); color: var(--ink-soft); }
.review-card footer { margin-top: auto; display: flex; align-items: center; gap: 10px; font-size: var(--fs-tiny); color: var(--ink-mute); }
.review-card footer .avatar {
  width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0;
  display: grid; place-items: center; color: #fff; font-weight: 700; font-size: .8rem;
}
.tp-note { text-align: center; margin-top: var(--sp-4); font-size: var(--fs-small); color: var(--ink-mute); }
.tp-note b { color: #00b67a; }
.tp-cta { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-3); margin-top: var(--sp-5); }

/* two-source reviews (Google + Trustpilot rows) */
.review-block { margin-top: var(--sp-6); }
.review-block:first-of-type { margin-top: var(--sp-5); }
.review-provider {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  font-family: var(--font-display); font-weight: 700; letter-spacing: -.01em;
  font-size: clamp(1.05rem, 2.4vw, 1.45rem); color: var(--ink);
  margin-bottom: var(--sp-4);
}
.review-provider .prov-logo { display: inline-flex; width: 22px; height: 22px; }
.review-provider .prov-logo svg { width: 100%; height: 100%; }
.prov-logo.tp-star { color: #00b67a; }
.review-track--rev { animation-direction: reverse; animation-duration: 68s; }
.review-block--google .stars { color: #fbbc04; }

/* ---------- guides ---------- */
.guide-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-3); }
.guide-card {
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
  background: #fff; display: flex; flex-direction: column;
  transition: transform .22s ease, box-shadow .22s ease;
}
.guide-card:hover { transform: translateY(-5px); box-shadow: var(--shadow-lift); }
.guide-thumb { aspect-ratio: 16/8.5; display: flex; align-items: flex-end; padding: var(--sp-2); color: #fff; position: relative; }
.guide-thumb .pill { background: rgba(255,255,255,.2); backdrop-filter: blur(6px); border-radius: var(--radius-sm); padding: 4px 12px; font-size: var(--fs-tiny); font-weight: 600; }
.guide-1 { background: linear-gradient(135deg, var(--navy-800), var(--blue-300)); }
.guide-2 { background: linear-gradient(135deg, #7c1024, var(--red-500)); }
.guide-3 { background: linear-gradient(135deg, #5b1a86, var(--pink-500)); }
.guide-4 { background: linear-gradient(135deg, #0e5a46, var(--green-500)); }
.guide-5 { background: linear-gradient(135deg, #9a6a00, #e0b52a); }
.guide-6 { background: linear-gradient(135deg, var(--navy-950), var(--navy-700)); }
.guide-body { padding: var(--sp-3); display: flex; flex-direction: column; gap: 8px; flex: 1; }
.guide-body h3 { font-size: var(--fs-h3); color: var(--navy-900); line-height: 1.4; }
.guide-body p { font-size: var(--fs-small); color: var(--ink-mute); }
.guide-body .read { margin-top: auto; font-weight: 600; font-size: var(--fs-small); color: var(--red-600); display: inline-flex; gap: 8px; align-items: center; }
.guide-body .read::after { content: "→"; transition: transform .2s ease; }
.guide-card:hover .read::after { transform: translateX(4px); }

/* ---------- guide article pages ---------- */
.article-layout {
  display: grid; grid-template-columns: minmax(0, 1fr) 260px;
  gap: var(--sp-6); padding-block: var(--sp-5) var(--sp-6);
  align-items: start;
}
.article-meta {
  display: flex; gap: var(--sp-2); flex-wrap: wrap; align-items: center;
  font-size: var(--fs-tiny); color: var(--ink-mute); margin-bottom: var(--sp-4);
  padding-bottom: var(--sp-3); border-bottom: 1px solid var(--line);
}
.article-meta .pill {
  background: var(--red-050); color: var(--red-600); border-radius: var(--radius-sm);
  padding: 4px 12px; font-weight: 600;
}
.article-body { max-width: 70ch; }
.article-body h2 {
  font-family: var(--font-display); font-weight: 700; font-size: 1.45rem;
  color: var(--navy-900); margin: var(--sp-5) 0 var(--sp-2);
  scroll-margin-top: 120px;
}
.article-body h2:first-child { margin-top: 0; }
.article-body h3 {
  font-family: var(--font-display); font-weight: 600; font-size: 1.1rem;
  color: var(--navy-900); margin: var(--sp-4) 0 var(--sp-1);
  scroll-margin-top: 120px;
}
.article-body p { margin-bottom: var(--sp-2); color: var(--ink-soft); }
.article-body p.lede { font-size: 1.08rem; color: var(--ink); }
.article-body ul, .article-body ol { margin: 0 0 var(--sp-2) 1.2em; color: var(--ink-soft); }
.article-body li { margin-bottom: 8px; }
.article-body li::marker { color: var(--red-600); font-weight: 600; }
.article-body strong { color: var(--navy-900); }
.article-body a:not(.btn) { color: var(--red-600); font-weight: 500; text-decoration: underline; text-underline-offset: 3px; }

/* data tables */
.article-table-wrap { overflow-x: auto; margin-block: var(--sp-3); border: 1px solid var(--line); border-radius: var(--radius-sm); }
.article-body table { border-collapse: collapse; width: 100%; font-size: var(--fs-small); }
.article-body th {
  background: var(--navy-900); color: #fff; text-align: left;
  font-family: var(--font-display); font-weight: 600; font-size: var(--fs-tiny);
  letter-spacing: .06em; text-transform: uppercase;
  padding: 12px 16px; white-space: nowrap;
}
.article-body td { padding: 12px 16px; border-top: 1px solid var(--line); color: var(--ink-soft); vertical-align: top; }
.article-body tbody tr:nth-child(even) { background: var(--paper-warm); }
.article-body td b { color: var(--navy-900); }

/* callouts */
.callout {
  border-radius: var(--radius-sm); padding: var(--sp-2) var(--sp-3);
  margin-block: var(--sp-3); font-size: var(--fs-small);
  border-left: 3px solid var(--navy-900); background: var(--blue-100);
  color: var(--navy-900);
}
.callout.key { border-left-color: var(--red-600); background: var(--red-050); }
.callout b { display: block; font-family: var(--font-display); margin-bottom: 4px; }
.callout p { margin-bottom: 0; color: inherit; }

/* key facts box */
.key-facts {
  background: var(--navy-950); color: var(--on-dark); border-radius: var(--radius);
  padding: var(--sp-4); margin-block: var(--sp-4);
}
.key-facts h2, .key-facts h3 { color: #fff; margin-top: 0 !important; font-size: 1.05rem; }
.key-facts ul { margin: var(--sp-2) 0 0 1.2em; }
.key-facts li { color: var(--on-dark-soft); font-size: var(--fs-small); }
.key-facts li b { color: #fff; }
.key-facts li::marker { color: var(--blue-300); }

/* sources */
.article-sources {
  margin-top: var(--sp-5); padding-top: var(--sp-3); border-top: 1px solid var(--line);
  font-size: var(--fs-tiny); color: var(--ink-mute);
}
.article-sources h2 { font-size: 1rem !important; margin: 0 0 var(--sp-2) !important; }
.article-sources ol { margin-left: 1.2em; }
.article-sources li { margin-bottom: 4px; overflow-wrap: anywhere; }

/* sticky sidebar
   The sticky lives on the ASIDE, never on .article-toc. A sticky element's travel is
   bounded by its containing block, and that aside is only as tall as the two cards in
   it (477px against a 262px TOC), so a sticky TOC slid 215px down over the card sitting
   beneath it. Measured on production before this changed: 103px of overlap at scrollY
   600 and 191px at 1600 — the whole height of the "Shop by strength" card, buried, on
   all six guide pages at ordinary reading positions.
   This works only because .article-layout sets align-items: start. A stretched grid
   item fills its own containing block and has nowhere to travel, so the fix would
   silently do nothing. test/guide-sidebar.test.mjs pins that. */
.article-layout > aside { position: sticky; top: 120px; }
.article-toc {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: var(--sp-3); font-size: var(--fs-small);
}
.article-toc h4 {
  font-size: var(--fs-tiny); letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-mute); margin-bottom: var(--sp-2);
}
.article-toc a {
  display: block; padding-block: 6px; color: var(--ink-soft);
  border-left: 2px solid transparent; padding-left: 12px; margin-left: -12px;
  transition: color .2s ease, border-color .2s ease;
}
.article-toc a:hover { color: var(--red-600); border-left-color: var(--red-600); }
.article-cta-card {
  margin-top: var(--sp-3); background: var(--navy-900); color: var(--on-dark);
  border-radius: var(--radius); padding: var(--sp-3); font-size: var(--fs-small);
}
.article-cta-card b { color: #fff; font-family: var(--font-display); display: block; margin-bottom: 6px; }
.article-cta-card p { color: var(--on-dark-soft); font-size: var(--fs-tiny); margin-bottom: var(--sp-2); }
.article-cta-card .btn { width: 100%; }

/* related guides strip */
.related-guides { background: var(--paper-warm); padding-block: var(--sp-6); }
.related-guides .guide-grid { grid-template-columns: repeat(3, 1fr); }

.article-disclaimer {
  font-size: var(--fs-tiny); color: var(--ink-mute); background: var(--paper-warm);
  border-radius: var(--radius-sm); padding: var(--sp-2) var(--sp-3); margin-top: var(--sp-4);
}

@media (max-width: 1024px) {
  .article-layout { grid-template-columns: minmax(0, 1fr); }
  /* One column here, so the aside sits UNDER the article rather than beside it, and a
     sticky card would follow the reader down the page over the copy it is meant to sit
     next to. Sticky is for the two-column layout only. */
  .article-layout > aside { position: static; }
  .article-toc { display: none; }
  .related-guides .guide-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .related-guides .guide-grid { grid-template-columns: 1fr; }
}

/* ---------- FAQ ---------- */
.faq-wrap { max-width: 760px; margin-inline: auto; }
.faq-item { border-bottom: 1px solid var(--line); }
.faq-item summary {
  list-style: none; cursor: pointer; padding: var(--sp-3) 0;
  font-weight: 600; color: var(--navy-900); font-size: var(--fs-body);
  display: flex; justify-content: space-between; align-items: center; gap: var(--sp-2);
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: "+"; font-size: 1.4rem; font-weight: 400; color: var(--red-600);
  transition: transform .25s ease; flex-shrink: 0;
}
.faq-item[open] summary::after { transform: rotate(45deg); }
.faq-item .faq-body { padding-bottom: var(--sp-3); color: var(--ink-mute); font-size: var(--fs-small); max-width: 64ch; }

/* ---------- newsletter ---------- */
.newsletter {
  background: linear-gradient(120deg, var(--red-600), #8f0f1f 70%);
  border-radius: var(--radius); color: #fff;
  padding: var(--sp-5); display: grid; grid-template-columns: 1fr auto;
  gap: var(--sp-4); align-items: center; position: relative; overflow: hidden;
}
.newsletter::before {
  content: ""; position: absolute; width: 300px; height: 300px; border-radius: 50%;
  border: 40px solid rgba(255,255,255,.07); right: -80px; top: -110px;
}
.newsletter h3 { font-family: var(--font-display); font-weight: 700; font-size: 1.5rem; }
.newsletter p { font-size: var(--fs-small); opacity: .88; margin-top: 6px; max-width: 46ch; }
.newsletter form { display: flex; gap: 8px; background: #fff; border-radius: var(--radius-sm); padding: 6px; min-width: 380px; }
.newsletter input {
  border: none; outline: none; flex: 1; padding: 10px 18px;
  font-family: inherit; font-size: var(--fs-small); border-radius: var(--radius-sm); color: var(--ink);
}
.newsletter .btn { padding: 12px 24px; }
.newsletter small.note { font-size: .68rem; opacity: .75; display: block; margin-top: 10px; }

/* ---------- footer ---------- */
.site-footer { background: var(--navy-black); color: var(--on-dark-soft); margin-top: var(--sp-7); }
.footer-main {
  display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: var(--sp-5);
  padding-block: var(--sp-6);
}
.footer-brand p { font-size: var(--fs-small); margin-top: var(--sp-2); max-width: 34ch; }
.footer-col h4 { color: #fff; font-size: var(--fs-small); font-weight: 600; letter-spacing: .08em; text-transform: uppercase; margin-bottom: var(--sp-2); }
.footer-col a { display: block; padding-block: 6px; font-size: var(--fs-small); transition: color .2s ease; }
.footer-col a:hover { color: #fff; }
.footer-compliance {
  border-top: 1px solid rgba(255,255,255,.08); padding-block: var(--sp-3);
  font-size: var(--fs-tiny); line-height: 1.7;
}
.footer-compliance b { color: var(--on-dark); }
.footer-badges { display: flex; gap: 10px; flex-wrap: wrap; margin-top: var(--sp-2); }
.footer-badges span {
  border: 1px solid rgba(255,255,255,.16); border-radius: var(--radius-sm);
  padding: 5px 14px; font-size: var(--fs-tiny); color: var(--on-dark);
}
.footer-legal {
  border-top: 1px solid rgba(255,255,255,.08); padding-block: var(--sp-3);
  display: flex; justify-content: space-between; gap: var(--sp-2); flex-wrap: wrap;
  font-size: var(--fs-tiny);
}

/* Agency credit. Same block as Lilliput Electrical and CP Driving, rewritten in this
   site's tokens rather than pasted with their raw rem/hex values. The mark is a
   photographic vapour render with a real alpha channel (mean alpha .12), so it needs
   the dark footer behind it — never place it on bone. */
.mad-mouse-credit {
  border-top: 1px solid rgba(255,255,255,.08);
  padding-block: var(--sp-4);
  display: flex; justify-content: center;
}
.mad-mouse-credit__link {
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-1);
  color: var(--on-dark-soft); font-size: var(--fs-small); text-decoration: none;
  transition: color .2s ease, opacity .2s ease;
}
.mad-mouse-credit__link:hover { color: #fff; opacity: .85; }
/* Height-driven, width auto: the variants differ in aspect ratio between clients
   (1000x505 here, 1000x475 on Lilliput), so pinning width would letterbox them.
   Taller than Lilliput's 80/112 on purpose — this artwork spends only its top ~45%
   on the script, with the mod and its trail below, so matching their pixel height
   renders the wordmark about half the size and it stops being legible. */
.mad-mouse-credit__link img { height: 104px; width: auto; display: block; }
@media (min-width: 640px) {
  .mad-mouse-credit__link { flex-direction: row; gap: var(--sp-2); }
  .mad-mouse-credit__link img { height: 148px; }
}

/* ---------- age gate ---------- */
.age-gate {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(8,20,32,.82); backdrop-filter: blur(10px);
  display: grid; place-items: center; padding: var(--sp-3);
}
.age-gate[hidden] { display: none; }
.age-card {
  background: #fff; border-radius: var(--radius); max-width: 460px; width: 100%;
  padding: var(--sp-5) var(--sp-4); text-align: center;
  box-shadow: var(--shadow-lift);
}
.age-card .logo-crest { margin-inline: auto; margin-bottom: var(--sp-2); height: 54px; width: auto; }
.age-card h2 { font-family: var(--font-display); font-weight: 700; color: var(--navy-900); font-size: 1.3rem; }
.age-card p { color: var(--ink-mute); font-size: var(--fs-small); margin-top: var(--sp-2); }
.age-card .age-actions { display: flex; gap: var(--sp-2); margin-top: var(--sp-3); }
.age-card .age-actions .btn { flex: 1; }
.age-card .age-warning {
  margin-top: var(--sp-3); font-size: var(--fs-tiny); color: var(--ink-mute);
  background: var(--paper-warm); border-radius: var(--radius-sm); padding: 10px 14px;
}

/* ---------- toast ---------- */
.toast {
  position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%) translateY(20px);
  background: var(--navy-900); color: #fff; border-radius: var(--radius-sm);
  padding: 12px 26px; font-size: var(--fs-small); font-weight: 500;
  opacity: 0; pointer-events: none; transition: all .3s ease; z-index: 150;
  box-shadow: var(--shadow-lift);
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ---------- reveal animation ---------- */
.reveal { opacity: 0; transform: translateY(26px); transition: opacity .6s ease, transform .6s ease; }
.reveal.visible { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  .hero-deal-chip, .hero-local-chip { animation: none; }
}

/* ---------- shop page ---------- */
.shop-layout { display: grid; grid-template-columns: 260px 1fr; gap: var(--sp-5); padding-block: var(--sp-5); align-items: start; }
.shop-main { min-width: 0; }

/* ---- filter panel (desktop = blends into the page, no card) ---- */
.shop-sidebar {
  position: sticky; top: 96px; align-self: start; display: flex; flex-direction: column;
  max-height: calc(100vh - 116px); overflow: hidden;
}
.filter-scroll {
  overflow-y: auto; overscroll-behavior: contain; padding: 0 var(--sp-2) var(--sp-2) 0;
  scrollbar-width: thin; scrollbar-color: var(--line) transparent;
}
.filter-scroll::-webkit-scrollbar { width: 6px; }
.filter-scroll::-webkit-scrollbar-thumb { background: var(--line); border-radius: 3px; }
.filter-scroll::-webkit-scrollbar-track { background: transparent; }

.filter-group { border-top: 1px solid var(--line); padding-top: var(--sp-3); margin-top: var(--sp-3); }
.filter-group:first-child { border-top: 0; padding-top: 0; margin-top: 0; }
.filter-group-head {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  background: none; border: 0; padding: 2px 0; cursor: pointer;
  font-family: var(--font-display); font-weight: 700; font-size: var(--fs-small);
  color: var(--navy-900); letter-spacing: .04em; text-transform: uppercase;
}
.filter-group-head .chev { flex: none; color: var(--ink-mute); transition: transform .2s ease; }
.filter-group.collapsed .chev { transform: rotate(-90deg); }
.filter-group.collapsed .chip-cloud { display: none; }

.chip-cloud { display: flex; flex-wrap: wrap; gap: 8px; margin-top: var(--sp-2); }
.chip {
  display: inline-flex; align-items: center; gap: 7px; line-height: 1;
  padding: 8px 14px; border: 1.5px solid var(--line); border-radius: var(--radius-sm);
  background: #fff; color: var(--ink-soft); font-family: inherit; font-size: var(--fs-small); font-weight: 500;
  cursor: pointer; -webkit-tap-highlight-color: transparent; transition: border-color .15s ease, background .15s ease, color .15s ease;
}
@media (hover: hover) { .chip:hover { border-color: var(--navy-900); color: var(--navy-900); } }
.chip .chip-n { font-size: var(--fs-tiny); color: var(--ink-mute); font-weight: 600; }
.chip[aria-pressed="true"] { background: var(--red-600); border-color: var(--red-600); color: #fff; }
.chip[aria-pressed="true"] .chip-n { color: rgba(255,255,255,.82); }
.chip-all[aria-pressed="true"] { background: var(--navy-900); border-color: var(--navy-900); }

/* ---- toolbar ---- */
.shop-toolbar { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-3); flex-wrap: wrap; }
.filters-toggle {
  display: none; align-items: center; gap: 8px;
  padding: 10px 16px; border: 1.5px solid var(--navy-900); border-radius: var(--radius-sm);
  background: var(--navy-900); color: #fff; font-family: inherit; font-size: var(--fs-small); font-weight: 600; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.filters-badge { display: inline-grid; place-items: center; min-width: 20px; height: 20px; padding: 0 5px; border-radius: var(--radius-sm); background: var(--red-600); color: #fff; font-size: var(--fs-tiny); font-weight: 700; }
.sort-wrap { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; }
.sort-wrap .sort-lbl { font-size: var(--fs-tiny); color: var(--ink-mute); text-transform: uppercase; letter-spacing: .08em; }
.shop-toolbar select {
  font-family: inherit; font-size: var(--fs-small); padding: 9px 34px 9px 14px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background-color: #fff; color: var(--ink-soft);
  -webkit-appearance: none; appearance: none; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%237a756b' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center;
}

/* ---- active-filter pills ---- */
.active-filters { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: var(--sp-3); }
.af-pill {
  display: inline-flex; align-items: center; gap: 6px; padding: 6px 9px 6px 12px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: #fff;
  font-family: inherit; font-size: var(--fs-small); color: var(--navy-900); cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.af-pill .af-x { font-size: 1.05rem; line-height: 1; color: var(--ink-mute); }
.af-clear { background: none; border: 0; color: var(--ink-mute); font-family: inherit; font-size: var(--fs-small); text-decoration: underline; cursor: pointer; padding: 6px; -webkit-tap-highlight-color: transparent; }
@media (hover: hover) {
  .af-pill:hover { border-color: var(--red-600); color: var(--red-600); }
  .af-pill:hover .af-x { color: var(--red-600); }
  .af-clear:hover { color: var(--red-600); }
}

/* ---- drawer chrome (only shown on mobile) + empty state ---- */
.filter-drawer-head, .filter-drawer-foot { display: none; }
/* author display rules must not override the hidden attribute (see search-overlay lesson) */
.filters-badge[hidden], .active-filters[hidden], .filter-backdrop[hidden] { display: none; }
.shop-empty { grid-column: 1 / -1; display: grid; gap: var(--sp-3); justify-items: center; text-align: center; padding: 56px 0; color: var(--ink-mute); }

/* A category that is empty on purpose. Reads as an announcement rather than a failed
   search — see COMING_SOON in shop.astro. Sits on the same bone mount the product tiles
   use, so an empty section still looks like part of the shop and not a broken page. */
.shop-soon { background: var(--paper-warm); border-radius: var(--radius); padding: 64px var(--sp-4); gap: var(--sp-2); }
.shop-soon-kicker { font-family: var(--font-display); font-weight: 700; font-size: clamp(1.3rem, 2.4vw, 1.9rem); color: var(--ink); }
.shop-soon-lede { max-width: 46ch; color: var(--ink-mute); }
.shop-soon .btn { margin-top: var(--sp-2); }
/* Every hero band is lit. This used to be a .page-hero--shop opt-in, on the
   reasoning that legal/help/guide pages "shouldn't carry product imagery" — but
   the mist is not product imagery, it is the brand, and the split left the shop
   glowing while every other landing read like a different site. Mark's call,
   19 Aug 2026: the same treatment everywhere there is a hero.
   overflow:hidden is what crops the layers to the band; nothing inside a hero is
   sticky, and a sticky descendant would stop sticking if one ever landed here. */
.page-hero {
  background: linear-gradient(150deg, var(--navy-black), var(--navy-900));
  color: #fff; padding-block: var(--sp-6);
  position: relative; overflow: hidden;
}
/* The copy's floor. Measured, not judged by eye: with the light at full ambience
   the brightest pixel that lands inside the intro paragraph's own box is
   rgb(162,132,157), and #a8a296 body copy on that is 1.31:1 — WCAG AA wants 4.5:1
   at 14px, so the sentence genuinely vanishes at the bright end of the cycle.
   A scrim anchored to the copy side fixes it structurally without dimming what
   Mark picked: the right-hand half, where the shield and most of the colour sit,
   is untouched. Feathered across two thirds of the band — a scrim with a visible
   edge is a worse defect than the one it fixes.
   Paints above .hero-mist (both are z-index 0; ::after is the last generated
   child, so DOM order settles it) and below .container, which is z-index 1.
   ⚠️ These alphas are the WEAKEST that hold 4.5:1, and finding that needed a
   sweep across the animation, not a screenshot. Sampling 5 frames said a lighter
   scrim passed at 5.18:1; sampling 14 caught a brighter moment in the same cycle
   and the identical CSS measured 4.28:1 — a fail. Re-measure over ≥14 frames
   before weakening this, and treat any single-frame reading as no reading. */
.page-hero::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background: linear-gradient(96deg,
    rgba(4, 4, 4, .60) 0%, rgba(4, 4, 4, .52) 26%, rgba(4, 4, 4, .38) 48%, transparent 72%);
}
.page-hero > .container { position: relative; z-index: 1; }
/* The cap keeps the title clear of the shield, and it is a measurement, not a
   taste. The container is max-width 1200px and CENTRED; the shield is anchored
   to the VIEWPORT at right 5% + 156px. Those two do not move together, so
   between ~900px and ~1400px the container's right edge always crosses the
   shield — whether that shows depends only on whether the text reaches it.
   Uncapped it did: four guide titles ran under the crest, worst -82px at 960px,
   and three of the four still overlapped at 1166px. Invisible while the crest
   was a .2 ghost, a defect the moment it became solid.
   Binding case is 960px, where the copy has 708px before the shield. Verified by
   measuring every hero page at 8 widths, not by eye.
   ⚠️ #shop-title is display:inline on shop/deals so the product count can sit
   beside it, and max-width does nothing to an inline box. Those two pages are
   clear because their titles are short (a category name); a long one would need
   this solving there too. */
/* line-height is set here because the cap above made these titles WRAP for the
   first time. It was never declared, so it inherited body 1.65 — which looked
   like nothing at all while every hero title was one line, and reads as two
   loose unrelated sentences the moment one is two lines. 1.08 is what this file
   already uses for display type at this same clamp. */
.page-hero h1 {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(1.7rem, 3.4vw, 2.6rem); line-height: 1.08; max-width: 34ch;
}
.page-hero p { color: var(--on-dark-soft); margin-top: var(--sp-2); max-width: 60ch; font-size: var(--fs-small); }
/* Hero secondary copy is --on-dark; only the breadcrumb stays --on-dark-soft.
   The soft grey cannot survive the lit band away from the left edge: the product
   count sits mid-title, where the scrim has already faded to .28, and measured
   3.65:1 there — a fail. The breadcrumb keeps the soft grey because it sits at
   the far left in the deepest part of the scrim and measures 7.5:1.
   .crumbs must be excluded by name: it is a <p> on some pages and .page-hero p
   outranks .crumbs, so a bare element selector silently brightens it. */
.page-hero p:not(.crumbs) { color: var(--on-dark); }

/* Shop/deals mini-hero — the modifier now carries only the tighter band and its
   grid, not the lighting. Keeping the shorter padding here is deliberate: these
   two pages are the ones whose job is getting people to products, so the band
   stays a header rather than a preamble. */
.page-hero--shop { padding-block: var(--sp-4); display: grid; align-items: center; }
/* .page-hero--shop is display:grid and .container is margin-inline:auto with
   width:auto — in a grid item auto margins beat stretch, so without this the
   container shrink-wraps its content and centres, starting the hero copy ~106px
   right of the header logo and the sidebar, and sliding whenever the copy length
   changes. Every other .page-hero is a plain block, so this is shop-only. */
.page-hero--shop .container { width: 100%; }
/* The ground glow. Its centre sits BELOW the band on purpose, so what shows is
   the top of a glow rather than a disc with a middle — but at 116% only the very
   edge reached in, which is most of why the band read dark. 102% keeps the centre
   out of frame and lets the falloff do the work. */
.page-hero::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(760px 400px at 84% 102%, rgba(var(--red-rgb), .58), transparent 66%),
    radial-gradient(500px 320px at 6% -8%, rgba(255, 255, 255, .11), transparent 62%);
}
/* Live product count, beside the title. */
.shop-hero-count {
  display: inline; margin-left: 12px; color: var(--on-dark-soft);
  font-size: var(--fs-small); font-weight: 500; white-space: nowrap;
}
.page-hero--shop #shop-title { display: inline; }
/* Atmospheric mist — replaces the product lineup. Pure CSS/SVG, no product
   imagery: literal to the "British Mist" name and brand-neutral across the
   multi-range catalogue. Sits behind the copy (container is z-index:1). */
.hero-mist { position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }
/* The Union Jack shield anchoring the right, where the lineup used to sit */
/* Sized to the band and centred in it. At min(46%, 520px) anchored bottom:-12%
   — which is what this was, from when the hero was 320px+ — the crop kept a
   corner of the flag and the lion's tail and lost the crest entirely.
   Mark's call, 19 Aug 2026: the crest reads as the brand, so it is a solid badge
   at .85 with no grayscale, not the .2 ghost it was. The grayscale is gone rather
   than zeroed — at .15 it was muting exactly the red and navy that make it read.
   ⚠️ At .2 the crest was invisible, so hero copy running underneath it cost
   nothing. At .85 it does: the meta line is the longest thing in the band and it
   is what reaches the shield first. That is why .page-hero-meta is capped below,
   and why the cap is a measurement rather than a taste. */
.hero-mist__shield {
  position: absolute; right: 5%; top: 50%; transform: translateY(-50%);
  width: min(21%, 156px); aspect-ratio: 1;
  background: url("../images/logo-mark.png") center / contain no-repeat;
  opacity: .85;
}
/* soft drifting vapour plumes — blurred radial gradients, screen-blended */
.hero-mist__plume {
  position: absolute; border-radius: 50%; filter: blur(44px); opacity: .68;
  mix-blend-mode: screen; will-change: transform;
  animation: mist-drift 28s ease-in-out infinite alternate;
}
/* Union Jack colour woven through the vapour — royal blue, brand red, white highlight */
.hero-mist__plume--1 { right: 3%;  bottom: -110px; width: 300px; height: 300px;
  background: radial-gradient(circle, rgba(38, 96, 230, .62), transparent 68%); animation-duration: 15s; }
.hero-mist__plume--2 { right: 33%; top: -90px;   width: 190px; height: 190px;
  background: radial-gradient(circle, rgba(230, 240, 255, .34), transparent 66%); animation-duration: 19s; animation-delay: -7s; }
.hero-mist__plume--3 { right: -3%; top: -40px;   width: 230px; height: 230px;
  background: radial-gradient(circle, rgba(var(--red-rgb), .55), transparent 64%); animation-duration: 12s; animation-delay: -4s; }
/* SVG turbulence fog for wispy texture over the plumes */
/* 🐛 The fog is a plain <rect>, so it is a hard-edged box. At its old .14 that
   edge sat below the visible threshold; lift the opacity to where the texture
   actually reads and its LEFT side draws a vertical seam straight across the
   band — the other three are clipped by .hero-mist's overflow:hidden, so the
   left is the only one that shows. The mask is what makes the opacity safe. */
.hero-mist__fog {
  position: absolute; right: -6%; bottom: -40%; width: 56%; height: 150%;
  opacity: .3; mix-blend-mode: screen; will-change: transform;
  -webkit-mask-image: radial-gradient(68% 76% at 64% 52%, #000 26%, transparent 76%);
  mask-image: radial-gradient(68% 76% at 64% 52%, #000 26%, transparent 76%);
  animation: mist-fog-drift 21s ease-in-out infinite alternate;
}
@keyframes mist-fog-drift {
  from { transform: translate3d(-3%, 2%, 0) scale(1.06); }
  to   { transform: translate3d(4%, -2%, 0) scale(1.18); }
}
@keyframes mist-drift {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(-26px, -18px, 0) scale(1.14); }
}

/* ---------- flows of light -------------------------------------------------
   Wide soft ribbons sitting mostly BELOW the band; .hero-mist is overflow:hidden
   so what shows is the spill, not the shape. They OVERLAP rather than each
   owning a zone — zoned ribbons lit the right and left the band dead past the
   title. Left to right the wash runs cool -> violet -> warm, ending on the
   shield. Colours are the two already in the plumes; nothing new is introduced.
   transform/opacity only, so each animates on the compositor and the expensive
   blur rasterises once instead of every frame. */
.hero-mist__flow {
  position: absolute; border-radius: 50%;
  mix-blend-mode: screen; pointer-events: none; will-change: transform;
}
/* Heights and offsets are set so each ribbon RISES INTO the band rather than
   spilling under it. Sat at bottom:-68px and friends, roughly two thirds of every
   ribbon was clipped away and the band read dead through its middle — the light
   was there, it was just below the floor. Mark's pick, 19 Aug 2026: full ambience. */
.hero-mist__flow--1 {
  left: 22%; right: -10%; bottom: -24px; height: 184px; filter: blur(28px);
  background: radial-gradient(56% 52% at 54% 50%, rgba(var(--red-rgb), 1), transparent 74%);
  animation: mist-flow-red 11s ease-in-out infinite alternate;
}
.hero-mist__flow--2 {
  left: 8%; right: 26%; bottom: -4px; height: 164px; filter: blur(30px);
  background: radial-gradient(54% 52% at 50% 50%, rgba(38, 96, 230, .84), transparent 72%);
  animation: mist-flow-blue 13s ease-in-out -5s infinite alternate;
}
/* Far-left cool ground — carries the left third. */
.hero-mist__flow--4 {
  left: -16%; right: 54%; bottom: -28px; height: 158px; filter: blur(28px);
  background: radial-gradient(56% 52% at 52% 50%, rgba(38, 96, 230, .74), transparent 74%);
  animation: mist-flow-cool 17s ease-in-out -4s infinite alternate;
}
/* A warm undertone reaching back down the LEFT, so red is not something that
   only happens near the shield and the gradient has no seam mid-band. */
.hero-mist__flow--5 {
  left: -10%; right: 58%; bottom: -38px; height: 160px; filter: blur(32px);
  background: radial-gradient(56% 52% at 54% 50%, rgba(var(--red-rgb), .64), transparent 74%);
  animation: mist-flow-warm 7s ease-in-out -3s infinite alternate;
}
/* The travelling sweep: spans the FULL width and crosses the whole bar rather
   than shimmering in place. Lower blur than the colour ribbons on purpose — a
   30px blur smears motion back into stillness, and the brighter the sweep the
   more that matters, which is why this went DOWN to 12px as it went up in alpha. */
.hero-mist__flow--3 {
  left: -12%; right: -12%; bottom: 0; height: 84px; filter: blur(12px);
  background: radial-gradient(30% 50% at 50% 50%, rgba(236, 244, 255, .78), transparent 72%);
  animation: mist-flow-glint 9s ease-in-out -3s infinite alternate;
}

/* Five paths at 11/13/17/7/9s — all prime, so the composite never lands back on
   itself. One shared keyframe at one duration reads as a single block sliding.
   Neighbouring ribbons travel in OPPOSITE directions so they cross and mix, and
   that crossing is what reads as flow. */
@keyframes mist-flow-red {
  from { transform: translate3d(-26%, 6px, 0) scaleX(.96); }
  to   { transform: translate3d(24%, -6px, 0) scaleX(1.22); }
}
@keyframes mist-flow-blue {
  from { transform: translate3d(28%, -5px, 0) scaleX(1.16); }
  to   { transform: translate3d(-22%, 7px, 0) scaleX(.9); }
}
@keyframes mist-flow-cool {
  from { transform: translate3d(-20%, 5px, 0) scaleX(.94); }
  to   { transform: translate3d(22%, -7px, 0) scaleX(1.2); }
}
@keyframes mist-flow-warm {
  from { transform: translate3d(24%, -6px, 0) scaleX(1.18); }
  to   { transform: translate3d(-18%, 6px, 0) scaleX(.92); }
}
/* The sweep swings opacity as well: light that only slides reads as an object
   moving, light that also brightens reads as light. */
@keyframes mist-flow-glint {
  from { transform: translate3d(-46%, 0, 0) scaleX(.85); opacity: .35; }
  50%  { opacity: 1; }
  to   { transform: translate3d(46%, 0, 0) scaleX(1.15); opacity: .4; }
}

/* ---------- the mist itself ------------------------------------------------
   The plumes are colour and the fog is texture; neither reads as vapour. These
   do: pale wisps crossing the WHOLE bar, fading in and out as they travel, so
   each forms and dissipates rather than sliding on and off an edge. linear +
   infinite, never alternate — an alternating wisp visibly reverses, and nothing
   in air does that. Staggered so one is always mid-crossing: the motion has to
   be visible on arrival, not after ten seconds of watching. */
.hero-mist__wisp {
  position: absolute; border-radius: 50%;
  mix-blend-mode: screen; pointer-events: none; will-change: transform, opacity;
  background: radial-gradient(50% 50% at 50% 50%, rgba(222, 234, 252, .60), transparent 70%);
  animation: mist-wisp 13s linear infinite;
}
.hero-mist__wisp--1 { left: -34%; width: 42%; height: 128px; bottom: -30px; filter: blur(22px); }
.hero-mist__wisp--2 { left: -30%; width: 34%; height: 92px;  bottom: 18px;  filter: blur(18px);
  animation-duration: 17s; animation-delay: -6s; opacity: 1; }
.hero-mist__wisp--3 { left: -26%; width: 28%; height: 74px;  bottom: -52px; filter: blur(15px);
  animation-duration: 9s;  animation-delay: -3s; opacity: .9; }
@keyframes mist-wisp {
  0%   { transform: translate3d(0, 0, 0) scaleX(1); opacity: 0; }
  18%  { opacity: 1; }
  72%  { opacity: .9; }
  100% { transform: translate3d(330%, -10px, 0) scaleX(1.35); opacity: 0; }
}

/* Motion here is ambience — nothing depends on it running. */
@media (prefers-reduced-motion: reduce) {
  .hero-mist__plume,
  .hero-mist__flow,
  .hero-mist__fog { animation: none; }
  /* A wisp frozen mid-fade would just sit there half-visible. */
  .hero-mist__wisp { display: none; }
}

/* The three facts, as one rule-separated line rather than three chips. Two of
   them used to restate the paragraph directly above them, 12px apart. */
.page-hero-meta {
  list-style: none; display: flex; flex-wrap: wrap; gap: 0; margin-top: 14px;
}
.page-hero-meta li {
  color: var(--on-dark); font-size: var(--fs-small); letter-spacing: .02em;
  border-right: 1px solid rgba(255, 255, 255, .2);
  padding: 0 14px 0 0; margin-right: 14px;
}
.page-hero-meta li:last-child { border-right: 0; margin-right: 0; padding-right: 0; }

@media (max-width: 900px) {
  /* copy reclaims full width; keep only a whisper of mist behind it */
  .hero-mist__shield,
  .hero-mist__plume--2, .hero-mist__plume--3 { display: none; }
  .hero-mist__plume--1 { opacity: .2; right: -12%; }
  .hero-mist__fog { opacity: .18; }
  /* The flows and wisps follow the shield down, or the phone gets the full
     light show in a band half the height. */
  .hero-mist__flow--2,
  .hero-mist__flow--5,
  .hero-mist__wisp--2 { display: none; }
  .hero-mist__flow--1 { left: 10%; bottom: -84px; }
  .hero-mist__flow--4 { right: 40%; }
  .hero-mist__flow--3 { opacity: .7; }
}
@media (max-width: 700px) {
  /* The rule-separated line only works on ONE line. Wrapped, border-right leaves
     a divider dangling in space at the end of every line except the last — and
     three facts cannot fit 390px. Stack them, drop the rules. */
  .page-hero-meta { display: block; margin-top: 12px; }
  .page-hero-meta li { display: block; border-right: 0; padding: 0; margin: 0 0 5px; }
  /* Inline beside the title on desktop; at this width it wraps to its own line
     and the inline margin reads as a stray indent under the H1. */
  .shop-hero-count { display: block; margin-left: 0; margin-top: 6px; }
}
.crumbs { font-size: var(--fs-tiny); color: var(--on-dark-soft); margin-bottom: var(--sp-2); }
.crumbs a:hover { color: #fff; }

/* ---------- product page ---------- */
.pdp {
  display: grid; grid-template-columns: minmax(0, 1.02fr) minmax(0, .98fr);
  gap: var(--sp-4) var(--sp-6); padding-block: var(--sp-4) var(--sp-6); align-items: start;
}

/* breadcrumb — the shared .crumbs is coloured for the dark page-hero, this one sits on bone */
.pdp-crumbs { font-size: var(--fs-tiny); color: var(--ink-mute); margin-bottom: var(--sp-2); }
.pdp-crumbs a { color: var(--ink-mute); }
.pdp-crumbs a:hover { color: var(--navy-900); }
.pdp-crumbs .sep { opacity: .5; margin-inline: 6px; }

/* --- media --- */
.pdp-gallery { position: sticky; top: 104px; display: grid; gap: 12px; }
.pdp-stage {
  position: relative; aspect-ratio: 1 / 1; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: linear-gradient(168deg, #fff 0%, var(--paper-warm) 78%);
}
/* the art is absolutely positioned so the stage's height comes purely from aspect-ratio —
   in flow, a percentage-height image feeds back into the row size and balloons the box */
.pdp-stage > svg,
.pdp-stage img.product-photo {
  position: absolute; inset: var(--sp-5);
  width: calc(100% - var(--sp-5) * 2); height: calc(100% - var(--sp-5) * 2);
  object-fit: contain;
  filter: drop-shadow(0 16px 26px rgba(15, 12, 8, .18));
}
/* splash renders are full-frame scenes — they fill the stage instead of floating in it */
.pdp-stage.is-bleed { background: none; }
.pdp-stage.is-bleed img.product-photo { inset: 0; width: 100%; height: 100%; object-fit: cover; filter: none; }
.pdp-stage .deal-badge { position: absolute; top: 14px; left: 14px; z-index: 2; }

.pdp-thumbs { display: flex; gap: 10px; flex-wrap: wrap; }
.pdp-thumb {
  width: 66px; height: 66px; padding: 6px; flex: none; cursor: pointer;
  border: 1.5px solid var(--line); border-radius: var(--radius-sm); background: #fff;
  display: grid; place-items: center; overflow: hidden; transition: border-color .18s ease;
}
.pdp-thumb img { width: 100%; height: 100%; object-fit: contain; }
.pdp-thumb.selected { border-color: var(--navy-900); }

/* --- info column --- */
.pdp-info { display: grid; gap: var(--sp-2); align-content: start; }
.pdp-brand { font-size: var(--fs-tiny); font-weight: 600; letter-spacing: .12em; text-transform: uppercase; color: var(--red-600); }
.pdp-info h1 {
  font-family: var(--font-display); font-weight: 700; letter-spacing: -.01em;
  font-size: clamp(1.8rem, 3.4vw, 2.6rem); color: var(--navy-900); line-height: 1.15; margin: 0;
}
.pdp-tags { display: flex; gap: 6px; flex-wrap: wrap; }
.pdp-tags span {
  font-size: var(--fs-tiny); color: var(--ink-mute); background: var(--paper-warm);
  border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 4px 10px;
}

.pdp-pricing { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-top: 4px; }
.pdp-price { font-family: var(--font-display); font-size: 2.4rem; font-weight: 700; color: var(--navy-900); letter-spacing: -.02em; }
.pdp-price .was { font-size: 1rem; color: var(--ink-mute); text-decoration: line-through; font-weight: 500; margin-left: 10px; }
.pdp-price-note { font-size: var(--fs-tiny); color: var(--ink-mute); }
.pdp-stock { display: inline-flex; align-items: center; gap: 7px; font-size: var(--fs-tiny); color: var(--ink-soft); }
.pdp-stock::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--green-500); }
.pdp-stock.soon::before { background: var(--ink-mute); }
.pdp-stock.out::before { background: var(--ink-mute); }
.pdp-price.pdp-price--oos { color: var(--ink-mute); }
.pdp-stage .stock-badge { top: 14px; left: 14px; z-index: 2; }
.pdp-stage.is-oos .product-photo,
.pdp-stage.is-oos svg { filter: grayscale(1); opacity: .6; }

/* The house deal device: a red rule and the words, no container. Same move the
   homepage made when .kuro-deal stopped being a bordered, tinted chip. As a
   filled pink box with a border and an icon it read as a system alert — the one
   thing on the page that looked like a warning was the good news. */
.pdp-promo {
  display: flex; align-items: flex-start;
  border-left: 3px solid var(--red-600); padding: 3px 0 3px 15px;
  font-size: var(--fs-small); color: var(--ink-soft); line-height: 1.55;
}
.pdp-promo svg { display: none; }
.pdp-promo b { color: var(--navy-900); font-weight: 600; }

/* Chris's copy, given the room it earns. It is client-owned, deliberately funny
   and he says it is a selling point — set at body weight in muted grey it read as
   filler between the price and the strength pills. Rule above it so the page
   breaks into a decision half and a reading half. */
.pdp-desc {
  color: var(--navy-900); font-size: 1.08rem; line-height: 1.75; max-width: 56ch;
  border-top: 1px solid var(--line); padding-top: 20px; margin-top: 10px;
}

.opt-group { margin-top: var(--sp-1); }
.opt-group h4 { font-size: var(--fs-tiny); letter-spacing: .1em; text-transform: uppercase; color: var(--ink-mute); margin-bottom: 10px; }
.opt-pills { display: flex; gap: 8px; flex-wrap: wrap; }
.opt-pills button {
  border: 1.5px solid var(--line); background: #fff; border-radius: var(--radius-sm);
  padding: 10px 18px; font-size: var(--fs-small); font-weight: 500; color: var(--ink-soft);
  cursor: pointer; transition: border-color .18s ease, color .18s ease;
}
.opt-pills button.selected { border-color: var(--navy-900); background: var(--navy-900); color: var(--on-dark); font-weight: 600; }
/* Nic salt at 0mg — struck through rather than hidden, so the choice is visibly
   unavailable at this strength instead of appearing never to have existed. */
.opt-pills button.is-unavailable { opacity: .38; cursor: not-allowed; text-decoration: line-through; }
.opt-help { margin: .35rem 0 0; font-size: var(--fs-small); color: var(--ink-mute); }
@media (hover: hover) {
  .opt-pills button:hover { border-color: var(--navy-900); color: var(--navy-900); }
  .opt-pills button.selected:hover { color: var(--on-dark); }
  .pdp-thumb:hover { border-color: var(--navy-900); }
}

.pdp-buy { display: flex; gap: 12px; margin-top: var(--sp-2); }
.pdp-qty {
  display: inline-flex; align-items: center; flex: none;
  border: 1.5px solid var(--line); border-radius: var(--radius-sm); background: #fff;
}
.pdp-qty button { width: 46px; height: 51px; border: none; background: none; font-size: 1.05rem; color: var(--navy-900); cursor: pointer; border-radius: var(--radius-sm); }
.pdp-qty span { min-width: 30px; text-align: center; font-weight: 600; font-variant-numeric: tabular-nums; }
.pdp-add { flex: 1; height: 54px; padding-inline: var(--sp-3); font-size: var(--fs-small); letter-spacing: .04em; }
/* a full-width primary shouldn't jump and rescale on hover the way the small site buttons do */
.pdp-add:hover { transform: none; box-shadow: var(--shadow-card); }
.pdp-add:disabled { background: var(--paper-warm); color: var(--ink-mute); border-color: var(--line); cursor: not-allowed; box-shadow: none; }

.pdp-assure { list-style: none; display: grid; gap: 13px; border-top: 1px solid var(--line); padding-top: var(--sp-3); margin-top: var(--sp-2); }
.pdp-assure li { display: flex; gap: 12px; align-items: flex-start; font-size: .79rem; color: var(--ink-mute); line-height: 1.5; }
.pdp-assure svg { flex: none; width: 18px; height: 18px; color: var(--navy-900); margin-top: 2px; }
.pdp-assure b { color: var(--navy-900); font-weight: 600; }

.pdp-details { margin-top: var(--sp-2); border-top: 1px solid var(--line); }
.pdp-details details { border-bottom: 1px solid var(--line); }
.pdp-details summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  padding: 13px 0; cursor: pointer; list-style: none;
  font-size: var(--fs-small); font-weight: 600; color: var(--navy-900);
}
.pdp-details summary::-webkit-details-marker { display: none; }
.pdp-details summary::after {
  content: ""; flex: none; width: 8px; height: 8px; margin-right: 4px;
  border-right: 1.5px solid var(--ink-mute); border-bottom: 1.5px solid var(--ink-mute);
  transform: translateY(-2px) rotate(45deg); transition: transform .2s ease;
}
.pdp-details details[open] summary::after { transform: translateY(2px) rotate(-135deg); }
.pdp-details-body { padding-bottom: 18px; font-size: var(--fs-small); color: var(--ink-soft); line-height: 1.7; }
.pdp-spec { display: grid; grid-template-columns: max-content 1fr; gap: 9px var(--sp-3); margin: 0; }
.pdp-spec dt { color: var(--ink-mute); }
.pdp-spec dd { color: var(--navy-900); margin: 0; }

.qty { display: inline-flex; align-items: center; border: 1.5px solid var(--line); border-radius: var(--radius-sm); }
.qty button { width: 42px; height: 46px; border: none; background: none; font-size: 1.1rem; color: var(--navy-900); }
.qty span { min-width: 34px; text-align: center; font-weight: 600; }

/* ---------- responsive ---------- */
@media (max-width: 1024px) {
  .cat-grid, .usp-grid, .product-grid { grid-template-columns: repeat(2, 1fr); }
  .deal-cards, .strength-cards, .review-grid, .guide-grid { grid-template-columns: repeat(2, 1fr); }
  .hero-inner, .kuro-inner, .pdp { grid-template-columns: 1fr; }
  .tax-facts { grid-template-columns: 1fr; }
  .tax-foot .btn { width: 100%; text-align: center; }
  .footer-main { grid-template-columns: 1fr 1fr; }
  .newsletter { grid-template-columns: 1fr; }
  .newsletter form { min-width: 0; }
  .shop-layout { grid-template-columns: 1fr; }
  .filters-toggle { display: inline-flex; }
  /* filter panel becomes a bottom sheet */
  .shop-sidebar {
    position: fixed; inset: auto 0 0 0; top: auto; z-index: 196;
    max-height: 82vh; background: #fff; border: 0; border-radius: var(--radius) var(--radius) 0 0;
    box-shadow: 0 -12px 40px rgba(12, 11, 10, .28);
    transform: translateY(102%); transition: transform .28s cubic-bezier(.4, 0, .2, 1);
  }
  .shop-sidebar.open { transform: translateY(0); }
  .filter-drawer-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--line);
    font-family: var(--font-display); font-weight: 700; font-size: 1.05rem; color: var(--navy-900);
  }
  .filter-drawer-close { display: grid; place-items: center; background: none; border: 0; color: var(--navy-900); cursor: pointer; padding: 4px; }
  .filter-scroll { flex: 1; padding: var(--sp-3) var(--sp-4); }
  .filter-drawer-foot {
    display: flex; align-items: center; gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--line); background: #fff;
  }
  .filter-drawer-foot .btn-textclear { background: none; border: 0; color: var(--ink-mute); font-family: inherit; font-size: var(--fs-small); text-decoration: underline; cursor: pointer; white-space: nowrap; }
  .filter-drawer-foot .filter-apply { flex: 1; }
  .filter-backdrop { position: fixed; inset: 0; z-index: 195; background: rgba(12, 11, 10, .5); }
  body.filter-open { overflow: hidden; }
  .pdp { gap: var(--sp-4); }
  .pdp-gallery { position: static; max-width: 520px; }
  .hero-visual { min-height: 300px; }
}
@media (max-width: 768px) {
  .header-inner { gap: var(--sp-2); }
  .icon-btn[aria-label="Account"] { display: none; }
  .main-nav {
    display: none; position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; background: #fff; border-bottom: 1px solid var(--line);
    padding: var(--sp-2); box-shadow: var(--shadow-lift);
  }
  .main-nav.open { display: flex; }
  .main-nav .nav-account { display: block; border-top: 1px solid var(--line); margin-top: 6px; padding-top: 12px; }
  .nav-toggle { display: grid; }
  .trust-strip-inner { grid-template-columns: 1fr 1fr; gap: var(--sp-2); }
  .section { padding-block: var(--sp-6); }
  .scale-tiers { grid-template-columns: 1fr; }
  .scale-track, .scale-caps { display: none; }
  .usp-bento { grid-template-columns: 1fr; grid-template-areas: "deliver" "reward" "curated" "support"; }
}
@media (max-width: 1024px) {
  .product-grid.cols-3 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .cat-grid, .deal-cards, .strength-cards, .review-grid, .guide-grid, .usp-grid { grid-template-columns: 1fr; }
  /* product cards go two-up on phones — a proper shop grid, half the scroll */
  .product-grid, .product-grid.cols-3 { grid-template-columns: repeat(2, 1fr); gap: var(--sp-2); }
  .product-card { padding: 10px; }
  .product-media { padding: var(--sp-2); }
  /* One column here, so the button drops beneath the money block instead of
     beside it. flex-direction would do nothing now the foot is a grid — the
     column count is what stacks it. Row 3 is the button's own row; without
     naming it, the span from the desktop rule would put it back alongside. */
  .product-foot {
    grid-template-columns: 1fr; grid-template-rows: auto auto auto; row-gap: 4px;
  }
  .product-foot .add-btn {
    grid-column: 1; grid-row: 3; width: 100%; padding: 10px 16px;
    align-self: stretch; margin-top: 5px;
  }
  .footer-main { grid-template-columns: 1fr; gap: var(--sp-4); }
  .hero-ctas .btn { width: 100%; }
  .age-card .age-actions { flex-direction: column; }
  .newsletter form { flex-direction: column; border-radius: var(--radius); }
  .newsletter input { padding: 14px 18px; }

  /* hero: anchor the bottle trio, remove the empty red gap it floated in */
  .hero-v2 .hero-inner { row-gap: var(--sp-4); padding-block: var(--sp-4) var(--sp-5); }
  .fh-visual { min-height: 0; gap: var(--sp-2); }
  .fh-glow { width: 300px; height: 300px; top: 54%; }
  .fh-trio img { height: clamp(320px, 84vw, 400px); }

  /* trust strip: stack the four proofs so nothing wraps mid-phrase */
  .trust-strip-inner { grid-template-columns: 1fr; gap: var(--sp-3); }

  /* claw back the vertical the warning bar eats above the fold */
  .compliance-bar { padding: 6px var(--sp-2); font-size: .72rem; line-height: 1.35; }

  /* tighten section rhythm — the phone scroll was sprawling */
  .section { padding-block: var(--sp-5); }
  .section-head { margin-bottom: var(--sp-4); }
}

/* ---------- basket drawer ---------- */
.basket-overlay {
  position: fixed; inset: 0; background: rgba(12, 11, 10, .5); z-index: 180;
  opacity: 0; transition: opacity .3s ease;
}
.basket-overlay.show { opacity: 1; }
.basket-drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 181;
  width: min(420px, 100vw); background: var(--paper);
  display: flex; flex-direction: column;
  transform: translateX(102%); transition: transform .32s cubic-bezier(.22,.7,.3,1);
  box-shadow: var(--shadow-lift);
}
.basket-drawer.open { transform: none; }
.basket-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 22px; border-bottom: 1px solid var(--line); background: #fbf8f1;
}
.basket-head h3 { font-family: var(--font-display); font-weight: 800; font-size: 1.05rem; }
.basket-count { font-family: var(--font-body); font-weight: 500; font-size: var(--fs-small); color: var(--ink-mute); }
.basket-close { color: var(--ink); }

.basket-delivery { padding: 14px 22px 0; }
.basket-delivery p { font-size: var(--fs-tiny); color: var(--ink-soft); margin-bottom: 8px; }
.delivery-meter {
  position: relative; height: 6px; border-radius: var(--radius-sm); background: var(--line); margin-bottom: 26px;
}
.delivery-fill { height: 100%; border-radius: var(--radius-sm); background: var(--red-600); transition: width .4s ease; }
.delivery-notch {
  position: absolute; top: -3px; width: 12px; height: 12px; border-radius: 50%;
  background: var(--paper); border: 2px solid var(--line); transform: translateX(-50%);
}
.delivery-notch.hit { border-color: var(--red-600); background: var(--red-600); }
.delivery-notch i {
  position: absolute; top: 14px; left: 50%; transform: translateX(-50%);
  font-style: normal; font-size: .66rem; font-weight: 600; color: var(--ink-mute); white-space: nowrap;
}
.delivery-notch.end i { left: auto; right: -2px; transform: none; }
.delivery-notch:not(.end) i { left: auto; right: 10px; transform: none; }

.basket-items { flex: 1; overflow-y: auto; padding: 6px 22px; overscroll-behavior: contain; }
.basket-item {
  display: grid; grid-template-columns: 64px 1fr auto; gap: 14px; align-items: start;
  padding: 16px 0; border-bottom: 1px solid var(--line);
}
.basket-item-media {
  width: 64px; height: 76px; border-radius: var(--radius-sm); background: #fbf8f1;
  display: grid; place-items: center; overflow: hidden;
}
.basket-item-media { position: relative; }
.basket-item-media img, .basket-item-media svg { position: absolute; inset: 5px; width: calc(100% - 10px); height: calc(100% - 10px); object-fit: contain; }
.basket-item-body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.basket-item-body > a { font-weight: 600; color: var(--ink); text-decoration: none; font-size: .92rem; line-height: 1.35; }
.basket-item-body > a:hover { color: var(--red-600); }
.basket-item-meta { font-size: var(--fs-tiny); color: var(--ink-mute); }
.basket-item-meta b { color: var(--red-600); font-weight: 700; }
.basket-item-row { display: flex; align-items: center; justify-content: space-between; margin-top: 6px; }
.basket-item .qty button { width: 32px; height: 34px; cursor: pointer; }
.basket-item .qty span { min-width: 26px; }
.basket-item-price { font-weight: 700; font-size: .95rem; }
.basket-item-remove {
  border: none; background: none; color: var(--ink-mute); cursor: pointer; padding: 4px;
}
.basket-item-remove:hover { color: var(--red-600); }
.basket-empty { text-align: center; padding: 64px 0; display: flex; flex-direction: column; gap: 18px; align-items: center; }
.basket-empty p { color: var(--ink-mute); }

.basket-foot { padding: 16px 22px 22px; border-top: 1px solid var(--line); background: #fbf8f1; }
.basket-row { display: flex; justify-content: space-between; font-size: var(--fs-small); padding: 3px 0; }
.basket-row.muted { color: var(--ink-mute); }
.basket-row.muted span:last-child { text-decoration: line-through; }
.basket-row.saving { color: var(--red-600); font-weight: 600; }
.basket-row.total { font-weight: 800; font-size: 1.05rem; padding-top: 8px; }
.basket-note { font-size: var(--fs-tiny); color: var(--ink-mute); margin: 8px 0 14px; }
.basket-checkout { width: 100%; }

/* ---------- search overlay ---------- */
.search-overlay {
  position: fixed; inset: 0; z-index: 185; background: rgba(12, 11, 10, .55);
  display: flex; justify-content: center; align-items: flex-start; padding: clamp(16px, 8vh, 90px) 16px 16px;
  opacity: 0; transition: opacity .25s ease;
}
.search-overlay[hidden] { display: none; }
.search-overlay.show { opacity: 1; }
.search-panel {
  width: min(560px, 100%); background: var(--paper); border-radius: var(--radius);
  box-shadow: var(--shadow-lift); overflow: hidden;
  transform: translateY(-10px); transition: transform .25s ease;
}
.search-overlay.show .search-panel { transform: none; }
.search-box {
  display: flex; align-items: center; gap: 12px; padding: 6px 16px;
  background: #fbf8f1; border-bottom: 1px solid var(--line); color: var(--ink-mute);
}
.search-box input {
  flex: 1; border: none; background: none; outline: none; padding: 14px 0;
  font-family: var(--font-body); font-size: 1rem; color: var(--ink);
}
.search-box input::-webkit-search-cancel-button { display: none; }
.search-close { color: var(--ink); }
.search-results { max-height: min(56vh, 480px); overflow-y: auto; overscroll-behavior: contain; padding: 8px; }
.search-hint { padding: 22px 16px; font-size: var(--fs-small); color: var(--ink-mute); text-align: center; }
.search-hint a { color: var(--red-600); }
.search-result {
  display: grid; grid-template-columns: 46px 1fr auto; gap: 14px; align-items: center;
  padding: 10px 12px; border-radius: var(--radius-sm); text-decoration: none; color: var(--ink);
}
.search-result:hover, .search-result:focus-visible { background: #fbf8f1; }
.search-result-media {
  width: 46px; height: 54px; border-radius: 6px; background: #fbf8f1;
  display: grid; place-items: center; overflow: hidden;
}
.search-result-media { position: relative; }
.search-result-media img, .search-result-media svg { position: absolute; inset: 4px; width: calc(100% - 8px); height: calc(100% - 8px); object-fit: contain; }
.search-result-body { display: flex; flex-direction: column; min-width: 0; }
.search-result-body b { font-size: .92rem; font-weight: 600; line-height: 1.3; }
.search-result-body small { font-size: var(--fs-tiny); color: var(--ink-mute); }
.search-result-price { font-weight: 700; font-size: .92rem; }
.search-more {
  display: block; text-align: center; padding: 12px; margin-top: 4px;
  font-size: var(--fs-small); font-weight: 600; color: var(--red-600); text-decoration: none;
  border-top: 1px solid var(--line);
}

/* ---------- product photos never have square corners ----------
   Mark, looking at the shop: no straight-edged images anywhere on the site.

   Only opaque art can show a corner — a transparent cutout has nothing there to round,
   so this is invisible on Bar Juice, RIOT and Pod Salt and does its work on the splash
   scenes. The big surfaces already inherit their container's rounding, because
   .product-media and .pdp-stage both clip with overflow: hidden; these are the smaller
   places where the photo is laid inside a tile rather than filling it. */
.basket-item-media img,
.search-result-media img,
.pdp-thumb img,
.deal-card-well img,
.dw-tile img { border-radius: var(--radius-sm); }

/* ---------- deal builder (/deals.html) ----------
   Every var() below is defined at :root — an undefined custom property is invalid
   at computed-value time and the declaration silently falls back (transparent, for
   a background). That has shipped twice: --accent on .rec-bar-fill, which exists
   only inside the strength badges, and --navy on the best-seller badge, where the
   token is --navy-900. A test at the bottom of test/deal-builder.test.mjs pins it.

   Heights here are explicit lengths, never percentages. A percentage height on a
   grid item resolves against a track sized by that same item and the browser drops
   the cyclic dependency — a 198x720 bottle once rendered 447px tall in a 104px box,
   straight over the product name. Resize by moving the length, not the unit.

   One dark surface carries all four brands. Giving each deal its own accent hue was
   measured and rejected on 30 July: the cards sit side by side so the all-pairs
   contrast test applies, and nine semantic hues fail it badly (worst dE 12.9 normal
   vision, 2.3 protan). The LOGOS and the BOTTLES carry the brand. */

.deal-wall { display: grid; grid-template-columns: repeat(auto-fit, minmax(258px, 1fr)); gap: var(--sp-3); }
.deal-wall[hidden], .deal-shop[hidden], .dw-body[hidden], .dw-bar[hidden], .dw-done[hidden] { display: none; }

/* The whole card is the button. It used to be a small red pill inside a card, which
   is a smaller target than the thing that looks clickable. */
.deal-card {
  display: grid; grid-template-rows: auto 1fr auto auto; padding: 0; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--paper-warm);
  box-shadow: var(--shadow-card); text-align: left; font: inherit; color: inherit; cursor: pointer;
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.deal-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-lift); border-color: var(--navy-800); }
.deal-card:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }

.deal-card-brand {
  display: grid; place-items: center; height: 104px; padding: var(--sp-2);
  background: linear-gradient(160deg, var(--navy-800), var(--navy-black));
}
/* Two of these marks are wide wordmarks and two are near-square badges, so both a
   height cap and a width cap are needed — capping one alone left Drifter half the
   optical size of Bar Juice in the same band. */
.deal-card-brand img { height: auto; max-height: 74px; max-width: 80%; width: auto; object-fit: contain; }
.deal-card-brand-text {
  font-family: var(--font-display); font-size: 1.15rem; font-weight: 800; color: var(--on-dark);
  letter-spacing: .04em; text-transform: uppercase; text-align: center;
}

.deal-card-main { display: grid; gap: 5px; justify-items: start; padding: var(--sp-3) var(--sp-3) var(--sp-1); }
.deal-card-offer { font-family: var(--font-display); font-size: 2.05rem; font-weight: 800; line-height: 1.05; }
.deal-card-save {
  font-size: var(--fs-tiny); font-weight: 700; letter-spacing: .03em; border-radius: 999px;
  padding: 4px 11px; background: var(--red-050); color: var(--red-ink);
}
.deal-card-each { font-size: var(--fs-small); font-weight: 700; color: var(--ink-soft); }
.deal-card-status { font-size: var(--fs-tiny); font-weight: 700; letter-spacing: .03em; color: var(--red-ink); }
.deal-card-status.is-part { color: var(--ink-soft); }
.deal-card-status.is-added { color: var(--green-500); }

/* The card previews the PACK — one well per slot, filled with that range's real bottles.
   It says how big the bundle is before you click, and it is the same tray you land in.
   A fan of overlapping bottles was tried first and the catalogue will not have it: Bar
   Juice ships 198x720 cutouts, British Mist ships 600x600 lifestyle squares, and no one
   transform flatters both. A fixed well plus object-fit: contain flatters either. */
.deal-card-pack {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 5px;
  margin: var(--sp-2) var(--sp-3) 0; padding: 7px; border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: linear-gradient(180deg, var(--navy-100), var(--line));
  box-shadow: inset 0 2px 6px rgba(15,12,8,.18), inset 0 -1px 0 rgba(255,255,255,.35);
}
.deal-card-well {
  display: grid; place-items: center; height: 60px; border-radius: 6px;
  background: rgba(255,255,255,.4); box-shadow: inset 0 1px 2px rgba(15,12,8,.14);
}
.deal-card-well img { height: 48px; width: auto; max-width: 100%; object-fit: contain; }
.deal-card-foot {
  padding: var(--sp-2) var(--sp-3) var(--sp-3); margin-top: var(--sp-2);
  border-top: 1px solid var(--line); font-size: var(--fs-tiny); color: var(--ink-mute);
}

/* ----- the workshop ----- */

/* No overflow:hidden here. An overflow ancestor becomes the nearest scrollport, and a
   position:sticky descendant of a scrollport that never scrolls simply never sticks —
   it silently cost the page BOTH the sticky pack and the sticky price bar. The corners
   are rounded on the band and the foot instead. */
.deal-shop {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--paper-warm); box-shadow: var(--shadow-card);
}

.dw-band {
  display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-2) var(--sp-3);
  background: linear-gradient(160deg, var(--navy-800), var(--navy-black)); color: var(--on-dark);
  border-radius: var(--radius) var(--radius) 0 0;
}
.dw-band-brand { display: grid; place-items: center; height: 58px; }
.dw-band-brand img { height: auto; max-height: 58px; max-width: 190px; width: auto; object-fit: contain; }
.dw-band-text { font-family: var(--font-display); font-size: 1.2rem; font-weight: 800; }
.dw-band-offer { margin-left: auto; font-family: var(--font-display); font-size: 1.4rem; font-weight: 800; }
.dw-back {
  border: 1px solid rgba(255,255,255,.42); background: none; color: var(--on-dark);
  border-radius: 999px; padding: 8px 16px; font: inherit; font-size: var(--fs-tiny);
  font-weight: 700; cursor: pointer;
}
.dw-back:hover { background: rgba(255,255,255,.12); }

.dw-body { display: grid; grid-template-columns: minmax(360px, 460px) 1fr; }
/* min-width: 0 is load-bearing, not tidying. A grid item defaults to min-width: auto,
   so the column takes its content's min-content width — and on a phone the horizontally
   scrolling tray (five 98px wells) made that 520px, which pushed the whole document to
   629px inside a 378px viewport and gave the SITE a horizontal scrollbar. overflow-x on
   the tray cannot shrink a track that is being sized by the thing inside it. */
.dw-pack {
  align-self: start; min-width: 0;
  padding: var(--sp-3); border-right: 1px solid var(--line);
}

/* The wells are cut into ONE tray. Five equal grid cells did not read as a container
   being filled, because there was nothing there to fill.
   auto-fit is still refused: it wrapped a five into 3+2 and left a hole where a sixth
   would be, which reads as a broken grid rather than as a pack.
   The row is the BASE tier instead — --tray-cols is set from DealBuilder.slotCount in
   renderPack, so it is derived and no slot count is ever written down twice. A five
   is one row of five; a grown ten is two rows of five, which is what "5 for £12 or 10
   for £22" means anyway. It was one row always, and ten wells across a half-width
   column squashed each to 46px: names collided into each other, the bottles became
   slivers and the remove button sat on top of the slot number. */
.dw-tray {
  display: grid; grid-template-columns: repeat(var(--tray-cols), minmax(0, 1fr)); gap: 6px;
  padding: var(--sp-2); border: 1px solid var(--line); border-radius: var(--radius);
  background: linear-gradient(180deg, var(--navy-100), var(--line));
  box-shadow: inset 0 2px 7px rgba(15,12,8,.2), inset 0 -1px 0 rgba(255,255,255,.35);
  transition: box-shadow .25s ease, border-color .25s ease;
}
.dw-well-wrap { position: relative; }
.dw-well {
  position: relative; display: grid; justify-items: center; align-content: start; gap: 2px;
  width: 100%; min-height: 148px; padding: 24px 6px 10px; text-align: center;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  background: rgba(255,255,255,.36); box-shadow: inset 0 1px 3px rgba(15,12,8,.16);
  font: inherit; color: inherit; cursor: pointer;
  transition: border-color .18s ease, box-shadow .18s ease, background .18s ease;
}
.dw-well:hover { background: rgba(255,255,255,.6); }
.dw-well.is-target { border-color: var(--red-500); box-shadow: inset 0 1px 3px rgba(15,12,8,.16), 0 0 0 3px rgba(var(--red-rgb), .2); }
.dw-well-n { position: absolute; top: 6px; left: 9px; font-size: var(--fs-tiny); font-weight: 700; color: var(--ink-mute); }
/* An empty well is a plain recessed card with a +. It used to be that range's own
   bottle ghosted out, and on a lifestyle photo — British Mist ships 600x600 shots with
   a full background, not cutouts — greyscale at low opacity reads as a smudge rather
   than a silhouette. Mark's call: one placeholder, identical everywhere, no faded
   photos. It also stops a customer wondering whether something is already in the well.

   SQUARE, and the picker tile is square too. That is what makes the bottle fly cleanly:
   a FLIP between two boxes of different aspect ratios needs a non-uniform scale, and a
   non-uniform scale on a bottle renders it visibly fat — a 20px-wide Bar Juice bottle
   stretched across a 61px well. Two squares scale by one number and land exactly, and
   object-fit: contain letterboxes any source, tall cutout or 600x600 lifestyle square.

   Capped at 120px because slot count sets well width: a two-slot British Mist tray gave
   each well ~150px, so a full-bleed lifestyle photo filled it edge to edge and crowded
   the number and the clear button. Capped, every deal's wells read the same size. */
.dw-well-slot,
.dw-well-img {
  width: 100%; max-width: 120px; aspect-ratio: 1; height: auto; justify-self: center;
  object-fit: contain; border-radius: 7px; background: rgba(255,255,255,.5);
}
.dw-well-slot { display: grid; place-items: center; box-shadow: inset 0 1px 3px rgba(15,12,8,.13); }
.dw-well-slot::after { content: '+'; font-size: 1.6rem; font-weight: 300; line-height: 1; color: var(--ink-mute); opacity: .5; }
.dw-well-img { filter: drop-shadow(0 5px 6px rgba(15,12,8,.22)); }
/* No word-break here. A five-well tray is narrow enough that break-word chopped names
   mid-syllable — "Blackber / ry Raspberr / y". Wrapping at spaces and clamping to two
   lines keeps every well the same height and every name readable. */
.dw-well-name {
  font-size: .72rem; font-weight: 700; line-height: 1.18; overflow: hidden;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
}
.dw-well-meta { font-size: var(--fs-tiny); color: var(--ink-mute); }
.dw-well-clear {
  position: absolute; top: 2px; right: 4px; z-index: 2; border: 0; background: none;
  padding: 3px 7px; font-size: 1.15rem; line-height: 1; color: var(--ink-mute); cursor: pointer;
}
.dw-well-clear:hover { color: var(--red-600); }

.dw-field { margin-top: var(--sp-3); }
.dw-field-label { font-size: var(--fs-tiny); letter-spacing: .06em; text-transform: uppercase; color: var(--ink-mute); margin-bottom: 6px; }
.dw-choices { display: flex; flex-wrap: wrap; gap: 8px; }
.dw-field-help { font-size: var(--fs-tiny); color: var(--ink-mute); margin-top: 6px; }
.dw-hint { margin-top: var(--sp-2); font-size: var(--fs-tiny); font-weight: 600; color: var(--red-ink); }
.chip.on { background: var(--ink); color: var(--on-dark); border-color: var(--ink); }
.chip[disabled] { opacity: .4; cursor: not-allowed; }

/* The picker never closes. Filling a five used to cost five open/close cycles, and the
   page jumped under the customer on every one of them. */
.dw-picker { display: grid; grid-template-rows: auto 1fr; min-width: 0; }
.dw-head { padding: var(--sp-3) var(--sp-3) var(--sp-2); background: var(--paper); border-bottom: 1px solid var(--line); }
.dw-head-top { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.dw-head-title { font-family: var(--font-display); font-size: 1.05rem; font-weight: 800; }
.dw-search {
  flex: 1 1 190px; min-width: 0; padding: 10px 15px; font: inherit; font-size: var(--fs-small);
  color: inherit; background: var(--paper-warm); border: 1px solid var(--line); border-radius: 999px;
}
.dw-search:focus { outline: 2px solid var(--red-500); outline-offset: 1px; }
.dw-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: var(--sp-2); }

/* The TILES scroll, not the page. A sticky picker head was tried and reads as broken:
   a sticky bar only paints over what is under it, so tiles ran through the gap between
   the site header and the head, and the row half-covered by its bottom edge showed its
   name with no bottle. Scrolling the list inside its own box means nothing ever passes
   the head at all, and the pack stays put beside it without needing sticky either.
   vh, not %: a percentage height on a grid item resolves against a track sized by that
   same item, and the browser discards the cyclic dependency. */
.dw-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: var(--sp-2); padding: var(--sp-3); align-content: start;
  max-height: 56vh; min-height: 300px; overflow-y: auto; overscroll-behavior: contain;
}
.dw-tile {
  position: relative; display: grid; justify-items: center; align-content: start; gap: 4px;
  padding: var(--sp-2) 6px; text-align: center; font: inherit; color: inherit; cursor: pointer;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius-sm);
  transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.dw-tile:hover { transform: translateY(-2px); border-color: var(--red-500); box-shadow: var(--shadow-card); }
/* Square, to match the well it flies into — see .dw-well-img. Capped so a wide column
   does not blow the bottle up. */
.dw-tile img { width: min(100%, 96px); aspect-ratio: 1; height: auto; object-fit: contain; }
.dw-tile-noimg { width: min(100%, 96px); aspect-ratio: 1; }
.dw-tile-name { font-size: var(--fs-tiny); font-weight: 600; line-height: 1.25; }
.dw-tile-in { min-height: 15px; font-size: var(--fs-tiny); font-weight: 700; color: var(--red-ink); }
.dw-tile.is-in { border-color: var(--red-500); background: var(--red-050); }
.dw-tile-best { position: absolute; top: 5px; right: 8px; font-size: .82rem; color: var(--red-ink); }
.dw-empty { padding: var(--sp-4); font-size: var(--fs-small); color: var(--ink-mute); }

/* The money was a line of body text under the slots. It is the emotional core of the
   page, and sticking it to the bottom also keeps it on screen while choosing on a phone. */
.dw-bar {
  position: sticky; bottom: 0; z-index: 3; display: flex; align-items: center; gap: var(--sp-3);
  flex-wrap: wrap; padding: var(--sp-2) var(--sp-3);
  background: var(--navy-900); color: var(--on-dark); border-top: 1px solid var(--navy-700);
  border-radius: 0 0 var(--radius) var(--radius); transition: background .3s ease;
}
.dw-bar-money { display: grid; gap: 3px; margin-right: auto; }
.dw-bar-lead { font-family: var(--font-display); font-size: 1.95rem; font-weight: 800; line-height: 1; }
.dw-bar-note { font-size: var(--fs-small); color: var(--on-dark-soft); }
.dw-bar-note s { color: var(--on-dark-soft); }
.dw-save { color: var(--accent-on-dark); }

/* A second, small reading of the pack, pinned inside the sticky bar — PHONE ONLY.
   Above 900px the tray sits beside the picker and is never off screen, so a second
   copy there would be noise. Below it the picker stacks UNDER the tray, so by the
   time you are choosing bottles the pack you are filling has scrolled off the top;
   this is the only view of it you have. Decoration: the tray is the real control. */
.dw-bar-pack { display: none; }
.dw-mini {
  /* Portrait, like the wells in the tray it stands in for. A square well letterboxes
     a 198x720 bottle down to an unreadable sliver; this shape gives it its height
     back. flex 1 1 0 means ten of them shrink to fit a 390px bar instead of
     overflowing it — the cap is what they grow to, not what they are. */
  flex: 1 1 0; max-width: 34px; aspect-ratio: 5 / 8; border-radius: 6px;
  background: rgba(255,255,255,.09); box-shadow: inset 0 0 0 1px rgba(255,255,255,.14);
  display: grid; place-items: center; overflow: hidden; padding: 2px;
}
.dw-mini.is-filled { background: rgba(255,255,255,.85); box-shadow: none; }
/* 🚨 aspect-ratio + height:auto, NEVER height:100%. This is a grid item in an
   auto-sized area, so a PERCENTAGE height resolves against a row whose size comes
   from this very item — a cyclic dependency the browser silently discards, leaving
   `auto`. It shipped as height:100% and rendered the bottle 30x109 inside a 30x30
   well with overflow:hidden, so every mini-pack showed a cropped cap. Same trap the
   .rec-art img note in account-dash-recommend.test.mjs describes; third time here. */
.dw-mini img { width: 100%; aspect-ratio: 5 / 8; height: auto; object-fit: contain; }

/* The step up to a bigger tier. Bar Juice reads "5 FOR £12 OR 10 FOR £22" and until
   this button existed the ten was reachable only by accident. Both figures are
   derived — see tierStepHTML in deal-wall.js — never price divided by quantity. */
.dw-upsell {
  display: grid; gap: 1px; justify-items: center; cursor: pointer;
  padding: 7px 15px; border-radius: 999px; font: inherit; font-weight: 700;
  font-size: var(--fs-small); line-height: 1.15;
  background: transparent; color: var(--on-dark);
  border: 1px solid var(--on-dark-soft);
  transition: background .2s ease, border-color .2s ease;
}
.dw-upsell:hover { background: rgba(255,255,255,.12); border-color: var(--on-dark); }
.dw-upsell-each { font-size: var(--fs-tiny); font-weight: 600; color: var(--on-dark-soft); }
/* On the completed (red) bar the soft navy-band greys drop under 4.5:1, so both
   lines take SOLID white — see the note above: 88% white measures 4.15 on #d62828,
   which looks like a reasonable compromise and is not one. The border may stay a
   wash; it is a shape, not type. */
.deal-shop.is-complete .dw-upsell { color: #fff; border-color: rgba(255,255,255,.8); }
.deal-shop.is-complete .dw-upsell-each { color: #fff; }
.deal-shop.is-complete .dw-upsell:hover { background: rgba(255,255,255,.16); border-color: #fff; }

/* Completion should feel like completing: the tray takes the brand ring, the bar goes
   solid red and the button owns what it does. No confetti — this brand is a crest. */
.deal-shop.is-complete .dw-tray { border-color: var(--red-500); box-shadow: inset 0 2px 7px rgba(15,12,8,.2), 0 0 0 3px rgba(var(--red-rgb), .22); }
.deal-shop.is-complete .dw-bar { background: var(--red-600); border-top-color: var(--red-500); }
/* Solid white, not a wash. On #d62828 NOTHING under 95% white clears 4.5:1 — .8
   measures 3.65 and .88 measures 4.15, and this line carries the saving, which is
   the number the whole page exists to show. Hierarchy comes from size and weight
   here; on the navy bar --on-dark-soft is 7.19:1 and stays. */
.deal-shop.is-complete .dw-bar-note, .deal-shop.is-complete .dw-bar-note s { color: #fff; }
.deal-shop.is-complete .dw-save { color: #fff; }
.deal-shop.is-complete #deal-add { background: #fff; color: var(--red-600); }
.deal-shop.is-complete #deal-add:hover { background: var(--paper-warm); }

/* A disabled Add must LOOK disabled. Found in the browser: ready() correctly held the
   button dead until a strength was chosen, but .btn-red kept it full red with
   cursor:pointer, so the only signal was a line of hint text under it. Matches the
   convention already used by .add-btn:disabled and .pdp-add:disabled. */
#deal-add:disabled {
  background: var(--navy-700); color: var(--on-dark-soft);
  border: 1px solid var(--navy-700); cursor: not-allowed; box-shadow: none;
}
#deal-add:disabled:hover { background: var(--navy-700); transform: none; }

.dw-done {
  display: grid; gap: 6px; justify-items: center; text-align: center;
  padding: var(--sp-6) var(--sp-3); background: var(--navy-900); color: var(--on-dark);
  border-radius: 0 0 var(--radius) var(--radius);
}
.dw-done-title { font-family: var(--font-display); font-size: var(--fs-h2); font-weight: 800; }
.dw-done-sum { font-size: var(--fs-body); color: var(--on-dark-soft); }
.dw-done-acts { display: flex; flex-wrap: wrap; gap: var(--sp-2); justify-content: center; margin-top: var(--sp-2); }

/* The bottle travels from the tile it was picked from into its well (FLIP: measure
   both rects, transform between them). Choosing is the whole pleasure of this page and
   it previously had no feedback at all. */
.dw-fly {
  position: fixed; z-index: 999; pointer-events: none; transform-origin: center center;
  object-fit: contain; filter: drop-shadow(0 12px 14px rgba(15,12,8,.32));
  transition: transform .4s cubic-bezier(.22,.61,.36,1);
}

@media (prefers-reduced-motion: reduce) {
  .deal-card, .dw-tile, .dw-well, .dw-tray, .dw-bar, .dw-fly { transition: none; }
  .deal-card:hover, .dw-tile:hover { transform: none; }
}

@media (max-width: 900px) {
  .dw-bar-pack { display: flex; flex: 1 0 100%; gap: 5px; }
  .dw-body { grid-template-columns: 1fr; }
  .dw-pack { border-right: 0; border-bottom: 1px solid var(--line); }
  .dw-grid { max-height: 46vh; min-height: 240px; }
  .dw-band { flex-wrap: wrap; gap: var(--sp-2); }
  .dw-band-offer { margin-left: 0; }
  .dw-bar-lead { font-size: 1.55rem; }
}

@media (max-width: 640px) {
  .dw-tray { display: flex; overflow-x: auto; gap: var(--sp-1); -webkit-overflow-scrolling: touch; }
  .dw-well-wrap { flex: 0 0 98px; }
  .dw-well { min-height: 128px; padding-top: 22px; }
  /* No height override: the square comes from width + aspect-ratio, and setting a
     height here would fight the aspect ratio the fly animation depends on. */
  .dw-well-slot, .dw-well-img { max-width: 78px; }
  .deal-card-offer { font-size: 1.8rem; }
  .dw-grid { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); }
}

/* ============================================================
   BRITISH MIST BAR — full-bleed promo band
   The photograph is the section. Its outer margin is faded to TRUE black by
   scripts/make-banner-image.mjs before the sides are extended, so the picture
   dissolves into the band rather than sitting in it — measured 0.02/255 across
   the join, and the outer 17% is exactly 0. The copy therefore sits on real
   black, not on a dark part of a photograph.
   ============================================================ */
/* The break above the band. Bone, matching the trust strip below, so the band is
   bracketed by the page rather than bleeding out of the panel above it. */
.band-sep {
  /* --paper, not --bone: the palette is described as "black · warm bone · red" in the
     :root comment but the token is named --paper, and var() on an undefined property
     falls back to transparent at computed-value time — the strip would simply not have
     been there. Caught by the custom-property guard, same class as --accent and --navy. */
  background: var(--paper);
  display: grid;
  place-items: center;
  padding-top: clamp(28px, 3.4vw, 52px);
  /* Deliberately tighter than the top. The chevron reads as aimed INTO the band, and
     the gap under it is what says so — even padding floated it mid-shelf, pointing at
     nothing in particular. */
  padding-bottom: clamp(18px, 2.1vw, 32px);
  padding-inline: clamp(24px, 3.6vw, 76px);
}

/* The hand-off line. Reads as one sentence, so the size half is bold rather than being
   split onto its own line — two centred lines here would read as a heading and a
   subheading, i.e. as a third section, which is the opposite of joining two. */
.band-sep-line {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1rem, 1.25vw, 1.22rem);
  font-weight: 500;
  line-height: 1.35;
  text-align: center;
  color: var(--ink);
  max-width: 34ch;
}
.band-sep-line b { font-weight: 700; }

/* The chevron. Bare stroke, no disc, no fill — a filled red circle read as a button,
   and it is not one; it is a direction. This also keeps it in the same vocabulary as
   the 56×2px red rule that used to be the whole separator.
   It stays ON the bone. Anything centred that crosses the seam lands on the bottle's
   cap: the bottle is centred in the photograph at every width, and the honest black
   above it is only ~23px at 1166. Proximity does the pointing instead. */
.band-sep-arrow {
  display: block;
  width: clamp(30px, 2.6vw, 38px);
  margin-top: clamp(12px, 1.4vw, 20px);
  padding: 6px 10px; /* tap target — the stroke itself is far too thin to hit on a phone */
  box-sizing: content-box;
  color: var(--red-500);
  transition: color .18s ease, transform .18s ease;
}
.band-sep-arrow svg { display: block; width: 100%; height: auto; }
.band-sep-arrow:hover,
.band-sep-arrow:focus-visible {
  color: var(--red-600);
  transform: translateY(2px);
}

/* The nudge. 3px, once every 2.4s — enough to catch the eye on a page this long,
   short of the bouncing-arrow cliché. On the INNER svg so it never fights the hover
   transform on the anchor itself. */
@media (prefers-reduced-motion: no-preference) {
  .band-sep-arrow svg { animation: band-sep-bob 2.4s ease-in-out infinite; }
}
@keyframes band-sep-bob {
  0%, 62%, 100% { transform: translateY(0); }
  78% { transform: translateY(3px); }
}

.mistbar-band {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  /* The banner asset is 2.75:1 and the band matches it, so at ordinary desktop widths
     NOTHING is cropped — 1920px wide lands at 698px tall.
     This is a FLOOR, not a cage. It used to be `aspect-ratio: 2.75` with a max-height,
     and on 6 Aug the copy grew by one paragraph and the "powered by" mark was silently
     CUT OFF below ~1300px — overflow:hidden has no other way to fail. A min-height grows
     instead of clipping, so the worst case is a slightly deeper crop on the photo rather
     than missing content. min() keeps the ultrawide ceiling the max-height used to give. */
  min-height: min(calc(100vw / 2.75), 860px);
  /* Lets .mistbar-inner stretch to the band without height:100%, which would resolve to
     auto against a parent that only has a min-height — and then the bottom-pinned row
     would not pin. */
  display: flex;
  flex-direction: column;
  /* The asset's own outer edge is #000, so this can only ever be invisible. Anything
     else here would reintroduce the seam the fade exists to remove. */
  background: #000;
}

.mistbar-bg {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Biased up, not centred. The bottle occupies 7%–92% of the picture's height, so
     when max-height does crop, an even crop would take the cap off first. */
  object-position: center 35%;
}

/* Does nothing over the outer 17% — that is already black — and only starts working
   where the picture ramps back up, which is exactly where a narrow desktop pushes the
   container's edge. Deliberately weaker than the first version: with the fade in the
   asset this is a safety net, not the contrast itself. */
.mistbar-scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(90deg, rgba(0, 0, 0, .6) 0%, rgba(0, 0, 0, 0) 30%),
    linear-gradient(270deg, rgba(0, 0, 0, .6) 0%, rgba(0, 0, 0, 0) 30%);
}

.mistbar-inner {
  position: relative;
  flex: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  /* Row 1 is the copy, row 2 is the "powered by" mark pinned to the bottom-right. The
     1fr row is what lets it sit on the floor of the band without absolute positioning. */
  grid-template-rows: auto 1fr;
  /* Top, not centred. Measured across the band's own height: a block sitting in rows
     6–50% has 540px of usable black at a 1920 viewport, one in rows 30–70% only 408px.
     The splash reaches much higher than the fruit does. */
  align-items: start;
  gap: var(--sp-4);
  padding-inline: clamp(24px, 3.6vw, 76px);
  padding-block: clamp(28px, 4.4vw, 64px) clamp(22px, 2.6vw, 44px);
}

.mistbar-copy { max-width: 460px; color: var(--on-dark); }
.mistbar-copy .kuro-kicker { font-size: clamp(.78rem, .88vw, .95rem); }
/* Wider than the left column: the offer chip carries a nowrap price line and its own
   padding, so it needs more room than a paragraph of the same nominal size. */
.mistbar-copy--right {
  justify-self: end;
  max-width: 520px;
  text-align: right;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--sp-2);
}

/* The range name stays live text, not artwork — it is the h2, it is what Google reads,
   and a customer can select it. The two marks pair up in the lockup below instead. */
.mistbar-title {
  margin: var(--sp-2) 0 0;
  font-size: clamp(2rem, 3.1vw, 3rem);
  line-height: 1.04;
  color: #fff;
}
.mistbar-title em { font-style: normal; color: var(--red-500); }

/* Chris's strapline. Held out of the product Description on purpose so it lands here. */
.mistbar-strap {
  font-family: var(--font-display);
  font-size: clamp(1.1rem, 1.45vw, 1.5rem);
  font-weight: 600;
  line-height: 1.35;
  color: #fff;
  margin: var(--sp-3) 0 0;
}

/* The explainer under the strapline. Narrower than the copy above it on purpose: it sits
   low in the band, and measured on the asset the honest black drops from ~500px across
   the upper rows to 362px from 44% down — so 362 minus the inset is all the width there
   is. The old floor of 230px was the bug: below ~1300px the clamp stopped scaling, the
   paragraph wrapped to six lines and pushed the powered-by mark out of a band that
   clips. Both the width and the copy are shorter now. */
.mistbar-note {
  margin: var(--sp-3) 0 0;
  max-width: clamp(190px, 15vw, 300px);
  font-size: clamp(.86rem, .95vw, .98rem);
  line-height: 1.5;
  color: var(--on-dark-soft);
}

/* "Powered by" + the Love Flavours mark, bottom-right.
   The British Mist mark is deliberately NOT here: the h2 already says the range name in
   3rem display type and the site header carries the crest a few hundred px above, so a
   third instance in one viewport bought nothing — and pairing the two marks was what
   forced theirs to stay too small to read.
   WIDTH is the sized property, not height, because the constraint is horizontal: the
   band's honest black runs ~18.4% in from the right edge, and the mark must end before
   the picture ramps back up. 14.5vw tracks that with ~5px to spare from 1024 to 1920. */
.mistbar-powered {
  grid-column: 2;
  grid-row: 2;
  align-self: end;
  justify-self: end;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
  text-align: right;
}
.mistbar-powered-label {
  font-family: var(--font-display);
  font-size: clamp(.68rem, .78vw, .8rem);
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--on-dark-soft);
  line-height: 1;
}
/* 14vw, not the 14.5vw the black would just about allow: at the tighter clamp the mark
   cleared the picture by 2–6px across the desktop range, which is a rounding error away
   from touching. 14vw keeps 10–15px of real margin and costs 0.3px of cap height. */
.mistbar-powered-mark { display: block; width: clamp(140px, 14vw, 268px); height: auto; }

/* The launch offer, set as type rather than as a chip.
   .kuro-deal is the site's standing deal device and it appears three other times on this
   page alone. Reusing it here made the launch of a new range read as furniture, and its
   card-era border and tint drew a floating rectangle on pure black. This treatment has
   no container at all: the money is the biggest thing in the column, which is what a
   launch offer should be. */
.mistbar-offer {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
}
.mistbar-offer-label {
  font-family: var(--font-display);
  font-size: clamp(.72rem, .82vw, .86rem);
  font-weight: 700;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--red-500);
  line-height: 1;
}
/* Baseline-aligned, so the small words sit on the price's baseline instead of floating
   against its cap height. */
.mistbar-offer-line {
  margin: 0;
  display: flex;
  align-items: baseline;
  gap: .3em;
  /* "3 for £24.99" is one fact — broken over two lines it stops reading as an offer. */
  white-space: nowrap;
}
.mistbar-offer-qty,
.mistbar-offer-for {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1rem, 1.2vw, 1.25rem);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--on-dark-soft);
}
.mistbar-offer-amount {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(2.2rem, 3.4vw, 3.3rem);
  line-height: 1;
  color: #fff;
}
.mistbar-offer-meta {
  margin: 0;
  font-size: clamp(.72rem, .82vw, .86rem);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--on-dark-soft);
}
.mistbar-soon {
  margin: 0;
  font-size: clamp(.8rem, .92vw, .95rem);
  color: var(--on-dark-soft);
}
.mistbar-ctas { margin: 0; }
.mistbar-ctas .btn {
  font-size: clamp(.95rem, 1.05vw, 1.1rem);
  padding: clamp(14px, 1.2vw, 18px) clamp(26px, 2.2vw, 38px);
}

/* Below this the two corners are too narrow to hold type beside the bottle, so the
   picture becomes a header and the copy stacks under it on the band's own black.
   The image switches to a square-ish crop: cover on a box NARROWER than the source
   crops horizontally, which throws away the deliberately empty edges and keeps the
   bottle — the opposite of what cover does to a wide box. */
/* Raised from 1024px on 6 Aug. The side-by-side layout needs roughly 500px of honest
   black per side to hold copy, and the asset only offers 18.4% of the viewport — below
   about 1200px the two corners are too narrow, which is what forced the copy to wrap
   deep enough to overflow the band in the first place. Stacking is the right answer
   there, not a tighter squeeze. */
@media (max-width: 1024px) {
  .mistbar-band {
    min-height: 0;
  }
  .mistbar-bg {
    position: static;
    width: 100%;
    height: auto;
    aspect-ratio: 5 / 4;
    object-position: center 40%;
  }
  .mistbar-scrim { display: none; }
  .mistbar-inner {
    grid-template-columns: 1fr;
    /* The two-row pin is a desktop device; stacked, everything flows in source order. */
    grid-template-rows: auto;
    height: auto;
    padding-top: var(--sp-4);
    padding-bottom: var(--sp-5);
    gap: var(--sp-3);
  }
  .mistbar-copy { max-width: none; }
  /* Stacked, the copy sits on the band's own black with no picture beside it, so the
     362px rule that narrows this on desktop does not apply. */
  .mistbar-note { max-width: 52ch; }
  .mistbar-copy--right {
    justify-self: start;
    text-align: left;
    align-items: flex-start;
  }
  /* grid-column: 2 would open a phantom second column in a one-column grid. */
  .mistbar-powered {
    grid-column: 1;
    grid-row: auto;
    justify-self: start;
    align-items: flex-start;
    text-align: left;
    margin-top: var(--sp-2);
  }
  /* Stacked, the mark sits on the band's own black with no picture beside it, so the
     18.4% rule that caps it on desktop does not apply here. */
  .mistbar-powered-mark { width: clamp(190px, 40vw, 280px); }
}

/* ---------- cookie consent ----------
   Fixed to the bottom rather than a centre modal: the age gate is already a modal on
   a first visit, and two of them stack. Renders only when PUBLIC_GA_MEASUREMENT_ID is
   set — see src/layouts/BaseLayout.astro. */
.consent-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 95;
  background: var(--navy-900); color: var(--on-dark);
  border-top: 3px solid var(--red-600);
  box-shadow: 0 -8px 32px rgba(0,0,0,.28);
}
.consent-bar[hidden] { display: none; }   /* an author display would beat the UA rule */
.consent-inner {
  max-width: var(--maxw); margin-inline: auto;
  padding: 16px var(--sp-2);
  display: flex; align-items: center; gap: 20px; flex-wrap: wrap;
}
.consent-inner p {
  margin: 0; flex: 1 1 420px;
  font-size: .85rem; line-height: 1.55; color: var(--on-dark-soft);
}
.consent-inner p a { color: var(--on-dark); text-decoration: underline; }
.consent-actions { display: flex; gap: 10px; flex: 0 0 auto; }
@media (max-width: 560px) {
  .consent-actions { width: 100%; }
  .consent-actions .btn { flex: 1 1 0; }
}

/* ---------- order-confirmed page (rendered by /js/order-confirmed.js) ---------- */
.oc-card { background: var(--paper, #faf7f2); border: 1px solid var(--line, #e5ded2); border-radius: 14px; padding: 28px; max-width: 640px; margin: 0 auto 18px; }
.oc-card--confirm { text-align: center; }
.oc-tick { width: 56px; height: 56px; margin: 0 auto 14px; border-radius: 50%; background: var(--red-600, #d62828); color: #fff; font-size: 30px; line-height: 56px; font-weight: 700; }
.oc-sub { color: var(--ink-soft, #5c564c); margin: 6px 0 18px; }
.oc-facts { display: grid; gap: 10px; margin: 0 0 6px; }
.oc-fact { display: flex; justify-content: space-between; gap: 14px; border-top: 1px solid var(--line, #e5ded2); padding-top: 10px; }
.oc-fact-label { color: var(--ink-soft, #5c564c); }
.oc-fact-value { font-weight: 600; }
.oc-fact-value--grand { font-size: 1.25rem; font-weight: 700; }
.oc-items-title { margin: 18px 0 8px; font-size: 1rem; text-align: left; }
.oc-items { list-style: none; margin: 0; padding: 0; text-align: left; }
.oc-item { display: flex; gap: 8px; align-items: baseline; padding: 6px 0; border-top: 1px solid var(--line, #e5ded2); }
.oc-item-qty { font-weight: 700; flex: 0 0 auto; }
.oc-item-strength { color: var(--ink-soft, #5c564c); font-size: 0.9em; }
.oc-actions { display: flex; gap: 10px; margin-top: 16px; flex-wrap: wrap; justify-content: center; }
.oc-card:not(.oc-card--confirm) .oc-actions { justify-content: flex-start; }
