/* ==========================================================================
   Mokdad Tobacco — stylesheet
   --------------------------------------------------------------------------
   Layout uses CSS *logical* properties (margin-inline, padding-block,
   inset-inline) rather than physical left/right. The site is English-only, so
   this is belt-and-braces — but it means adding another language later would
   not require rewriting the stylesheet.

   Colour strategy: the shell is dark, the catalog is light. Both use the same
   component classes; the `--fg / --bg / --card / --line / --gold*` tokens are
   re-declared inside `.section--light`, so components adapt automatically.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Reset
   -------------------------------------------------------------------------- */
*,
*::before,
*::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-block-start: 5.5rem;
}

body {
  min-height: 100dvh;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img,
svg,
video { display: block; max-width: 100%; }

button,
input,
select,
textarea { font: inherit; color: inherit; }

button { background: none; border: 0; cursor: pointer; }

a { color: inherit; text-decoration: none; }

ul, ol { list-style: none; padding: 0; }

/* The `hidden` attribute works through the UA stylesheet as
   `[hidden] { display: none }`, and ANY author `display` rule outranks it.
   That silently broke four elements here — `.btn` and `.deflist > div` are
   both `display: flex`, so setting `.hidden = true` from JavaScript left them
   on screen: the delivery rows showed "Ask us on WhatsApp" three times over,
   and the Get-directions button would have stayed visible with no link
   whenever no address is set.

   `!important` is the standard remedy and is correct in this one case: an
   element carrying `hidden` is never meant to be visible, so there is nothing
   that should ever override it. This is why no per-component `[hidden]` rules
   are needed below. */
[hidden] { display: none !important; }

:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Icons are all line-style, so stroke comes from the referencing element and
   the shadow content inside <use> inherits it. Sizing is per-component. */
svg[aria-hidden="true"] {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex: none;
}

/* --------------------------------------------------------------------------
   2. Design tokens
   -------------------------------------------------------------------------- */
:root {
  /* Shell (dark) — the default context */
  --bg:          #0E0E10;
  --bg-2:        #17171A;
  --bg-3:        #1E1E22;
  --fg:          #F2EFE9;
  --fg-soft:     #A5A29B;
  --fg-faint:    #6E6B66;
  --line:        rgba(242, 239, 233, 0.10);
  --line-strong: rgba(242, 239, 233, 0.18);
  --card:        #17171A;

  --gold:        #C9A227;
  --gold-soft:   #E0C458;
  --gold-text:   #C9A227;
  --gold-wash:   rgba(201, 162, 39, 0.12);

  /* Feedback */
  --ok:    #6FBF73;
  --muted: #8A8781;
  --bad:   #C96A5A;

  /* Type */
  --font-display: "Cormorant Garamond", "Times New Roman", serif;
  --font-body:    "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;

  /* Scale */
  --step--1: clamp(0.80rem, 0.77rem + 0.15vw, 0.88rem);
  --step-0:  clamp(0.94rem, 0.90rem + 0.20vw, 1.02rem);
  --step-1:  clamp(1.10rem, 1.02rem + 0.40vw, 1.30rem);
  --step-2:  clamp(1.42rem, 1.24rem + 0.90vw, 2.00rem);
  --step-3:  clamp(1.85rem, 1.48rem + 1.85vw, 3.10rem);
  --step-4:  clamp(2.30rem, 1.55rem + 3.75vw, 4.60rem);

  /* Space */
  --space-section: clamp(3.5rem, 2.2rem + 6vw, 7.5rem);
  --gutter:        clamp(1.05rem, 0.6rem + 2.4vw, 2.75rem);
  --max:           1240px;
  --max-narrow:    760px;

  /* Shape and elevation, on the same scale as the dashboard so the two halves
     of the project read as one design. */
  --radius-xs:   6px;
  --radius:      10px;
  --radius-lg:   16px;
  --radius-xl:   22px;
  --radius-pill: 999px;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .28);
  --shadow:    0 2px 6px rgba(0, 0, 0, .30), 0 14px 30px -18px rgba(0, 0, 0, .50);
  --shadow-lg: 0 4px 10px rgba(0, 0, 0, .32), 0 26px 50px -22px rgba(0, 0, 0, .60);
  --ring:      0 0 0 3px rgba(201, 162, 39, .28);

  --dur: 0.32s;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);

  --header-h: 4.5rem;
}

/* Light catalog context */
.section--light {
  --bg:          #F7F5F1;
  --bg-2:        #FFFFFF;
  --bg-3:        #EFEBE3;
  --fg:          #17171A;
  --fg-soft:     #5C5C63;
  --fg-faint:    #8B8880;
  --line:        #E4DFD6;
  --line-strong: #D3CCC0;
  --card:        #FFFFFF;

  --gold:        #A9871B;
  --gold-soft:   #8A6D12;
  --gold-text:   #7E6410;
  --gold-wash:   rgba(169, 135, 27, 0.10);

  --muted: #6E6B66;
  --bad:   #A8412F;

  background: var(--bg);
  color: var(--fg);
}

/* --------------------------------------------------------------------------
   3. Base
   -------------------------------------------------------------------------- */
body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--step-0);
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.14;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

p { text-wrap: pretty; }

/* Grain: a faint noise wash over the dark shell. Sells "premium" cheaply. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.038;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* --------------------------------------------------------------------------
   4. Layout primitives
   -------------------------------------------------------------------------- */
.wrap {
  width: 100%;
  max-width: var(--max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.wrap--narrow { max-width: var(--max-narrow); }

.section { padding-block: var(--space-section); position: relative; }
.section--tight { padding-block: calc(var(--space-section) * 0.62); }
.section--light { position: relative; z-index: 2; }

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold-text);
  margin-block-end: 1rem;
}

.eyebrow::before {
  content: "";
  inline-size: 1.75rem;
  block-size: 1px;
  background: currentColor;
  opacity: 0.6;
}

.section-head { max-width: 42rem; margin-block-end: clamp(2rem, 1.4rem + 2vw, 3.25rem); }
.section-head h2 { font-size: var(--step-3); }
.section-head p { color: var(--fg-soft); margin-block-start: 0.9rem; font-size: var(--step-1); }
.section-head--center { margin-inline: auto; text-align: center; }
.section-head--center .eyebrow { justify-content: center; }

.rule {
  block-size: 1px;
  background: var(--line);
  border: 0;
}

/* --------------------------------------------------------------------------
   5. Buttons
   -------------------------------------------------------------------------- */
.btn {
  --btn-bg: transparent;
  --btn-fg: var(--fg);
  --btn-line: var(--line-strong);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  padding: 0.78rem 1.5rem;
  min-block-size: 2.9rem;
  background: var(--btn-bg);
  color: var(--btn-fg);
  border: 1px solid var(--btn-line);
  border-radius: var(--radius);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease),
              transform 0.16s var(--ease);
}
.btn:hover { border-color: var(--gold); color: var(--gold-text); }
.btn:active { transform: translateY(1px); }

.btn--gold {
  --btn-bg: var(--gold);
  --btn-fg: #14140F;
  --btn-line: var(--gold);
  font-weight: 700;
}
.btn--gold:hover { --btn-bg: var(--gold-soft); --btn-line: var(--gold-soft); color: #14140F; }

.btn--solid {
  --btn-bg: var(--fg);
  --btn-fg: var(--bg);
  --btn-line: var(--fg);
}
.btn--solid:hover { --btn-bg: var(--gold); --btn-fg: #14140F; --btn-line: var(--gold); }

.btn--ghost { --btn-line: transparent; padding-inline: 0.75rem; }
.btn--ghost:hover { --btn-line: transparent; }

.btn--block { inline-size: 100%; }
.btn--sm { padding: 0.55rem 1rem; min-block-size: 2.4rem; font-size: 0.78rem; }

.btn svg { inline-size: 1.05em; block-size: 1.05em; flex: none; }

/* --------------------------------------------------------------------------
   6. Skip link
   -------------------------------------------------------------------------- */
.skip {
  position: absolute;
  inset-block-start: -100px;
  inset-inline-start: 1rem;
  z-index: 999;
  padding: 0.7rem 1.1rem;
  background: var(--gold);
  color: #14140F;
  font-weight: 700;
  border-radius: var(--radius);
  transition: inset-block-start 0.2s var(--ease);
}
.skip:focus { inset-block-start: 1rem; }

/* --------------------------------------------------------------------------
   7. Announcement bar
   -------------------------------------------------------------------------- */
.announce {
  background: var(--gold);
  color: #14140F;
  font-size: var(--step--1);
  font-weight: 600;
  position: relative;
  z-index: 60;
}
.announce[hidden] { display: none; }

/* Set by the pre-paint script in the page head for a visitor who has already
   dismissed the bar. app.js makes the same call once data/site.json arrives;
   doing it before the first paint is what stops the bar appearing and then
   snapping the page up. */
.announce-off .announce { display: none; }
.announce__inner {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  padding-block: 0.55rem;
  text-align: center;
}
.announce__inner svg { inline-size: 1rem; block-size: 1rem; flex: none; }
.announce__close {
  position: absolute;
  inset-inline-end: var(--gutter);
  inset-block-start: 50%;
  translate: 0 -50%;
  padding: 0.3rem;
  opacity: 0.75;
  line-height: 0;
}
.announce__close:hover { opacity: 1; }
.announce__close svg { inline-size: 0.95rem; block-size: 0.95rem; }

/* --------------------------------------------------------------------------
   8. Age gate
   -------------------------------------------------------------------------- */
.gate {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: grid;
  place-items: center;
  padding: var(--gutter);
  background: #0A0A0C;
  overflow-y: auto;
}
.gate[hidden] { display: none; }

.gate__glow {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 60% 45% at 50% 8%,  rgba(201, 162, 39, 0.16), transparent 70%),
    radial-gradient(ellipse 80% 50% at 50% 108%, rgba(201, 162, 39, 0.07), transparent 70%);
  pointer-events: none;
}

.gate__box {
  position: relative;
  inline-size: min(100%, 34rem);
  padding: clamp(2rem, 1.2rem + 3vw, 3.25rem);
  background: #141417;
  border: 1px solid rgba(201, 162, 39, 0.26);
  border-radius: var(--radius-lg);
  text-align: center;
}

.gate__mark {
  display: grid;
  place-items: center;
  inline-size: 3.4rem;
  block-size: 3.4rem;
  margin-inline: auto;
  margin-block-end: 1.5rem;
  border: 1px solid rgba(201, 162, 39, 0.45);
  border-radius: 50%;
  color: var(--gold);
  font-family: var(--font-display);
  font-size: 1.6rem;
  font-weight: 700;
}
.gate__mark svg { inline-size: 1.6rem; block-size: 1.6rem; }

.gate h1,
.gate h2 {
  font-size: var(--step-2);
  color: #F2EFE9;
}

.gate p {
  color: #A5A29B;
  margin-block-start: 1rem;
}

.gate__actions {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  margin-block-start: 2rem;
}
@media (min-width: 30rem) {
  .gate__actions { flex-direction: row; }
  .gate__actions .btn { flex: 1; }
}

.gate__legal {
  font-size: var(--step--1);
  color: #6E6B66;
  margin-block-start: 1.75rem;
  padding-block-start: 1.5rem;
  border-block-start: 1px solid rgba(242, 239, 233, 0.10);
}

.gate__denied { display: none; }
.gate.is-denied .gate__ask { display: none; }
.gate.is-denied .gate__denied { display: block; }

/* --------------------------------------------------------------------------
   9. Legal review ribbon
   -------------------------------------------------------------------------- */
.legal-ribbon {
  background: #3A2410;
  border-block-end: 1px solid #7A4E14;
  color: #F4D9A8;
  font-size: var(--step--1);
  position: relative;
  z-index: 70;
}
.legal-ribbon[hidden] { display: none; }
.legal-ribbon__inner {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  padding-block: 0.7rem;
}
.legal-ribbon strong { display: block; }
.legal-ribbon p { color: #C9A97A; margin-block-start: 0.15rem; }
.legal-ribbon svg { inline-size: 1.05rem; block-size: 1.05rem; flex: none; margin-block-start: 0.2rem; color: #E0A83C; }

/* --------------------------------------------------------------------------
   10. Header
   -------------------------------------------------------------------------- */
.header {
  position: sticky;
  inset-block-start: 0;
  z-index: 50;
  background: rgba(14, 14, 16, 0.72);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border-block-end: 1px solid transparent;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.header.is-stuck {
  background: rgba(14, 14, 16, 0.94);
  border-block-end-color: var(--line);
}

.header__inner {
  display: flex;
  align-items: center;
  gap: 1rem;
  min-block-size: var(--header-h);
}

.logo {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-inline-end: auto;
}

/* The badge artwork is black on a white disc with transparent corners. Giving
   the image itself the white and the circular clip means the disc meets the gold
   ring exactly — no sliver of page showing between them — and it only applies
   where an image is used, so the age gate's alert-icon state is untouched. The
   disc also means a badge that fails to load still reads as a ringed plate
   rather than a broken image. */
.logo__mark img,
.gate__mark img {
  inline-size: 100%;
  block-size: 100%;
  display: block;
  background: #fff;
  border-radius: 50%;
}
.logo__mark {
  display: grid;
  place-items: center;
  inline-size: 2.35rem;
  block-size: 2.35rem;
  border: 1px solid rgba(201, 162, 39, 0.5);
  border-radius: 50%;
  overflow: hidden;
  flex: none;
}
.logo__text { display: flex; flex-direction: column; line-height: 1.15; }
.logo__name {
  font-family: var(--font-display);
  font-size: 1.22rem;
  font-weight: 600;
  letter-spacing: 0.01em;
}
.logo__tag {
  font-size: 0.63rem;
  letter-spacing: 0.19em;
  text-transform: uppercase;
  color: var(--fg-faint);
}

.nav { display: none; }
@media (min-width: 62rem) {
  .nav {
    display: flex;
    align-items: center;
    gap: 0.35rem;
  }
}

.nav__link {
  position: relative;
  padding: 0.5rem 0.7rem;
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--fg-soft);
  border-radius: var(--radius);
  transition: color var(--dur) var(--ease);
}
.nav__link:hover { color: var(--fg); }
.nav__link::after {
  content: "";
  position: absolute;
  inset-inline: 0.7rem;
  inset-block-end: 0.22rem;
  block-size: 1px;
  background: var(--gold);
  scale: 0 1;
  transition: scale var(--dur) var(--ease);
}
.nav__link:hover::after { scale: 1 1; }

.header__actions { display: flex; align-items: center; gap: 0.5rem; }

.header__cta { display: none; }
@media (min-width: 46rem) { .header__cta { display: inline-flex; } }

.burger {
  display: grid;
  place-items: center;
  inline-size: 2.6rem;
  block-size: 2.6rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--fg);
}
@media (min-width: 62rem) { .burger { display: none; } }
.burger svg { inline-size: 1.2rem; block-size: 1.2rem; }

/* Mobile drawer */
.drawer {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: none;
  background: rgba(10, 10, 12, 0.97);
  backdrop-filter: blur(8px);
  padding: var(--gutter);
  overflow-y: auto;
}
.drawer.is-open { display: block; }
.drawer__top { display: flex; align-items: center; justify-content: space-between; margin-block-end: 2rem; }
.drawer__list { display: flex; flex-direction: column; }
.drawer__list a {
  padding-block: 1rem;
  border-block-end: 1px solid var(--line);
  font-family: var(--font-display);
  font-size: var(--step-2);
  color: var(--fg);
}
.drawer__list a:hover { color: var(--gold-text); }
.drawer__foot { margin-block-start: 2rem; display: flex; flex-direction: column; gap: 0.7rem; }

/* --------------------------------------------------------------------------
   11. Hero
   -------------------------------------------------------------------------- */
.hero {
  position: relative;
  padding-block: clamp(3.5rem, 1rem + 12vw, 8.5rem);
  overflow: hidden;
  border-block-end: 1px solid var(--line);
}
.hero__glow {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 55% 60% at 78% 18%, rgba(201, 162, 39, 0.15), transparent 65%),
    radial-gradient(ellipse 70% 55% at 8% 92%,  rgba(201, 162, 39, 0.07), transparent 68%);
}
.hero__inner { position: relative; }

/* Compact variant used on the catalog page */
.hero--page { padding-block: clamp(2.5rem, 1.4rem + 5vw, 4.5rem); }
.hero--page h1 { font-size: var(--step-3); }
.hero--page .hero__sub { margin-block-start: 1.1rem; }

.hero__grid {
  display: grid;
  gap: clamp(2.5rem, 1rem + 5vw, 4.5rem);
  align-items: center;
}
@media (min-width: 62rem) {
  .hero__grid { grid-template-columns: 1.06fr 0.94fr; }
}

.hero h1 {
  font-size: var(--step-4);
  margin-block-start: 0.4rem;
}
.hero h1 em {
  font-style: italic;
  color: var(--gold-text);
}
.hero__sub {
  color: var(--fg-soft);
  font-size: var(--step-1);
  margin-block-start: 1.5rem;
  max-width: 34rem;
}
.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-block-start: 2.25rem;
}

.hero__panel {
  position: relative;
  display: grid;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.hero__panel-row {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1px;
  background: var(--line);
}
.hero__tile {
  background: var(--bg-2);
  padding: clamp(1.25rem, 0.8rem + 1.6vw, 2rem);
  min-block-size: 8.5rem;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 0.35rem;
  position: relative;
  transition: background var(--dur) var(--ease);
}
.hero__tile:hover { background: var(--bg-3); }
.hero__tile svg { inline-size: 1.5rem; block-size: 1.5rem; color: var(--gold); margin-block-end: auto; }
.hero__tile b { font-family: var(--font-display); font-size: var(--step-1); font-weight: 600; }
.hero__tile span { font-size: var(--step--1); color: var(--fg-faint); }

/* --------------------------------------------------------------------------
   12. Trust strip
   -------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   Live stat strip
   --------------------------------------------------------------------------
   The hairlines are the 1px grid gap showing the line colour through, the same
   trick the trust strip below uses. The figures are counted from the real
   catalog by initStats(); the whole strip stays hidden if that fetch fails, so
   it can never advertise a wrong number. */
.statstrip {
  padding-block: clamp(1.75rem, 1rem + 3vw, 3rem);
  border-block-end: 1px solid var(--line);
  background: var(--bg);
}

/* The counters are only revealed once api/products answers, and the strip was
   display:none until then — so everything below it slid down by the strip's
   whole height when it appeared. Keeping the box holds that space.

   This has to out-shout the global `[hidden] { display: none !important }`
   above, which is why it too is `!important`. The promise that rule makes still
   holds: `visibility: hidden` keeps the strip invisible, and the stagger
   children pinned at opacity 0 mean the entrance plays exactly as it did when
   initStats() unhides it. */
#statStrip[hidden] { display: block !important; visibility: hidden; }
#statStrip[hidden] .statstrip__inner > * { opacity: 0; }
.statstrip__inner {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.statstrip__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  padding: clamp(1rem, 0.7rem + 1.4vw, 1.6rem) 0.75rem;
  background: var(--bg-2);
  text-align: center;
  transition: background-color var(--ui) var(--ease);
}
.statstrip__item:hover { background: var(--bg-3); }
.statstrip__item b {
  font-family: var(--font-display);
  font-size: var(--step-3);
  font-weight: 600;
  line-height: 1;
  color: var(--gold-text);
  font-variant-numeric: tabular-nums;
}
.statstrip__item span {
  font-size: var(--step--1);
  color: var(--fg-soft);
  letter-spacing: 0.04em;
}

.trust {
  border-block-end: 1px solid var(--line);
  background: var(--bg-2);
}
.trust__grid {
  display: grid;
  gap: 1px;
  background: var(--line);
}
@media (min-width: 40rem) { .trust__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64rem) { .trust__grid { grid-template-columns: repeat(4, 1fr); } }

.trust__item {
  background: var(--bg-2);
  padding: clamp(1.5rem, 1rem + 2vw, 2.4rem);
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.trust__item svg { inline-size: 1.35rem; block-size: 1.35rem; color: var(--gold); }
.trust__item b { font-family: var(--font-display); font-size: var(--step-1); font-weight: 600; }
.trust__item p { color: var(--fg-soft); font-size: var(--step--1); }

/* --------------------------------------------------------------------------
   13. Best sellers rail
   -------------------------------------------------------------------------- */
.rail-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1.5rem;
  flex-wrap: wrap;
  margin-block-end: clamp(1.5rem, 1rem + 1.5vw, 2.5rem);
}
.rail-head .section-head { margin-block-end: 0; }

.rail-nav { display: flex; gap: 0.4rem; }
.rail-nav button {
  display: grid;
  place-items: center;
  inline-size: 2.6rem;
  block-size: 2.6rem;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  color: var(--fg-soft);
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.rail-nav button:hover { color: var(--gold-text); border-color: var(--gold); }
.rail-nav svg { inline-size: 1.05rem; block-size: 1.05rem; }

/* The arrow glyph points right; "previous" is the same glyph mirrored. */
.rail-nav .icon-prev svg { rotate: 180deg; }

.rail {
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-block-end: 1rem;
  margin-inline: calc(var(--gutter) * -1);
  padding-inline: var(--gutter);
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}
.rail::-webkit-scrollbar { block-size: 6px; }
.rail::-webkit-scrollbar-track { background: transparent; }
.rail::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 99px; }

.rail > * {
  flex: 0 0 min(15.5rem, 74vw);
  scroll-snap-align: start;
}

/* --------------------------------------------------------------------------
   Category tiles — the catalog landing view
   --------------------------------------------------------------------------
   The catalog is never dumped in full. One tile per category, in the order the
   owner arranged them on the dashboard, and its products load only when it is
   chosen. The tiles are buttons, not links, because the switch happens without
   a page load. */
.tiles__head {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 600;
  line-height: 1.15;
}
.tiles__hint {
  margin-block-start: 0.4rem;
  margin-block-end: clamp(1.25rem, 0.9rem + 1.2vw, 2rem);
  color: var(--fg-soft);
  font-size: var(--step-0);
}

.tiles {
  display: grid;
  gap: 0.9rem;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}

.tile {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  background: var(--card);
  color: var(--fg);
  font: inherit;
  text-align: start;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  transition:
    border-color var(--ui) var(--ease),
    box-shadow var(--ui) var(--ease),
    transform var(--ui) var(--ease),
    background-color var(--ui) var(--ease);
}
/* The photo, or the icon standing in for one. Fixing the ratio here is what
   keeps a grid of mixed photo / no-photo tiles lined up. */
.tile__media {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 4 / 3;
  background: linear-gradient(150deg, var(--gold-wash), var(--bg-3));
  overflow: hidden;
}
.tile__photo {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  transition: transform var(--slow) var(--ease);
}
.tile:hover .tile__photo { transform: scale(1.06); }

/* A photo carries the tile, so the icon steps back to a small badge rather
   than competing with it. Without a photo it is the whole visual. */
.tile__icon {
  display: grid;
  place-items: center;
  inline-size: 3rem;
  block-size: 3rem;
  border-radius: var(--radius);
  background: var(--gold-wash);
  color: var(--gold-text);
  transition: background-color var(--ui) var(--ease), transform var(--ui) var(--ease);
}
.tile__icon svg { inline-size: 1.5rem; block-size: 1.5rem; }
.tile.has-photo .tile__icon {
  position: absolute;
  inset-block-end: 0.6rem;
  inset-inline-start: 0.6rem;
  inline-size: 2.1rem;
  block-size: 2.1rem;
  background: rgba(14, 14, 16, 0.62);
  color: #F2EFE9;
  backdrop-filter: blur(4px);
}
.tile.has-photo .tile__icon svg { inline-size: 1.05rem; block-size: 1.05rem; }
.tile:hover .tile__icon { transform: rotate(-6deg) scale(1.06); }

.tile__body {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.85rem 1rem 0.95rem;
}
.tile__name {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 600;
  line-height: 1.2;
}
.tile__meta {
  font-size: var(--step--1);
  color: var(--fg-faint);
}

.tile__go {
  position: absolute;
  inset-block-start: 0.7rem;
  inset-inline-end: 0.7rem;
  inline-size: 1.15rem;
  block-size: 1.15rem;
  padding: 0.35rem;
  box-sizing: content-box;
  border-radius: var(--radius-pill);
  background: rgba(14, 14, 16, 0.55);
  color: #F2EFE9;
  backdrop-filter: blur(4px);
  transition: transform var(--ui) var(--ease), background-color var(--ui) var(--ease);
}
.tile:hover .tile__go { transform: translateX(3px); background: var(--gold); }

/* A gold edge that grows in from the left, so the tile reads as pressable. */
.tile::before {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  inline-size: 3px;
  z-index: 2;
  background: var(--gold);
  transform: scaleY(0);
  transform-origin: center;
  transition: transform var(--move) var(--ease);
}
.tile:hover {
  transform: perspective(900px) rotateY(-5deg) rotateX(2deg) translateY(-4px);
  border-color: var(--gold);
  box-shadow: var(--shadow-lg);
}
.tile:hover::before { transform: scaleY(1); }
.tile:active { transform: translateY(-1px) scale(0.99); }

/* --------------------------------------------------------------------------
   Results header — where you are, and how to leave
   -------------------------------------------------------------------------- */
.results__head {
  display: flex;
  align-items: center;
  gap: 0.75rem 1rem;
  flex-wrap: wrap;
  margin-block-end: 1.35rem;
}
.results__title {
  margin-inline-end: auto;
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 600;
  line-height: 1.15;
}
/* The back button reuses the arrow icon, mirrored to point left. */
.btn__back { transform: scaleX(-1); }
#backToCategories:hover .btn__back { transform: scaleX(-1) translateX(3px); }
#backToCategories .btn__back { transition: transform var(--ui) var(--ease); }

@media (max-width: 34rem) {
  /* The tile grid goes single-column and the header stacks, because a title
     squeezed between two buttons on a phone is unreadable. */
  .tiles { grid-template-columns: 1fr; }
  .results__head { gap: 0.6rem; }
  .results__title { inline-size: 100%; order: -1; margin-inline-end: 0; }
}

/* --------------------------------------------------------------------------
   Catalog controls
   -------------------------------------------------------------------------- */
.controls {
  display: flex;
  flex-direction: column;
  gap: 1.15rem;
  margin-block-end: clamp(1.75rem, 1.2rem + 1.5vw, 2.75rem);
}

/* Search. Sits above the chip rows because it is the fastest route to one
   product in a catalog of this size, and because the brand row is a long
   single-line scroll that is hard to scan. */
.search {
  position: relative;
  display: flex;
  align-items: center;
  max-width: 30rem;
}
.search__icon {
  position: absolute;
  inset-inline-start: 0.95rem;
  inline-size: 1.05rem;
  block-size: 1.05rem;
  color: var(--fg-faint);
  pointer-events: none;
  transition: color var(--ui) var(--ease);
}
.search:focus-within .search__icon { color: var(--gold-text); }

.search input {
  inline-size: 100%;
  padding: 0.72rem 2.7rem 0.72rem 2.7rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-pill);
  background: var(--bg-2);
  color: var(--fg);
  font: inherit;
  font-size: var(--step-0);
  transition:
    border-color var(--ui) var(--ease),
    box-shadow var(--ui) var(--ease),
    background-color var(--ui) var(--ease);
}
.search input::placeholder { color: var(--fg-faint); }
.search input:hover { border-color: var(--gold); }
.search input:focus {
  outline: none;
  border-color: var(--gold);
  box-shadow: var(--ring);
  background: var(--bg-3);
}
/* Safari draws its own clear affordance; ours is the one that is styled, and
   two of them side by side look like a mistake. */
.search input::-webkit-search-cancel-button { display: none; }

.search__clear {
  position: absolute;
  inset-inline-end: 0.55rem;
  display: grid;
  place-items: center;
  inline-size: 1.9rem;
  block-size: 1.9rem;
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--fg-faint);
  cursor: pointer;
  transition:
    background-color var(--ui) var(--ease),
    color var(--ui) var(--ease),
    transform var(--ui) var(--ease);
}
.search__clear:hover {
  background: var(--gold-wash);
  color: var(--gold-text);
  transform: rotate(90deg);
}
.search__clear svg { inline-size: 1rem; block-size: 1rem; }

/* Each filter group gets its own labelled row, so neither is easy to miss.
   The label sits above so it does not eat horizontal space on a phone. */
.filter-row { display: block; }

.filter-label {
  display: block;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-faint);
  margin-block-end: 0.6rem;
}

.chips {
  display: flex;
  gap: 0.45rem;
  overflow-x: auto;
  padding-block-end: 0.6rem;
  margin-inline: calc(var(--gutter) * -1);
  padding-inline: var(--gutter);
  /* The scrollbar is kept, not hidden. With 40-odd brands this row is several
     screens wide, and hiding the scrollbar left no sign that more existed — the
     chips past the first few were effectively undiscoverable on a desktop,
     where there is no swipe. Styled down to a thin line rather than removed. */
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}
.chips::-webkit-scrollbar { height: 6px; }
.chips::-webkit-scrollbar-track { background: transparent; }
.chips::-webkit-scrollbar-thumb {
  background: var(--line-strong);
  border-radius: var(--radius-pill);
}
.chips:hover::-webkit-scrollbar-thumb { background: var(--gold); }
/* Firefox has no hover-able thumb, so the colour shift above is Chromium-only
   and that is fine — the affordance itself exists in both. */

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.55rem 0.95rem;
  border: 1px solid var(--line-strong);
  border-radius: 99px;
  background: var(--bg-2);
  color: var(--fg-soft);
  font-size: var(--step--1);
  font-weight: 600;
  white-space: nowrap;
  transition: background var(--dur) var(--ease),
              color var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}
.chip:hover { border-color: var(--gold); color: var(--fg); }
.chip[aria-pressed="true"] {
  background: var(--fg);
  border-color: var(--fg);
  color: var(--bg);
}
.chip svg { inline-size: 1rem; block-size: 1rem; opacity: 0.85; }

.controls__row {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  justify-content: space-between;
}

.count {
  font-size: var(--step--1);
  color: var(--fg-faint);
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------------
   15. Product grid + card
   -------------------------------------------------------------------------- */
.grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 15.5rem), 1fr));
}

.card {
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: border-color var(--dur) var(--ease),
              transform var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.card:hover {
  border-color: var(--line-strong);
  transform: translateY(-3px);
  box-shadow: 0 14px 34px -18px rgba(23, 23, 26, 0.35);
}

.card__media {
  position: relative;
  aspect-ratio: 4 / 3;
  display: grid;
  place-items: center;
  overflow: hidden;
  background:
    linear-gradient(150deg, var(--mono1, #E8E2D6) 0%, var(--mono2, #D8CFBC) 100%);
}
.card__mono {
  font-family: var(--font-display);
  font-size: clamp(1.9rem, 1.2rem + 2.4vw, 2.9rem);
  font-weight: 700;
  letter-spacing: 0.02em;
  color: rgba(23, 23, 26, 0.30);
  user-select: none;
}
.card__img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

.card__badges {
  position: absolute;
  inset-block-start: 0.6rem;
  inset-inline-start: 0.6rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.24rem 0.5rem;
  border-radius: 99px;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: rgba(23, 23, 26, 0.82);
  color: #F7F5F1;
  backdrop-filter: blur(4px);
}
.badge--gold { background: var(--gold); color: #14140F; }
.badge--free { background: #2F5D3A; color: #E6F3E8; }

.card__body {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: 1rem 1rem 1.15rem;
  flex: 1;
}

.card__brand {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--gold-text);
}

.card__name {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 600;
  line-height: 1.25;
}
.card__meta {
  font-size: var(--step--1);
  color: var(--fg-faint);
}

.card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-block-start: auto;
  padding-block-start: 0.9rem;
}

.card__price {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.card__price small { font-size: 0.62em; font-weight: 600; color: var(--fg-faint); margin-inline-start: 0.15rem; }
.card__price--ask { font-family: var(--font-body); font-size: var(--step--1); font-weight: 600; color: var(--gold-text); }

.card__cta {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 0.85rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.card__cta:hover { background: var(--gold); border-color: var(--gold); color: #14140F; }
.card__cta svg { inline-size: 1rem; block-size: 1rem; }

.card.is-out { opacity: 0.62; }
.card.is-out .card__media { filter: grayscale(0.85); }
.card.is-out .card__cta {
  pointer-events: none;
  color: var(--muted);
  border-color: var(--line);
}

/* Per-category monogram tint — keeps the placeholder tiles from looking flat */
[data-cat="molasses"]    { --mono1: #EADFC4; --mono2: #D9C79C; }
[data-cat="charcoal"]    { --mono1: #DCDCDE; --mono2: #C2C3C7; }
[data-cat="pipes"]       { --mono1: #E7DCBE; --mono2: #CBB584; }
[data-cat="vapes"]       { --mono1: #D5E4E2; --mono2: #B3CFCB; }
[data-cat="cigarettes"]  { --mono1: #E8D8D2; --mono2: #D5B8AD; }
[data-cat="rolling"]     { --mono1: #E2E4D2; --mono2: #C8CDAB; }
[data-cat="pouches"]     { --mono1: #D9E2ED; --mono2: #B8C8DC; }
[data-cat="accessories"] { --mono1: #E4E1DA; --mono2: #CBC6BA; }
[data-cat="cigars"]      { --mono1: #E0D2C2; --mono2: #C0A78C; }

.grid__empty {
  grid-column: 1 / -1;
  padding: clamp(2.5rem, 2rem + 3vw, 4.5rem) 1.5rem;
  text-align: center;
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-lg);
  color: var(--fg-soft);
}

/* Skeleton shimmer while the catalog loads */
.skeleton {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.skeleton__media { aspect-ratio: 4 / 3; background: var(--bg-3); }
.skeleton__line {
  block-size: 0.72rem;
  margin: 1rem;
  border-radius: 99px;
  background: linear-gradient(90deg, var(--bg-3) 25%, var(--line) 37%, var(--bg-3) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
}
.skeleton__line--short { inline-size: 55%; }
@keyframes shimmer {
  0%   { background-position: 100% 0; }
  100% { background-position: 0 0; }
}

/* --------------------------------------------------------------------------
   Loading placeholders for the containers the API fills
   --------------------------------------------------------------------------
   #categoryTiles, #rail and #hoursTable ship empty and are drawn from
   api/categories and api/products a moment later. An empty container is shorter
   than the finished one by its whole height, so the page used to grow ~2.6k px
   on the home page — and ~2.9k px on the shop page on a phone, where the tiles
   stack in one column. These placeholders hold that space; the real renderers
   replace them with innerHTML, so they never need removing.

   The lines reserve 1lh instead of a pixel guess, which keeps the geometry level
   with the real thing at every breakpoint without a magic number per size. */
.rail__ghost { pointer-events: none; }
.rail__ghost-body { block-size: 10.5rem; }   /* a product card's body height */

.tile--ghost { pointer-events: none; }
.tile--ghost .tile__media { position: relative; overflow: hidden; }

.tile--ghost .tile__name,
.tile--ghost .tile__meta,
.hours-table td[data-ghost] { min-block-size: 1lh; }

.tile--ghost .tile__name,
.tile--ghost .tile__meta { display: flex; align-items: center; }
.tile--ghost .tile__name::before,
.tile--ghost .tile__meta::before,
.hours-table .ghost-line {
  content: "";
  border-radius: 99px;
  background: linear-gradient(90deg, var(--bg-3) 25%, var(--line) 37%, var(--bg-3) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
}
.tile--ghost .tile__name::before,
.tile--ghost .tile__meta::before { block-size: 0.62em; }
.tile--ghost .tile__name::before { inline-size: 70%; }
.tile--ghost .tile__meta::before { inline-size: 42%; }

/* A category photo fills the tile's media box, so the placeholder shimmers over
   the whole of it rather than one text line's worth. */
.tile--ghost .tile__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, var(--bg-3) 25%, var(--line) 37%, var(--bg-3) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
}

/* Table cells ignore min-block-size, so the placeholder row carries its own
   element, sized to one line box the way a real cell's text is. The margins
   centre the bar inside it, leaving the row exactly as tall as a real one. */
.hours-table .ghost-line {
  display: block;
  block-size: 0.62em;
  margin-block: calc((1lh - 0.62em) / 2);
}
.hours-table td[data-ghost]:first-child .ghost-line { inline-size: 38%; }
.hours-table td[data-ghost]:last-child .ghost-line { inline-size: 58%; margin-inline-start: auto; }

/* Catalog note */
.note {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 0.6rem + 2vw, 2.5rem);
  flex-wrap: wrap;
  justify-content: space-between;
  margin-block-start: clamp(2rem, 1.4rem + 2vw, 3rem);
  padding: clamp(1.5rem, 1rem + 2vw, 2.25rem);
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-inline-start: 3px solid var(--gold);
  border-radius: var(--radius-lg);
}
.note p { color: var(--fg-soft); max-width: 46rem; }

/* --------------------------------------------------------------------------
   Phones: two products per row
   --------------------------------------------------------------------------
   The auto-fill track above needs 15.5rem per column, so below roughly 500px
   it collapsed to a single column and the catalog read like a feed rather
   than a shelf. Two columns are forced here, with the card compacted to suit.

   A ~148px card cannot fit the price and the Order button side by side, and
   both are `white-space: nowrap` — so the foot stacks rather than
   overflowing, and the name and price drop one step on the type scale. */
@media (max-width: 45.99rem) {
  .grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.75rem;
  }

  .card__body {
    padding: 0.8rem 0.75rem 0.9rem;
    gap: 0.25rem;
  }
  .card__name,
  .card__price { font-size: var(--step-0); }
  .card__brand { font-size: 0.62rem; letter-spacing: 0.1em; }

  .card__foot {
    flex-direction: column;
    align-items: stretch;
    gap: 0.5rem;
    padding-block-start: 0.7rem;
  }
  .card__cta {
    justify-content: center;
    padding: 0.5rem 0.5rem;
    font-size: 0.7rem;
    /* "Request price" is the longest label, and on a 320px screen the button
       is only ~102px wide while the icon plus that text needs ~106px. Letting
       it wrap is the safety net — without it, `nowrap` clipped the label. It
       stays on one line from about 340px up. */
    white-space: normal;
    text-align: center;
    line-height: 1.2;
  }

  .card__badges {
    inset-block-start: 0.4rem;
    inset-inline-start: 0.4rem;
    gap: 0.25rem;
  }
  .badge { font-size: 0.6rem; padding: 0.2rem 0.42rem; }
}

/* --------------------------------------------------------------------------
   16. Steps (how ordering works)
   -------------------------------------------------------------------------- */
.steps {
  display: grid;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-block-start: clamp(2rem, 1.4rem + 2vw, 3rem);
}
@media (min-width: 52rem) { .steps { grid-template-columns: repeat(3, 1fr); } }

.step {
  background: var(--bg-2);
  padding: clamp(1.5rem, 1rem + 2vw, 2.25rem);
}
.step__n {
  display: grid;
  place-items: center;
  inline-size: 2.1rem;
  block-size: 2.1rem;
  margin-block-end: 1rem;
  border: 1px solid rgba(201, 162, 39, 0.45);
  border-radius: 50%;
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--gold);
}
.step b { display: block; font-family: var(--font-display); font-size: var(--step-1); font-weight: 600; }
.step p { color: var(--fg-soft); font-size: var(--step--1); margin-block-start: 0.5rem; }

/* --------------------------------------------------------------------------
   17. Two-column info (delivery / payments)
   -------------------------------------------------------------------------- */
.cols {
  display: grid;
  gap: clamp(1.5rem, 1rem + 3vw, 3.5rem);
  margin-block-start: clamp(2rem, 1.4rem + 2vw, 3rem);
}
@media (min-width: 58rem) { .cols { grid-template-columns: repeat(2, 1fr); } }

.info-block h3 {
  font-size: var(--step-1);
  margin-block-end: 1rem;
  padding-block-end: 0.75rem;
  border-block-end: 1px solid var(--line);
}

.taglist { display: flex; flex-wrap: wrap; gap: 0.45rem; }
.taglist li {
  padding: 0.42rem 0.8rem;
  border: 1px solid var(--line);
  border-radius: 99px;
  background: var(--bg-2);
  font-size: var(--step--1);
  color: var(--fg-soft);
}

.deflist { display: flex; flex-direction: column; }
.deflist > div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.5rem;
  padding-block: 0.7rem;
  border-block-end: 1px solid var(--line);
}
.deflist dt { font-size: var(--step--1); color: var(--fg-soft); }
.deflist dd { font-weight: 600; text-align: end; font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------------------
   18. Wholesale
   -------------------------------------------------------------------------- */
.wholesale {
  position: relative;
  overflow: hidden;
  background: var(--bg-2);
  border-block: 1px solid var(--line);
}
.wholesale__glow {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(ellipse 60% 70% at 88% 50%, rgba(201, 162, 39, 0.13), transparent 70%);
}
.wholesale__inner { position: relative; }
.wholesale__grid {
  display: grid;
  gap: clamp(2rem, 1.2rem + 4vw, 4rem);
  align-items: center;
}
@media (min-width: 60rem) { .wholesale__grid { grid-template-columns: 1.1fr 0.9fr; } }

.points { display: flex; flex-direction: column; gap: 0.9rem; }
.points li { display: flex; gap: 0.75rem; align-items: flex-start; color: var(--fg-soft); }
.points svg { inline-size: 1.15rem; block-size: 1.15rem; color: var(--gold); flex: none; margin-block-start: 0.18rem; }

/* --------------------------------------------------------------------------
   19. About
   -------------------------------------------------------------------------- */
.about__grid {
  display: grid;
  gap: clamp(2rem, 1.2rem + 4vw, 4.5rem);
}
@media (min-width: 62rem) { .about__grid { grid-template-columns: 0.85fr 1.15fr; } }

.about__body p + p { margin-block-start: 1.1rem; }
.about__body p { color: var(--fg-soft); }

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-block-start: clamp(2rem, 1.4rem + 2vw, 3rem);
}
.stat { background: var(--bg-2); padding: clamp(1.1rem, 0.8rem + 1.4vw, 1.75rem); }
.stat b {
  display: block;
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 700;
  color: var(--gold-text);
}
.stat span { font-size: var(--step--1); color: var(--fg-faint); }

/* --------------------------------------------------------------------------
   20. Shop
   -------------------------------------------------------------------------- */
.mosaic {
  display: grid;
  gap: 0.85rem;
  grid-template-columns: repeat(2, 1fr);
  grid-auto-rows: 1fr;
}
@media (min-width: 46rem) { .mosaic { grid-template-columns: repeat(4, 1fr); } }

.mosaic__item {
  position: relative;
  aspect-ratio: 1;
  border-radius: var(--radius-xl);
  overflow: hidden;
  border: 1px solid var(--line);
  background: linear-gradient(150deg, var(--bg-3), var(--bg-2));
  display: grid;
  place-items: center;
}
/* Until real photos exist, each tile is a designed panel rather than an empty
   box: a warm gold bloom over the base gradient, plus a large watermark icon.
   The bloom is positioned differently per tile so the grid reads as art
   direction instead of seven identical "missing image" rectangles. Add photos
   to assets/img/shop/ and run `npm run photos` to replace all of it. */
.mosaic__item::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(90% 70% at 22% 4%, rgba(201, 162, 39, .20), transparent 62%);
  transition: opacity var(--move) var(--ease);
}
.mosaic__item:nth-child(2)::before {
  background: radial-gradient(90% 70% at 78% 8%, rgba(201, 162, 39, .22), transparent 62%);
}
.mosaic__item:nth-child(3)::before {
  background: radial-gradient(110% 80% at 50% 100%, rgba(201, 162, 39, .18), transparent 66%);
}
.mosaic__item:nth-child(4)::before {
  background: radial-gradient(80% 90% at 4% 60%, rgba(201, 162, 39, .20), transparent 64%);
}
.mosaic__item:nth-child(5)::before {
  background: radial-gradient(95% 75% at 90% 92%, rgba(201, 162, 39, .18), transparent 62%);
}
.mosaic__item:nth-child(6)::before {
  background: radial-gradient(120% 70% at 30% 100%, rgba(201, 162, 39, .20), transparent 68%);
}
.mosaic__item:nth-child(7)::before {
  background: radial-gradient(85% 85% at 60% 10%, rgba(201, 162, 39, .22), transparent 60%);
}
/* Feature tiles — one tall tile and one wide tile break the mosaic up on wider
   screens. Phones deliberately get a plain two-up grid instead: a full-width
   tile there means one photo per row. */
@media (min-width: 46rem) {
  .mosaic__item:nth-child(1) { grid-column: span 2; grid-row: span 2; aspect-ratio: 1; }
  .mosaic__item:nth-child(6) { grid-column: span 2; aspect-ratio: 2 / 1; }
}
/* On phones the last tile spans the full width, but only when it would
   otherwise sit alone on its row. */
@media (max-width: 45.99rem) {
  .mosaic > :last-child:nth-child(odd) { grid-column: span 2; aspect-ratio: 2 / 1; }
}

/* The photo fills the tile instead of setting its size. Out of flow, it cannot
   push the square open: the tiles stay exactly as the grid below describes them
   (square, one 2×2 feature, a 2:1 full-width tile on phones), and the 4:5 photos
   are cover-cropped to that shape. In flow, a 4:5 photo would silently win over
   `aspect-ratio` and every tile would grow taller — and resize again as each
   lazy image arrived. Being absolute also puts the photo above the tile's gold
   bloom, which is there for the empty state, not for a photograph. */
.mosaic__item img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}
.mosaic__ph {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  padding: 1rem;
  color: var(--fg-faint);
  font-size: var(--step--1);
  text-align: center;
}
/* A watermark, not an error state: large, low-opacity, and gold so the tiles
   look deliberate from across the room. */
.mosaic__ph svg {
  inline-size: clamp(2.5rem, 7vw, 4.25rem);
  block-size: clamp(2.5rem, 7vw, 4.25rem);
  color: var(--gold-text);
  opacity: 0.30;
}
.mosaic__ph span {
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.75;
}
.mosaic__item:hover .mosaic__ph { color: var(--gold-text); }

/* --------------------------------------------------------------------------
   21. FAQ
   -------------------------------------------------------------------------- */
.faq { display: flex; flex-direction: column; }
.faq__item { border-block-end: 1px solid var(--line); }
.faq__item:first-child { border-block-start: 1px solid var(--line); }

.faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.25rem;
  padding-block: clamp(1.05rem, 0.85rem + 0.8vw, 1.5rem);
  cursor: pointer;
  list-style: none;
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 600;
  transition: color var(--dur) var(--ease);
}
.faq__q::-webkit-details-marker { display: none; }
.faq__q:hover { color: var(--gold-text); }

.faq__icon {
  position: relative;
  inline-size: 1.15rem;
  block-size: 1.15rem;
  flex: none;
}
.faq__icon::before,
.faq__icon::after {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 0;
  inline-size: 100%;
  block-size: 1px;
  background: currentColor;
  transition: scale var(--dur) var(--ease), rotate var(--dur) var(--ease);
}
.faq__icon::after { rotate: 90deg; }
.faq__item[open] .faq__icon::after { scale: 0 1; }
.faq__item[open] .faq__q { color: var(--gold-text); }

.faq__a {
  padding-block-end: clamp(1.1rem, 0.9rem + 0.8vw, 1.6rem);
  color: var(--fg-soft);
  max-width: 52rem;
}

/* --------------------------------------------------------------------------
   22. Visit
   -------------------------------------------------------------------------- */
.visit__grid {
  display: grid;
  gap: clamp(2rem, 1.2rem + 4vw, 4rem);
}
@media (min-width: 62rem) { .visit__grid { grid-template-columns: 1fr 1.1fr; } }

.visit__rows { display: flex; flex-direction: column; }

.visit__row {
  padding-block: 1.15rem;
  border-block-end: 1px solid var(--line);
}
.visit__row:first-child { border-block-start: 1px solid var(--line); }

.visit__row dt {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--fg-faint);
}
.visit__row dt svg { inline-size: 1.15rem; block-size: 1.15rem; color: var(--gold); }

/* Value sits under its label, clear of the icon column */
.visit__row dd { font-size: var(--step-1); margin-block-start: 0.4rem; padding-inline-start: 1.75rem; }
.visit__row dd a:hover { color: var(--gold-text); }
.visit__row .hours-table { margin-block-start: 0.5rem; }

.status {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.25rem 0.7rem;
  border-radius: 99px;
  font-size: var(--step--1);
  font-weight: 600;
  margin-inline-start: 0.6rem;
  vertical-align: middle;
}
.status--open   { background: rgba(111, 191, 115, 0.14); color: #7FD08A; border: 1px solid rgba(111, 191, 115, 0.3); }
.status--closed { background: rgba(201, 106, 90, 0.14);  color: #D98A7B; border: 1px solid rgba(201, 106, 90, 0.3); }
.status[hidden] { display: none; }
.status__dot { inline-size: 0.45rem; block-size: 0.45rem; border-radius: 50%; background: currentColor; }

.hours-table { inline-size: 100%; font-size: var(--step--1); }
.hours-table td { padding-block: 0.3rem; }
.hours-table td:last-child { text-align: end; color: var(--fg-faint); font-variant-numeric: tabular-nums; }
.hours-table tr[data-today="true"] td { color: var(--gold-text); font-weight: 700; }

.map {
  position: relative;
  min-block-size: 20rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--bg-2);
}
.map iframe { inline-size: 100%; block-size: 100%; min-block-size: 20rem; border: 0; filter: grayscale(0.55) contrast(1.05); }
.map__ph {
  display: grid;
  place-items: center;
  block-size: 100%;
  min-block-size: 20rem;
  padding: 2rem;
  text-align: center;
  color: var(--fg-faint);
  gap: 0.6rem;
}
.map__ph svg { inline-size: 1.75rem; block-size: 1.75rem; opacity: 0.5; }

.visit__actions { display: flex; flex-wrap: wrap; gap: 0.7rem; margin-block-start: 1.75rem; }

/* --------------------------------------------------------------------------
   23. Footer
   -------------------------------------------------------------------------- */
.footer {
  background: #0A0A0C;
  border-block-start: 1px solid var(--line);
  padding-block: clamp(2.75rem, 2rem + 3vw, 4.5rem);
}
.footer__grid {
  display: grid;
  gap: clamp(2rem, 1.2rem + 3vw, 3.5rem);
}
@media (min-width: 44rem) { .footer__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 68rem) { .footer__grid { grid-template-columns: 1.5fr 1fr 1fr 1.1fr; } }

.footer h4 {
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--gold-text);
  margin-block-end: 1.1rem;
}

.footer p { color: var(--fg-faint); font-size: var(--step--1); }
.footer__brand p { margin-block-start: 1rem; max-width: 26rem; }

.footer__list { display: flex; flex-direction: column; gap: 0.6rem; }
.footer__list a,
.footer__list span { font-size: var(--step--1); color: var(--fg-soft); }
.footer__list a:hover { color: var(--gold-text); }

.footer__social { display: flex; gap: 0.5rem; margin-block-start: 1.1rem; }
.footer__social a {
  display: grid;
  place-items: center;
  inline-size: 2.4rem;
  block-size: 2.4rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--fg-soft);
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.footer__social a:hover { color: var(--gold-text); border-color: var(--gold); }
.footer__social svg { inline-size: 1.15rem; block-size: 1.15rem; }

.footer__bottom {
  margin-block-start: clamp(2.5rem, 2rem + 2vw, 3.5rem);
  padding-block-start: 1.75rem;
  border-block-start: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: space-between;
  align-items: center;
}
.footer__bottom p { font-size: var(--step--1); }

.footer__age {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.85rem;
  border: 1px solid rgba(201, 162, 39, 0.35);
  border-radius: 99px;
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--gold-text);
  letter-spacing: 0.06em;
}

/* --------------------------------------------------------------------------
   24. Sticky action bar (mobile)
   -------------------------------------------------------------------------- */
.bar {
  position: fixed;
  inset-block-end: 0;
  inset-inline: 0;
  z-index: 80;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem;
  padding: 0.6rem var(--gutter) calc(0.6rem + env(safe-area-inset-bottom, 0px));
  background: rgba(14, 14, 16, 0.94);
  backdrop-filter: blur(14px);
  border-block-start: 1px solid var(--line);
  transform: translateY(110%);
  transition: transform var(--dur) var(--ease);
}
.bar.is-visible { transform: translateY(0); }

.bar__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding-block: 0.85rem;
  border-radius: var(--radius);
  font-size: var(--step--1);
  font-weight: 700;
  transition: filter var(--dur) var(--ease);
}
.bar__btn svg { inline-size: 1.1rem; block-size: 1.1rem; }
.bar__btn--wa { background: #25D366; color: #062B13; }
.bar__btn--call { background: var(--bg-3); color: var(--fg); border: 1px solid var(--line-strong); }
.bar__btn:hover { filter: brightness(1.08); }

/* Keep the footer clear of the bar */
body { padding-block-end: calc(4.4rem + env(safe-area-inset-bottom, 0px)); }
@media (min-width: 62rem) {
  .bar { display: none; }
  body { padding-block-end: 0; }
}

/* --------------------------------------------------------------------------
   25. Motion system
   --------------------------------------------------------------------------
   Everything here animates transform/opacity only, so nothing triggers layout
   and the phone stays smooth. All of it is disabled under
   prefers-reduced-motion (see section 26).
   -------------------------------------------------------------------------- */

/* ==========================================================================
   25.1  Timing tokens
   ==========================================================================
   --dur and --ease come from the tokens at the top of this file. These are the
   longer values, for movements that should read as deliberate rather than
   twitchy.

   Every entrance animation below uses `backwards` fill, and none of the
   keyframes declares a `to` state. That pairing matters: `backwards` applies
   the start state during any `animation-delay` and then hands the element back
   to the cascade, so nothing owns `transform` afterwards. A keyframe with an
   explicit `to` state plus a forwards fill *does* pin the property and would
   block every later :hover transition on that element — so if you add a `to`,
   drop the forwards fill. */
:root {
  --ui:     0.3s;   /* colour, border, shadow */
  --move:   0.5s;   /* small movements: lifts, slides */
  --travel: 0.7s;   /* arrivals: reveals, cards */
  --slow:   0.9s;   /* deliberate one-offs: sheen, underline draws */
}

/* ==========================================================================
   25.2  Baseline — every remaining interactive surface reacts
   ==========================================================================
   Listed by hand rather than reaching for `*`, so we never animate a layout
   property by accident. Anything that already declares a transition elsewhere
   keeps it; this fills the gaps. */
.announce__close,
.burger,
.burger svg,
.hero__tile,
.logo__mark,
.rail-nav button,
.rail-nav button svg,
.mosaic__ph,
.gate__mark,
.hours-table td,
.hours-table th,
.card__badges {
  transition:
    color var(--ui) var(--ease),
    background-color var(--ui) var(--ease),
    border-color var(--ui) var(--ease),
    box-shadow var(--ui) var(--ease),
    opacity var(--ui) var(--ease),
    transform var(--ui) var(--ease);
}

.burger:hover {
  border-color: var(--gold);
  color: var(--gold-text);
  transform: translateY(-1px);
}
.burger:active { transform: translateY(0) scale(0.94); }
.burger:hover svg { transform: rotate(90deg); }

.logo:hover .logo__mark { transform: rotate(-6deg) scale(1.07); }

.rail-nav button:active { transform: scale(0.92); }
.rail-nav button:hover svg { transform: scale(1.15); }

.hours-table tbody tr { transition: background-color var(--ui) var(--ease); }
.hours-table tbody tr:hover { background-color: var(--gold-wash); }

/* ==========================================================================
   25.3  Buttons — lift on hover, press on click, icon leans in
   ========================================================================== */
.btn {
  transition:
    background-color var(--ui) var(--ease),
    border-color var(--ui) var(--ease),
    color var(--ui) var(--ease),
    box-shadow var(--ui) var(--ease),
    transform 0.18s var(--ease);
}
.btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 24px -14px rgba(0, 0, 0, 0.65);
}
.btn--gold:hover,
.btn--solid:hover { box-shadow: 0 12px 28px -12px rgba(201, 162, 39, 0.6); }
.btn:active { transform: translateY(0) scale(0.98); box-shadow: none; }
.btn svg { transition: transform var(--ui) var(--ease); }
.btn:hover svg { transform: scale(1.14) rotate(-5deg); }

/* ==========================================================================
   25.4  Navigation — active underline slides in, hover adds a pill
   ========================================================================== */
.nav__link {
  transition:
    color var(--ui) var(--ease),
    background-color var(--ui) var(--ease);
}
.nav__link:hover { background-color: rgba(242, 239, 233, 0.05); }
.nav__link::after {
  block-size: 2px;
  border-radius: 2px;
  box-shadow: 0 0 10px var(--gold);
  transition: scale 0.38s var(--ease);
}

/* ==========================================================================
   25.5  Header — the bar deepens and casts a shadow once it detaches
   ========================================================================== */
.header {
  transition:
    background-color var(--ui) var(--ease),
    border-color var(--ui) var(--ease),
    box-shadow var(--ui) var(--ease);
}
.header.is-stuck { box-shadow: 0 12px 32px -22px rgba(0, 0, 0, 0.9); }

/* ==========================================================================
   25.6  Announcement bar — slides up and out on dismiss
   ==========================================================================
   app.js adds .is-leaving, waits for the transition, then sets [hidden]. */
.announce {
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease);
}
.announce.is-leaving { opacity: 0; transform: translateY(-100%); }
.announce__close:hover { transform: rotate(90deg); }

/* ==========================================================================
   25.7  Age gate — the panel rises on arrival, lifts away on confirm
   ========================================================================== */
.gate { transition: opacity 0.4s var(--ease); }
.gate__box { transition: opacity 0.5s var(--ease), transform 0.5s var(--ease); }

@media (prefers-reduced-motion: no-preference) {
  .js .gate:not([hidden]) .gate__box {
    animation: gate-in 0.62s var(--ease) backwards;
  }
  .js .gate:not([hidden]) .gate__mark {
    animation: gate-in 0.62s var(--ease) 0.1s backwards;
  }
  .gate.is-denied .gate__denied { animation: gate-in 0.42s var(--ease) backwards; }
}
@keyframes gate-in {
  from { opacity: 0; transform: translateY(16px) scale(0.97); }
}

.gate.is-leaving { opacity: 0; }
.gate.is-leaving .gate__box { opacity: 0; transform: translateY(-14px) scale(0.98); }

/* ==========================================================================
   25.8  Mobile drawer — fades and slides, links cascade in
   ==========================================================================
   This used to be display:none / display:block, which cannot animate. It now
   stays rendered but `visibility: hidden`, so while closed it takes no
   clicks, is not focusable and stays out of the accessibility tree. */
.drawer {
  display: block;
  visibility: hidden;
  opacity: 0;
  transform: translateY(-10px);
  transition:
    opacity var(--ui) var(--ease),
    transform 0.42s var(--ease),
    visibility 0s linear var(--ui);
}
.drawer.is-open {
  visibility: visible;
  opacity: 1;
  transform: none;
  transition:
    opacity var(--ui) var(--ease),
    transform 0.42s var(--ease),
    visibility 0s;
}

.drawer__list a {
  transition:
    color var(--ui) var(--ease),
    padding-inline-start var(--ui) var(--ease);
}
.drawer__list a:hover { padding-inline-start: 0.6rem; }

@media (prefers-reduced-motion: no-preference) {
  .drawer.is-open .drawer__list a { animation: drawer-link 0.42s var(--ease) backwards; }
  .drawer.is-open .drawer__list a:nth-child(1) { animation-delay: 0.06s; }
  .drawer.is-open .drawer__list a:nth-child(2) { animation-delay: 0.11s; }
  .drawer.is-open .drawer__list a:nth-child(3) { animation-delay: 0.16s; }
  .drawer.is-open .drawer__list a:nth-child(4) { animation-delay: 0.21s; }
  .drawer.is-open .drawer__list a:nth-child(n+5) { animation-delay: 0.26s; }
}
@keyframes drawer-link { from { opacity: 0; transform: translateX(-14px); } }

/* ==========================================================================
   25.9  Hero — the glow drifts, tiles lift, icon leans
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {
  .hero__glow { animation: glow-drift 18s ease-in-out infinite alternate; }
}
@keyframes glow-drift {
  from { transform: translate3d(-2%, -1%, 0) scale(1); }
  to { transform: translate3d(2%, 2%, 0) scale(1.09); }
}

.hero__tile:hover {
  transform: translateY(-4px);
  border-color: rgba(201, 162, 39, 0.4);
  box-shadow: 0 14px 30px -20px rgba(0, 0, 0, 0.85);
}
.hero__tile svg { transition: transform var(--move) var(--ease); }
.hero__tile:hover svg { transform: scale(1.16) rotate(-6deg); }

/* Hero headline — each word rises into place on its own. The spans are created
   by initHeroTitle() in js/app.js, which needs the i18n text in place first.
   Only a `from` state, so nothing owns `transform` once the word has landed. */
.hero__word {
  display: inline-block;
  animation: word-in 0.62s var(--ease) backwards;
  animation-delay: var(--word-delay, 0ms);
}
@keyframes word-in {
  from { opacity: 0; transform: translateY(0.44em) scale(0.97); }
}

/* A light sweep across the two panel grids. It sits above the tile background
   but under the content, and is clipped by the tile's own radius. */
.hero__tile,
.trust__item { position: relative; overflow: hidden; }
.hero__tile::after,
.trust__item::after {
  content: "";
  position: absolute;
  inset-block: -40%;
  inset-inline-start: -60%;
  inline-size: 45%;
  pointer-events: none;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.09), transparent);
  transform: translateX(-120%) rotate(8deg);
  transition: transform var(--slow) var(--ease);
}
.hero__tile:hover::after,
.trust__item:hover::after { transform: translateX(320%) rotate(8deg); }

/* ==========================================================================
   25.10  Section headings — the gold rule beside the eyebrow draws out
   ========================================================================== */
.eyebrow::before { transition: inline-size var(--travel) var(--ease) 0.12s; }
.js .is-in > .eyebrow::before,
.js .is-in .eyebrow::before { inline-size: 2.6rem; }

/* ==========================================================================
   25.11  Filter chips — lift, press, and pop when selected
   ========================================================================== */
.chip {
  transition:
    background-color var(--ui) var(--ease),
    color var(--ui) var(--ease),
    border-color var(--ui) var(--ease),
    box-shadow var(--ui) var(--ease),
    transform 0.22s var(--ease);
}
.chip:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 18px -12px rgba(201, 162, 39, 0.85);
}
.chip:active { transform: translateY(0) scale(0.95); }

@media (prefers-reduced-motion: no-preference) {
  .chip[aria-pressed="true"] { animation: chip-pop 0.36s var(--ease); }
}
@keyframes chip-pop {
  0% { transform: scale(1); }
  45% { transform: scale(1.08); }
  100% { transform: scale(1); }
}

/* ==========================================================================
   25.12  Product cards — lift, gold edge, deep shadow, light sweep
   ========================================================================== */
.card:hover {
  border-color: rgba(201, 162, 39, 0.42);
  transform: translateY(-5px);
  box-shadow:
    0 22px 44px -26px rgba(0, 0, 0, 0.75),
    0 0 0 1px rgba(201, 162, 39, 0.13);
}
.card:hover .card__badges { transform: translateY(-2px); }

.card__cta {
  transition:
    background-color var(--ui) var(--ease),
    border-color var(--ui) var(--ease),
    color var(--ui) var(--ease),
    transform var(--ui) var(--ease);
}
.card__cta:hover { transform: translateY(-1px); }
.card__cta svg { transition: transform var(--ui) var(--ease); }
.card__cta:hover svg { transform: translateX(3px); }

/* Photos arrive rather than pop: fade in *and* sharpen from a blur. The blur
   is what makes the arrival obvious on a real photograph.

   The `.js` guard means a visitor without JavaScript still sees the photos. */
.card__img,
.mosaic__item img {
  transition:
    opacity 0.6s ease,
    filter var(--travel) var(--ease),
    transform 0.8s var(--ease);
}
.js .card__img,
.js .mosaic__item img {
  opacity: 0;
  filter: blur(10px);
}
.js .card__img.is-loaded,
.js .mosaic__item img.is-loaded {
  opacity: 1;
  filter: blur(0);
}

.card:hover .card__img { transform: scale(1.06); }
.mosaic__item:hover img { transform: scale(1.05); }

/* A soft scrim that rises on hover, to make the zoom feel deliberate. */
.card__media::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(to top, rgba(23, 23, 26, 0.22), transparent 62%);
  opacity: 0;
  transition: opacity 0.4s var(--ease);
}
.card:hover .card__media::after { opacity: 1; }
.card__badges { z-index: 3; }

/* A slow light sweep across the media on hover — the classic "expensive"
   tell. Kept faint on purpose; anything stronger reads as a cheap template. */
.card__media::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(105deg,
    transparent 38%, rgba(255, 255, 255, 0.34) 50%, transparent 62%);
  transform: translateX(-130%);
  opacity: 0;
  transition: transform 0.85s var(--ease), opacity 0.2s ease;
}
.card:hover .card__media::before { transform: translateX(130%); opacity: 1; }

/* The monogram tile is the photo slot until a real image is supplied, so it
   gets the same treatment: it responds now, and swapping in an <img> inherits
   the fade and zoom above without touching the CSS. */
.card__mono {
  transition: transform var(--travel) var(--ease), color 0.4s ease;
}
.card:hover .card__mono {
  transform: scale(1.09) rotate(-1.5deg);
  color: rgba(23, 23, 26, 0.42);
}

/* Shop tiles (the mosaic)
   The lift is paired with a brightening bloom and a gold border, so hovering a
   tile reads clearly even when the tile is mostly flat gradient. */
.mosaic__item {
  transition:
    transform var(--move) var(--ease),
    border-color 0.4s ease,
    box-shadow var(--ui) var(--ease);
}
.mosaic__item:hover {
  transform: perspective(900px) rotateY(-8deg) rotateX(4deg) translateY(-7px) scale(1.02);
  border-color: var(--gold);
  box-shadow: var(--shadow-lg);
}
.mosaic__item:hover::before { opacity: 1.35; }
.mosaic__ph svg { transition: transform 0.6s var(--ease), opacity 0.4s ease; }
.mosaic__item:hover .mosaic__ph svg { transform: scale(1.16) rotate(-2deg); opacity: 0.55; }

/* ==========================================================================
   25.13  Cards arriving after a filter change
   ==========================================================================
   `card-in` declares only a `from` state, so `backwards` is enough and the
   element is handed back to the cascade once it lands (see 25.1). */
@media (prefers-reduced-motion: no-preference) {
  .card--enter {
    animation: card-in 0.5s var(--ease) backwards;
    animation-delay: var(--enter-delay, 0ms);
  }
}
@keyframes card-in {
  from { opacity: 0; transform: translateY(14px) scale(0.985); }
}

/* ==========================================================================
   25.14  Sticky action bar — buttons rise in sequence
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {
  .bar.is-visible .bar__btn { animation: bar-in 0.42s var(--ease) backwards; }
  .bar.is-visible .bar__btn:nth-child(2) { animation-delay: 0.07s; }
}
@keyframes bar-in { from { opacity: 0; transform: translateY(14px); } }

/* ==========================================================================
   25.15  Lists, grids and rows — gentle response to hover
   ==========================================================================
   .trust__item, .step and .stat are cells of a hairline grid: the container
   uses `gap: 1px` over a line-coloured background so the gaps read as borders.
   Translating a cell would slide it off that 1px seam and expose the
   background underneath, so these three change colour and move their *inner*
   content instead. .points and .visit__row are ordinary stacks and can move. */
.trust__item,
.step,
.stat,
.points li,
.visit__row {
  transition:
    transform var(--ui) var(--ease),
    background-color var(--ui) var(--ease);
}
.trust__item:hover,
.step:hover,
.stat:hover { background-color: var(--gold-wash); }

.trust__item svg { transition: transform var(--ui) var(--ease); }
.trust__item:hover svg { transform: scale(1.18) rotate(-5deg); }

.step__n {
  transition:
    transform var(--ui) var(--ease),
    background-color var(--ui) var(--ease),
    border-color var(--ui) var(--ease),
    color var(--ui) var(--ease);
}
.step:hover .step__n {
  transform: scale(1.12);
  background-color: var(--gold);
  border-color: var(--gold);
  color: #14140F;
}

.stat b { transition: transform var(--ui) var(--ease); }
.stat:hover b { transform: translateY(-2px); }

.points li:hover { transform: translateX(5px); }
.points li svg { transition: transform var(--ui) var(--ease); }
.points li:hover svg { transform: scale(1.2); }

.visit__row dt svg { transition: transform var(--ui) var(--ease); }
.visit__row:hover dt svg { transform: scale(1.14); }

/* Inline links draw their underline in from the left rather than toggling on.
   background-size is used instead of text-decoration because it transitions. */
.visit__row dd a,
.footer__list a {
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 0% 1px;
  background-position: 0 100%;
  background-repeat: no-repeat;
  transition:
    background-size 0.4s var(--ease),
    color var(--ui) var(--ease),
    transform var(--ui) var(--ease);
}
.visit__row dd a:hover,
.footer__list a:hover { background-size: 100% 1px; }
.footer__list a:hover { transform: translateX(4px); }

/* ==========================================================================
   25.16  FAQ — the answer unrolls, the icon turns into a minus
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {
  .faq__item[open] .faq__a { animation: faq-in 0.42s var(--ease) backwards; }
}
@keyframes faq-in { from { opacity: 0; transform: translateY(-8px); } }

.faq__q {
  transition:
    color var(--ui) var(--ease),
    padding-inline-start 0.42s var(--ease);
}
.faq__q:hover { padding-inline-start: 0.4rem; }

.faq__icon::before,
.faq__icon::after {
  transition:
    scale var(--ui) var(--ease),
    rotate 0.42s var(--ease),
    background-color var(--ui) var(--ease);
}
.faq__item[open] .faq__icon::before { rotate: 180deg; }

/* Chrome 131+ can animate the real block-size of a <details>, so closing
   animates too. Everywhere else the answer still animates open and the close
   is instant, which is a native <details> limitation, not a bug. */
@supports selector(::details-content) {
  .faq__item { interpolate-size: allow-keywords; }
  .faq__item::details-content {
    block-size: 0;
    overflow: hidden;
    transition: block-size 0.4s var(--ease), content-visibility 0.4s allow-discrete;
  }
  .faq__item[open]::details-content { block-size: auto; }
}

/* ==========================================================================
   25.17  Footer and warning ribbon
   ========================================================================== */
.footer__social a:hover { transform: translateY(-3px); }
.footer__social svg { transition: transform var(--ui) var(--ease); }
.footer__social a:hover svg { transform: scale(1.14); }

/* A slow pulse on the "draft, not approved" ribbon. It is a warning, so it is
   allowed to quietly ask for attention. */
@media (prefers-reduced-motion: no-preference) {
  .legal-ribbon { animation: ribbon-pulse 3.4s ease-in-out infinite; }
}
@keyframes ribbon-pulse { 50% { background-color: #4A2E14; } }

/* ==========================================================================
   25.18  Page-to-page transitions (cross-document View Transitions API)
   ==========================================================================
   Chrome 126+, Edge, Safari 18+. Browsers without support simply navigate
   normally — there is no fallback to maintain. The whole page crossfades and
   drifts, which reads as "the new page arrived" rather than a hard cut. */
@view-transition {
  navigation: auto;
}

@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root) {
    animation: vt-out 0.42s var(--ease) both;
  }
  ::view-transition-new(root) {
    animation: vt-in 0.62s var(--ease) both;
  }
}
/* The flip between pages: the outgoing page rotates away on the Y axis while
   the incoming one swings in from the opposite side, so it reads as one
   surface turning over rather than two pages crossfading. */
@keyframes vt-out {
  to {
    opacity: 0;
    transform: perspective(1400px) rotateY(-15deg) translateX(-4%) scale(0.95);
  }
}
@keyframes vt-in {
  from {
    opacity: 0;
    transform: perspective(1400px) rotateY(17deg) translateX(5%) scale(0.95);
  }
}

/* ==========================================================================
   25.19  Reading progress bar
   ========================================================================== */
.progress {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  block-size: 3px;
  z-index: 100;
  pointer-events: none;
}
.progress span {
  display: block;
  block-size: 100%;
  background: linear-gradient(90deg, var(--gold), var(--gold-soft));
  box-shadow: 0 0 12px rgba(201, 162, 39, 0.65);
  transform: scaleX(0);
  /* transform-origin has no logical equivalent; the site is LTR-only. */
  transform-origin: 0 50%;
  transition: transform 0.15s linear;
}

/* ==========================================================================
   25.20  Section reveal
   ==========================================================================
   The hidden state is gated behind `.js`, which app.js adds to <html> on
   boot. If the script never runs, nothing is hidden — content failing to
   appear is a far worse outcome than losing an animation. */
.reveal {
  transition: opacity var(--travel) var(--ease), transform var(--travel) var(--ease);
  transition-delay: var(--reveal-delay, 0ms);
}
/* The hidden state carries opacity only. The movement is a flip ANIMATION rather
   than a transform left on this rule, for the reason spelled out in 25.1: a
   transform still in the cascade after `.is-in` would out-specify every later
   :hover lift. With a `from`-only keyframe and `backwards` fill, nothing owns
   `transform` once the flip has landed. */
.js .reveal { opacity: 0; }
.js .reveal.is-in { opacity: 1; }

@media (prefers-reduced-motion: no-preference) {
  .js .reveal.is-in {
    animation: flip-in 0.75s var(--ease) backwards;
    animation-delay: var(--reveal-delay, 0ms);
  }
}

/* Arrives like a card being turned over: it rotates up from below the fold and
   twists slightly on the Y axis, so a section reads as flipping into place
   instead of sliding. */
@keyframes flip-in {
  from {
    opacity: 0;
    transform: perspective(1100px) rotateX(-17deg) rotateY(7deg) translateY(36px) scale(0.985);
  }
}

/* Staggered variant: the container stays put and its children arrive in
   sequence. Used for grids and lists where a cascade reads better than one
   block fading in.

   The arrival is an ANIMATION, not a transition, and that is deliberate.
   Declaring `transition` here would replace each child's own transition list —
   these children are cards, mosaic tiles and grid cells that each declare
   their own — silently killing their hover animations. An animation leaves
   `transition` untouched.

   The hidden state uses `:not(.is-in)` rather than a base rule so it
   disappears the moment the container is revealed, and `backwards` fill means
   nothing keeps owning `transform` afterwards. */
.js .reveal-stagger:not(.is-in) > * { opacity: 0; }

@media (prefers-reduced-motion: no-preference) {
  .js .reveal-stagger.is-in > * {
    animation: stagger-in 0.62s var(--ease) backwards;
    /* calc() so a --reveal-delay on the container shifts the whole cascade
       instead of being overridden by the rules below. */
    animation-delay: calc(var(--reveal-delay, 0ms) + 0ms);
  }
  .js .reveal-stagger.is-in > *:nth-child(1) { animation-delay: calc(var(--reveal-delay, 0ms) + 0ms); }
  .js .reveal-stagger.is-in > *:nth-child(2) { animation-delay: calc(var(--reveal-delay, 0ms) + 80ms); }
  .js .reveal-stagger.is-in > *:nth-child(3) { animation-delay: calc(var(--reveal-delay, 0ms) + 160ms); }
  .js .reveal-stagger.is-in > *:nth-child(4) { animation-delay: calc(var(--reveal-delay, 0ms) + 240ms); }
  .js .reveal-stagger.is-in > *:nth-child(5) { animation-delay: calc(var(--reveal-delay, 0ms) + 320ms); }
  .js .reveal-stagger.is-in > *:nth-child(6) { animation-delay: calc(var(--reveal-delay, 0ms) + 390ms); }
  .js .reveal-stagger.is-in > *:nth-child(7) { animation-delay: calc(var(--reveal-delay, 0ms) + 450ms); }
  .js .reveal-stagger.is-in > *:nth-child(n+8) { animation-delay: calc(var(--reveal-delay, 0ms) + 500ms); }
}
@keyframes stagger-in {
  from {
    opacity: 0;
    transform: perspective(900px) rotateY(-17deg) rotateX(-7deg) translateY(28px);
  }
}

/* ==========================================================================
   25.21  Active section in the nav
   ========================================================================== */
.nav__link.is-active { color: var(--fg); }
.nav__link.is-active::after { scale: 1 1; }

/* ==========================================================================
   25.22  Flip — the hover half
   ==========================================================================
   The arrival half lives in 25.20 (flip-in and stagger-in). This is the other
   half: the surfaces that already lift now also tilt toward the viewer, so
   pointing at a card reads as turning it rather than only raising it.

   Every transform carries its own `perspective()` in the list, so no parent
   needs a `perspective` property and no wrapper markup is required. The angles
   stay shallow deliberately — a deep rotation on a shop card stops reading as a
   flourish and starts reading as a bug.

   These come last in the motion system so they win over the plain
   `translateY(-Npx)` lifts declared earlier. */
.card:hover,
.hero__tile:hover,
.trust__item:hover {
  transform: perspective(900px) rotateY(-6deg) rotateX(3deg) translateY(-6px);
}

/* A press should push the surface away, not just shrink it, so the small
   controls rotate back on the X axis as they go down. */
.chip:active,
.bar__btn:active,
.card__cta:active {
  transform: perspective(700px) rotateX(8deg) scale(0.96);
}

/* ==========================================================================
   25.23  The last few snaps
   ==========================================================================
   Surfaces that still changed state instantly. Added to the 25.2 baseline list
   rather than reached for with `*`: a blanket `transition: all` would animate
   layout properties (width, top, height) and turn a window resize into a slow
   slide. Anything that declares its own transition later still wins. */
.faq__q,
.faq__a,
.faq__i,
.step,
.stat,
.points li,
.visit__row,
.zones li,
.payments li {
  transition:
    color var(--ui) var(--ease),
    background-color var(--ui) var(--ease),
    border-color var(--ui) var(--ease),
    box-shadow var(--ui) var(--ease),
    opacity var(--ui) var(--ease),
    transform var(--ui) var(--ease);
}

.faq__q:hover,
.step:hover,
.stat:hover,
.points li:hover,
.visit__row:hover {
  transform: perspective(1000px) rotateX(1.6deg) translateX(4px);
}

/* --------------------------------------------------------------------------
   26. Motion & print
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  /* Nothing animates, but everything must stay visible. The blanket rule
     below cannot reach the properties we set explicitly further up, so they
     are reset by hand. */
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }

  /* Specificity matters here: the hidden rule is
     `.js .reveal-stagger:not(.is-in) > *` (0,3,0), so this must match it
     rather than sit below it — and being later in the file, it then wins. */
  .js .reveal,
  .js .reveal.is-in,
  .js .reveal-stagger:not(.is-in) > *,
  .js .reveal-stagger.is-in > * { opacity: 1; transform: none; }

  .js .card__img,
  .js .mosaic__item img { opacity: 1; filter: none; }

  /* Hover movement is the one thing that is actively unpleasant for some
     people, so it is removed rather than merely shortened. Both the elements
     that move and the inner content they move are covered. */
  .btn:hover,
  .btn:active,
  .chip:hover,
  .chip:active,
  .card:hover,
  .points li:hover,
  .hero__tile:hover,
  .trust__item:hover,
  .faq__q:hover,
  .step:hover,
  .stat:hover,
  .visit__row:hover,
  .bar__btn:active,
  .card__cta:active,
  .footer__list a:hover,
  .footer__social a:hover,
  .mosaic__item:hover,
  .trust__item:hover svg,
  .step:hover .step__n,
  .stat:hover b,
  .points li:hover svg,
  .visit__row:hover dt svg,
  .card__cta:hover,
  .card__cta:hover svg,
  .footer__social a:hover svg { transform: none; }

  /* The reveal animations keep their --reveal-delay, which would hold elements
     at opacity 0 (and mid-flip) for the whole length of the stagger even
     though the animation itself is instant. Zero the delays too. */
  .js .reveal.is-in,
  .js .reveal-stagger.is-in > * { animation-delay: 0ms !important; }

  .footer__list a:hover,
  .visit__row dd a:hover { background-size: 0% 1px; }

  /* The sheen is pure decoration, and nulling its transform would park it
     mid-tile as a static white streak — so it is hidden outright instead. */
  .hero__tile::after,
  .trust__item::after { display: none; }

  /* initHeroTitle() skips the split when this preference is already set, but it
     can be switched on later — and the spans' delays would then hold the
     headline invisible for the length of the cascade. */
  .hero__word { animation-delay: 0ms !important; }

  /* View-transition pseudo-elements are not matched by `*`, so turn them off
     explicitly. */
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}

@media print {
  .header, .bar, .gate, .drawer, .announce, .legal-ribbon, .rail-nav { display: none !important; }
  body { background: #fff; color: #000; padding: 0; }
  .section { padding-block: 1.5rem; }
}
