/* ═══════════════════════════════════════════════════════════════════════════
   AURA — main.css

   Reading order:
     1  tokens          the palette, type scale and spacing. Start here.
     2  reset & base
     3  primitives      buttons, fields, pills, the SVG can
     4  chrome          header, cart button, footer
     5  stage           the fixed 3D canvas and its glow
     6  sections        hero, craft, shop, bundles, values, reviews
     7  cart & dialog   the drawer, the scrim, the quick view
     8  loader
     9  states          reduced motion, no-WebGL, focus, small screens

   ONE ACCENT, AND IT MOVES. --flavour is rewritten by ui.js whenever the
   selected flavour changes, and everything accented on the page reads from it.
   It is declared with @property so it can be TWEENED: a custom property with no
   registered syntax is a string to the engine, and transitioning between two
   strings snaps. Registering it as <color> makes the browser interpolate, which
   is what lets the entire page change colour in step with the can turning.
   ═══════════════════════════════════════════════════════════════════════════ */

@property --flavour {
  syntax: '<color>';
  inherits: true;
  initial-value: #F7A72B;
}
@property --flavour-deep {
  syntax: '<color>';
  inherits: true;
  initial-value: #E0670F;
}

/* ── 1. tokens ──────────────────────────────────────────────────────────── */

:root {
  --paper:    #F6F4F0;
  --card:     #FFFFFF;
  --ink:      #14120F;
  --ink-2:    #5A554D;
  --ink-3:    #8E8880;
  --line:     rgba(20, 18, 15, 0.11);
  --line-soft:rgba(20, 18, 15, 0.055);

  --flavour:      #F7A72B;
  --flavour-deep: #E0670F;

  --radius:    20px;
  --radius-sm: 12px;
  --radius-pill: 999px;

  --shadow-1: 0 1px 2px rgba(20, 18, 15, 0.04), 0 6px 20px rgba(20, 18, 15, 0.05);
  --shadow-2: 0 2px 6px rgba(20, 18, 15, 0.06), 0 18px 50px rgba(20, 18, 15, 0.10);
  --shadow-3: 0 6px 20px rgba(20, 18, 15, 0.10), 0 40px 90px rgba(20, 18, 15, 0.16);

  --display: 'Bricolage Grotesque', 'Manrope', system-ui, sans-serif;
  --text:    'Manrope', system-ui, -apple-system, 'Segoe UI', sans-serif;

  /* Fluid type. min at 360px, max at 1440px — set once, never overridden with
     a media query further down, so there is exactly one place a size lives. */
  /* Capped against viewport HEIGHT as well as width. A headline sized purely
     off vw pushes the Add button below the fold on a short laptop screen,
     which for a shop is the one element that must never be off screen. */
  --t-hero:  min(clamp(2.5rem, 1.1rem + 5.2vw, 5rem), 11vh);
  --t-h2:    clamp(2rem, 1.2rem + 3.2vw, 3.6rem);
  --t-h3:    clamp(1.25rem, 1.05rem + 0.8vw, 1.7rem);
  --t-body:  clamp(1rem, 0.96rem + 0.18vw, 1.09rem);
  --t-small: 0.83rem;
  --t-micro: 0.72rem;

  --gap:  clamp(1.25rem, 0.8rem + 1.6vw, 2.25rem);
  --pad:  clamp(1.25rem, 0.6rem + 3.2vw, 3.5rem);
  --band: clamp(4.5rem, 2rem + 8vw, 9rem);

  --ease: cubic-bezier(0.22, 1, 0.36, 1);

  color-scheme: light;

  /* The reason --flavour and --flavour-deep are registered with @property.
     A custom property with no declared syntax is an unparsed string, and the
     engine cannot interpolate between two strings — it snaps at the halfway
     point. Registered as <color>, they animate, so assigning a new flavour in
     ui.js cross-fades the entire page's accent in step with the can turning. */
  transition: --flavour 0.75s var(--ease), --flavour-deep 0.75s var(--ease);
}

/* ── 2. reset & base ────────────────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
/* Lenis drives the wheel on desktop; when it is active the native smooth
   behaviour would fight it on anchor jumps. */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--text);
  font-size: var(--t-body);
  line-height: 1.6;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

/* The drawer and the quick view both lock the page behind them. */
body.is-locked { overflow: hidden; }

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 600;
  line-height: 1.02;
  letter-spacing: -0.025em;
  margin: 0;
  text-wrap: balance;
}
p { margin: 0; }
img, svg { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }

a { color: inherit; text-decoration: none; }
a:not([class]) {
  text-decoration: underline;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--line);
}
a:not([class]):hover { text-decoration-color: currentColor; }

::selection { background: var(--flavour); color: #fff; }

.wrap {
  width: 100%;
  max-width: 1280px;
  margin-inline: auto;
  padding-inline: var(--pad);
}

.sr {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
/* Skip link — visible only once it has focus. */
.skip {
  position: fixed; top: 0; left: 50%; transform: translate(-50%, -120%);
  z-index: 200; background: var(--ink); color: var(--paper);
  padding: 0.7rem 1.2rem; border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  font-size: var(--t-small); font-weight: 700;
}
.skip:focus-visible { transform: translate(-50%, 0); }

.eyebrow {
  font-size: var(--t-micro);
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.lede { font-size: clamp(1.05rem, 0.98rem + 0.4vw, 1.28rem); color: var(--ink-2); text-wrap: pretty; }
.muted { color: var(--ink-2); }
.num { font-variant-numeric: tabular-nums; }

/* ── 3. primitives ──────────────────────────────────────────────────────── */

.btn {
  --btn-bg: var(--ink);
  --btn-fg: #fff;
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6em;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-family: var(--text);
  font-size: var(--t-small);
  font-weight: 800;
  letter-spacing: 0.01em;
  /* 48px tall at the default size: a real thumb target, not a 32px desktop
     button that a phone user has to aim at. */
  padding: 0.95rem 1.5rem;
  cursor: pointer;
  transition: transform 0.25s var(--ease), background-color 0.25s, box-shadow 0.25s, opacity 0.2s;
  will-change: transform;
}
.btn:hover { transform: translateY(-2px); box-shadow: var(--shadow-2); }
.btn:active { transform: translateY(0); }
.btn[disabled] { opacity: 0.45; cursor: not-allowed; transform: none; box-shadow: none; }

.btn--flavour { --btn-bg: var(--flavour-deep); }
.btn--ghost {
  --btn-bg: transparent; --btn-fg: var(--ink);
  border-color: var(--line);
}
.btn--ghost:hover { border-color: var(--ink); box-shadow: none; }
.btn--block { width: 100%; }
.btn--lg { padding: 1.1rem 1.9rem; font-size: 0.95rem; }
.btn--sm { padding: 0.6rem 1rem; font-size: var(--t-micro); }

.btn__price {
  font-variant-numeric: tabular-nums;
  opacity: 0.75;
  font-weight: 700;
}

/* Pills — the flavour picker and the pack selector. */
.pills { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.pill {
  appearance: none;
  display: inline-flex; align-items: center; gap: 0.5rem;
  border: 1px solid var(--line);
  background: transparent;
  border-radius: var(--radius-pill);
  padding: 0.55rem 0.95rem;
  font-size: var(--t-small); font-weight: 700;
  cursor: pointer;
  transition: border-color 0.2s, background-color 0.2s, transform 0.2s var(--ease);
}
.pill:hover { transform: translateY(-1px); border-color: var(--ink-3); }
.pill[aria-pressed='true'], .pill[aria-checked='true'] {
  border-color: var(--ink);
  background: var(--ink);
  color: #fff;
}
.pill__dot {
  width: 0.78em; height: 0.78em; border-radius: 50%;
  background: var(--dot, var(--flavour));
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12);
  flex: none;
}

/* Pack selector: a radio group that looks like cards. */
.packs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem; }
.pack {
  position: relative;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--card);
  padding: 0.7rem 0.6rem;
  text-align: center;
  cursor: pointer;
  transition: border-color 0.2s, transform 0.2s var(--ease);
}
.pack:hover { transform: translateY(-2px); }
.pack[aria-checked='true'] { border-color: var(--ink); }
.pack__n { display: block; font-weight: 800; font-size: 0.95rem; }
.pack__p { display: block; font-size: var(--t-micro); color: var(--ink-2); font-variant-numeric: tabular-nums; }
.pack__tag {
  position: absolute; top: -0.6rem; left: 50%; transform: translateX(-50%);
  background: var(--flavour-deep); color: #fff;
  font-size: 0.6rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase;
  padding: 0.15rem 0.5rem; border-radius: var(--radius-pill);
  white-space: nowrap;
}

/* Quantity stepper. */
.qty { display: inline-flex; align-items: center; border: 1px solid var(--line); border-radius: var(--radius-pill); }
.qty button {
  appearance: none; border: 0; background: none; cursor: pointer;
  width: 2.2rem; height: 2.2rem; display: grid; place-items: center;
  border-radius: 50%; font-size: 1.05rem; line-height: 1; color: var(--ink-2);
  transition: background-color 0.15s, color 0.15s;
}
.qty button:hover:not([disabled]) { background: var(--line-soft); color: var(--ink); }
.qty button[disabled] { opacity: 0.3; cursor: not-allowed; }
.qty output { min-width: 1.6rem; text-align: center; font-weight: 800; font-variant-numeric: tabular-nums; font-size: 0.9rem; }

/* Fields. */
.field { display: grid; gap: 0.35rem; }
.field > label { font-size: var(--t-micro); font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-2); }
.field input, .field textarea, .field select {
  width: 100%;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--card);
  padding: 0.8rem 0.9rem;
  font-size: 0.95rem;
  transition: border-color 0.2s;
}
.field input:focus, .field textarea:focus, .field select:focus { outline: none; border-color: var(--ink); }
.field[data-invalid='true'] input, .field[data-invalid='true'] textarea { border-color: #C4331E; }
.field__err { font-size: var(--t-micro); color: #C4331E; font-weight: 700; min-height: 1em; }

/* The SVG can. */
.cansvg { width: 100%; height: auto; overflow: visible; }
.cansvg--mini { width: auto; height: 100%; }

.canstack { display: flex; align-items: flex-end; height: 100%; }
.canstack__i {
  height: 100%;
  /* Overlap, so a case reads as a case rather than a row of singles. Each can
     sits slightly higher and smaller than the one before it and stacks in
     front, which is what gives the row its depth. */
  margin-left: -2.5rem;
  z-index: var(--i);
  filter: drop-shadow(0 6px 14px rgba(20, 18, 15, 0.14));
  transform: translateY(calc(var(--i) * -2px)) scale(calc(1 - var(--i) * 0.022));
}
.canstack__i:first-child { margin-left: 0; }

/* ── 4. chrome ──────────────────────────────────────────────────────────── */

.hdr {
  position: sticky; top: 0; z-index: 60;
  background: color-mix(in srgb, var(--paper) 78%, transparent);
  backdrop-filter: saturate(1.6) blur(14px);
  -webkit-backdrop-filter: saturate(1.6) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.3s, background-color 0.3s;
}
body[data-scrolled='true'] .hdr { border-bottom-color: var(--line-soft); }

.hdr__in {
  display: flex; align-items: center; gap: var(--gap);
  height: 4.4rem;
}
.hdr__mark {
  font-family: var(--display);
  font-weight: 800; font-size: 1.4rem; letter-spacing: -0.03em;
  display: inline-flex; align-items: center; gap: 0.5rem;
}
.hdr__mark::before {
  content: ''; width: 0.72em; height: 0.72em; border-radius: 50%;
  background: radial-gradient(circle at 32% 30%, #fff6, transparent 60%), var(--flavour);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.08), 0 0 18px -2px var(--flavour);
}
.hdr__nav { display: flex; gap: 1.5rem; margin-left: auto; font-size: var(--t-small); font-weight: 700; }
.hdr__nav a { color: var(--ink-2); transition: color 0.2s; }
.hdr__nav a:hover, .hdr__nav a[aria-current='true'] { color: var(--ink); }
.hdr__end { display: flex; align-items: center; gap: 0.6rem; margin-left: auto; }
.hdr__nav + .hdr__end { margin-left: 0; }

.cartbtn {
  appearance: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: 0.55rem;
  border: 1px solid var(--line); border-radius: var(--radius-pill);
  background: var(--card);
  padding: 0.6rem 1rem 0.6rem 0.85rem;
  font-size: var(--t-small); font-weight: 800;
  transition: border-color 0.2s, transform 0.2s var(--ease), box-shadow 0.2s;
}
.cartbtn:hover { transform: translateY(-1px); box-shadow: var(--shadow-1); border-color: var(--ink-3); }
.cartbtn svg { width: 1.05rem; height: 1.05rem; stroke: currentColor; fill: none; stroke-width: 1.7; }
.cartbtn__n {
  min-width: 1.35rem; height: 1.35rem; padding: 0 0.3rem;
  display: grid; place-items: center;
  background: var(--flavour-deep); color: #fff;
  border-radius: var(--radius-pill);
  font-size: 0.7rem; font-weight: 800; font-variant-numeric: tabular-nums;
  transition: transform 0.3s var(--ease);
}
.cartbtn__n[data-empty='true'] { display: none; }
.cartbtn.is-bumped .cartbtn__n { transform: scale(1.35); }

/* ── 5. stage ───────────────────────────────────────────────────────────── */

/* The canvas is fixed behind everything and never re-created. Sections that
   should reveal the can leave their background transparent; every section
   below the 3D storytelling simply paints over it. That is why the whole page
   costs one WebGL context and one render loop rather than one per section. */
.stage {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.6s var(--ease);
}
.stage.is-live { opacity: 1; }
.stage canvas {
  width: 100%; height: 100%; display: block;
  /* Dragging must not also scroll the page or select text. */
  touch-action: none;
}

/* The glow is CSS, not bloom. A post-processing pass to put a coloured haze
   behind the can would cost a full-screen render target and a blur on every
   frame; this costs one composited layer that the GPU was going to handle
   anyway, and it can be tweened with the flavour for free. */
.stage__glow {
  position: absolute;
  left: 50%; top: 50%;
  width: min(90vw, 780px); aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, var(--flavour) 0%, transparent 62%);
  opacity: 0.3;
  filter: blur(28px);
  transition: opacity 0.8s var(--ease);
}

/* See index.html for why this region exists. It follows the can: right-hand
   side on a wide screen, centre-low in portrait, matching the lens shift in
   VIEWS so the grab area sits where the can actually is. */
.stage__pad {
  position: absolute;
  inset: 0 0 0 auto;
  width: 52%;
  pointer-events: auto;
  touch-action: pan-y;
  cursor: grab;
}
.stage:not(.is-live) .stage__pad { pointer-events: none; }

.stage__hint {
  position: absolute;
  left: 50%; bottom: clamp(1.5rem, 4vh, 3rem);
  transform: translateX(-50%);
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-size: var(--t-micro); font-weight: 800;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-3);
  opacity: 0; transition: opacity 0.5s;
  pointer-events: none;
}
.stage__hint.is-on { opacity: 1; }
.stage__hint svg { width: 1.2rem; stroke: currentColor; fill: none; stroke-width: 1.6; }

/* Content sits above the canvas. */
.page { position: relative; z-index: 1; }

/* The still fallback: shown when there is no WebGL, or the context is lost. */
.stage__still {
  position: absolute; inset: 0;
  display: none; place-items: center;
  padding: 6rem 1rem;
}
html.no-webgl .stage__still { display: grid; }
html.no-webgl .stage { opacity: 1; }
.stage__still .cansvg { height: min(62vh, 520px); width: auto; }

/* ── 6. sections ────────────────────────────────────────────────────────── */

.band { padding-block: var(--band); }
.band--tint { background: var(--paper); }

/* The shop is the first opaque section, so it is the one that covers the fixed
   canvas — and a hard edge cuts the can clean in half on the way past, which
   looks like a rendering bug rather than a transition. Fading the background in
   over the section's own top padding lets the can dissolve behind it instead.
   A gradient holds its last stop forever, so everything below 190px is solid
   paper and nothing shows through further down the page. */
#shop {
  background:
    linear-gradient(to bottom, rgba(246, 244, 240, 0) 0, var(--paper) 190px);
}
.band--card { background: var(--card); }

.band__head { display: grid; gap: 0.9rem; max-width: 46ch; margin-bottom: clamp(2rem, 4vw, 3.5rem); }
.band__head h2 { font-size: var(--t-h2); }

/* — hero — */
.hero {
  min-height: 100svh;
  min-height: 100vh;
  display: grid;
  align-content: center;
  padding-block: clamp(4rem, 8vh, 6rem) clamp(2.5rem, 6vh, 4.5rem);
  /* Transparent: this is the one section the can shows through. */
}
.hero__in { display: grid; gap: clamp(0.85rem, 1.6vw, 1.35rem); max-width: 34rem; }
.hero h1 {
  font-size: var(--t-hero);
  font-weight: 500;
  letter-spacing: -0.04em;
  line-height: 0.95;
}
.hero h1 em {
  font-style: normal;
  font-weight: 700;
  color: var(--flavour-deep);
}
.hero__buy {
  display: grid; gap: 0.9rem;
  padding: 1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--card) 74%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: var(--shadow-1);
  max-width: 27rem;
}
.hero__row { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.hero__trust {
  display: flex; flex-wrap: wrap; gap: 0.4rem 1.1rem;
  font-size: var(--t-micro); font-weight: 700; color: var(--ink-3);
}
.hero__trust li { list-style: none; display: flex; align-items: center; gap: 0.35rem; }
.hero__trust li::before { content: ''; width: 4px; height: 4px; border-radius: 50%; background: var(--flavour); }
.hero__trust { padding: 0; margin: 0; }

/* — the pour —
   The section is tall so that there is scroll to spend on the sequence; the
   sticky child is what stays on screen while it is spent. 320vh gives the four
   phases about eighty viewport-heights each, which is slow enough to read and
   short enough that nobody feels trapped on the way to the shop. */
.pour { height: 320vh; position: relative; }
.pour__sticky {
  position: sticky; top: 0;
  height: 100svh; height: 100vh;
  display: grid; align-content: end;
  padding-bottom: clamp(2.5rem, 9vh, 6rem);
  /* The captions must never intercept a drag meant for the can behind them. */
  pointer-events: none;
}
/* All three captions occupy the same grid cell and cross-fade, so the block
   does not change height as the words change and shove the rail around. */
.pour__caps { display: grid; max-width: 30rem; }
.pour__cap {
  grid-area: 1 / 1;
  font-family: var(--display);
  font-size: clamp(1.15rem, 0.9rem + 1.1vw, 1.75rem);
  font-weight: 500; line-height: 1.25; letter-spacing: -0.02em;
  color: var(--ink-2);
  opacity: 0; transform: translateY(10px);
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
}
.pour__cap b { color: var(--ink); font-weight: 700; }
.pour__cap[data-active='true'] { opacity: 1; transform: none; }

.pour__rail {
  margin-top: 1.4rem; width: min(14rem, 40vw);
  height: 3px; border-radius: 2px; background: var(--line);
  overflow: hidden;
}
.pour__rail span {
  display: block; height: 100%; width: 0%;
  background: var(--flavour-deep); border-radius: 2px;
}

/* — craft: the third 3D section — */
.craft { min-height: 100svh; min-height: 100vh; display: grid; align-content: center; padding-block: var(--band); }
.craft__in { display: grid; gap: clamp(1rem, 2vw, 1.6rem); max-width: 32rem; margin-left: auto; }
.craft__list { display: grid; gap: 1.1rem; margin: 0; padding: 0; }
.craft__list div { display: grid; gap: 0.2rem; padding-top: 1.1rem; border-top: 1px solid var(--line); }
.craft__list dt { font-family: var(--display); font-weight: 600; font-size: var(--t-h3); letter-spacing: -0.02em; }
.craft__list dd { margin: 0; color: var(--ink-2); font-size: 0.97rem; }

/* — shop grid — */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
  gap: var(--gap);
}

.card {
  position: relative;
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: 0.9rem;
  background: var(--card);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 1.1rem;
  transition: transform 0.35s var(--ease), box-shadow 0.35s var(--ease), border-color 0.3s;
}
.card:hover { transform: translateY(-5px); box-shadow: var(--shadow-2); border-color: var(--line); }

.card__art {
  position: relative;
  /* Flex, not grid, and the reason is load-bearing: the can inside is sized
     with a percentage height. On a grid item a percentage height resolves
     against the grid ROW, whose height depends on the item in turn — that is
     circular, so the percentage is treated as indefinite and silently dropped,
     and the can falls back to its intrinsic size and gets clipped. A flex item
     resolves it against the container's own definite height. The same applies
     to .line__art and .bundle__art below. */
  display: flex; align-items: center; justify-content: center;
  height: clamp(180px, 26vw, 250px);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background:
    radial-gradient(120% 80% at 50% 108%, color-mix(in srgb, var(--dot) 42%, transparent), transparent 70%),
    color-mix(in srgb, var(--dot) 9%, var(--paper));
}
.card__art .cansvg {
  height: 90%; width: auto;
  filter: drop-shadow(0 14px 26px rgba(20, 18, 15, 0.2));
  transition: transform 0.5s var(--ease);
}
.card:hover .card__art .cansvg { transform: translateY(-6px) rotate(-2deg); }

.card__badge {
  position: absolute; top: 0.6rem; left: 0.6rem;
  background: var(--ink); color: #fff;
  font-size: 0.62rem; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase;
  padding: 0.28rem 0.55rem; border-radius: var(--radius-pill);
}
.card__body { display: grid; gap: 0.35rem; align-content: start; }
.card__name { font-size: 1.15rem; font-weight: 700; font-family: var(--display); letter-spacing: -0.02em; }
.card__notes { font-size: var(--t-small); color: var(--ink-2); }
.card__foot { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; }
.card__price { font-weight: 800; font-variant-numeric: tabular-nums; }
.card__price small { display: block; font-weight: 600; font-size: var(--t-micro); color: var(--ink-3); }

/* The whole card is a link to the quick view; the Add button sits on top of it
   and stops the click. A card that is one big button cannot also contain a
   button, so this is a link-overlay rather than nested interactives. */
.card__link {
  position: absolute; inset: 0;
  border-radius: inherit;
  z-index: 1;
}
.card__foot .btn, .card__art { position: relative; z-index: 2; }

/* — bundles — */
.bundles { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); gap: var(--gap); }
.bundle {
  display: grid; gap: 1rem;
  padding: 1.4rem;
  border-radius: var(--radius);
  border: 1px solid var(--line-soft);
  background: var(--card);
  transition: transform 0.35s var(--ease), box-shadow 0.35s var(--ease);
}
.bundle:hover { transform: translateY(-4px); box-shadow: var(--shadow-2); }
.bundle[data-featured='true'] { border-color: var(--ink); box-shadow: var(--shadow-1); }
.bundle__art { height: clamp(130px, 18vw, 170px); display: flex; align-items: center; justify-content: center; }
.bundle__top { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; }
.bundle__name { font-family: var(--display); font-size: var(--t-h3); font-weight: 600; }
.bundle__price { font-weight: 800; font-variant-numeric: tabular-nums; }
.bundle__save {
  display: inline-block;
  font-size: var(--t-micro); font-weight: 800;
  color: var(--flavour-deep);
}

/* — values — */
.values { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr)); gap: var(--gap); }
.value { display: grid; gap: 0.45rem; padding-top: 1.2rem; border-top: 2px solid var(--ink); }
.value h3 { font-size: var(--t-h3); }
.value p { color: var(--ink-2); font-size: 0.97rem; }

.statement {
  font-family: var(--display);
  font-size: clamp(1.6rem, 0.9rem + 3.2vw, 3.2rem);
  font-weight: 500;
  line-height: 1.08;
  letter-spacing: -0.03em;
  max-width: 20ch;
  text-wrap: balance;
}
.statement b { font-weight: 700; color: var(--flavour-deep); }

/* — reviews — */
.reviews { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); gap: var(--gap); }
.review {
  display: grid; gap: 0.8rem; align-content: start;
  padding: 1.4rem;
  background: var(--card);
  border-radius: var(--radius);
  border: 1px solid var(--line-soft);
}
.review__stars { color: var(--flavour-deep); letter-spacing: 0.12em; font-size: 0.85rem; }
.review p { font-size: 1.02rem; }
.review__who { font-size: var(--t-small); color: var(--ink-3); font-weight: 700; }

/* — footer — */
.ftr { background: var(--ink); color: #EDE9E2; padding-block: var(--band) 2.5rem; }
.ftr a { color: #EDE9E2; }
.ftr__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr)); gap: var(--gap); }
.ftr h2 { font-size: var(--t-h2); max-width: 14ch; }
.ftr__col { display: grid; gap: 0.5rem; align-content: start; font-size: var(--t-small); }
.ftr__col h3 { font-family: var(--text); font-size: var(--t-micro); letter-spacing: 0.14em; text-transform: uppercase; opacity: 0.5; font-weight: 800; margin-bottom: 0.3rem; }
.ftr__col a { opacity: 0.8; transition: opacity 0.2s; }
.ftr__col a:hover { opacity: 1; }
.ftr__sub { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.8rem; }
.ftr__sub input {
  flex: 1 1 12rem;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: var(--radius-pill);
  padding: 0.85rem 1.1rem; color: #fff; font-size: 0.9rem;
}
.ftr__sub input::placeholder { color: rgba(255, 255, 255, 0.42); }
.ftr__sub input:focus { outline: none; border-color: var(--flavour); }
.ftr__sub .btn { --btn-bg: var(--flavour); --btn-fg: #14120F; }
.ftr__base {
  margin-top: 3rem; padding-top: 1.5rem;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  display: flex; flex-wrap: wrap; gap: 0.6rem 1.5rem;
  font-size: var(--t-micro); opacity: 0.55;
}

/* ── 7. cart & dialog ───────────────────────────────────────────────────── */

.scrim {
  position: fixed; inset: 0; z-index: 90;
  background: rgba(20, 18, 15, 0.42);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  opacity: 0; visibility: hidden;
  transition: opacity 0.4s var(--ease), visibility 0.4s;
}
.scrim.is-open { opacity: 1; visibility: visible; }

.drawer {
  position: fixed; top: 0; right: 0; z-index: 100;
  width: min(30rem, 100vw);
  height: 100svh; height: 100vh;
  background: var(--paper);
  display: grid;
  grid-template-rows: auto 1fr auto;
  transform: translateX(101%);
  transition: transform 0.45s var(--ease);
  box-shadow: var(--shadow-3);
}
.drawer.is-open { transform: translateX(0); }

.drawer__head {
  display: flex; align-items: center; gap: 1rem;
  padding: 1.25rem var(--pad);
  border-bottom: 1px solid var(--line);
}
.drawer__head h2 { font-size: 1.35rem; }
.iconbtn {
  appearance: none; border: 1px solid var(--line); background: var(--card);
  width: 2.4rem; height: 2.4rem; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer; margin-left: auto;
  transition: border-color 0.2s, transform 0.2s var(--ease);
}
.iconbtn:hover { border-color: var(--ink); transform: rotate(90deg); }
.iconbtn svg { width: 1rem; height: 1rem; stroke: currentColor; stroke-width: 2; fill: none; }

.drawer__body { overflow-y: auto; overscroll-behavior: contain; padding: 1rem var(--pad); }

.free {
  display: grid; gap: 0.5rem;
  padding: 0.85rem 1rem; margin-bottom: 1rem;
  background: var(--card); border-radius: var(--radius-sm);
  border: 1px solid var(--line-soft);
  font-size: var(--t-small);
}
.free__bar { height: 5px; border-radius: 3px; background: var(--line); overflow: hidden; }
.free__bar span {
  display: block; height: 100%; width: 0%;
  background: var(--flavour-deep);
  border-radius: 3px;
  transition: width 0.55s var(--ease);
}
.free b { font-variant-numeric: tabular-nums; }

.lines { display: grid; gap: 0.75rem; margin: 0; padding: 0; list-style: none; }
.line {
  display: grid;
  grid-template-columns: 3.6rem 1fr auto;
  gap: 0.85rem; align-items: center;
  padding: 0.75rem;
  background: var(--card);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
}
.line.is-new { animation: pop 0.5s var(--ease); }
@keyframes pop { from { transform: scale(0.96); opacity: 0.4; } to { transform: none; opacity: 1; } }
.line__art { height: 3.6rem; display: flex; align-items: center; justify-content: center; }
.line__art .cansvg, .line__art .canstack { height: 100%; width: auto; }
.line__art .canstack__i { margin-left: -1.1rem; }
.line__art .canstack__i:first-child { margin-left: 0; }
.line__name { font-weight: 800; font-size: 0.92rem; line-height: 1.3; }
.line__sub { font-size: var(--t-micro); color: var(--ink-3); font-weight: 700; }
.line__end { display: grid; gap: 0.4rem; justify-items: end; }
.line__price { font-weight: 800; font-size: 0.9rem; font-variant-numeric: tabular-nums; }
.line__rm {
  appearance: none; border: 0; background: none; cursor: pointer;
  font-size: var(--t-micro); color: var(--ink-3); text-decoration: underline;
  padding: 0.2rem;
}
.line__rm:hover { color: #C4331E; }

.empty { display: grid; gap: 1rem; justify-items: center; text-align: center; padding: 3rem 1rem; }
.empty .cansvg { height: 8rem; width: auto; opacity: 0.5; }

.drawer__foot { padding: 1.1rem var(--pad) calc(1.1rem + env(safe-area-inset-bottom)); border-top: 1px solid var(--line); display: grid; gap: 0.7rem; background: var(--card); }
.sum { display: grid; gap: 0.35rem; font-size: var(--t-small); }
.sum div { display: flex; justify-content: space-between; gap: 1rem; }
.sum div span:last-child { font-variant-numeric: tabular-nums; font-weight: 700; }
.sum .sum__total { font-size: 1.05rem; font-weight: 800; padding-top: 0.5rem; border-top: 1px solid var(--line); }
.sum .sum__total span:last-child { font-weight: 800; }

/* Checkout slides over the cart body rather than replacing the drawer, so the
   totals never leave the screen while the visitor is typing. */
.checkout { display: grid; gap: 0.8rem; }
.checkout__two { display: grid; grid-template-columns: 1fr 1fr; gap: 0.8rem; }
.notice {
  font-size: var(--t-small); line-height: 1.5;
  padding: 0.85rem 1rem; border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--flavour) 14%, var(--card));
  border: 1px solid color-mix(in srgb, var(--flavour) 34%, transparent);
}

/* — quick view — */
.qv {
  position: fixed; inset: 0; z-index: 110;
  display: grid; place-items: center;
  padding: 1rem;
  opacity: 0; visibility: hidden;
  transition: opacity 0.35s var(--ease), visibility 0.35s;
}
.qv.is-open { opacity: 1; visibility: visible; }
.qv__panel {
  position: relative;
  width: min(56rem, 100%);
  max-height: 90svh; max-height: 90vh;
  overflow-y: auto; overscroll-behavior: contain;
  background: var(--paper);
  border-radius: var(--radius);
  box-shadow: var(--shadow-3);
  display: grid;
  grid-template-columns: 1fr 1fr;
  transform: translateY(18px) scale(0.985);
  transition: transform 0.4s var(--ease);
}
.qv.is-open .qv__panel { transform: none; }
.qv__art {
  display: grid; place-items: center; padding: 2rem;
  border-radius: var(--radius) 0 0 var(--radius);
  background:
    radial-gradient(120% 70% at 50% 106%, color-mix(in srgb, var(--dot) 46%, transparent), transparent 70%),
    color-mix(in srgb, var(--dot) 11%, var(--paper));
}
.qv__art .cansvg { height: min(48vh, 380px); width: auto; filter: drop-shadow(0 20px 34px rgba(20, 18, 15, 0.22)); }
.qv__body { padding: clamp(1.4rem, 3vw, 2.4rem); display: grid; gap: 1rem; align-content: start; }
.qv__body h2 { font-size: clamp(1.6rem, 1.2rem + 1.6vw, 2.4rem); }
.qv__facts { display: flex; flex-wrap: wrap; gap: 0.4rem 1.4rem; font-size: var(--t-small); color: var(--ink-2); padding: 0.9rem 0; border-block: 1px solid var(--line); margin: 0; }
.qv__facts div { display: grid; }
.qv__facts dt { font-size: var(--t-micro); font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3); }
.qv__facts dd { margin: 0; font-weight: 700; color: var(--ink); }
.qv .iconbtn { position: absolute; top: 0.8rem; right: 0.8rem; z-index: 3; margin: 0; }

/* — toast — */
.toast {
  position: fixed; left: 50%; bottom: 1.5rem; z-index: 120;
  transform: translate(-50%, 150%);
  background: var(--ink); color: #fff;
  padding: 0.85rem 1.3rem; border-radius: var(--radius-pill);
  font-size: var(--t-small); font-weight: 700;
  box-shadow: var(--shadow-3);
  transition: transform 0.45s var(--ease);
  max-width: calc(100vw - 2rem);
}
.toast.is-on { transform: translate(-50%, 0); }

/* ── 8. loader ──────────────────────────────────────────────────────────── */

/* There is no "enter" button. The splash covers the first paint only — the
   moment the fonts are in and the first WebGL frame has been drawn, it lifts by
   itself. It exists so the visitor never sees an unstyled headline or a blank
   canvas, not to make them ask permission to see the shop. */
.boot {
  position: fixed; inset: 0; z-index: 150;
  display: grid; place-items: center; gap: 1rem;
  background: var(--paper);
  transition: opacity 0.7s var(--ease);
}
.boot.is-gone { opacity: 0; pointer-events: none; }
.boot__in { display: grid; justify-items: center; gap: 1.1rem; }
.boot__mark {
  font-family: var(--display); font-weight: 800;
  font-size: clamp(2.4rem, 8vw, 3.6rem); letter-spacing: -0.04em;
}
.boot__track { width: min(12rem, 40vw); height: 3px; border-radius: 2px; background: var(--line); overflow: hidden; }
.boot__bar { display: block; height: 100%; width: 0%; background: var(--ink); border-radius: 2px; }
.boot__note { font-size: var(--t-micro); font-weight: 800; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-3); }

/* ── 9. states ──────────────────────────────────────────────────────────── */

/* Scroll reveals. Scoped to .has-js so that without JavaScript the page is
   simply visible rather than permanently blank — a store that hides its whole
   catalogue behind a failed script is worse than one with no animation. */
.has-js .rv { opacity: 0; transform: translateY(18px); }
.has-js .rv.is-in { opacity: 1; transform: none; transition: opacity 0.75s var(--ease), transform 0.75s var(--ease); }
.wm { display: inline-block; overflow: hidden; vertical-align: bottom; }
.wi { display: inline-block; }
.has-js .hero h1 .wi { transform: translateY(105%); }

:where(a, button, input, textarea, select, [tabindex]):focus-visible {
  outline: 2.5px solid var(--ink);
  outline-offset: 3px;
  border-radius: 4px;
}

@media (max-width: 900px) {
  /* Shorter on a phone: the same sequence over the same number of viewports
     takes far more thumb-swipes than it does wheel-clicks. */
  .pour { height: 240vh; }
  /* The can now sits in a band across the TOP of the hero on a phone, so the
     grab area follows it up there. */
  .stage__pad { inset: 8% 0 46% 0; width: 100%; }

  /* Six flavour pills wrapped to three rows, plus three stacked packs, made
     the buy card 500 px tall — which on an 844 px screen left the can nowhere
     to be and pushed the price below the fold. One scrolling row of pills is
     the standard mobile pattern and gives back about 190 px. */
  .pills {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    /* Bleed to the card's edges so the row visibly runs off the side, which is
       what tells someone there is more to scroll to. */
    margin-inline: -1rem;
    padding-inline: 1rem;
    scroll-padding-inline: 1rem;
  }
  .pills::-webkit-scrollbar { display: none; }
  .pill { flex: none; scroll-snap-align: start; }

  /* Load-bearing. A grid or flex item's automatic minimum size is its
     MIN-CONTENT width, and the min-content width of a nowrap row of six pills
     is the whole unwrapped row — about 590 px. That propagates up through
     every ancestor grid and stretches the entire hero to 432 px inside a
     390 px screen, which is how one horizontal scroller ends up giving the
     whole page a horizontal scrollbar. `min-width: 0` opts out of that rule
     and lets the scroller actually scroll instead of pushing. */
  .hero__in, .hero__buy, .hero__in > *, .hero__buy > *, .pills { min-width: 0; }

  /* The shop section is the very next thing on the page; a button to reach it
     is not worth 70 px of the fold. Kept on desktop, where it costs nothing. */
  .hero__row .btn--ghost { display: none; }

  /* Clear the can's band at the top. */
  .hero .lede { font-size: 1rem; line-height: 1.5; }
  .hdr__nav { display: none; }
  .qv__panel { grid-template-columns: 1fr; }
  .qv__art { border-radius: var(--radius) var(--radius) 0 0; padding: 1.5rem; }
  .qv__art .cansvg { height: min(34vh, 260px); }
  .craft__in { margin-left: 0; }
  /* The card sits over the tail of the can, so it needs a near-solid ground to
     stay legible. */
  .hero__buy { background: color-mix(in srgb, var(--card) 94%, transparent); }
  /* The top padding is not decoration — it is the can's band. The camera puts
     the can in the upper part of the portrait frame (VIEWS.hero.shiftPortrait)
     and this is the matching hole in the type. Change one and the other has to
     move with it, or the headline lands on top of the product. */
  .hero {
    min-height: auto;
    padding-block: clamp(12rem, 33vh, 18rem) 0;
  }
  .craft { min-height: auto; }
}

@media (max-width: 560px) {
  .checkout__two { grid-template-columns: 1fr; }
  .drawer { width: 100vw; }
  .line { grid-template-columns: 3rem 1fr; grid-template-areas: 'art body' 'end end'; }
  .line__end { grid-area: end; justify-items: stretch; }
  .line__end { display: flex; align-items: center; justify-content: space-between; }
}

/* Honour the system setting rather than approximating it. Everything that
   moves on its own stops; nothing that conveys information disappears. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  /* The pour does not run, so its scroll track would be three viewports of
     nothing between the hero and the shop. Collapse it to the captions. */
  .pour { height: auto; }
  .pour__sticky { position: static; height: auto; padding-block: var(--band); }
  .pour__caps { max-width: none; }
  .pour__cap { grid-area: auto; opacity: 1; transform: none; }
  .pour__rail { display: none; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .has-js .rv { opacity: 1; transform: none; }
  .has-js .hero h1 .wi { transform: none; }
  .btn:hover, .card:hover, .bundle:hover { transform: none; }
}

/* Touch targets. On a coarse pointer everything interactive gets a real
   44 px minimum in both directions — that is the size of a fingertip, and a
   40 px "Add" button on a product card is the difference between buying and
   giving up. Scoped to `pointer: coarse` so a mouse still gets the tighter,
   better-proportioned desktop sizes. */
@media (pointer: coarse) {
  .btn, .pill, .pack, .cartbtn, .iconbtn { min-height: 44px; }
  .iconbtn { min-width: 44px; }
  .qty button { width: 44px; height: 44px; }
  .hdr__mark, .hdr__nav a { padding-block: 0.5rem; }
  .ftr__col a, .ftr__base a { padding-block: 0.72rem; }
  .line__rm { padding: 0.72rem 0.25rem; }
  /* Steppers live in a flex row and were being shrunk below their set width. */
  .qty button { flex: none; }
  .card__link { min-height: 44px; }
}

@media (prefers-contrast: more) {
  :root { --line: rgba(20, 18, 15, 0.35); --ink-2: #3A362F; --ink-3: #55504A; }
}
