/* HOOGA Scents — public catalogue.
   Built to the design team's Aug 2026 mock-up deck (Product Catalogue_2.pdf):
   browse screens on pure black with one light stadium pill or image card per
   item; product screens on a soft grey field with a near-white card. Lato
   throughout, per Alex. Mobile-first; on a wide screen the app runs as a
   centred phone-width column.

   Two grounds, and every colour belongs to one of them:
     · black  — splash, category, product-type and series screens
     · light  — the product detail screen
   Anything that sets a colour takes it from that ground's tokens. */

:root {
  /* Say what each screen already is. Samsung Internet's "Dark website" and
     Chrome's auto-dark re-tint a page only while they think it has no dark
     styling of its own; both stand down once it declares one. `only light`
     was the wrong claim to make — the browse screens are black — and Samsung
     ignored it. The browse screens are dark and the product screens are
     light, and `html.light` already marks which is showing. */
  color-scheme: dark;

  /* black ground */
  --black: #000000;
  --face: #e8e8e8;          /* every pill and card on black */
  --face-ink: #000000;
  --on-black: #ffffff;
  --on-black-soft: #8f8f8f;

  /* light ground (product detail) */
  --card: #ececec;          /* the panel the content sits on */
  --well: #ededed;          /* the selector container */
  --ink: #141414;
  --ink-soft: #6d6d6d;
  --rule: rgba(0, 0, 0, .12);
  --pill-off: #b9b9b9;      /* an option you have not chosen */
  --pill-off-ink: #ffffff;
  --pill-on: #000000;
  --pill-on-ink: #ffffff;

  /* The deck's product screens sit on a brushed silver field. This gradient
     stands in until the design team send the artwork — swapping it for
     `url(...)` is the only change needed. */
  --ground: radial-gradient(118% 74% at 50% 0%,
              #f2f2f2 0%, #dcdcdc 34%, #c2c2c2 68%, #ababab 100%);

  /* Scent-family chips, sampled from the deck itself (p5, p9, p11) rather
     than matched by eye. Every chip carries dark type there — the earlier
     white-on-colour made three of the six read as a different kind of thing. */
  --chip-ink: #2a2a2a;
  --spicy: #9fa4a9;
  --floral: #d99c98;
  --nature: #6098af;
  --citrus: #f3ce63;
  --woody: #ac8545;
  --fruity: #749760;
  /* Earth Series' own four, given by the design team (Kevin, 27 Aug) rather
     than sampled — the deck does not draw this range. */
  --sea: #8ea7b4;
  --rainforest: #d5c2ad;
  --orchard: #89a19b;
  --highland: #b4b7ba;
  /* Fine Fragrance's own four (p5) */
  --clear-reflection: #abaea7;
  --warm-embrace: #d9ad89;
  --golden-escape: #e7c5b9;
  --deep-echoes: #9b8d7c;

  /* The field's own top edge, measured: what the status bar and any overscroll
     show on a product screen, rather than black. Safari paints both its bars
     this one colour, and the field runs from #d8d8d8 at the top to #6a6a6a at
     the bottom — so the foot of the field is faded into it below, or the
     bottom bar meets dark artwork with a hard edge. */
  --ground-top: #d8d8d8;

  --radius: 26px;
  --pill: 999px;
}

/* The same values again under the dark preference. Nothing changes — the
   browse screens are always black — but a page carrying dark rules is how a
   browser recognises one that handles dark itself, and some check for this
   rather than for `color-scheme`. Deliberately a no-op. */
@media (prefers-color-scheme: dark) {
  :root { --black: #000000; --face: #e8e8e8; --face-ink: #000000; }
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html {
  -webkit-text-size-adjust: 100%;
  background: var(--black);        /* paint the canvas itself, so nothing pale
                                      shows while a screen swaps in */
  scrollbar-color: #333 transparent;
}
/* Set while a light screen is showing, so rubber-banding past either end of a
   product page reveals the field's colour rather than black. */
html.light { color-scheme: light; }
html.light, html.light body { background: var(--ground-top); }
html.light { scrollbar-color: #9a9a9a transparent; }

body {
  background: var(--black);
  font-family: 'Lato', 'Helvetica Neue', Arial, sans-serif;
  font-weight: 400;
  color: var(--ink);
  min-height: 100dvh;
  display: flex;
  justify-content: center;
}

.phone {
  width: 100%;
  max-width: 520px;
  min-height: 100dvh;
  background: var(--black);
  position: relative;
  /* `clip`, not `hidden`: `hidden` would make this a scroll container and the
     sticky header would scroll away with the page instead of holding. */
  overflow-x: clip;
}
.boot { padding: 40vh 24px 0; text-align: center; color: var(--on-black-soft); letter-spacing: 2px; }

/* ── Screen chrome ────────────────────────────────────────────────────────── */
.screen { min-height: 100dvh; display: flex; flex-direction: column; position: relative; }

/* The per-series brush artwork belonged to the beige design; the new deck puts
   product screens on the grey field alone. The Portal still stores a bg_image
   per series — it simply goes unused until someone retires the field. */
.bg-art { display: none; }

.topbar {
  display: flex; align-items: center; justify-content: space-between;
  padding: max(18px, env(safe-area-inset-top)) 18px 10px;
  min-height: 74px;
  /* Holds on long pages — a 24-scent list runs well past a screen — so back,
     home and the product's name stay reachable. Opaque, so content passes
     behind the bar rather than through the title. */
  position: sticky; top: 0; z-index: 20;
  background: var(--ground-fill, var(--black));
}
.phone.dark .topbar { background: var(--black); }

.round-btn {
  width: 44px; height: 44px; border-radius: 50%;
  border: none; cursor: pointer; flex: none;
  background: rgba(0, 0, 0, .06); color: var(--ink);
  display: flex; align-items: center; justify-content: center;
  text-decoration: none;
}
.round-btn svg { width: 21px; height: 21px; display: block; }
.phone.dark .round-btn { background: rgba(255, 255, 255, .22); color: var(--on-black); }
.round-btn:active { transform: scale(.94); }
.topbar .spacer { width: 44px; }

.page-heading { text-align: center; flex: 1; padding: 0 6px; color: var(--ink); }
.phone.dark .page-heading { color: var(--on-black); }
.page-heading .kicker {
  font-size: 12px; letter-spacing: 3.5px; font-weight: 700; text-transform: uppercase;
}
.page-heading h1 {
  font-size: 26px; letter-spacing: 2px; font-weight: 900;
  text-transform: uppercase; margin-top: 2px; line-height: 1.15;
}

/* ── Splash (deck p1) ─────────────────────────────────────────────────────── */
.splash {
  background: var(--black) center/cover no-repeat;
  min-height: 100dvh;
  display: flex; align-items: stretch; position: relative;
}
.splash::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(0,0,0,.10) 0%, rgba(0,0,0,0) 40%, rgba(0,0,0,.55) 100%);
}
.splash-inner {
  position: relative; z-index: 1;
  flex: 1; display: flex; flex-direction: column;
  align-items: center;
  /* The deck puts the lockup a little above centre and the button right down
     at the foot of the screen (p1: DISCOVER NOW sits at 90% of the page). */
  padding: 36dvh 26px max(7dvh, env(safe-area-inset-bottom));
}
.splash-inner .cta { margin-top: auto; }
.splash-lockup { width: 100%; display: flex; justify-content: center; }
.splash-lockup .brand-logo { max-width: 66%; max-height: 26dvh; }
.splash-lockup .brand { font-size: 30px; letter-spacing: 11px; color: var(--on-black); }
.splash-lockup .script { font-size: 16px; letter-spacing: 6px; color: #e2e2e2; margin-top: 6px; }

.cta {
  border: none; cursor: pointer; font-family: inherit;
  background: rgba(130, 130, 130, .55); color: var(--on-black);
  backdrop-filter: blur(3px);
  padding: 20px 46px; border-radius: var(--pill);
  font-size: 15px; letter-spacing: 4px; font-weight: 700; text-transform: uppercase;
}
.cta:active { transform: scale(.98); }

/* ── Brand lockup on the category screen (deck p2) ────────────────────────── */
.home-head { text-align: center; padding: 54px 20px 26px; color: var(--on-black); }
.home-head.compact { padding: max(30px, env(safe-area-inset-top)) 20px 26px; }
.home-head.compact .brand-logo { max-width: 46%; max-height: 62px; }
.brand-logo { max-width: 62%; max-height: 96px; object-fit: contain; margin: 0 auto; display: block; }
.home-head .brand { font-size: 28px; letter-spacing: 10px; font-weight: 400; }
.home-head .script { font-size: 15px; letter-spacing: 5px; color: #b0b0b0; margin-top: 4px; text-transform: lowercase; }

/* ── Browse: the stadium pill ─────────────────────────────────────────────
   Categories (deck p2) and series (p7, p12) are one pill per row. ───────── */
.cat-grid, .series-grid { display: grid; grid-template-columns: 1fr; gap: 16px; padding: 0 16px 44px; }
.series-grid { padding-top: 6px; }

.cat-tile, .series-tile {
  border: none; cursor: pointer; font-family: inherit;
  background: var(--face); color: var(--face-ink);
  border-radius: var(--pill);
  padding: 30px 20px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  text-align: center;
}
.cat-tile:active, .series-tile:active { transform: scale(.99); }
.cat-label, .series-tile {
  font-size: 17px; letter-spacing: 3px; font-weight: 700;
  text-transform: uppercase; line-height: 1.5;
}
/* The pills carry no packshot — the deck reserves imagery for the product-type
   cards and the line-up above a series list. This is why a category tile never
   showed the first type's card even though the markup put one there. */
.cat-shot, .series-tile .art { display: none; }

/* A category given its OWN artwork is the exception the deck draws: Fine
   Fragrance is a photograph with the label over it, the other four are plain.
   So this is not the hidden `.cat-shot` under another name — it appears only
   where somebody uploaded a picture for that category, and a tile without one
   is untouched. */
.cat-tile:has(.cat-art) { padding: 0; overflow: hidden; }
.cat-art {
  position: absolute; inset: 0;
  background: center/cover no-repeat;
}
.cat-tile:has(.cat-art) {
  position: relative; min-height: 26dvh;
  /* The deck draws this one as a rounded rectangle while its neighbours stay
     stadium pills (p1 of both decks). A photograph in a 999px stadium loses its
     corners to the curve — which is fine for a word and wrong for a picture. */
  border-radius: var(--radius);
}
/* The label sits over the photograph, so it needs its own stacking context and
   enough shadow to hold on a light crop — the Fine Fragrance image is a near
   white ground and black text on it is legible only just. */
.cat-tile:has(.cat-art) .cat-label {
  position: relative; z-index: 1;
  text-shadow: 0 1px 14px rgba(255, 255, 255, .85);
}
.tile-note { display: none; }
.series-tile.soon { cursor: default; color: #7a7a7a; }
.series-tile.soon small {
  display: block; font-size: 10px; letter-spacing: 2px;
  color: #969696; margin-top: 4px; text-transform: lowercase;
}

/* ── Browse: product-type cards (deck p6, p14, p21, p23) ──────────────────
   Two across, packshot above the label. ─────────────────────────────────── */
.prod-wrap { background: var(--black); flex: 1; }
/* Two columns that pack independently, as the deck lays them out (p6, p23):
   a tall card on one side does not push its neighbour down. CSS columns give
   that for free; a grid would hold every row to the tallest card in it. Card
   height comes from the photograph, which is why the shot is an <img> rather
   than a background. */
.prod-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px;
             align-items: start; padding: 4px 16px 0; }
.prod-grid.tail, .prod-grid:last-child { padding-bottom: 50px; }
/* the trailing full-width card keeps the same gap as any other pair */
.prod-grid.tail { padding-top: 14px; }
.prod-grid.solo { grid-template-columns: 1fr; }
/* Equal tiles: `1fr` rows make every row the height of the tallest, and a
   square picture box lets shapes as different as a floor-standing column and a
   rock lid sit at the same size. */
.prod-grid.uniform { grid-auto-rows: 1fr; }
.prod-grid.uniform .prod-card { height: 100%; justify-content: space-between; }
/* one shape for every picture box, matching what the cards are padded to */
.prod-grid.uniform .shot { aspect-ratio: 3 / 4; }
/* Stand the picture on the label rather than floating it.
   The card is pinned to the row height and laid out `space-between`, so a
   picture shrunk by its tile size left ALL of the freed height in one lump
   underneath it — Cooling Spray at 30% sat 18px from the top with 165px of grey
   below, which reads as the same "small picture, big box" the scale field was
   meant to answer. Centring fixed that and introduced a subtler one: a small
   product floated in the middle of its card while a tall one filled its own, so
   nothing shared a line and the grid read as unsettled.
   All of the free space goes above instead, which puts every product on one
   baseline — a shelf rather than a scatter. An auto margin claims that space
   before `justify-content` distributes it, so `space-between` stops governing
   this child; the single `0` is what keeps the bottom edge fixed.
   Measured across Body Care: the gap under the picture is 14px on every card,
   scaled or not, where it ranged 14-90 when centred. */
.prod-grid.uniform .shot { margin-block: auto 0; }
.prod-col { display: flex; flex-direction: column; gap: 14px; }

.prod-card {
  border: none; cursor: pointer; font-family: inherit;
  background: var(--face); color: var(--face-ink);
  border-radius: 22px;
  padding: 18px 12px 16px; text-align: center;
  display: flex; flex-direction: column; gap: 14px; align-items: center;
  width: 100%;
}
.prod-card:active { transform: scale(.99); }
.prod-card .shot {
  width: 100%; height: auto; display: block;
  aspect-ratio: 3 / 4;              /* until the bundle names the real one */
  object-fit: contain;
  /* No height cap: capping it letterboxes the picture inside a box the wrong
     shape, which is what made a full-width card show a small photograph in a
     lot of empty grey. The varying heights are the point of the layout. */
}
.prod-card .shot.placeholder { aspect-ratio: 1; height: auto; }
.prod-card .shot.placeholder {
  display: flex; align-items: center; justify-content: center;
  color: #9a9a9a; font-size: 11px; letter-spacing: 2px;
}
.prod-card .name {
  font-size: 14px; letter-spacing: 2px; font-weight: 700;
  text-transform: uppercase; line-height: 1.45;
}
/* Fine Fragrance is the one category with just two types, and the deck gives
   it a full-width card each (p3) rather than a pair side by side. */
/* Fine Fragrance's two types, and any lone trailing card, take the full width
   with a larger picture (deck p3). */
.prod-grid.solo .prod-card { border-radius: var(--radius); padding: 22px 16px 20px; }
/* A full-width pair is a wide, shallow card in the deck, not a tall one — its
   picture box is about 3:2 (p3, p17). The odd card trailing a staggered grid
   keeps its own shape, since that grid is about differing heights. */
.prod-grid.solo:not(.tail) .shot { aspect-ratio: 3 / 2; }

/* ── Product detail ───────────────────────────────────────────────────────
   The light ground. Everything below takes its colour from the light set. ─ */
.screen:not(.dark) { --ground-fill: transparent; }
.phone:not(.dark) { background: var(--ground-top); }
/* The field fills the screen and stays there; the page scrolls over it.
   It is a fixed layer rather than `background-attachment: fixed`, which iOS
   Safari quietly downgrades to `scroll` — the artwork then stretches down the
   whole document instead of framing one screen. `isolation` keeps the
   negative z-index inside this element, so the layer sits above the phone's
   own background and below everything on the page. */
.phone:not(.dark) .screen { isolation: isolate; }
.phone:not(.dark) .screen::before {
  content: '';
  position: fixed;
  top: 0; bottom: 0; left: 50%;
  transform: translateX(-50%);
  width: min(100vw, 520px);      /* the phone column, not the whole desktop */
  /* `--ground` is the design team's artwork, set per screen from the bundle;
     the gradient in :root stands in when no artwork is published. */
  background-image:
    linear-gradient(to bottom, rgba(216, 216, 216, 0) calc(100% - 170px),
                               var(--ground-top) 100%),
    var(--ground);
  background-size: 100% 100%, cover;
  background-position: center, center;
  background-repeat: no-repeat, no-repeat;
  z-index: -1;
  pointer-events: none;
}
.phone:not(.dark) .topbar { background: transparent; backdrop-filter: blur(14px); }

/* The bar and the selectors ride together: on a long page you can still see
   which size you are looking at, and switch, without scrolling back up. */
.head-stack { position: sticky; top: 0; z-index: 20; padding-bottom: 8px; }
.head-stack > .topbar { position: static; }
.phone:not(.dark) .head-stack { backdrop-filter: blur(14px); }

.selector-panel {
  background: var(--well);
  margin: 8px 16px 0; padding: 10px;
  border-radius: var(--radius);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .10);
}
/* Every size on one row, however many there are — the pill shrinks to fit
   rather than wrapping onto a second line. */
.size-row { display: grid; grid-template-columns: repeat(var(--cols, 4), 1fr); gap: 6px; }
.int-row { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-top: 6px; }
.int-row.cols-1 { grid-template-columns: 1fr; }

.sel-tile {
  border: none; cursor: pointer; font-family: inherit;
  background: var(--pill-off); color: var(--pill-off-ink);
  border-radius: 18px; padding: 16px 2px;
  font-size: clamp(10px, calc(62px / var(--cols, 4)), 15px);
  letter-spacing: .3px; line-height: 1.25;
  text-transform: uppercase;
}
.int-row .sel-tile { font-size: 15px; padding: 18px 6px; letter-spacing: .5px; }
.sel-tile.on { background: var(--pill-on); color: var(--pill-on-ink); font-weight: 700; }
/* the qualifier under a size — "35ml / with charm" (deck p24) */
.sel-tile small {
  display: block; margin-top: 2px;
  font-size: clamp(8px, calc(44px / var(--cols, 4)), 11px);
  letter-spacing: .2px; font-weight: 400;
  text-transform: none; opacity: .85;
}
.sel-tile:disabled { opacity: .45; cursor: default; }

/* The panel paints nothing itself. The chosen tab and the body below it carry
   the card colour between them, so the step above the unchosen tab shows the
   page — painting the panel put a strip of card colour there instead, running
   the full width like a rule. */
.tab-panel { margin: 16px 16px 44px; flex: 1; }
/* Folder tabs, as the deck draws them (p8, p13): the chosen one stands taller
   and is the card continuing upward; the other sits recessed beside it. Both
   carry dark type — the unchosen tab is a paler surface, not a darker one. */
.tab-row { display: grid; grid-template-columns: 1fr 1fr; align-items: end; }
.tab-btn {
  border: none; cursor: pointer; font-family: inherit;
  padding: 15px 8px; font-size: 15px; letter-spacing: 1px; text-transform: uppercase;
  background: var(--pill-off); color: var(--ink);
  border-radius: 22px 22px 0 0;
}
.tab-btn.on {
  background: var(--card); font-weight: 700;
  padding-top: 20px; padding-bottom: 16px;   /* raised above its neighbour */
  position: relative; z-index: 1;
}
.tab-body {
  /* The extra bottom room is for iOS Safari, whose toolbar overlays the end of
     the page — and the end of the page is exactly where the swipe hint sits. */
  padding: 22px 18px calc(34px + env(safe-area-inset-bottom, 0px));
  background: var(--card);
  border-radius: 0 0 var(--radius) var(--radius);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .10);
}
/* a product with only one tab's worth of content has no tab row above it */
.tab-panel:not(:has(.tab-row)) .tab-body { border-radius: var(--radius); }

.design-shot {
  width: 100%; max-height: 62dvh; object-fit: contain; display: block; margin: 8px auto;
  /* The last fixed number in the reel. 8px is 2.6% of the reel at phone width,
     and unlike the corner and the gap this one is spacing rather than
     composition — it does not distort anything by staying 8px. It is a
     proportion anyway so the whole reel scales as one thing: on a tablet a
     photograph half again as large with the same 8px under it reads as sitting
     lower in its card than it does on a phone. `margin-block` leaves the
     horizontal margins to the rules below, which still set the gap. */
  margin-block: 2.6cqw;
  /* The deck rounds the photographs (EDP EXP.pdf p4). It matters now that these
     are photographs with their own background rather than cut-outs on the card's
     own grey — a cut-out has no visible edge to round, a photograph does.

     The reference has the corner curve at about 2.9% of the photograph's width.
     Written as a fixed 8px that is only true at one screen size — right at
     375pt, and thinning to 2.1% at 540 and 1.4% on a tablet, because the photo
     grows with the frame and the corner did not. So it is 2.55cqw of the reel
     (2.9% of a photo that is 88% of the reel), which holds the proportion at
     any width. The px value stays as the fallback for Safari before 16.4,
     where container units do not resolve. */
  border-radius: 8px;
  border-radius: 2.55cqw;
}
/* One photograph at a time, snapped, with its neighbours showing at both edges.
   I first built this with no peek at all, reasoning that a half-shown image
   reads as a layout fault — the deck (EDP EXP.pdf p4) disagrees, and it is
   right: the gap is what separates the two readings. Butted together, a sliver
   of the next photo looks like the current one is clipped; with air between
   them it plainly says there is another. The scrollbar stays hidden because the
   hint says what it is, and `overscroll-behavior` keeps a swipe that runs out of
   reel from turning into a page swipe.

   The end slides carry a margin so the first and last can reach the centre;
   without it they snap flush to the edges and the peek is one-sided. */
.design-reel {
  /* the container the photo corners are sized against */
  container-type: inline-size;
  display: flex; overflow-x: auto; scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain; scrollbar-width: none; -ms-overflow-style: none;
}
.design-reel::-webkit-scrollbar { display: none; }
.design-reel .design-shot {
  /* min-width:0 is required, not tidiness: a flex item's automatic minimum is
     its content size, and `width:100%` above makes that the full reel width —
     which silently overrides the 82% basis and butts the photos together
     again. */
  flex: 0 0 88%; min-width: 0; scroll-snap-align: center;
  /* One photograph per gesture. Without this a quick flick carries through two
     or three snap points, which on a seven-shot reel means arriving somewhere
     you did not choose and not knowing what you skipped. */
  scroll-snap-stop: always;
}
/* The first photograph fills the reel; the peek starts from the second.
   Arriving on a packshot shot on near-white with a dark lifestyle photo showing
   at its edge, the edge reads as a rendering fault rather than as another
   picture — there is nothing yet to tell you this is a reel. Once you have
   swiped there is, and the neighbours are then useful rather than puzzling.
   It centres at scroll 0 without a margin, being the full width. */
/* The gap, as a proportion rather than 6px — the reference has it at 1.9% of
   the frame, and a fixed gap thins on a wide screen exactly as the corner
   radius did. It is a margin on the children rather than `gap` on the reel
   because a percentage gap would resolve against the reel's own width in some
   engines and its content in others, while a percentage margin on a flex item
   is defined against the container's inline size. (cqw is no use here either:
   the reel is itself the container, and an element does not resolve container
   units against itself.)

   88% slide + 2 x (1.9% gap + 4.1% peek) = 100%, which is the reference. */
.design-reel .design-shot + .design-shot { margin-left: 1.9%; }
.design-reel .design-shot:first-child { flex-basis: 100%; }
.design-reel .design-shot:last-child { margin-right: 6%; }
.design-reel:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
/* The reel's position, said as a number rather than an instruction (Kevin,
   25 Sep): R&D found a Chinese "SWIPE TO DISCOVER" odd to read, and "3/8" needs
   no translation at all. Tabular figures so the number does not jog sideways as
   it counts past 1.

   This replaces `.reel-hint` — the deck's rule with a barb at its left end,
   an arrow pointing the way the thumb goes. Removed rather than left behind:
   nothing renders it now, and a styled element with no markup is the kind of
   thing somebody later reads as a feature that broke. It is in git if the
   instruction is ever wanted back. */
.reel-count {
  text-align: center; font-size: 12px; letter-spacing: 1.5px;
  color: var(--ink); opacity: .55; padding: 4px 0 12px;
  font-variant-numeric: tabular-nums;
}
.design-empty {
  text-align: center; color: var(--ink-soft); letter-spacing: 2px;
  padding: 60px 10px; font-size: 13px; text-transform: uppercase;
}

/* ── Download (App Preview.pdf p4) ─────────────────────────────────────────
   Two store badges, stacked and centred. Apple's guidelines set a minimum
   size and require clear space around the badge, so it is capped rather than
   stretched to the panel width, and the gap is generous. */
.store-links {
  display: flex; flex-direction: column; align-items: center;
  gap: 16px; padding: 26px 0 10px;
}
/* text-decoration:none for the same reason .btn needed it — an <a> carries the
   browser's underline, and here it would run through the middle of a badge. */
.store-badge { display: block; max-width: 220px; width: 62%; text-decoration: none; }
.store-badge img { width: 100%; height: auto; display: block; }
.store-badge:active { opacity: .8; }
/* Only when the badge artwork has not been uploaded: the link still has to
   work, so it falls back to a button rather than to nothing. */
.store-fallback {
  display: block; text-align: center; padding: 13px 18px;
  border-radius: 999px; background: var(--ink); color: var(--face);
  font-size: 13px; letter-spacing: 1.5px; text-transform: uppercase;
}

/* ── Available scent, Fine Fragrance (EDP EXP.pdf p2, p3) ──────────────────
   A scent whose notes are published gets a pill of its own and opens a card.
   The pills are deliberately plain: the interesting thing here is not which
   family a scent belongs to but what is inside it, and that is one tap away. */
.scent-list { display: flex; flex-direction: column; gap: 10px; padding: 4px 0 2px; }
.scent-pill {
  border: none; font: inherit; cursor: pointer;
  background: var(--pill-off); color: var(--pill-off-ink);
  border-radius: 999px; padding: 15px 18px;
  font-size: 15px; letter-spacing: .3px; text-align: center;
  transition: filter .15s;
}
.scent-pill:active { filter: brightness(.94); }
.scent-hint {
  text-align: center; font-style: italic; font-size: 12.5px;
  color: var(--ink-soft); padding: 14px 0 2px;
}

/* The note itself sits over the page rather than pushing it, so the list keeps
   its place underneath and closing puts you back exactly where you were. */
.note-sheet {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(20, 20, 20, .45);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 18px; padding: 20px;
}
/* Blur the page behind the note, so the card reads as sitting above the list
   rather than replacing it. The scrim stays for browsers without the filter —
   iOS needed the -webkit- prefix until Safari 18 — and is lightened only where
   the blur is actually doing the separating, so an unsupported browser is left
   with the darker dim rather than a barely-veiled page. */
.note-sheet {
  -webkit-backdrop-filter: blur(14px) saturate(.92);
  backdrop-filter: blur(14px) saturate(.92);
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .note-sheet { background: rgba(20, 20, 20, .3); }
}
/* Required: the display above outranks the browser's own [hidden] rule, so
   without this the sheet is open the moment the page renders. */
.note-sheet[hidden] { display: none; }
body.note-open { overflow: hidden; }
.note-reel {
  display: flex; gap: 12px; width: 100%; max-width: 460px;
  overflow-x: auto; scroll-snap-type: x mandatory; overscroll-behavior: contain;
  scrollbar-width: none; -ms-overflow-style: none;
}
.note-reel::-webkit-scrollbar { display: none; }
.note-card {
  flex: 0 0 100%; min-width: 0; scroll-snap-align: center; scroll-snap-stop: always;
  background: var(--face); color: var(--ink);
  border-radius: 26px; padding: 0 22px 26px;
  max-height: 74dvh; overflow-y: auto;
}
.note-card h3 {
  position: sticky; top: 0;
  margin: 0 -22px 20px; padding: 15px 18px;
  background: var(--ink); color: var(--face);
  border-radius: 26px 26px 22px 22px;
  font-size: 15px; font-weight: 500; letter-spacing: .3px; text-align: center;
}
.note-tier { text-align: center; margin-bottom: 18px; }
.note-tier:last-child { margin-bottom: 0; }
.note-tier h4 {
  font-size: 13px; font-weight: 700; letter-spacing: 1.5px; margin: 0 0 4px;
}
.note-tier p {
  margin: 0; font-size: 14px; line-height: 1.45; text-transform: uppercase;
  letter-spacing: .4px;
  /* A note line is a list of ingredients and can be long. The exporter now
     replaces the no-break spaces Word leaves in them, which is the real fix —
     this is the seatbelt for the next unbreakable thing that gets in, because
     without it the text does not overflow tidily, it leaves the card and the
     screen. `anywhere` rather than `break-word` so the overflow cannot size the
     box either. */
  overflow-wrap: anywhere;
}
.note-close {
  flex: none; width: 46px; height: 46px; border-radius: 50%; border: none;
  background: var(--face); color: var(--ink); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.note-close svg { width: 20px; height: 20px; }

/* ── Available scent (deck p5, p9, p11): centred, family by family ────────── */
.fam-table { display: flex; flex-direction: column; gap: 22px; padding: 6px 2px 2px; }
.fam-row { display: flex; flex-direction: column; align-items: center; gap: 7px; }
.fam-chip {
  display: inline-block; padding: 3px 16px;
  font-size: 13px; letter-spacing: 2px; font-weight: 700; text-transform: uppercase;
  color: var(--chip-ink);
}
.fam-scents { text-align: center; font-size: 17px; line-height: 1.55; color: var(--ink); }
.fam-chip { color: var(--chip-ink); background: #c4c4c4; }   /* the fallback */
.f-spicy  { background: var(--spicy); }
.f-floral { background: var(--floral); }
.f-nature { background: var(--nature); }
.f-citrus { background: var(--citrus); }
.f-woody  { background: var(--woody); }
.f-fruity { background: var(--fruity); }
.f-clear-reflection { background: var(--clear-reflection); }
.f-warm-embrace     { background: var(--warm-embrace); }
.f-golden-escape    { background: var(--golden-escape); }
.f-deep-echoes      { background: var(--deep-echoes); }
/* Lab and Rituals name four of the core families their own way. Same family,
   same colour — without this the chip fell through to grey and the page read as
   half-finished beside one that uses the core names. The five mood families
   Rituals also uses (balance, calm, clarity, soothing, uplift) have no colour
   yet; the design team gave the Earth four by hand and these want the same. */
.f-blooming-floral  { background: var(--floral); }
.f-cosy-wood        { background: var(--woody); }
.f-graceful-nature  { background: var(--nature); }
.f-joyous-fruit     { background: var(--fruity); }
.f-sea        { background: var(--sea); }
.f-rainforest { background: var(--rainforest); }
.f-orchard    { background: var(--orchard); }
.f-highland   { background: var(--highland); }

/* ── Available colour (deck p19, p20) ─────────────────────────────────────── */
/* The deck stands each finish at the full width of its column with the name
   directly beneath (p19, p20). The swatch is published already trimmed to the
   product, so `contain` has nothing to letterbox and the picture fills the
   column; its own ratio sizes the box. */
.colour-grid {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 26px 18px; padding: 12px 4px 4px;
}
.colour { display: flex; flex-direction: column; align-items: center; gap: 12px; }
.colour img {
  width: 100%; height: auto; display: block; object-fit: contain;
  aspect-ratio: 3 / 2;      /* stands in until the published ratio lands */
}
.colour .swatch-blank { width: 100%; aspect-ratio: 3 / 2; background: var(--pill-off); border-radius: 8px; }
.colour-name {
  font-size: 13px; letter-spacing: 1.5px; text-transform: uppercase;
  color: var(--ink); text-align: center; line-height: 1.4;
}

/* ── Copy and specs ──────────────────────────────────────────────────────── */
.spec-block { margin: 18px 6px 0; border-top: 1px solid var(--rule); padding-top: 14px; }
.spec-row {
  display: grid; grid-template-columns: 110px 1fr; gap: 12px;
  font-size: 13px; line-height: 1.55; letter-spacing: .3px;
  color: #3a3a3a; padding: 4px 0;
}
.spec-row span:first-child {
  font-weight: 700; text-transform: uppercase; font-size: 11px;
  letter-spacing: 1.5px; color: var(--ink-soft); padding-top: 2px;
}


/* ── Preview channel only. Production bundles carry no `preview`/`status`
      fields, so none of this renders there. ──────────────────────────────── */
.preview-ribbon {
  position: fixed; top: 0; left: 0; right: 0; z-index: 99;
  background: #8a6412; color: #fff; text-align: center;
  font: 700 11px/1 'Lato', sans-serif; letter-spacing: 2px; padding: 6px 4px;
}
.preview-ribbon ~ .phone .topbar { padding-top: max(34px, env(safe-area-inset-top)); }
.draft-tag {
  display: inline-block; background: rgba(199, 154, 62, .9); color: #fff;
  font-size: 9px; letter-spacing: 1.5px; padding: 2px 7px; border-radius: var(--pill);
  vertical-align: middle; text-transform: uppercase; margin-left: 6px;
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
  .cta:active, .cat-tile:active, .series-tile:active, .prod-card:active, .round-btn:active { transform: none; }
}

/* ── Chinese and pinyin ────────────────────────────────────────────────────
   The site's whole visual language — uppercase, 2-3px of tracking — is an
   English-display convention that does not transfer. `letter-spacing` applied
   to 汉字 spaces every single ideograph and reads as broken rather than as
   tracking; `text-transform: uppercase` does nothing to Chinese and is wrong
   for pinyin, which is written in caps nowhere. So each script gets its own
   treatment, and the English keeps the one it had.

   These are children of the existing caption elements (.cat-label, .name,
   .scent-pill), so they inherit size and colour and override only what has to
   change. */
.t-en { display: block; }
.t-zh, .t-py {
  display: block;
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
}
.t-zh {
  /* Chinese carries more weight per character at the same size, so it reads
     larger than the English beside it unless it is set smaller. */
  font-size: .92em;
  line-height: 1.5;
  margin-top: 2px;
}
.t-py {
  /* The deck sets the reading lighter and smaller than both — an aid, not a
     third name. Tone marks need the extra line-height; at 1.45 the macron on
     ā clips the line above. */
  font-size: .76em;
  line-height: 1.6;
  opacity: .62;
  margin-top: 1px;
}
/* A scent pill puts the Chinese and its reading on one line together (deck p3),
   which keeps a 131-row list from becoming three times its length. */
.t-row {
  display: flex; align-items: baseline; justify-content: center;
  gap: .45em; flex-wrap: wrap;
  text-transform: none; letter-spacing: normal;
}
.t-row .t-zh, .t-row .t-py { display: inline; margin: 0; }
/* A tier label keeps its Chinese on the same line, in brackets (deck p4). */
.t-par {
  text-transform: none; letter-spacing: normal; font-weight: 400;
}
.t-par i { font-style: normal; opacity: .62; font-size: .86em; }

/* The language gate on the splash (CHINESE deck p0) — two buttons where the
   single call to action used to be.

   `margin-top:auto` is what puts the call to action at the foot of the splash,
   and it was written as `.splash-inner .cta`. Wrapping the buttons in this
   element stopped that matching, so the gate climbed up under the lockup and
   sat across the script. The rule has to move to whatever is the last child —
   and be cancelled on the buttons inside, or the auto margin reappears between
   them and pushes the two apart. */
.lang-gate { display: flex; flex-direction: column; gap: 14px; width: 100%; }
.splash-inner .lang-gate { margin-top: auto; }
.splash-inner .lang-gate .cta { margin-top: 0; }
.lang-gate .cta[data-lang="zh"] { text-transform: none; letter-spacing: 2px; }

/* And the switch at the foot of the category list (CHINESE deck p1, where it
   sits on the bottom edge rather than tucked under the last pill).

   `.screen` is already a flex column at 100dvh, so `margin-top:auto` is all it
   takes — the same mechanism the splash uses for its call to action. On a short
   screen where the pills overflow, the auto margin collapses and the switch
   simply follows the list, which is the right answer there too. */
.lang-switch {
  display: flex; justify-content: center;
  margin: auto 0 max(24px, env(safe-area-inset-bottom));
}
.lang-switch button {
  border: none; cursor: pointer; font: inherit;
  background: var(--pill-off); color: var(--pill-off-ink);
  font-size: 13px; letter-spacing: 1.5px; padding: 9px 22px;
  transition: background .15s, color .15s;
}
.lang-switch button:first-child { border-radius: 999px 0 0 999px; }
.lang-switch button:last-child { border-radius: 0 999px 999px 0; letter-spacing: normal; }
.lang-switch button.on { background: var(--face); color: var(--face-ink); }
