/* ===== Sweet Sip — theme layer =====
   Loaded after style.css on every public page. Started as the reviewed design
   mockup (mockup/mockup.css): stroke SVG icons (js/icons.js), Outfit + Geist
   type, bento category grid, numbered values list, Emil Kowalski-style motion,
   flavour picker / price card, shop quantity stepper and phone bag bar. */

:root {
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
}

/* ---------- 2. Typography ---------- */

/* Outfit is geometric and wider than Fredoka — tighter tracking keeps headlines
   dense and deliberate instead of bubbly. */
h1, h2, h3 { letter-spacing: -0.025em; }
h1 { letter-spacing: -0.035em; line-height: 1.04; }
.hero h1 { font-weight: 600; }
.btn, .filter-btn, .price, .brand span { letter-spacing: -0.01em; }
body { line-height: 1.6; }

/* ---------- 1. Icons instead of emoji ---------- */

.product-card .emoji,
.modal-emoji,
.category-tile .tile-icon {
  color: var(--cocoa);
  background: var(--cream);
}

.product-card .emoji { width: 60px; height: 60px; border-radius: 18px; }
.product-card .emoji svg { width: 28px; height: 28px; }
.modal-emoji { border-radius: 32px; }
.modal-emoji svg { width: 52px; height: 52px; }

/* Category tint behind the icon — carries the pink/mint brand split into the
   cards without adding a new accent colour. */
[data-cat="drinks"] { background: rgba(168, 213, 186, 0.5) !important; }
[data-cat="sweets"] { background: rgba(244, 184, 196, 0.55) !important; }
[data-cat="combo"]  { background: rgba(217, 119, 6, 0.14) !important; }

.hero-can-fallback { color: var(--cocoa); }
.hero-can-fallback svg { width: 96px; height: 96px; }

/* ---------- 3a. Home: bento category grid (was 3 equal tiles) ---------- */

.category-tiles.bento {
  grid-template-columns: 1.35fr 1fr;
  grid-template-rows: 1fr 1fr;
}

.bento .category-tile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  text-align: left;
  padding: 28px;
}

.bento .category-tile p { margin: 0; color: rgba(var(--ink-rgb), 0.75); }

.bento .category-tile--hero {
  grid-row: span 2;
  min-height: 380px;
  justify-content: flex-end;
  background:
    radial-gradient(120% 90% at 100% 0%, rgba(168, 213, 186, 0.55), transparent 60%),
    var(--white);
}

.bento .category-tile--hero h3 { font-size: clamp(1.8rem, 3vw, 2.4rem); }

/* The hero tile is as tall as the two stacked tiles beside it, which left a
   blank middle. A big faint can outline fills it — tilted to echo the 3D can
   in the hero — and straightens a touch on hover. Decorative only. */
.tile-art {
  position: absolute;
  right: 32px;
  top: 44%;
  width: min(42%, 230px);
  color: var(--cocoa);
  opacity: 0.1;
  transform: translateY(-50%) rotate(-14deg);
  transition: transform 320ms var(--ease-out);
  pointer-events: none;
}

.tile-art svg { display: block; width: 100%; height: auto; }

@media (hover: hover) and (pointer: fine) {
  .category-tile--hero:hover .tile-art { transform: translateY(-50%) rotate(-8deg); }
}

/* Phones stack the tiles, so the hero tile is short and the art would sit
   behind its description — there's no empty middle to fill anyway. */
@media (max-width: 700px) {
  .tile-art { display: none; }
}

.tile-icon {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: 16px;
  margin-bottom: auto; /* pins the icon top, copy bottom */
}

.tile-icon svg { width: 26px; height: 26px; }
.category-tile--hero .tile-icon { width: 72px; height: 72px; border-radius: 22px; }
.category-tile--hero .tile-icon svg { width: 36px; height: 36px; }

.bento .category-tile:not(.category-tile--hero) .tile-icon { margin-bottom: 18px; }

.tile-cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  font: 600 0.95rem/1 var(--font-display);
  color: var(--cocoa);
}

.tile-cta svg {
  width: 16px;
  height: 16px;
  transition: transform 160ms var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .category-tile:hover .tile-cta svg { transform: translateX(3px); }
}

@media (max-width: 700px) {
  .category-tiles.bento { grid-template-columns: 1fr; grid-template-rows: none; }
  .bento .category-tile--hero { grid-row: auto; min-height: 260px; }
}

/* ---------- Idea 3: "Add to bag" beside the settled can ---------- */

/* Phones / static layout: sits centred under the can. */
.buy-chip {
  display: flex;
  align-items: center;
  gap: 12px;
  width: fit-content;
  margin: 12px auto 0;
  padding: 8px;
  border-radius: 22px;
  background: var(--white);
  box-shadow: 0 14px 34px rgba(var(--shade-rgb), 0.16);
  text-align: left;
  /* Hidden until the can settles. visibility (not just opacity) so the button
     can't be tabbed to while invisible; exit is faster than enter. */
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px) scale(0.98);
  transition:
    opacity 150ms var(--ease-out),
    transform 150ms var(--ease-out),
    visibility 0s linear 150ms;
}

.hero-can.is-settled ~ .buy-chip {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition:
    opacity 260ms var(--ease-out),
    transform 260ms var(--ease-out),
    visibility 0s;
}

/* Tall frame + tall crop (pipeline-punch-thumb.jpg): a can in a square thumb of
   the 4:3 photo came out tiny. */
.buy-chip-photo {
  width: 44px;
  height: 64px;
  border-radius: 12px;
  object-fit: cover;
  background: var(--cream);
}

.buy-chip-text { display: flex; flex-direction: column; gap: 2px; padding-right: 4px; }
.buy-chip-name { font: 600 0.98rem/1.2 var(--font-display); color: var(--cocoa); }
.buy-chip-price { font-size: 0.85rem; line-height: 1.2; color: rgba(var(--ink-rgb), 0.75); }
.buy-chip-btn { min-height: 44px; padding: 10px 18px; font-size: 0.9rem; }

/* Desktop scene: a price tag floating at the can's lower left, above the
   hint and credit rows. */
@media (min-width: 861px) and (prefers-reduced-motion: no-preference) {
  /* Bottom row, just left of the centred flavour dot bar (~296px wide): clear
     of the can's label and of the headline copy. Higher up it covered one or
     the other on 1035×549 / 1280×632 laptops. */
  .buy-chip {
    position: absolute;
    right: calc(50% + 160px);
    bottom: 10px;
    width: max-content; /* right-pinned abspos otherwise shrinks to the gap and wraps */
    margin: 0;
    z-index: 2;
  }
}

/* Narrower laptops: drop the thumbnail so the card stays clear of the buttons. */
@media (min-width: 861px) and (max-width: 1100px) and (prefers-reduced-motion: no-preference) {
  .buy-chip-photo { display: none; }
  .buy-chip { padding-left: 16px; }
}

/* ---------- Idea 4: swipeable trending row on phones ---------- */

@media (max-width: 760px) {
  #trending-grid {
    display: flex;
    gap: 16px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: 24px;
    margin-inline: -24px; /* bleed to the screen edges; the next card peeks in */
    padding: 16px 24px 24px; /* top room for the cards' scalloped edge */
    scrollbar-width: none;
  }

  #trending-grid::-webkit-scrollbar { display: none; }

  #trending-grid > .product-card,
  #trending-grid > .skeleton-card {
    flex: 0 0 78%;
    scroll-snap-align: start;
  }
}

/* ==================== Round 3: review fixes + ideas ==================== */

/* Headline: even line lengths — Outfit left "shelf" alone on the last line. */
.hero h1 { text-wrap: balance; }

/* © line: the global p { max-width: 62ch } pinned its centred text to a narrow
   box on the left (the live site has the same bug). */
.footer-bottom { max-width: none; }

/* 3D model credit moved from over the can into the footer (still visible, as
   CC BY requires). */
.footer-credit {
  /* Outside .container, so it brings its own gutter (it ran edge to edge on
     phones) and a readable measure (one ~1250px line on desktop). */
  max-width: 72ch;
  margin: 6px auto 0;
  padding-inline: 24px;
  text-wrap: balance;
  text-align: center;
  font-size: 0.78rem;
  color: rgba(var(--ink-rgb), 0.75);
}
.footer-credit a { text-decoration: underline; text-underline-offset: 2px; }

/* Hint wording follows the input: drag with a mouse, swipe on touch. */
.hint-touch { display: none; }
@media (hover: none) {
  .hint-mouse { display: none; }
  .hint-touch { display: inline; }
}

/* Swipe row: off-screen cards were waiting for a vertical-scroll reveal that a
   sideways swipe never triggers, then fading in mid-swipe. */
@media (max-width: 760px) {
  #trending-grid > .reveal { opacity: 1; transform: none; transition: none; }
}

/* Price card on the static (phone) layout: stack instead of cramming
   "Add to bag" and the price onto two wrapped lines each. */
@media (max-width: 860px) {
  .buy-chip {
    display: grid;
    grid-template-columns: 44px minmax(0, 1fr);
    column-gap: 12px;
    row-gap: 10px;
    align-items: center;
    width: min(100%, 340px);
    padding: 12px;
  }
  .buy-chip-btn { grid-column: 1 / -1; justify-content: center; }
}

/* Poster: a still render in exactly the phone hero's pose, painted instantly
   while three.js loads; the 3D canvas fades in over it. The desktop scene
   moves the can, so it keeps the icon fallback instead. */
.hero-can-poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
  transition: opacity 400ms var(--ease-out);
}
.hero-can.is-ready .hero-can-poster { opacity: 0; }
.hero-can-poster ~ .hero-can-fallback { display: none; }

.hero-can-load {
  position: absolute;
  bottom: 14%;
  z-index: 2;
  background: var(--white);
}

@media (min-width: 861px) and (prefers-reduced-motion: no-preference) {
  .hero-can-poster { display: none; }
  .hero-can-poster ~ .hero-can-fallback { display: inline; }
}

/* ---------- Flavour picker ---------- */

.flavour-picker {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 14px 0 0;
  /* Same settle-gated reveal as the price card, a beat later. */
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition:
    opacity 150ms var(--ease-out),
    transform 150ms var(--ease-out),
    visibility 0s linear 150ms;
}

.hero-can.is-settled ~ .flavour-picker {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition:
    opacity 260ms var(--ease-out) 60ms,
    transform 260ms var(--ease-out) 60ms,
    visibility 0s;
}

.flavour-picker-label {
  font: 600 0.85rem/1 var(--font-body);
  color: rgba(var(--ink-rgb), 0.75);
  margin-right: 2px;
}

.flavour-swatch {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  min-height: 44px;
  padding: 6px 14px 6px 8px;
  border: 2px solid transparent;
  border-radius: 999px;
  background: rgba(var(--surface-rgb), 0.88);
  color: var(--cocoa);
  font: 600 0.85rem/1.1 var(--font-body);
  cursor: pointer;
  transition: border-color 160ms var(--ease-out), transform 160ms var(--ease-out);
}

.flavour-swatch .dot {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--swatch);
  box-shadow: inset 0 0 0 1.5px rgba(var(--shade-rgb), 0.45);
}

.flavour-swatch[aria-pressed="true"] { border-color: var(--cocoa); }
.flavour-swatch:active { transform: scale(0.96); }

/* Phones: one sideways-scrolling row under the can. */
@media (max-width: 860px) {
  /* The row's full width counted toward the hero grid column's minimum,
     stretching the column (and the centred headline) past the screen edges. */
  .hero .container > * { min-width: 0; }
  .flavour-picker {
    overflow-x: auto;
    margin-inline: -24px;
    padding: 4px 24px;
    scroll-padding-inline: 24px; /* snap to the padded edge, not flush with the screen */
    scrollbar-width: none;
    scroll-snap-type: x proximity;
  }
  .flavour-picker::-webkit-scrollbar { display: none; }
  /* The label is a snap point too, or the row opens snapped past it. */
  .flavour-swatch, .flavour-picker-label { scroll-snap-align: start; }
}

/* Desktop scene: a compact bar of colour dots centred under the settled can.
   (A vertical list of named pills in the top-right corner covered most of the
   can on shorter laptop screens.) Names stay for tooltips and screen readers. */
@media (min-width: 861px) and (prefers-reduced-motion: no-preference) {
  .flavour-picker {
    position: absolute;
    left: 50%;
    bottom: 10px;
    z-index: 2;
    margin: 0;
    gap: 0;
    padding: 0 4px 0 16px;
    border-radius: 999px;
    background: rgba(var(--surface-rgb), 0.9);
    box-shadow: var(--shadow);
    transform: translate(-50%, 6px);
  }

  .hero-can.is-settled ~ .flavour-picker { transform: translate(-50%, 0); }

  .flavour-picker-label { margin-right: 6px; }

  .flavour-picker .flavour-swatch {
    width: 44px;
    padding: 0;
    justify-content: center;
    border-color: transparent;
    background: transparent;
  }

  .flavour-picker .flavour-name {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  /* Selection shown as a ring around the dot instead of the pill border. */
  .flavour-picker .flavour-swatch[aria-pressed="true"] .dot {
    box-shadow: 0 0 0 2px var(--white), 0 0 0 4px var(--cocoa);
  }

  /* Make room: hint sits between the dot bar and the price card. */
  .hero-can-hint { bottom: 66px; }
}

/* ---------- Shop: quieter Add + quantity stepper ---------- */

/* One prominent action per view: eight filled, glowing pink buttons competed.
   Pink now belongs to the bag and checkout. */
.product-card .price-row .add-to-cart.btn-primary {
  background: transparent;
  color: var(--cocoa);
  border: 2px solid var(--cocoa);
  box-shadow: none;
}

@media (hover: hover) and (pointer: fine) {
  .product-card .price-row .add-to-cart.btn-primary:not(:disabled):hover {
    background: var(--cocoa);
    color: var(--cream);
  }
}

.qty-stepper {
  display: inline-flex;
  align-items: center;
  border-radius: 999px;
  background: var(--cocoa);
  color: var(--cream);
}

.qty-stepper.is-new { animation: stepperIn 220ms var(--ease-out); }

@keyframes stepperIn {
  from { opacity: 0; transform: scale(0.92); }
  to { opacity: 1; transform: none; }
}

.qty-step {
  width: 44px;
  height: 44px;
  border: none;
  border-radius: 999px;
  background: transparent;
  color: inherit;
  font: 600 1.2rem/1 var(--font-display);
  cursor: pointer;
  transition: background-color 160ms var(--ease-out), transform 120ms var(--ease-out);
}

.qty-step:active { transform: scale(0.9); }
.qty-step:disabled { opacity: 0.35; cursor: not-allowed; }
.qty-step:focus-visible { outline: 3px solid var(--pink-text); outline-offset: 2px; }

@media (hover: hover) and (pointer: fine) {
  .qty-step:not(:disabled):hover { background: rgba(var(--paper-rgb), 0.14); }
}

.qty-count {
  min-width: 22px;
  text-align: center;
  font: 600 1rem/1 var(--font-display);
  font-variant-numeric: tabular-nums;
}

/* Phones: filters in one sideways-scrolling row (they wrapped, leaving
   "Combo Packs" alone on a second line). */
@media (max-width: 760px) {
  .filter-bar {
    flex-wrap: nowrap;
    overflow-x: auto;
    margin-inline: -24px;
    padding: 6px 24px;
    scroll-padding-inline: 24px;
    scrollbar-width: none;
    scroll-snap-type: x proximity;
  }
  .filter-bar::-webkit-scrollbar { display: none; }
  .filter-btn { flex: 0 0 auto; scroll-snap-align: start; }
}

/* ---------- Mini bag bar (phones) ---------- */

.bag-bar { display: none; }

@media (max-width: 760px) {
  .bag-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    position: sticky;
    bottom: calc(96px + env(safe-area-inset-bottom));
    z-index: 49;
    min-height: 56px;
    margin: 0 16px 8px;
    padding: 8px 8px 8px 18px;
    border-radius: 20px;
    background: var(--cocoa);
    color: var(--cream);
    font: 600 0.9rem/1.2 var(--font-body);
    box-shadow: 0 12px 28px rgba(var(--shade-rgb), 0.28);
    transition: transform 260ms var(--ease-out), opacity 260ms var(--ease-out);
  }

  @starting-style {
    .bag-bar { transform: translateY(12px); opacity: 0; }
  }

  .bag-bar:active { transform: scale(0.98); }

  .bag-bar-cta {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 40px;
    padding: 0 14px;
    border-radius: 14px;
    background: var(--accent);
    color: var(--on-accent);
    font-family: var(--font-display);
  }

}

/* ---------- Category tile photos (once uploaded) ---------- */

.tile-photo {
  width: 72px;
  height: 72px;
  border-radius: 18px;
  object-fit: cover;
  margin-bottom: 18px;
}

.category-tile--hero .tile-photo {
  position: absolute;
  right: 28px;
  top: 28px;
  width: min(44%, 240px);
  height: auto;
  aspect-ratio: 3 / 4;
  margin: 0;
}

/* ---------- 3b. About: values list (was 3 equal cards; the 01/02/03 column
   was dropped — the values aren't a sequence) ---------- */

.values-list { margin-top: 12px; }

.value-row {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.3fr);
  gap: 32px;
  align-items: baseline;
  padding: 30px 0;
  border-top: 1px solid rgba(var(--ink-rgb), 0.16);
}

.value-row:last-child { border-bottom: 1px solid rgba(var(--ink-rgb), 0.16); }

.value-row h3 { margin: 0; font-size: 1.45rem; }
.value-row p { margin: 0; }

@media (max-width: 700px) {
  .value-row { grid-template-columns: 1fr; gap: 8px; padding: 24px 0; }
}

/* ---------- Motion (Emil Kowalski) ---------- */

/* Smaller travel + strong ease-out: arrives fast, settles gently. 28px over a
   plain 0.6s `ease` felt floaty. */
.reveal {
  transform: translateY(12px);
  transition: opacity 500ms var(--ease-out), transform 500ms var(--ease-out);
}

.btn { transition: transform 160ms var(--ease-out), box-shadow 160ms var(--ease-out); }

.product-card,
.category-tile,
.value-card {
  transition: transform 200ms var(--ease-out), box-shadow 200ms var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .product-card:hover { transform: translateY(-4px); }
  .category-tile:hover { transform: translateY(-3px); box-shadow: 0 16px 34px rgba(var(--shade-rgb), 0.14); }
}

/* Modal: never from nothing (0.96, not 0.92 + a 12px drop), under 300ms on the
   way in, and faster on the way out than in. */
.modal-overlay { transition: opacity 200ms var(--ease-out); }
.modal-card {
  transform: scale(0.96);
  transition: transform 240ms var(--ease-out), opacity 200ms var(--ease-out);
}
.modal-overlay:not(.open),
.modal-overlay:not(.open) .modal-card { transition-duration: 140ms; }
.modal-overlay.open .modal-card { transform: scale(1); }

/* ---------- Quick view on short screens ----------
   A phone in landscape is ~390px tall: the 120px visual pushed "Add to cart"
   below the fold of the dialog with no hint to scroll. Compact it there. */
@media (max-height: 500px) {
  .modal-overlay { padding: 12px; }
  .modal-card { padding: 18px 24px; max-height: calc(100vh - 24px); }
  .modal-emoji { width: 64px; height: 64px; margin-bottom: 10px; border-radius: 20px; }
  .modal-emoji svg { width: 30px; height: 30px; }
  .modal-emoji.has-photo { width: auto; height: 28vh; }
  .modal-card h2 { font-size: 1.35rem; margin-bottom: 4px; }
  .modal-card .modal-desc { margin-bottom: 8px; }
}

/* ---------- Footer ----------
   The brand blurb takes the flexible space and wraps inside it, so the two
   link columns stay side by side (at 844px landscape "Get in touch" used to
   drop onto its own row under the blurb). */
.site-footer .container > div:first-child { flex: 1 1 280px; max-width: 460px; }

/* ---------- Motion springs ----------
   When Motion has loaded (html.has-motion, set by its script's onload), it
   owns these transforms (js/main.js window.spring); CSS keeps only opacity. */
.has-motion .island { transition: opacity 240ms var(--ease-out); }
.has-motion .modal-card { transition: opacity 200ms var(--ease-out); }
.has-motion .qty-stepper.is-new { animation: none; }

/* ---------- Page background ----------
   The brand's diagonal pink→mint as a soft blend (style.css still has the old
   hard 48/52 split for admin.html). On a fixed, pointer-events-none layer
   rather than `background-attachment: fixed`, which iOS Safari ignores.
   Static on purpose — it's on every screen, so no motion. */

body { background: var(--bg-b); } /* behind the fixed layer, e.g. overscroll */

body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /* 80% of the pastel strength so muted text clears 4.5:1 on both ends
     (4.69 / 4.83); stops at 34/66% give a soft blend instead of a seam. */
  background: linear-gradient(135deg, var(--bg-a) 0%, var(--bg-a) 34%, var(--bg-b) 66%, var(--bg-b) 100%);
}
:root { --bg-a: #f6c5cd; --bg-b: #b9dcc5; }

/* The one loud spot: a pink glow behind the can. */
.hero-stage {
  background: radial-gradient(38vmax 34vmax at 50% 72%, rgba(236, 102, 116, 0.22), transparent 70%);
}
@media (min-width: 861px) {
  .hero-stage {
    background: radial-gradient(34vmax 38vh at 76% 58%, rgba(236, 102, 116, 0.22), transparent 70%);
  }
}


/* ---------- Dark mode ("dark chocolate") ----------
   Follows the OS setting (Apple HIG) — no toggle. A deep cocoa base, not
   near-black; cards get LIGHTER than the page, never darker; pastel accents
   turn into lighter text tints. Every pair measured: ink 15.9:1 on page,
   muted (0.75) 8.4:1 on cards, pink text 7.8:1, stock badges ≥6.3:1, white on
   the pink button 5.1:1. Only tokens change — the light rules above resolve
   through them — plus the few surfaces that must not simply invert.
   No transition on the switch: it's a system event, not an interaction. */

:root {
  color-scheme: light dark;   /* native scrollbars, form controls, autofill */
  accent-color: var(--accent); /* checkboxes / radios */
}
::selection { background: rgba(232, 99, 127, 0.28); color: var(--cocoa); }
input, textarea { caret-color: var(--accent); }

@media (prefers-color-scheme: dark) {
  :root {
    --cream: #1a1210;          /* page */
    --white: #2d221d;          /* cards, inputs' parent surfaces */
    --cocoa: #f6ece4;          /* primary text, outline buttons */
    --pink-text: #ff94ad;
    --mint-text: #7fcfa3;
    --warn-text: #f2b35e;
    --danger-text: #ff9494;

    --ink-rgb: 246, 236, 228;
    --shade-rgb: 0, 0, 0;
    --paper-rgb: 26, 18, 16;
    --surface-rgb: 45, 34, 29;
    --sheen: rgba(255, 255, 255, 0.05);
    --highlight: rgba(255, 255, 255, 0.07);
    /* Shadows vanish on dark, so cards carry a hairline edge instead. */
    --shadow: 0 0 0 1px rgba(246, 236, 228, 0.07), 0 12px 32px rgba(0, 0, 0, 0.35);
  }

  /* Same diagonal, as pink- and mint-tinted cocoa (ink ≥14:1 on both). */
  :root { --bg-a: #2a1519; --bg-b: #12201a; }
  .hero-stage {
    background: radial-gradient(38vmax 34vmax at 50% 72%, rgba(236, 102, 116, 0.2), transparent 70%);
  }

  /* Category icon tiles: pastel at light-mode alpha turned dusty mauve with a
     low-contrast icon. Deep tint + matching text-tint icon (same pairs as the
     stock badges, ≥6.3:1). */
  [data-cat="drinks"] { background: rgba(111, 184, 143, 0.2) !important; color: var(--mint-text); }
  [data-cat="sweets"] { background: rgba(232, 99, 127, 0.2) !important; color: var(--pink-text); }
  [data-cat="combo"]  { background: rgba(217, 119, 6, 0.2) !important; color: var(--warn-text); }

  /* Drinks hero tile: the 0.55 pastel wash went olive over brown. */
  .bento .category-tile--hero {
    background:
      radial-gradient(120% 90% at 100% 0%, rgba(111, 184, 143, 0.14), transparent 60%),
      var(--white);
  }

  /* The confirmation island stays a dark pill in both themes, like the real one. */
  .island {
    background: #0d0907;
    color: #f6ece4;
    box-shadow: 0 0 0 1px rgba(246, 236, 228, 0.12), 0 14px 34px rgba(0, 0, 0, 0.5);
  }

  /* Logo tile is a light pastel square — a soft edge keeps it from floating. */
  .brand img { box-shadow: 0 0 0 1px rgba(246, 236, 228, 0.1); }
}
@media (prefers-color-scheme: dark) and (min-width: 861px) {
  .hero-stage {
    background: radial-gradient(34vmax 38vh at 76% 58%, rgba(236, 102, 116, 0.2), transparent 70%);
  }
}

/* ---------- Review fixes (2026-09-17) ---------- */

/* Skip link: first Tab stop, visible only when focused. */
.skip-link {
  position: absolute;
  left: 16px;
  top: -64px;
  z-index: 200;
  padding: 12px 18px;
  border-radius: 12px;
  background: var(--cocoa);
  color: var(--cream);
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus { top: 12px; }
main:focus { outline: none; }

/* Empty bag / nothing saved: a real card with the next step in it. */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 24px 8px;
  text-align: center;
}
.empty-state p { margin: 0; color: rgba(var(--ink-rgb), 0.75); }
.product-grid > .empty-state {
  grid-column: 1 / -1;
  padding: 40px 24px;
  border-radius: var(--radius);
  background: var(--white);
  box-shadow: var(--shadow);
}
.empty-icon {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: rgba(232, 99, 127, 0.14);
  color: var(--pink-text);
}

/* Hero price card: sold out is a status plus the save heart, not a faded button. */
.buy-chip-status { display: flex; align-items: center; gap: 4px; }
.buy-chip-status .stock-badge { margin: 0; }
.buy-chip-fav .fav-btn { position: static; }
@media (max-width: 860px) {
  .buy-chip-status { grid-column: 1 / -1; justify-content: space-between; }
}
@media (min-width: 861px) and (prefers-reduced-motion: no-preference) {
  /* A fixed floor so the card's left edge doesn't jump between flavours. */
  /* Fits "Pipeline Punch" + either action. The dot bar names the flavour and has
     four dots (~359px wide at its longest, "Pipeline Punch"), so the card clears
     its half-width (180px) plus a 16px gap. A fifth flavour adds 44px: re-check. */
  .buy-chip { min-width: 340px; right: calc(50% + 196px); }
  .buy-chip-text { flex: 1; }
}
@media (min-width: 861px) and (max-width: 1100px) and (prefers-reduced-motion: no-preference) {
  .buy-chip { min-width: 280px; } /* no thumbnail at these widths */
}

/* The chosen flavour's name beside "Flavour" (desktop shows dots only). */
.flavour-picker-label { white-space: nowrap; }
.flavour-current { margin-left: 6px; color: var(--cocoa); }
.flavour-current::before { content: "· "; color: rgba(var(--ink-rgb), 0.75); }
@media (max-width: 860px), (prefers-reduced-motion: reduce) {
  .flavour-current { display: none; }
}

/* Phones: the bag bar tucks away while scrolling down (bag-bar.js), so it
   and the tab bar don't hold ~18% of the screen the whole time. */
@media (max-width: 760px) {
  .bag-bar.is-tucked { transform: translateY(calc(100% + 16px)); opacity: 0; pointer-events: none; }
}

/* About: one centred reading column; the values' heading column was wide
   enough to leave a ~280px gap before each description. */
.about-page > .container { max-width: 820px; }
.about-page .value-row { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: 24px; }
@media (max-width: 700px) {
  .about-page .value-row { grid-template-columns: 1fr; gap: 8px; }
}

/* Increased contrast variants for both appearances (Apple HIG). */
@media (prefers-contrast: more) {
  :root { --cocoa: #24170f; --ink-rgb: 36, 23, 15; }
}
@media (prefers-contrast: more) and (prefers-color-scheme: dark) {
  :root { --cocoa: #ffffff; --ink-rgb: 255, 255, 255; --cream: #110b09; }
}

/* Apple HIG: Increase Contrast / Reduce Transparency → plain cream, so every
   text colour gets its full on-cream ratio (pink text 4.9:1).
   Last in the file so it also beats the dark-mode background rules. */
@media (prefers-contrast: more), (prefers-reduced-transparency: reduce) {
  body { background: var(--cream); }
  body::before { content: none; }
  .hero-stage { background: none; }
}

/* ---------- Policy pages, policy links, consent (2026-09-18) ---------- */

.legal { max-width: 780px; padding-top: 40px; padding-bottom: 96px; }
.legal-updated { margin: 6px 0 18px; font-size: 0.9rem; color: rgba(var(--ink-rgb), 0.75); }
.legal h2 {
  margin: 44px 0 12px;
  font-size: 1.35rem;
  scroll-margin-top: calc(var(--header-h, 70px) + 16px);
}
.legal h3 { margin: 22px 0 8px; font-size: 1.05rem; }
.legal p, .legal li { line-height: 1.65; }
.legal ul, .legal ol { margin: 0 0 14px; padding-left: 1.3em; }
.legal li + li { margin-top: 8px; }
.legal li::marker { color: rgba(var(--ink-rgb), 0.6); }
.legal a { text-decoration: underline; text-underline-offset: 2px; }

/* Refund summary: a card, scrolls sideways on narrow phones rather than squashing. */
.legal-table {
  margin: 12px 0 8px;
  overflow-x: auto;
  border-radius: 18px;
  background: var(--white);
  box-shadow: var(--shadow);
}

.legal-table table { width: 100%; min-width: 520px; border-collapse: collapse; font-size: 0.95rem; }
.legal-table th, .legal-table td {
  padding: 12px 16px;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid rgba(var(--ink-rgb), 0.1);
}
.legal-table th { font-family: var(--font-display); font-weight: 600; }
.legal-table tr:last-child td { border-bottom: 0; }

/* Footer policy links: 44px targets on every device. */
.footer-legal {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0 8px;
  margin-top: 28px;
}
.footer-legal a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 10px;
  font-size: 0.9rem;
  color: rgba(var(--ink-rgb), 0.8);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: rgba(var(--ink-rgb), 0.3);
}
@media (hover: hover) and (pointer: fine) {
  .footer-legal a:hover { color: var(--cocoa); text-decoration-color: currentColor; }
}
.footer-legal + .footer-bottom { margin-top: 8px; }

/* Quick view: caffeine + allergen advice under the actions. */
.modal-note {
  max-width: 44ch;
  margin: 18px auto 0;
  font-size: 0.82rem;
  line-height: 1.5;
  color: rgba(var(--ink-rgb), 0.75);
}
.modal-note:empty { display: none; }

/* Phones: each refund row becomes a small labelled block, so "What we do" is never
   scrolled off-screen. Same pattern as the admin tables. */
@media (max-width: 600px) {
  .legal-table { overflow: visible; }
  .legal-table table { min-width: 0; }
  .legal-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .legal-table tr { display: block; padding: 12px 16px; border-bottom: 1px solid rgba(var(--ink-rgb), 0.1); }
  .legal-table tr:last-child { border-bottom: 0; }
  .legal-table td { display: block; padding: 2px 0; border: 0; }
  .legal-table td:first-child { font-family: var(--font-display); font-weight: 600; margin-bottom: 2px; }
  .legal-table td:not(:first-child)::before {
    content: attr(data-label) ": ";
    color: rgba(var(--ink-rgb), 0.75);
  }
}

/* ---------- Reviews (2026-09-19) ----------
   Stars are decorative: the rating is always in text beside them (visible or
   sr-only), so nothing depends on colour alone. Filled stars use the graphic
   pink (3:1 on either theme's surface); unfilled ones are a faint ink tint.
   Content layer, so plain surfaces and hairlines, no glass. */
.stars { display: inline-flex; gap: 2px; vertical-align: -2px; }
.stars svg { display: block; }
.stars .on { color: var(--pink-deep); }
.stars .off,
.stars .half { color: rgba(var(--ink-rgb), 0.18); }
.stars .half { position: relative; }
.stars .half svg + svg { position: absolute; inset: 0; color: var(--pink-deep); clip-path: inset(0 50% 0 0); }

.product-card .card-rating {
  display: flex;
  align-items: center;
  gap: 5px;
  margin: -4px 0 0;
  font-size: 0.85rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.card-rating svg { color: var(--pink-deep); flex: none; }
.card-rating-count { font-weight: 400; color: rgba(var(--ink-rgb), 0.75); }

.modal-reviews {
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid rgba(var(--ink-rgb), 0.1);
  text-align: left;
}
.modal-reviews h3 { font-size: 1.05rem; margin: 0 0 10px; }

.review-summary {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 8px;
  margin: 0 0 8px;
  font-variant-numeric: tabular-nums;
}
.review-avg { font-family: var(--font-display); font-weight: 600; font-size: 1.25rem; line-height: 1; }
.review-count,
.review-summary.is-empty { color: rgba(var(--ink-rgb), 0.75); font-size: 0.94rem; }

.review-list { list-style: none; margin: 0; padding: 0; }
.review {
  padding: 14px 0;
  border-top: 1px solid rgba(var(--ink-rgb), 0.08);
  /* Arrives once per load, 40ms apart: short enough never to hold anyone up. */
  animation: reviewIn 260ms var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 40ms);
}
.review:first-child { border-top: 0; }
@keyframes reviewIn { from { opacity: 0; transform: translateY(6px); } }

.review-head { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px; }
.review-name { font-weight: 600; overflow-wrap: anywhere; }
.review time { margin-left: auto; font-size: 0.85rem; color: rgba(var(--ink-rgb), 0.75); }
.review-body {
  margin: 6px 0 0;
  color: rgba(var(--ink-rgb), 0.85);
  white-space: pre-line;
  overflow-wrap: anywhere;
}
/* The quick view is a preview: long reviews stop at four lines there. */
.modal-reviews .review-body {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.review-links { display: flex; flex-wrap: wrap; gap: 0 20px; margin: 4px 0 0; }
.review-links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--pink-text);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
@media (hover: hover) and (pointer: fine) {
  .review-links a:hover { text-decoration-thickness: 2px; }
}

/* Loading: three bars shaped like a summary and one review. */
.review-skeleton { position: relative; display: grid; gap: 10px; padding: 6px 0 14px; overflow: hidden; }
.review-skeleton span { height: 12px; border-radius: 6px; background: rgba(var(--ink-rgb), 0.07); }
.review-skeleton span:nth-child(1) { width: 42%; height: 18px; }
.review-skeleton span:nth-child(2) { width: 92%; }
.review-skeleton span:nth-child(3) { width: 68%; }
.review-skeleton::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 20%, var(--sheen) 50%, transparent 80%);
  transform: translateX(-100%);
  animation: skeletonShimmer 1.4s linear infinite;
}

/* reviews.html: product header, then the list beside the form (stacked
   below 960px, where the form follows the list; "Write a review" links
   jump to it with #write). */
.review-page { padding-top: 24px; padding-bottom: 88px; }
.back-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-weight: 600;
  color: rgba(var(--ink-rgb), 0.75);
  text-decoration: none;
}
@media (hover: hover) and (pointer: fine) {
  .back-link:hover { color: var(--cocoa); text-decoration: underline; text-underline-offset: 3px; }
}

.review-product { display: flex; align-items: center; gap: 20px; margin: 8px 0 36px; }
.review-product h1 { font-size: clamp(1.7rem, 3.4vw, 2.4rem); margin: 0 0 6px; line-height: 1.1; }
.review-product .review-summary { margin: 0; }
.review-product-visual { flex: none; width: 96px; height: 96px; }
.review-product-visual .emoji,
.review-product-visual .product-photo {
  width: 96px;
  height: 96px;
  margin: 0;
  border-radius: 28px;
  display: grid;
  place-items: center;
  color: var(--cocoa);
  background: var(--cream);
  object-fit: cover;
}
.review-product-visual .emoji svg { width: 44px; height: 44px; }
.review-product-skeleton { border-radius: 28px; background: rgba(var(--ink-rgb), 0.07); }

.review-page-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 380px);
  gap: 48px;
  align-items: start;
}
@media (max-width: 960px) {
  .review-page-layout { grid-template-columns: minmax(0, 1fr); gap: 32px; }
}
@media (max-width: 560px) {
  .review-product { gap: 16px; margin-bottom: 28px; }
  .review-product-visual,
  .review-product-visual .emoji,
  .review-product-visual .product-photo { width: 72px; height: 72px; border-radius: 22px; }
  .review-product-visual .emoji svg { width: 34px; height: 34px; }
}

.review-all h2 { font-size: 1.25rem; margin: 0 0 6px; }
.review-all .review-list { max-width: 65ch; }
.review-empty { padding: 8px 0; color: rgba(var(--ink-rgb), 0.75); }
.review-empty p { margin: 0 0 4px; }
.review-empty strong { color: var(--cocoa); }

/* The form: label above, help and errors below, 8px inside a field group. */
.review-form-panel { scroll-margin-top: 90px; }
.review-form-panel:focus { outline: none; }
.review-form-intro { margin: -6px 0 0; color: rgba(var(--ink-rgb), 0.75); font-size: 0.94rem; }
.review-form-panel .checkout-form { gap: 8px; }
.review-form-panel .checkout-form > label { margin-top: 10px; }
.review-form-panel .turnstile-wrap { margin-top: 14px; }
.optional { font-weight: 400; color: rgba(var(--ink-rgb), 0.75); }
.field-help { margin: 0; font-size: 0.85rem; color: rgba(var(--ink-rgb), 0.75); }
.field-error { margin: 2px 0 0; font-size: 0.85rem; font-weight: 600; color: var(--danger-text); }

.star-input { border: 0; margin: 0; padding: 0; min-width: 0; }
.star-input legend { padding: 0; margin-bottom: 2px; font-size: 0.85rem; font-weight: 700; }
.star-input-row { display: flex; align-items: center; flex-wrap: wrap; margin-left: -6px; }
.checkout-form .star-input label {
  position: relative;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  cursor: pointer;
  color: rgba(var(--ink-rgb), 0.55); /* unselected stars are a control: ≥3:1 on the panel */
}
/* The real radio covers its star: native click, keyboard and focus. */
.star-input input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
.star-input svg { width: 32px; height: 32px; transition: transform 160ms var(--ease-out), color 120ms ease; }
.star-input label:active svg { transform: scale(0.86); }
.star-input label:has(input:focus-visible) { outline: 3px solid var(--pink-text); outline-offset: -3px; }
.star-input-row[data-show="1"] label:nth-of-type(-n+1),
.star-input-row[data-show="2"] label:nth-of-type(-n+2),
.star-input-row[data-show="3"] label:nth-of-type(-n+3),
.star-input-row[data-show="4"] label:nth-of-type(-n+4),
.star-input-row[data-show="5"] label:nth-of-type(-n+5) { color: var(--pink-deep); }
.star-caption { margin-left: 8px; font-size: 0.94rem; font-weight: 600; color: rgba(var(--ink-rgb), 0.75); }

.review-thanks { display: grid; justify-items: center; gap: 10px; padding: 8px 0 4px; text-align: center; }
.review-thanks h2 { margin: 0; font-size: 1.25rem; }
.review-thanks h2:focus { outline: none; }
.review-thanks p { margin: 0 0 6px; color: rgba(var(--ink-rgb), 0.75); }
.review-thanks-icon {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--mint-text);
  background: rgba(111, 184, 143, 0.18);
  animation: reviewIn 260ms var(--ease-out) both;
}

.review-rules { margin-top: 24px; padding-top: 18px; border-top: 1px solid rgba(var(--ink-rgb), 0.1); }
.review-rules h3 { margin: 0 0 8px; font-size: 0.94rem; }
.review-rules ul { margin: 0; padding-left: 18px; display: grid; gap: 6px; font-size: 0.85rem; color: rgba(var(--ink-rgb), 0.75); }
.review-all { padding: 0; }
.review-form-panel .checkout-form > label { display: block; }
