/* ============================================================
   APOTHECARY BOUTIQUE SHOP — Stylesheet
   Classic apothecary palette · Playfair Display + Cormorant
   ============================================================ */

:root {
  /* Palette */
  --cream: #f4efe6;
  --cream-deep: #e8dfd0;
  --paper: #faf6ee;
  --sage: #87936b;
  --sage-deep: #5c6b4a;
  --sage-dark: #3f4a30;
  --amber: #8b6f3f;
  --amber-deep: #5a4a2e;
  --amber-dark: #3a2e1d;
  --ink: #1f1b17;
  --ink-soft: #4a423a;
  --muted: #6b6359;
  --line: #c9beac;
  --line-soft: #ddd4c1;
  --white: #ffffff;

  /* Translucency — the header bar and the Shop dropdown let the page show
     through as it scrolls behind them. Both are --paper with an alpha, so a
     change to the palette carries over. Raise the alpha for more solid. */
  --glass-header: rgba(250, 246, 238, 0.66);           /* lower alpha = more glass */
  --glass-dropdown: rgba(250, 246, 238, 0.88);
  --glass-search: rgba(250, 246, 238, 0.86);
  --glass-blur: 22px;
  --glass-saturate: 135%;
  /* The round floating buttons: Back to top, the back control, and on a phone the
     menu and cart. One definition, because they are meant to read as one family and
     four hand-copied sets of these three values would drift apart. */
  --glass-button: rgba(244, 239, 230, 0.55);
  --glass-button-edge: rgba(31, 27, 23, 0.10);
  --glass-button-lift: inset 0 1px 0 rgba(255, 255, 255, 0.5), 0 2px 10px rgba(31, 27, 23, 0.07);
  --glass-button-blur: blur(12px) saturate(1.5);
  --glass-button-hover: rgba(244, 239, 230, 0.85);
  /* The site's round button. Every circular control is this size and this glass,
     so one number moves all of them. The hero arrows are the single exception —
     they keep their own larger size and take the glass only. */
  --btn-round: 50px;

  /* Typography */
  --font-display: "Playfair Display", "Times New Roman", serif;
  --font-serif: "Cormorant Garamond", "Times New Roman", serif;
  --font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  /* Layout */
  --max-w: 1280px;
  --max-w-narrow: 820px;
  /* TRIAL — every rounded corner in the theme at 22px, so the shape can be judged as a
     whole rather than one component at a time. The filter chips are deliberately outside
     this: they carry their own literal radii (24px desktop, 6px mobile) and are untouched.
     To go back, restore the three numbers in the comment below — nothing else changed. */
  --radius: 22px;        /* was 2px */
  --radius-lg: 22px;     /* was 4px */
  /* The corner on the card family: the product card and its photo frame, the category
     tile, and the same photo frame again on the product page and in Quick View. They
     were four separate hard-coded values between 12px and 22px, which is why they never
     quite matched; one token means one number to tune and no drift. */
  --radius-card: 22px;   /* was 8px */

  /* Spacing scale */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;
  --space-9: 96px;

  /* ============================================================
     MOTION — the knobs worth tuning. Change a value here, not in
     the rules below. The three marked (JS) are read by app.js too,
     so they stay in sync with the markup that uses them.
     ============================================================ */

  /* Hero carousel */
  --hero-slide-duration: 0.85s;                        /* slide-to-slide glide */
  --hero-slide-ease: cubic-bezier(0.4, 0, 0.2, 1);
  --hero-slide-interval: 6000ms;                       /* (JS) how long each slide holds */
  --hero-photo-zoom-duration: 18s;                     /* slow push on the photo slides */
  --hero-video-pan-duration: 24s;

  /* Category tiles — lift + zoom on hover and tap */
  --tile-icon-zoom: 1.08;                              /* the icon grows inside the tile, as a card's photo does */
  --tile-zoom-scale: 1.05;
  --tile-zoom-lift: -8px;
  --tile-zoom-scale-mobile: 1.02;                      /* softer, full-width tiles */
  --tile-zoom-lift-mobile: -5px;
  --tile-zoom-duration: 0.25s;
  --tile-zoom-ease: ease;

  /* Category tiles — scroll-in reveal */
  --tile-reveal-duration: 0.6s;
  --tile-reveal-ease: cubic-bezier(0.22, 1, 0.36, 1);
  --tile-reveal-rise: 34px;
  --tile-reveal-stagger: 80ms;                         /* (JS) delay between tiles in a row */

  /* Best Sellers / Our Latest marquee */
  --carousel-speed: 24;                                /* (JS) px per second */

  /* Floating leaves down the page margins.
     --particles is the on/off switch: 1 shows them, 0 removes them entirely. */
  --particles: 1;                                      /* (JS) 1 = on, 0 = off */
  --particle-count: 24;                                /* (JS) total, split between the two sides */
  --particle-opacity: 0.2;                             /* how visible at their brightest */
  --particle-size: 56px;                               /* base size; each one varies around it */
  --particle-duration: 22s;
  --particle-rise: 560px;                              /* how far one leaf drifts before it fades */
  /* Rise over duration is the whole feel of this effect: it works out to roughly
     25px a second, and it is the number to change if the drift reads wrong.
     It was 220px over 30s — about 7px a second, which is slower than the eye
     registers as movement, so the leaves looked painted on rather than floating.
     Much past 40px a second they start pulling attention off the products. */
  --particle-min-gutter: 75px;                         /* (JS) below this much free margin they stay off */

  /* Shop dropdown */
  --pdp-enter-duration: 0.42s;                         /* product page settle-in */
  --dropdown-panel-duration: 0.18s;                    /* the panel itself: quick, it is the stage */
  --dropdown-duration: 0.24s;                          /* each link's own fade */
  --dropdown-ease: cubic-bezier(0.22, 1, 0.36, 1);   /* settles, no bounce */
  --dropdown-stagger: 48ms;                            /* delay between category links */

  /* Announcement bar ticker */
  --announce-speed: 34;                                /* (JS) px per second, whatever the text length */

  /* Product cards */
  --card-hover-duration: 0.25s;
  --card-hover-lift: -4px;

  /* Effects */
  --shadow-sm: 0 2px 8px rgba(31, 27, 23, 0.06);
  --shadow-md: 0 8px 24px rgba(31, 27, 23, 0.10);
  --shadow-lg: 0 20px 60px rgba(31, 27, 23, 0.16);
}

/* ---------- Naming ----------
   BEM: .block__element--modifier. Two kinds of extra class:
     --modifier   a permanent variant of the thing   (.category-tile--soon)
     .is-*        a state JavaScript toggles         (.is-open, .is-visible)
   Keep new classes on that split and the stylesheet stays greppable.
   --------------------------------------------------------------- */

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html {
  scroll-behavior: smooth;
  -webkit-font-smoothing: antialiased;
  /* Safety net: nothing may ever cause sideways scroll (clip avoids creating
     a scroll container; hidden is the fallback for older browsers) */
  overflow-x: hidden;
  overflow-x: clip;
}
body {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 17px;
  line-height: 1.55;
  color: var(--ink);
  background: var(--cream);
  font-weight: 400;
  /* The containing block for .page-particles — see its rule for why that matters.
     Safe as a global: <body> starts at the document origin with no margin, so every
     absolutely positioned descendant that previously resolved against the initial
     containing block resolves against a box with the same origin. */
  position: relative;
  /* No overflow-x here. html already carries the sideways-scroll safety net, and a
     second one on <body> makes body its own scroll container nested inside html's.
     Two nested scrollers is the classic iOS "the page scrolls further than it should,
     with empty space after the footer" — Safari scrolls the inner one past the end of
     the outer. One scroller, on html, is all the safety net needs. */
  min-width: 320px;
}
img, svg, video { max-width: 100%; display: block; }
button { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; padding: 0; margin: 0; }
input { font-family: inherit; }

/* ---------- Layout primitives ---------- */
.container {
  max-width: var(--max-w);
  margin: 0 auto;
  padding: 0 var(--space-5);
}
.container--narrow { max-width: var(--max-w-narrow); }

.section {
  padding: var(--space-9) 0;
  position: relative;
}
.section--products { background: var(--paper); }
.section--latest { background: var(--cream); }
.section--categories { background: var(--cream-deep); }
.section--faq { background: var(--paper); padding-bottom: var(--space-8); }

.section__head {
  text-align: center;
  margin-bottom: var(--space-8);
}
.section__title {
  font-family: var(--font-display);
  font-size: clamp(2rem, 4vw, 3.25rem);
  font-weight: 500;
  margin: var(--space-2) 0 var(--space-3);
  letter-spacing: -0.01em;
  color: var(--ink);
}
.section__sub {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 1.15rem;
  color: var(--muted);
  margin: 0 auto;
  max-width: 540px;
}

/* ---------- Eyebrow (decorative label) ---------- */
.eyebrow {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 0.85rem;
  letter-spacing: 0.1em;
  color: var(--amber-deep);
  text-transform: lowercase;
  margin: 0 0 var(--space-3);
  font-weight: 500;
}
.eyebrow--light { color: var(--cream); opacity: 0.85; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: 14px 32px;
  font-family: var(--font-display);
  font-size: 0.92rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border-radius: var(--radius);
  cursor: pointer;
  transition: all 0.25s ease;
  border: 1px solid transparent;
  white-space: nowrap;
}
.btn--primary {
  background: var(--ink);
  color: var(--cream);
  border-color: var(--ink);
}
.btn--primary:hover {
  background: var(--sage-deep);
  border-color: var(--sage-deep);
  transform: translateY(-1px);
}
.btn--ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--ink);
}
.btn--ghost:hover {
  background: var(--ink);
  color: var(--cream);
}
.btn--light {
  background: var(--cream);
  color: var(--ink);
  border-color: var(--cream);
}
.btn--light:hover {
  background: var(--amber);
  color: var(--cream);
  border-color: var(--amber);
}

/* ============================================================
   AGE GATE
   ============================================================ */
.age-gate {
  position: fixed;
  inset: 0;
  background: rgba(31, 27, 23, 0.92);
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.age-gate.is-hidden { display: none; }

.age-gate__panel {
  background: var(--paper);
  max-width: 520px;
  width: 100%;
  padding: var(--space-8) var(--space-7);
  text-align: center;
  border: 1px solid var(--line);
  box-shadow: var(--shadow-lg);
  position: relative;
}
.age-gate__panel::before,
.age-gate__panel::after {
  content: "";
  position: absolute;
  left: 16px;
  right: 16px;
  height: 1px;
  background: var(--line);
}
.age-gate__panel::before { top: 12px; }
.age-gate__panel::after { bottom: 12px; }

.age-gate__ornament {
  color: var(--amber-deep);
  width: 60px;
  margin: 0 auto var(--space-4);
}
.age-gate__brandline {
  font-family: var(--font-display);
  font-style: italic;
  color: var(--amber-deep);
  font-size: 0.95rem;
  letter-spacing: 0.05em;
  margin: 0 0 var(--space-3);
}
.age-gate__title {
  font-family: var(--font-display);
  font-size: 2.6rem;
  font-weight: 500;
  margin: 0 0 var(--space-4);
  color: var(--ink);
  letter-spacing: -0.01em;
}
.age-gate__copy {
  font-family: var(--font-serif);
  font-size: 1.05rem;
  color: var(--ink-soft);
  margin: 0 0 var(--space-6);
  line-height: 1.6;
}
.age-gate__actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}
.age-gate__fineprint {
  font-family: var(--font-serif);
  font-size: 0.85rem;
  font-style: italic;
  color: var(--muted);
  margin: 0;
  line-height: 1.5;
}

/* ============================================================
   ANNOUNCEMENT BAR
   ============================================================ */
.announcement {
  background: var(--ink);
  color: var(--cream);
  overflow: hidden;
  padding: 10px 0;
  border-bottom: 1px solid var(--amber-deep);
}
.announcement__track {
  display: flex;
  gap: var(--space-4);
  /* app.js repeats the items until they overflow the bar and sets the duration
     from --announce-speed, so the strip is always full and always moves at the
     same pace however much text there is. */
  animation: scrollAnnounce 45s linear infinite;
  white-space: nowrap;
  width: max-content;
}
/* Separator after every item, so cloned copies stay evenly punctuated */
.announcement__item::after {
  content: '·';
  margin-left: var(--space-4);
  opacity: 0.5;
}
.announcement__item {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 0.88rem;
  letter-spacing: 0.05em;
}
@keyframes scrollAnnounce {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

/* ============================================================
   HEADER
   ============================================================ */
.site-header {
  background: var(--glass-header);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  position: sticky;
  top: 0;
  z-index: 100;
  border-bottom: 1px solid var(--line-soft);
}
.site-header__inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  grid-template-rows: auto auto;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-5);
  position: relative;
}

.site-header__left {
  justify-self: start;
  display: flex;
  align-items: center;
}

.site-header__menu-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  padding: 8px;
  width: 36px;
}
.site-header__menu-toggle span {
  display: block;
  height: 1.5px;
  background: var(--ink);
  width: 100%;
  transition: 0.2s;
}

.brand {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  line-height: 1;
  gap: 2px;
  justify-self: center;
  grid-column: 2;
  color: var(--ink);
}
.brand__ornament {
  color: var(--amber-deep);
  width: 96px;
  height: 16px;
  margin-bottom: 1px;
}
.brand__name {
  font-family: var(--font-display);
  font-size: 1.95rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--ink);
  line-height: 1.05;
}
.brand__tagline {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.78rem;
  color: var(--muted);
  letter-spacing: 0.06em;
  margin-top: 0;
  line-height: 1.1;
}

.primary-nav {
  grid-column: 1 / -1;
  grid-row: 2;
  border-top: 1px solid var(--line-soft);
  padding-top: 6px;
  margin-top: 6px;
}
.primary-nav__link { padding: 4px 0; }

.primary-nav__list {
  display: flex;
  justify-content: center;
  gap: var(--space-7);
}
.primary-nav__link {
  font-family: var(--font-display);
  font-size: 0.95rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  position: relative;
  padding: 6px 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition: color 0.2s;
}
.primary-nav__link::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 0;
  height: 1px;
  background: var(--amber-deep);
  transition: width 0.25s, left 0.25s;
}
.primary-nav__link:hover::after,
.primary-nav__link--active::after {
  width: 100%;
  left: 0;
}
.primary-nav__link:hover { color: var(--amber-deep); }
.primary-nav__caret { width: 9px; height: 5px; transition: transform 0.2s; }

.has-dropdown { position: relative; }
.dropdown {
  position: absolute;
  top: 100%;
  left: 50%;
  /* Drops from just under the nav and settles: a little up, a little small. */
  transform: translateX(-50%) translateY(-10px) scale(0.97);
  transform-origin: top center;
  background: var(--glass-dropdown);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  border: 1px solid var(--line);
  min-width: 260px;
  padding: var(--space-4);
  box-shadow: var(--shadow-md);
  /* The panel is taller than a phone held sideways. initNavDropdown() measures the
     room left below it and writes max-height; this is what turns the overspill into
     something you can reach. overscroll-behavior stops the page scrolling underneath
     once the list hits its end, which on a touch screen reads as the menu falling
     apart. The max-height here is only the no-JS net — JS replaces it with the exact
     figure, and on a normal screen nothing ever overflows. */
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgba(31, 27, 23, 0.25) transparent;
  opacity: 0;
  /* visibility, not just pointer-events: a closed dropdown must not be
     tabbable. Focusing the Shop link itself opens it, so keyboard users still
     reach the categories. */
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--dropdown-panel-duration) var(--dropdown-ease),
              transform var(--dropdown-panel-duration) var(--dropdown-ease),
              visibility 0s linear var(--dropdown-panel-duration);
  z-index: 50;
}
.has-dropdown:hover .dropdown,
.has-dropdown:focus-within .dropdown,
.has-dropdown.is-open .dropdown {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateX(-50%) translateY(0) scale(1);
  transition-delay: 0s;
}
.has-dropdown:hover .primary-nav__caret,
.has-dropdown:focus-within .primary-nav__caret,
.has-dropdown.is-open .primary-nav__caret {
  transform: rotate(180deg);
}
.dropdown::-webkit-scrollbar { width: 6px; }
.dropdown::-webkit-scrollbar-thumb {
  background: rgba(31, 27, 23, 0.22);
  border-radius: 3px;
}
.dropdown::-webkit-scrollbar-track { background: transparent; }

/* The no-JS net, only where it can matter: a viewport short enough for ten
   categories not to fit. 150px is the header plus the announcement bar at their
   tallest, so the panel is never cut off above its own first item. */
@media (max-height: 700px) {
  .dropdown { max-height: calc(100vh - 150px); }
}

.dropdown__inner {
  display: grid;
  gap: 2px;
}
.dropdown__link {
  font-family: var(--font-serif);
  font-size: 1.05rem;
  padding: 8px 12px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-radius: var(--radius);
  /* Each link starts a touch high and transparent; --i (set in renderNav) walks
     the delay down the list so they arrive one after another. */
  opacity: 0;
  transform: translateY(-6px);
  transition: background 0.08s linear, color 0.08s linear,
              opacity var(--dropdown-duration) var(--dropdown-ease),
              transform var(--dropdown-duration) var(--dropdown-ease);
}
.has-dropdown:hover .dropdown__link,
.has-dropdown:focus-within .dropdown__link,
.has-dropdown.is-open .dropdown__link {
  opacity: 1;
  transform: none;
  /* One delay per property, in the order they are listed above:
     background and colour stay instant so the hover highlight answers the
     pointer straight away; only the fade-in waits its turn in the cascade. */
  transition-delay: 0s, 0s,
                    calc(var(--i, 0) * var(--dropdown-stagger)),
                    calc(var(--i, 0) * var(--dropdown-stagger));
}
.dropdown__link {
  color: var(--ink);
}
/* The highlight has to read against a near-cream, semi-transparent panel, so it is a
   sage wash rather than another cream. --cream-deep was almost invisible there. */
.dropdown__link:hover,
.dropdown__link:focus-visible {
  background: rgba(92, 107, 74, 0.13);
  color: var(--sage-dark);
}
.dropdown__link em {
  font-style: italic;
  font-size: 0.75rem;
  color: var(--muted);
  font-family: var(--font-display);
}
.dropdown__link--soon { opacity: 0.65; }

.site-header__actions {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  justify-self: end;
  grid-column: 3;
  grid-row: 1;
}
.icon-btn {
  width: 50px;
  height: 50px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  transition: background 0.2s;
  position: relative;
  color: var(--ink);
}
.icon-btn:hover { background: var(--cream-deep); }
.icon-btn svg { width: 28px; height: 28px; }
.cart-btn__count {
  position: absolute;
  top: 2px;
  right: -2px;
  background: var(--amber-deep);
  color: var(--cream);
  font-family: var(--font-sans);
  font-size: 0.72rem;
  font-weight: 600;
  min-width: 22px;
  height: 22px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 5px;
}

/* Search overlay */
/* A panel, not a full-width band. It sits inside the header's own measure, is
   translucent like the dropdown, and closes on any click outside it (theme.js). */
.search-overlay {
  position: absolute;
  inset: 0 0 auto 0;
  padding: var(--space-4) var(--space-5) var(--space-5);
  z-index: 60;
  background: transparent;
  border-bottom: 0;
  box-shadow: none;
}
.search-overlay > .container {
  max-width: min(760px, 100%);
  margin: 0 auto;
  padding: var(--space-4) var(--space-5) var(--space-3);
  background: var(--glass-search);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: var(--shadow-md);
}
.search-overlay[hidden] { display: none; }
.search-overlay__form {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  border-bottom: 1px solid var(--ink);
  padding-bottom: var(--space-2);
}
.search-overlay__form input {
  flex: 1;
  border: none;
  background: transparent;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 1.5rem;
  outline: none;
  padding: var(--space-2) 0;
  color: var(--ink);
}
.search-overlay__form input::placeholder { color: var(--muted); }

/* Mobile menu */
.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(31, 27, 23, 0.6);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s;
}
.mobile-menu.is-open {
  opacity: 1;
  pointer-events: auto;
}
.mobile-menu__panel {
  position: absolute;
  top: 0;
  left: 0;
  width: 86%;
  max-width: 340px;
  height: 100%;
  background: var(--paper);
  padding: var(--space-7) var(--space-5);
  transform: translateX(-100%);
  transition: transform 0.3s ease;
  overflow-y: auto;
}
.mobile-menu.is-open .mobile-menu__panel {
  transform: translateX(0);
}
.mobile-menu__close {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 36px;
  height: 36px;
  color: var(--ink);
}
.mobile-menu__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.mobile-menu__list > li > a,
.mobile-menu__group > a {
  display: block;
  padding: 10px 0;
  font-family: var(--font-display);
  font-size: 1.15rem;
  letter-spacing: 0.04em;
  color: var(--ink);
  border-bottom: 1px solid var(--line-soft);
}
.mobile-menu__group { padding-top: var(--space-3); }
.mobile-menu__label {
  display: block;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 0.95rem;
  color: var(--amber-deep);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin-bottom: var(--space-2);
}
.mobile-menu__group > a {
  font-family: var(--font-serif);
  font-size: 1.05rem;
  padding-left: var(--space-3);
}
.mobile-menu__group > a.mobile-menu__link--soon { opacity: 0.55; font-style: italic; }

/* ============================================================
   HERO SECTIONS
   ============================================================ */
.hero {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.hero__inner {
  position: relative;
  z-index: 2;
  text-align: center;
  padding: var(--space-9) var(--space-5);
}
.eyebrow + .hero__title { margin-top: 0; }

.hero__title {
  font-family: var(--font-display);
  font-size: clamp(2.6rem, 6vw, 5.4rem);
  font-weight: 500;
  line-height: 1.05;
  letter-spacing: -0.015em;
  margin: var(--space-3) 0 var(--space-5);
  color: var(--ink);
}
.hero__title--medium { font-size: clamp(2.2rem, 4.8vw, 4.2rem); }
.hero__title-line { display: block; }
.hero__title-line--italic {
  font-style: italic;
  font-weight: 400;
  color: var(--amber-deep);
}
.hero__copy {
  font-family: var(--font-serif);
  font-size: 1.2rem;
  max-width: 580px;
  margin: 0 auto var(--space-6);
  color: var(--ink-soft);
  line-height: 1.6;
}
.hero__copy--light { color: var(--cream); opacity: 0.92; }
.hero__actions {
  display: flex;
  gap: var(--space-3);
  justify-content: center;
  flex-wrap: wrap;
  margin-bottom: var(--space-6);
}
.hero__ornament {
  color: var(--amber-deep);
  opacity: 0.6;
  width: 120px;
  margin: 0 auto;
}

/* Hero 1 — Primary */
.hero--primary {
  background:
    radial-gradient(ellipse at 20% 10%, rgba(135, 147, 107, 0.18), transparent 50%),
    radial-gradient(ellipse at 80% 90%, rgba(139, 111, 63, 0.15), transparent 50%),
    linear-gradient(180deg, var(--paper), var(--cream));
  min-height: 80vh;
  display: flex;
  align-items: center;
}

/* Hero 2 — Video / Movement */
.hero--video {
  min-height: 70vh;
  display: flex;
  align-items: center;
  color: var(--cream);
  background: var(--ink);
}
.hero__video-bg {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  /* Same guarantee as .hero__photo-bg. The pan gradient inside is already dark, but it
     is one more thing that has to paint before the copy is legible; this is underneath
     it from the first frame and costs nothing. */
  background-color: var(--ink);
}
.hero__video-pan {
  position: absolute;
  inset: -10% -10%;
  background:
    radial-gradient(ellipse at 30% 40%, rgba(139, 111, 63, 0.55), transparent 50%),
    radial-gradient(ellipse at 75% 65%, rgba(92, 107, 74, 0.45), transparent 55%),
    linear-gradient(135deg, #2a2419, #3f4a30 40%, #5a4a2e 80%, #1f1b17);
  background-size: 200% 200%;
  animation: kenBurns 20s ease-in-out infinite alternate;
  filter: brightness(0.85);
}
.hero__video-pan::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle at 20% 20%, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 0.55) 100%);
}
.hero__video-element {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.6s;
}
.hero__video-element.is-loaded { opacity: 0.5; }

@keyframes kenBurns {
  0%   { transform: scale(1) translate(0,0); background-position: 0% 50%; }
  100% { transform: scale(1.15) translate(-3%, -2%); background-position: 100% 50%; }
}

.hero__particles {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.hero__particles span {
  position: absolute;
  width: 50px;
  height: 50px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath d='M50 5 C60 25 65 50 50 90 C35 50 40 25 50 5 Z' fill='%23e8dfd0' opacity='0.4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-size: contain;
  opacity: 0.45;
  animation: floatParticle 12s linear infinite;
}
.hero__particles span:nth-child(1) { left: 8%;  top: 110%; animation-duration: 14s; animation-delay: 0s; transform: rotate(15deg); }
.hero__particles span:nth-child(2) { left: 22%; top: 110%; animation-duration: 18s; animation-delay: 3s; width: 35px; height: 35px; transform: rotate(-30deg); }
.hero__particles span:nth-child(3) { left: 38%; top: 110%; animation-duration: 16s; animation-delay: 6s; width: 60px; height: 60px; transform: rotate(45deg); }
.hero__particles span:nth-child(4) { left: 52%; top: 110%; animation-duration: 20s; animation-delay: 1s; width: 40px; height: 40px; transform: rotate(-15deg); }
.hero__particles span:nth-child(5) { left: 65%; top: 110%; animation-duration: 15s; animation-delay: 9s; width: 45px; height: 45px; transform: rotate(60deg); }
.hero__particles span:nth-child(6) { left: 78%; top: 110%; animation-duration: 17s; animation-delay: 4s; width: 30px; height: 30px; transform: rotate(-45deg); }
.hero__particles span:nth-child(7) { left: 88%; top: 110%; animation-duration: 19s; animation-delay: 7s; width: 55px; height: 55px; transform: rotate(20deg); }
.hero__particles span:nth-child(8) { left: 94%; top: 110%; animation-duration: 22s; animation-delay: 2s; width: 38px; height: 38px; transform: rotate(-60deg); }

@keyframes floatParticle {
  0%   { transform: translateY(0) rotate(0); opacity: 0; }
  10%  { opacity: 0.5; }
  90%  { opacity: 0.5; }
  100% { transform: translateY(-130vh) rotate(360deg); opacity: 0; }
}

.hero__inner--video {
  color: var(--cream);
}
.hero--video .hero__title-line--italic { color: var(--amber); }
.hero--video .hero__title { color: var(--cream); }

/* Hero 3 — Full-bleed apothecary photo with overlaid text */
.hero--tertiary {
  background: linear-gradient(135deg, var(--cream-deep), var(--paper));
  padding: var(--space-7) 0;
  min-height: 70vh;
  display: flex;
  align-items: center;
}
.hero--photo {
  position: relative;
  color: var(--cream);
  background: var(--ink);
}
.hero__photo-bg {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  /* A DARK BASE UNDER THE PHOTO, and it is not decoration.

     The hero copy is cream (#f4efe6) and reads against the photo plus the ::after scrim
     below. Before the photo has painted there was nothing here at all — this box was
     transparent — so the scrim, which is only rgba(31,27,23,0.78) fading to 0.05,
     composited over the PAGE's cream background. Cream text on near-cream: invisible.
     When the photo finally landed the words appeared to blink into existence a second
     after the slide.

     Slide 1 never showed it because its image is fetchpriority="high" and is already
     there on first paint. Slides 2 and 3 were "low" (inc/hero.php, now "auto"), so on a
     phone they arrived late and every one of them blinked.

     var(--ink) is the same colour the scrim is made of, so nothing changes once the
     photo covers it — .hero__photo is width/height 100% with object-fit: cover and its
     zoom keyframes never scale below 1, so it can never uncover this. */
  background-color: var(--ink);
}
.hero__photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 38%;
  animation: heroPhotoZoom var(--hero-photo-zoom-duration) ease-in-out infinite alternate;
}
@keyframes heroPhotoZoom {
  0%   { transform: scale(1); }
  100% { transform: scale(1.08); }
}
/* Scrim for text readability — darker on the left where the text sits */
.hero__photo-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(90deg, rgba(31,27,23,0.78) 0%, rgba(31,27,23,0.5) 38%, rgba(31,27,23,0.15) 70%, rgba(31,27,23,0.05) 100%),
    linear-gradient(0deg, rgba(31,27,23,0.45), rgba(31,27,23,0.1));
}
.hero__inner--photo {
  position: relative;
  z-index: 2;
  text-align: left;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 100%;
}
.hero__inner--photo .hero__title { color: var(--cream); }
.hero__inner--photo .hero__title-line--italic { color: var(--amber); }
.hero__inner--photo .hero__copy { margin-left: 0; margin-right: 0; max-width: 520px; }

/* Centered photo hero (hero 1) — even scrim, centered light text */
.hero--photo-center {
  background: var(--ink);
}
.hero--photo-center .hero__photo-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse at center, rgba(31,27,23,0.35) 0%, rgba(31,27,23,0.62) 70%, rgba(31,27,23,0.78) 100%),
    linear-gradient(0deg, rgba(31,27,23,0.35), rgba(31,27,23,0.25));
}
.hero--photo-center .hero__inner {
  position: relative;
  z-index: 3;
}
.hero--photo-center .hero__title { color: var(--cream); }
.hero--photo-center .hero__title-line--italic { color: var(--amber); }
.hero__ornament--light { color: var(--cream); opacity: 0.7; }

/* (1) Stronger Ken Burns — pan + zoom (overrides the shared slow zoom) */
.hero--photo-center .hero__photo {
  animation: heroKenBurns var(--hero-video-pan-duration) ease-in-out infinite alternate;
  transform-origin: center 40%;
}
@keyframes heroKenBurns {
  0%   { transform: scale(1.06) translate3d(0, 0, 0); }
  50%  { transform: scale(1.13) translate3d(-2%, -1.5%, 0); }
  100% { transform: scale(1.2) translate3d(1.5%, -3%, 0); }
}

/* (2) Particles layered above the scrim, below the text */
.hero--photo-center .hero__particles--front { z-index: 2; }

/* (3) Drifting amber light glow */
.hero__glow {
  position: absolute;
  z-index: 2;
  top: -20%;
  left: 50%;
  width: 70vw;
  height: 70vw;
  max-width: 900px;
  max-height: 900px;
  transform: translateX(-50%);
  background: radial-gradient(circle, rgba(139,111,63,0.28) 0%, rgba(139,111,63,0) 60%);
  pointer-events: none;
  animation: heroGlowDrift 16s ease-in-out infinite alternate;
}
@keyframes heroGlowDrift {
  0%   { transform: translateX(-62%) translateY(-4%) scale(1); opacity: 0.8; }
  100% { transform: translateX(-38%) translateY(6%) scale(1.15); opacity: 1; }
}

/* (4) Staggered text entrance — replays each time hero 1 becomes the active slide */
.hero--primary[aria-hidden="false"] .eyebrow { animation: heroRise 0.8s ease both; }
.hero--primary[aria-hidden="false"] .hero__title-line:nth-child(1) { animation: heroRise 0.9s 0.10s ease both; }
.hero--primary[aria-hidden="false"] .hero__title-line:nth-child(2) { animation: heroRise 0.9s 0.24s ease both; }
.hero--primary[aria-hidden="false"] .hero__copy { animation: heroRise 0.9s 0.40s ease both; }
.hero--primary[aria-hidden="false"] .hero__actions { animation: heroRise 0.9s 0.54s ease both; }
.hero--primary[aria-hidden="false"] .hero__ornament { animation: heroRise 0.9s 0.68s ease both; }
@keyframes heroRise {
  from { opacity: 0; transform: translateY(30px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Respect reduced-motion preference */
@media (prefers-reduced-motion: reduce) {
  .hero--photo-center .hero__photo,
  .hero__glow,
  .hero__particles span,
  .hero--primary[aria-hidden="false"] .eyebrow,
  .hero--primary[aria-hidden="false"] .hero__title-line,
  .hero--primary[aria-hidden="false"] .hero__copy,
  .hero--primary[aria-hidden="false"] .hero__actions,
  .hero--primary[aria-hidden="false"] .hero__ornament {
    animation: none !important;
  }
}

/* Light ghost button for dark photo heroes */
.btn--ghost-light {
  background: transparent;
  color: var(--cream);
  border-color: var(--cream);
}
.btn--ghost-light:hover {
  background: var(--cream);
  color: var(--ink);
}
.hero__split-text .hero__copy { margin-left: 0; margin-right: 0; }
.hero__split-text .hero__actions { justify-content: flex-start; }
.hero__split-text .hero__title { margin-top: var(--space-3); }

/* ============================================================
   PRODUCT GRID & CARDS
   ============================================================ */
.product-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-5);
}

.product-card {
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-card);
  display: flex;
  flex-direction: column;
  transition: transform var(--card-hover-duration), box-shadow var(--card-hover-duration),
              border-color var(--card-hover-duration);
  position: relative;
  overflow: hidden;
}
/* Every :hover on a card is behind a real-pointer test.
   
   iOS has no hover, so it emulates one: the FIRST tap on an element whose :hover
   changes anything is spent applying that hover, and only the second tap becomes a
   click. Three rules here changed something — the lift, the photo zoom, and above all
   .product-card__quick, which goes from opacity 0 to 1. That is why a product needed
   tapping twice to open, and why the second tap sometimes hit the Quick View pill the
   first tap had just revealed.

   (hover: hover) and (pointer: fine) is true for a mouse and false for a finger, so a
   touch device now has no hover state to spend a tap on. The same pair already gates
   the nav dropdown for the same reason. */
@media (hover: hover) and (pointer: fine) {
  .product-card:hover {
    transform: translateY(var(--card-hover-lift));
    box-shadow: var(--shadow-md);
    border-color: var(--line);
  }
}

.product-card__media {
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--cream-deep);
  overflow: hidden;
  border-bottom: 1px solid var(--line-soft);
  border-radius: var(--radius-card) var(--radius-card) 0 0;
}

/* Product photo layer (sits on top of the decorative label fallback) */
.product-card__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 2;
  transition: opacity 0.45s ease, transform 0.6s ease;
}
/* Hover-swap to the 2nd photo when present */
/* Subtle zoom on hover for single-image cards — pointer only, see .product-card:hover */
@media (hover: hover) and (pointer: fine) {
  .product-card.has-img:hover .product-card__img--primary { transform: scale(1.04); }
}
.product-card__label {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  text-align: center;
  background:
    repeating-linear-gradient(
      90deg,
      transparent 0,
      transparent 7px,
      rgba(31, 27, 23, 0.03) 7px,
      rgba(31, 27, 23, 0.03) 8px
    ),
    linear-gradient(180deg, var(--cream) 0%, var(--cream-deep) 100%);
}
.product-card__label::before {
  content: "";
  position: absolute;
  top: 12px; bottom: 12px; left: 12px; right: 12px;
  border: 1px solid var(--line);
  pointer-events: none;
}
.product-card__label::after {
  content: "";
  position: absolute;
  top: 18px; bottom: 18px; left: 18px; right: 18px;
  border: 1px solid var(--line-soft);
  pointer-events: none;
}

.product-card__label-eyebrow {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 0.75rem;
  color: var(--amber-deep);
  letter-spacing: 0.12em;
  text-transform: lowercase;
  margin-bottom: 8px;
  z-index: 1;
}
.product-card__label-name {
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 500;
  color: var(--ink);
  line-height: 1.1;
  margin-bottom: 6px;
  z-index: 1;
  padding: 0 var(--space-3);
}
.product-card__label-divider {
  width: 30px;
  height: 1px;
  background: var(--amber-deep);
  margin: 6px 0;
  z-index: 1;
}
.product-card__label-desc {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.85rem;
  color: var(--muted);
  letter-spacing: 0.04em;
  z-index: 1;
}

/* Product badges */
.product-card__badges {
  position: absolute;
  top: 10px;
  left: 10px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  z-index: 5;
}
.badge {
  font-family: var(--font-sans);
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 4px 8px;
  background: var(--ink);
  color: var(--cream);
  border-radius: var(--radius);
}
.badge--thc {
  background: var(--sage-deep);
}

.product-card__stock {
  position: absolute;
  top: 10px;
  right: 10px;
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.78rem;
  color: var(--sage-deep);
  background: var(--paper);
  padding: 3px 10px;
  border: 1px solid var(--sage);
  border-radius: 20px;
  z-index: 5;
}

/* The glass pill, at every width. It used to be a solid ink slab on a pointer and glass
   only on a touch screen — two looks for one control, for no reason other than that the
   touch version was added later. This is the same glass as the cart, the search and Back
   to top. What still differs by device is only whether it is SHOWN: a pointer reveals it
   on hover, a finger has no hover to reveal it with, so there it is always there. */
.product-card__quick {
  position: absolute;
  left: 50%;
  bottom: 12px;
  transform: translateX(-50%) translateY(20px);
  background: var(--glass-button);
  -webkit-backdrop-filter: var(--glass-button-blur);
  backdrop-filter: var(--glass-button-blur);
  border: 1px solid var(--glass-button-edge);
  box-shadow: var(--glass-button-lift);
  color: var(--ink);
  font-family: var(--font-display);
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 8px 16px;
  border-radius: 999px;
  opacity: 0;
  transition: opacity 0.25s, transform 0.25s, background 0.2s;
  z-index: 5;
  cursor: pointer;
  white-space: nowrap;
}
.product-card__quick:hover,
.product-card__quick:focus-visible { background: var(--glass-button-hover); }
@media (hover: hover) and (pointer: fine) {
  .product-card:hover .product-card__quick {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
  }
}

/* On a touch screen there is no hover to reveal it, so it is either always there or
   unreachable. It stays — Quick View is the whole point of the pill. The look is in the
   base rule now; the only thing this block does is show it. */
@media (hover: none), (pointer: coarse) {
  .product-card__quick {
    opacity: 1;
    transform: translateX(-50%);
  }
}

.product-card__body {
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1;
}
.product-card__cat {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--amber-deep);
}
.product-card__name {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 500;
  color: var(--ink);
  line-height: 1.2;
  margin: 2px 0 0;
}
.product-card__desc {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.92rem;
  color: var(--muted);
  margin: 0 0 var(--space-3);
}
.product-card__price-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: auto;
  padding-top: var(--space-3);
  border-top: 1px solid var(--line-soft);
}
.product-card__price {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 500;
  color: var(--ink);
}
.product-card__per {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.82rem;
  color: var(--muted);
}

/* ============================================================
   CATEGORY GRID — uniform 5×2 cards with illustrated icons
   ============================================================ */
.category-grid-wide {
  width: 100%;
  padding: 0 var(--space-2);
}
.category-grid {
  display: grid;
  /* Ten categories: 5 x 2 on a wide screen. The ladder steps down so a tile is
     never narrower than the 210px icon it has to hold (see RESPONSIVE). */
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--space-4);
  width: 100%;
  /* This grid runs the full window width, not the 1280px content column, so it
     sits above the floating leaves (z-index 1) and they pass behind the tiles. */
  position: relative;
  z-index: 2;
}
/* Equal tiles: rows share a height, so the tall icons don't deepen their row */
@media (min-width: 821px) {
  .category-grid { grid-auto-rows: 1fr; }
  .category-tile { height: 100%; }
}

.category-tile {
  background: var(--paper);
  /* min-width:0 — the 1/0.92 ratio would otherwise transfer the icon's width
     as a minimum and push the last column off screen */
  min-width: 0;
  border: 1px solid rgba(31, 27, 23, 0.05);
  border-radius: var(--radius-card);
  padding: var(--space-5) var(--space-3) var(--space-4);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  position: relative;
  /* Hover/pop (transform, shadow) fire instantly; only the scroll-reveal
     (opacity, translate) uses the staggered --reveal-delay. */
  transition: transform var(--tile-zoom-duration) var(--tile-zoom-ease),
              box-shadow var(--tile-zoom-duration) var(--tile-zoom-ease),
              opacity var(--tile-reveal-duration) ease var(--reveal-delay, 0ms),
              translate var(--tile-reveal-duration) var(--tile-reveal-ease) var(--reveal-delay, 0ms);
  justify-content: center;
  aspect-ratio: 1 / 0.92;
  box-shadow: 0 6px 18px rgba(31, 27, 23, 0.06);
  /* Scroll-reveal start state (translate is separate from hover's transform) */
  opacity: 0;
  translate: 0 var(--tile-reveal-rise);
}
.category-tile.is-visible {
  opacity: 1;
  translate: 0 0;
}
.category-tile:hover,
.category-tile.is-hovered,
.category-tile.is-pressed {
  transform: translateY(var(--tile-zoom-lift)) scale(var(--tile-zoom-scale));
  box-shadow: 0 24px 44px rgba(31, 27, 23, 0.18);
  border-color: rgba(31, 27, 23, 0.1);
  z-index: 2;
}
@media (prefers-reduced-motion: reduce) {
  .category-tile {
    opacity: 1;
    translate: none;
    transition: transform var(--tile-zoom-duration) var(--tile-zoom-ease),
                box-shadow var(--tile-zoom-duration) var(--tile-zoom-ease);
  }
}

/* Icon sits directly on the card — no circle backdrop */
.category-tile__art {
  width: 150px;
  height: 150px;
  margin-bottom: var(--space-3);
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}
/* Provided image icon — overlays the SVG fallback when it loads */
.category-tile__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  z-index: 2;
}

/* Per-category icon sizing — bump ones whose artwork has more padding */
.category-tile[data-icon="accessories"] .category-tile__art,
.category-tile[data-icon="microdose"] .category-tile__art,
.category-tile[data-icon="vapes"] .category-tile__art {
  width: 182px;
  height: 182px;
}
.category-tile[data-icon="hash"] .category-tile__art,
.category-tile[data-icon="flower"] .category-tile__art {
  width: 210px;
  height: 210px;
}
/* Icon rides with the tile's transform so both lift + scale together, in sync */
.category-tile__art svg {
  width: 100%;
  height: 100%;
}
.category-tile__name {
  font-family: var(--font-display);
  font-size: 1.2rem;
  color: var(--sage-dark);
  font-weight: 500;
  letter-spacing: 0.01em;
  line-height: 1.2;
}
/* Counts hidden to match the reference; remove this rule to bring them back */
.category-tile__count { display: none; }

/* Microdose and Pre-Rolls have no stock yet: the tile stays in the grid and
   says so, which is the one case the hidden count line is used for. */
.category-tile--soon .category-tile__count {
  display: block;
  font-family: var(--font-body);
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--sage);
  opacity: 0.75;
}

/* ============================================================
   FAQ
   ============================================================ */
.faq {
  display: flex;
  flex-direction: column;
  gap: 0;
  border-top: 1px solid var(--line);
}
.faq__item {
  border-bottom: 1px solid var(--line);
}
.faq__q {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-5) 0;
  text-align: left;
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 500;
  color: var(--ink);
  transition: color 0.2s;
}
.faq__q:hover { color: var(--amber-deep); }
.faq__icon {
  position: relative;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}
.faq__icon::before, .faq__icon::after {
  content: "";
  position: absolute;
  background: var(--ink);
  transition: transform 0.25s, background 0.25s;
}
.faq__icon::before {
  top: 50%; left: 0;
  width: 100%; height: 1.5px;
  transform: translateY(-50%);
}
.faq__icon::after {
  left: 50%; top: 0;
  height: 100%; width: 1.5px;
  transform: translateX(-50%);
}
.faq__q[aria-expanded="true"] .faq__icon::after { transform: translateX(-50%) rotate(90deg); }
.faq__q[aria-expanded="true"] .faq__icon::before { background: var(--amber-deep); }
.faq__q[aria-expanded="true"] { color: var(--amber-deep); }

.faq__a {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.35s ease, padding 0.25s;
}
.faq__a p {
  font-family: var(--font-serif);
  font-size: 1.08rem;
  color: var(--ink-soft);
  margin: 0;
  padding-bottom: var(--space-5);
  padding-right: 10%;
  line-height: 1.65;
}
.faq__item.is-open .faq__a {
  max-height: 400px;
}

/* ============================================================
   FOOTER
   ============================================================ */
.site-footer {
  background: var(--ink);
  color: var(--cream);
  padding: var(--space-8) 0 var(--space-5);
}
.site-footer__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-5);
}
.site-footer__brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  color: var(--cream);
}
.site-footer__brand .brand__ornament {
  color: var(--amber);
  width: 80px;
}
.site-footer__name {
  font-family: var(--font-display);
  font-size: 1.5rem;
  margin: 0;
  font-weight: 500;
}
.site-footer__tagline {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.85rem;
  color: var(--cream);
  opacity: 0.7;
  margin: 0;
  letter-spacing: 0.06em;
}

.site-footer__disclaimers {
  max-width: 760px;
  margin-top: var(--space-3);
}
.site-footer__disclaim {
  font-family: var(--font-serif);
  font-size: 0.98rem;
  color: var(--cream);
  opacity: 0.85;
  margin: 0 0 var(--space-3);
  line-height: 1.55;
}
.site-footer__disclaim strong { color: var(--amber); font-weight: 600; }
.site-footer__disclaim--small {
  font-size: 0.82rem;
  font-style: italic;
  opacity: 0.6;
}

.site-footer__payments {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  justify-content: center;
  margin-top: var(--space-3);
}
.pay-pill {
  font-family: var(--font-sans);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  padding: 6px 12px;
  border: 1px solid var(--amber-deep);
  border-radius: var(--radius);
  color: var(--cream);
  opacity: 0.8;
}

.site-footer__bottom {
  border-top: 1px solid rgba(244, 239, 230, 0.12);
  width: 100%;
  padding-top: var(--space-4);
  margin-top: var(--space-3);
}
.site-footer__bottom p {
  font-family: var(--font-serif);
  font-size: 0.85rem;
  margin: 0;
  opacity: 0.55;
}

/* ============================================================
   QUICK VIEW MODAL
   ============================================================ */
.quickview {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
}
.quickview[hidden] { display: none; }
.quickview__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(31, 27, 23, 0.7);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.quickview__panel {
  position: relative;
  background: var(--paper);
  width: 100%;
  max-width: 820px;
  max-height: 90vh;
  overflow-y: auto;
  padding: var(--space-7);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-lg);
  border-radius: 20px;
}
.quickview__close {
  position: absolute;
  top: 14px;
  right: 14px;
  width: 36px;
  height: 36px;
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  transition: background 0.2s;
}
.quickview__close:hover { background: var(--cream-deep); }

/* Mobile back button — hidden on desktop, replaces the X on small screens */
.quickview__back {
  display: none;
  align-items: center;
  gap: 6px;
  font-family: var(--font-display);
  font-size: 0.95rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
  padding: 6px 10px 6px 0;
  margin-bottom: var(--space-4);
  cursor: pointer;
}
.quickview__back svg { width: 20px; height: 20px; }
.quickview__back:active { opacity: 0.6; }

.quickview__body {
  display: grid;
  /* minmax(0,…) keeps one long unbreakable value from widening the column */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-6);
  align-items: start;
}

.quickview__media {
  aspect-ratio: 1 / 1;
  position: relative;
  background: var(--cream-deep);
  border: 1px solid var(--line-soft);
  overflow: hidden;
  border-radius: var(--radius-card);
}
.quickview__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 2;
}
.quickview__cat {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 0.85rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--amber-deep);
  margin: 0 0 var(--space-2);
}
.quickview__name {
  font-family: var(--font-display);
  font-size: 2rem;
  font-weight: 500;
  margin: 0 0 var(--space-3);
  color: var(--ink);
}
.quickview__desc {
  font-family: var(--font-serif);
  font-style: italic;
  color: var(--muted);
  margin: 0 0 var(--space-4);
}
.quickview__badges {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}
.quickview__tiers {
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
  background: var(--cream);
}
.quickview__tiers-label {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 0.85rem;
  color: var(--amber-deep);
  letter-spacing: 0.08em;
  margin-bottom: 8px;
  display: block;
}
.quickview__tiers ul {
  display: grid;
  gap: 4px;
}
.quickview__tiers li {
  font-family: var(--font-serif);
  font-size: 1rem;
  display: flex;
  justify-content: space-between;
  padding: 4px 0;
  border-bottom: 1px dashed var(--line-soft);
}
.quickview__tiers li:last-child { border-bottom: none; }
.quickview__tiers li span:first-child { color: var(--ink-soft); }
.quickview__tiers li span:last-child { font-family: var(--font-display); color: var(--ink); }

.quickview__actions {
  display: flex;
  gap: var(--space-3);
  margin-top: var(--space-5);
  align-items: center;
}
.quickview__qty {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--ink);
  border-radius: var(--radius);
}
.quickview__qty button {
  width: 38px;
  height: 44px;
  font-size: 1.1rem;
  color: var(--ink);
}
.quickview__qty input {
  width: 50px;
  height: 44px;
  text-align: center;
  border: none;
  border-left: 1px solid var(--ink);
  border-right: 1px solid var(--ink);
  background: transparent;
  font-family: var(--font-display);
  font-size: 1rem;
  outline: none;
}

/* ============================================================
   HERO CAROUSEL
   ============================================================ */
.hero-carousel {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.hero-carousel__track {
  display: flex;
  width: 100%;
  transition: transform var(--hero-slide-duration) var(--hero-slide-ease);
  will-change: transform;
}
.hero-carousel__track > .hero {
  flex: 0 0 100%;
  min-width: 100%;
}

/* Light arrows — semi-transparent, hover darken */
.hero-carousel__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: rgba(244, 239, 230, 0.72);
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
  transition: background 0.25s, color 0.25s, transform 0.25s;
  border: 1px solid rgba(31, 27, 23, 0.08);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.hero-carousel__arrow:hover {
  background: var(--ink);
  color: var(--cream);
  transform: translateY(-50%) scale(1.06);
}
.hero-carousel__arrow svg {
  width: 22px;
  height: 22px;
}
.hero-carousel__arrow--prev { left: var(--space-5); }
.hero-carousel__arrow--next { right: var(--space-5); }

.hero-carousel__dots {
  position: absolute;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 10px;
  z-index: 10;
  padding: 8px 14px;
  background: rgba(244, 239, 230, 0.5);
  border-radius: 30px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.hero-carousel__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: rgba(31, 27, 23, 0.3);
  transition: background 0.25s, width 0.25s;
  border: none;
  padding: 0;
}
.hero-carousel__dot:hover { background: rgba(31, 27, 23, 0.55); }
.hero-carousel__dot.is-active {
  background: var(--ink);
  width: 26px;
  border-radius: 4px;
}

/* Ensure inner hero heights stay even between slides */
.hero-carousel .hero { min-height: 78vh; }
.hero-carousel .hero--video { min-height: 78vh; }
.hero-carousel .hero--tertiary { min-height: 78vh; }

/* ============================================================
   PRODUCTS HORIZONTAL CAROUSEL (Best Sellers)
   ============================================================ */
.products-carousel {
  position: relative;
  /* Negative margin must never exceed .container's side padding (space-5),
     or it pushes horizontal overflow on narrow viewports. */
  margin: 0 calc(var(--space-5) * -1);
  padding: 0 var(--space-5);
}

.product-row {
  display: flex;
  gap: var(--space-5);
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  padding: 6px 4px 24px;
  -webkit-overflow-scrolling: touch;
}
.product-row::-webkit-scrollbar { display: none; }

.product-row > .product-card {
  flex: 0 0 280px;
}
.product-row > .product-card.is-clone {
  /* Cloned card used purely for visual loop continuity */
  pointer-events: auto; /* still clickable to product page */
}

.products-carousel__arrow {
  position: absolute;
  top: calc(50% - 12px);
  transform: translateY(-50%);
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: rgba(244, 239, 230, 0.85);
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  z-index: 12;
  transition: background 0.25s, color 0.25s, transform 0.25s, opacity 0.25s;
  border: 1px solid var(--line-soft);
  box-shadow: var(--shadow-sm);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.products-carousel__arrow:hover {
  background: var(--ink);
  color: var(--cream);
  transform: translateY(-50%) scale(1.05);
}
.products-carousel__arrow:disabled {
  opacity: 0;
  pointer-events: none;
}
.products-carousel__arrow svg {
  width: 20px;
  height: 20px;
}
.products-carousel__arrow--prev { left: 12px; }
.products-carousel__arrow--next { right: 12px; }

/* ============================================================
   CART DRAWER
   ============================================================ */
.cart-drawer {
  position: fixed;
  inset: 0;
  z-index: 400;
  pointer-events: none;
}
.cart-drawer.is-open { pointer-events: auto; }
.cart-drawer__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(31, 27, 23, 0.55);
  opacity: 0;
  transition: opacity 0.3s ease;
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
.cart-drawer.is-open .cart-drawer__backdrop { opacity: 1; }

.cart-drawer__panel {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: min(440px, 92vw);
  background: var(--paper);
  display: grid;
  grid-template-rows: auto 1fr auto;
  transform: translateX(100%);
  transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
  box-shadow: var(--shadow-lg);
}
.cart-drawer.is-open .cart-drawer__panel { transform: translateX(0); }

.cart-drawer__head {
  padding: var(--space-5);
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
  border-bottom: 1px solid var(--line-soft);
}
.cart-drawer__head .eyebrow { margin: 0 0 4px; }
.cart-drawer__title {
  font-family: var(--font-display);
  font-size: 1.6rem;
  font-weight: 500;
  margin: 0;
  color: var(--ink);
}
.cart-drawer__close {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--ink);
  transition: background 0.2s;
}
.cart-drawer__close:hover { background: var(--cream-deep); }
.cart-drawer__close svg { width: 18px; height: 18px; }

.cart-drawer__body {
  overflow-y: auto;
  padding: var(--space-3) var(--space-5);
  /* A 1fr grid track still refuses to shrink below its content, because a grid
     item's min-height is auto. Without this the list pushes the foot past the
     bottom of the panel instead of scrolling inside its own row. */
  min-height: 0;
}

.cart-empty {
  text-align: center;
  padding: var(--space-7) var(--space-3);
  color: var(--muted);
}
.cart-empty__icon {
  width: 80px;
  height: 80px;
  color: var(--line);
  margin: 0 auto var(--space-4);
}
.cart-empty__title {
  font-family: var(--font-display);
  font-size: 1.2rem;
  color: var(--ink);
  margin: 0 0 6px;
}
.cart-empty__sub {
  font-family: var(--font-serif);
  font-style: italic;
  margin: 0;
}

/* Cart item rows */
.cart-item {
  display: grid;
  grid-template-columns: 88px 1fr auto;
  gap: var(--space-3);
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--line-soft);
  align-items: start;
}
.cart-item__media {
  width: 88px;
  height: 88px;
  border-radius: 12px;
  border: 1px solid var(--line-soft);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 8px 6px;
  overflow: hidden;
}
.cart-item__media-name {
  font-family: var(--font-display);
  font-size: 0.78rem;
  font-weight: 500;
  line-height: 1.1;
}
.cart-item__media-cat {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  margin-top: 4px;
}

.cart-item__info { min-width: 0; }
.cart-item__cat {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--amber-deep);
  margin: 0 0 2px;
}
.cart-item__name {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 500;
  margin: 0 0 4px;
  color: var(--ink);
  line-height: 1.2;
}
.cart-item__tier {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.88rem;
  color: var(--muted);
  margin: 0 0 var(--space-2);
}
.cart-item__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  /* Edit and Remove drop to their own line before the stepper is squeezed. */
  flex-wrap: wrap;
}
.cart-item__qty {
  /* Never shrink. On a phone the touch pass gives each button a 44px minimum, so
     the control needs 116px — and a flex item shrinks below its content by
     default, which cropped the "+" clean off behind the rounded corner. */
  flex: none;
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: 6px;
  overflow: hidden;
}
.cart-item__qty button {
  width: 28px;
  height: 28px;
  color: var(--ink);
  background: transparent;
  font-size: 0.95rem;
}
.cart-item__qty button:hover { background: var(--cream-deep); }
.cart-item__qty span {
  min-width: 28px;
  text-align: center;
  font-family: var(--font-display);
  font-size: 0.95rem;
  border-left: 1px solid var(--line);
  border-right: 1px solid var(--line);
  padding: 0 6px;
  line-height: 28px;
}
.cart-item__remove {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.82rem;
  color: var(--muted);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.cart-item__remove:hover { color: var(--amber-deep); }
.cart-item__line {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 500;
  color: var(--ink);
  align-self: start;
}

.cart-drawer__foot {
  padding: var(--space-4) var(--space-5) var(--space-5);
  border-top: 1px solid var(--line-soft);
  background: var(--cream);
}
.cart-drawer__row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-family: var(--font-display);
  font-size: 1.1rem;
  margin-bottom: var(--space-3);
}
.cart-drawer__subtotal {
  font-size: 1.5rem;
  color: var(--ink);
}
.cart-drawer__checkout {
  width: 100%;
  padding: 14px;
}
.cart-drawer__checkout:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.cart-drawer__note {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.8rem;
  color: var(--muted);
  text-align: center;
  margin: var(--space-3) 0 0;
}

/* ============================================================
   TIER OPTIONS (used in Quick View + PDP)
   ============================================================ */
.tier-options {
  display: grid;
  gap: 6px;
}
.tier-option {
  display: grid;
  grid-template-columns: 18px 1fr auto;
  gap: var(--space-3);
  align-items: center;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 8px;
  cursor: pointer;
  transition: border-color 0.2s, background 0.2s;
}
.tier-option:hover { background: var(--cream); }
.tier-option input { margin: 0; accent-color: var(--ink); }
.tier-option__label {
  font-family: var(--font-serif);
  font-size: 1.02rem;
  color: var(--ink-soft);
}
.tier-option__price {
  font-family: var(--font-display);
  font-size: 1.02rem;
  color: var(--ink);
}
.tier-option:has(input:checked) {
  border-color: var(--ink);
  background: var(--paper);
  box-shadow: inset 0 0 0 1px var(--ink);
}

.quickview__link {
  margin: var(--space-4) 0 0;
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.95rem;
}
.quickview__link a {
  color: var(--amber-deep);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ============================================================
   PRODUCT DETAIL PAGE
   ============================================================ */
.pdp {
  /* --space-9 at the foot was sized for a page that ended here. The attribute table
     and the related-products row both sit below it now, so a third of it is enough
     to separate them. */
  padding: var(--space-7) 0 var(--space-6);
  background: var(--cream);
}
.pdp__crumbs {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.88rem;
  color: var(--muted);
  margin: 0 0 var(--space-5);
}
.pdp__crumbs a { color: var(--amber-deep); }
.pdp__crumbs a:hover { text-decoration: underline; }

.pdp__layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  /* Split, because the two gaps do different jobs. 64px between the photo and the
     copy is the column the design wants; 64px again under the Add to cart row was
     just a band of empty page before the attribute table, which brings its own
     rule and padding to separate itself. */
  column-gap: var(--space-8);
  row-gap: var(--space-5);
  align-items: start;
}
/* No sticky, no offset: the photo is an ordinary grid item and scrolls with the
   page, the way anything sitting beside the description would. .pdp__layout's
   align-items: start is what keeps it at the top of its row rather than stretched
   down the column. */
.pdp__main {
  aspect-ratio: 1 / 1;
  background: var(--cream-deep);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-card);
  overflow: hidden;
  position: relative;
  box-shadow: var(--shadow-sm);
}
.pdp__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 2;
}
.pdp__label {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
  text-align: center;
}
.pdp__label::before {
  content: "";
  position: absolute;
  top: 22px; bottom: 22px; left: 22px; right: 22px;
  border: 1px solid var(--line);
  border-radius: 14px;
  pointer-events: none;
}
.pdp__label::after {
  content: "";
  position: absolute;
  top: 32px; bottom: 32px; left: 32px; right: 32px;
  border: 1px solid var(--line-soft);
  border-radius: 10px;
  pointer-events: none;
}
.pdp__label-eyebrow {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 0.92rem;
  letter-spacing: 0.12em;
  text-transform: lowercase;
  margin-bottom: 14px;
  z-index: 1;
}
.pdp__label-name {
  font-family: var(--font-display);
  font-size: 2.4rem;
  font-weight: 500;
  color: var(--ink);
  line-height: 1.1;
  margin-bottom: 12px;
  z-index: 1;
  padding: 0 var(--space-5);
}
.pdp__label-divider {
  width: 60px;
  height: 1px;
  margin: 8px 0 14px;
  z-index: 1;
}
.pdp__label-desc {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 1.05rem;
  color: var(--muted);
  letter-spacing: 0.04em;
  z-index: 1;
  margin-bottom: 28px;
}

.pdp__info { padding-top: var(--space-3); }
.pdp__cat {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 0.95rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--amber-deep);
  margin: 0 0 var(--space-2);
}
.pdp__name {
  font-family: var(--font-display);
  font-size: clamp(2rem, 4vw, 3rem);
  font-weight: 500;
  margin: 0 0 var(--space-3);
  color: var(--ink);
  line-height: 1.05;
}
.pdp__badges {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin: 0 0 var(--space-4);
}
.pdp__desc {
  font-family: var(--font-serif);
  font-size: 1.1rem;
  color: var(--ink-soft);
  margin: 0 0 var(--space-5);
  line-height: 1.6;
}

.pdp__tiers {
  margin: var(--space-4) 0;
}
.pdp__tiers-label {
  display: block;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 0.95rem;
  letter-spacing: 0.08em;
  color: var(--amber-deep);
  margin-bottom: var(--space-2);
}

.pdp__actions {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  margin-top: var(--space-5);
}
.pdp__actions .btn { flex: 1; }

.section--related {
  background: var(--paper);
  padding: var(--space-8) 0;
}

/* ============================================================
   CATEGORY LANDING PAGE
   ============================================================ */
.cat-hero {
  background:
    radial-gradient(ellipse at 20% 10%, rgba(135, 147, 107, 0.18), transparent 50%),
    radial-gradient(ellipse at 80% 90%, rgba(139, 111, 63, 0.15), transparent 50%),
    linear-gradient(180deg, var(--paper), var(--cream));
  /* Every value below is a fallback. inc/category-hero.php writes the matching
     custom property inline only when the client has set one, so a category nobody
     has touched never sees a var() with anything in it and renders as it always did.
     No !important anywhere: the cascade does the work. */
  text-align: var(--cat-hero-align, center);
  /* --space-9 was too much air around a one-word title, and --space-7/--space-6 was
     still more than it needed once the filter bar moved up underneath it. */
  padding: var(--space-6) var(--space-5) var(--space-5);
  border-bottom: 1px solid var(--line-soft);
}
.cat-hero__title {
  font-family: var(--cat-hero-font, var(--font-display));
  font-size: var(--cat-hero-size, clamp(2.4rem, 5vw, 4.2rem));
  font-weight: 500;
  margin: var(--space-2) 0 var(--space-3);
  color: var(--cat-hero-title, var(--ink));
  letter-spacing: -0.01em;
}
.cat-hero .eyebrow { color: var(--cat-hero-eyebrow, var(--amber-deep)); }
.cat-hero__desc {
  font-family: var(--font-serif);
  font-size: 1.2rem;
  color: var(--cat-hero-desc, var(--ink-soft));
  max-width: 600px;
  /* `auto` keeps it centred; a left or right hero needs the margin off that side or
     the copy floats away from the title it belongs to. */
  margin-inline: var(--cat-hero-desc-margin, auto);
  line-height: 1.55;
}
.cat-hero[style*="--cat-hero-align:left"] { --cat-hero-desc-margin: 0 auto 0 0; }
.cat-hero[style*="--cat-hero-align:right"] { --cat-hero-desc-margin: 0 0 0 auto; }

/* A solid colour replaces the gradient outright. */
.cat-hero--custom-bg { background: var(--cat-hero-bg); }

/* A photo or a video. The media sits behind an overlay so the title stays readable
   whatever the client uploads, and the whole band becomes a stacking context so the
   copy can be lifted above both. */
.cat-hero--media {
  position: relative;
  isolation: isolate;
  background-image: var(--cat-hero-image, none);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
.cat-hero--media > * { position: relative; z-index: 2; }
.cat-hero--media::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: rgba(31, 27, 23, var(--cat-hero-overlay, 0.35));
  pointer-events: none;
}
.cat-hero__video {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Not above the overlay, and never catching a click meant for the page. */
  pointer-events: none;
}
/* Asked for stillness: the poster is already painted as the background image, so
   dropping the video leaves the frame rather than a flat panel. */
@media (prefers-reduced-motion: reduce) {
  .cat-hero__video { display: none; }
}

.cat-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  padding: 18px 0 var(--space-5);
  border-bottom: 1px solid var(--line-soft);
  margin-bottom: var(--space-5);
  /* Sticks under the header, then tucks away as you read down the page and
     comes back when you scroll up (app.js initStickyToolbar). */
  position: sticky;
  top: var(--header-h, 64px);
  z-index: 60;
  background: var(--cream);
  transition: opacity 0.3s ease, transform 0.3s ease;
}
.cat-filters {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.filter-chip {
  font-family: var(--font-display);
  font-size: 0.85rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 8px 14px;
  border: 1px solid var(--line);
  border-radius: 20px;
  color: var(--ink);
  background: var(--paper);
  transition: all 0.2s;
}
/* Two spellings of the same label ship in the markup; the media query below swaps them.
   display:none also takes the hidden one out of the accessibility tree, so a screen reader
   reads one label, not both. */
.filter-chip__short { display: none; }
.filter-chip:hover { background: var(--cream-deep); }
.filter-chip.is-active {
  background: var(--ink);
  color: var(--cream);
  border-color: var(--ink);
}

.cat-tools {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.cat-count {
  font-family: var(--font-serif);
  font-style: italic;
  color: var(--muted);
  font-size: 0.95rem;
}
.cat-sort {
  font-family: var(--font-display);
  font-size: 0.9rem;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--paper);
  color: var(--ink);
  cursor: pointer;
}

/* The sort control, rebuilt.
   Styled to match the <select> it replaces exactly, so nothing else on the toolbar
   moves; only the option list is ours. initSortDropdown() explains why it exists —
   short version: a native popup anchors to the layout box, and this toolbar is
   sticky, so the list opened hundreds of pixels from the button. */
.cat-sort-ui {
  position: relative;
}
.cat-sort-ui__button {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-family: var(--font-display);
  font-size: 0.9rem;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--paper);
  color: var(--ink);
  cursor: pointer;
  white-space: nowrap;
}
.cat-sort-ui.is-open .cat-sort-ui__button,
.cat-sort-ui__button:hover { border-color: var(--sage-deep); }
.cat-sort-ui__caret {
  width: 9px;
  height: 9px;
  margin-left: auto;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translate(-2px, -2px);
  opacity: 0.55;
  flex: none;
}
.cat-sort-ui__list {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  /* Never narrower than the button, never so wide it leaves the page. */
  min-width: 100%;
  max-width: min(18rem, calc(100vw - 2 * var(--space-4)));
  z-index: 120;
  margin: 0;
  padding: 4px;
  list-style: none;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 10px 28px rgba(31, 27, 23, 0.14);
}
.cat-sort-ui__list[hidden] { display: none; }
.cat-sort-ui__option {
  font-family: var(--font-display);
  font-size: 0.9rem;
  padding: 9px 12px;
  border-radius: 5px;
  color: var(--ink);
  cursor: pointer;
  white-space: nowrap;
}
.cat-sort-ui__option:hover,
.cat-sort-ui__option:focus-visible {
  background: var(--cream);
  outline: none;
}
.cat-sort-ui__option[aria-selected="true"] {
  background: var(--ink);
  color: var(--cream);
}

.cat-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-5);
  padding-bottom: var(--space-8);
}

.cat-empty {
  grid-column: 1 / -1;
  text-align: center;
  padding: var(--space-9) var(--space-4);
  color: var(--muted);
}
.cat-empty__title {
  font-family: var(--font-display);
  font-size: 1.4rem;
  color: var(--ink);
  margin: 0 0 var(--space-2);
}
.cat-empty__sub {
  font-family: var(--font-serif);
  font-style: italic;
  margin: 0;
}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 1280px) {
  .category-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (max-width: 1024px) {
  .product-grid { grid-template-columns: repeat(3, 1fr); }
  .category-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 820px) {
  .site-header__menu-toggle { display: flex; }
  /* The hero arrows are hidden on a phone — see the block below THE ROUND BUTTON,
     which is where that now has to live. */
  .hero-carousel__arrow--prev { left: 6px; }
  .hero-carousel__arrow--next { right: 6px; }
  .hero-carousel .hero,
  .hero-carousel .hero--video,
  .hero-carousel .hero--tertiary { min-height: 62vh; }
  .products-carousel { margin: 0 calc(var(--space-4) * -1); padding: 0 var(--space-4); }
  .products-carousel__arrow { display: none; }
  .product-row > .product-card { flex: 0 0 240px; }
  .primary-nav { display: none; }
  .brand__tagline { display: none; }
  .brand__name { font-size: 1.25rem; }
  .brand__ornament { width: 60px; }

  .hero__inner { padding: var(--space-8) var(--space-4); }
  .hero--primary { min-height: 60vh; }
  .hero--video { min-height: 55vh; }
  .hero--tertiary { min-height: 50vh; }
  .hero__split-text .hero__actions { justify-content: center; }

  .product-grid { grid-template-columns: repeat(2, 1fr); gap: var(--space-3); }

  /* Mobile: single column — one category per row, horizontal layout */
  .category-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-3);
  }
  .category-tile {
    flex-direction: row;
    justify-content: flex-start;
    align-items: center;
    text-align: left;
    gap: var(--space-4);
    aspect-ratio: auto;
    min-height: 104px;
    padding: var(--space-3) var(--space-5);
  }
  /* Uniform icon size for the horizontal rows (overrides per-category desktop sizes) */
  .category-tile .category-tile__art,
  .category-tile[data-icon="accessories"] .category-tile__art,
  .category-tile[data-icon="microdose"] .category-tile__art,
  .category-tile[data-icon="vapes"] .category-tile__art,
  .category-tile[data-icon="hash"] .category-tile__art,
  .category-tile[data-icon="flower"] .category-tile__art {
    width: 80px;
    height: 80px;
    margin-bottom: 0;
    flex-shrink: 0;
  }
  .category-tile__name {
    flex: 1;
    text-align: center;
    font-size: 1.6rem;
  }
  /* Softer pop on mobile so full-width tiles don't overflow sideways */
  .category-tile.is-pressed,
  .category-tile.is-hovered,
  .category-tile:hover {
    transform: translateY(var(--tile-zoom-lift-mobile)) scale(var(--tile-zoom-scale-mobile));
  }

  .section { padding: var(--space-8) 0; }
  .section__head { margin-bottom: var(--space-6); }

  .faq__q { font-size: 1.05rem; padding: var(--space-4) 0; }
  .faq__a p { padding-right: 0; font-size: 1rem; }

  .quickview__body { grid-template-columns: minmax(0, 1fr); }
  .quickview__actions { flex-wrap: wrap; }
  .quickview__actions .btn { flex: 1 1 100%; }
  .quickview__panel { padding: var(--space-5); }
  .quickview__name { font-size: 1.5rem; }
  .quickview__back { display: inline-flex; }
  .quickview__close { display: none; }

  .pdp__layout { grid-template-columns: minmax(0, 1fr); gap: var(--space-5); }
  .pdp__label-name { font-size: 1.8rem; }
  .pdp__actions { flex-wrap: wrap; }
  .pdp__actions .btn { flex: 1 1 100%; }

  .cat-grid { grid-template-columns: repeat(2, 1fr); gap: var(--space-3); }
  .cat-hero { padding: var(--space-5) var(--space-4); }
  .cat-toolbar { padding: var(--space-3) 0; }

  .age-gate__panel { padding: var(--space-6) var(--space-5); }
  .age-gate__title { font-size: 2rem; }
}

@media (max-width: 480px) {
  body { font-size: 16px; }
  .product-grid { gap: var(--space-3); }
  .product-card__label-name { font-size: 1.05rem; }
  .product-card__body { padding: var(--space-3); }
  .product-card__name { font-size: 1rem; }
  .hero__title { font-size: 2.3rem; }
  .section__title { font-size: 1.85rem; }
  .pay-pill { font-size: 0.62rem; padding: 5px 9px; }

  /* Compact header so wordmark + icons always fit 320px-wide screens */
  .site-header__inner { gap: var(--space-2); padding: var(--space-2) var(--space-3); }
  .brand__name { font-size: 1.02rem; letter-spacing: 0; }
  .brand__ornament { width: 48px; height: 12px; }
  .icon-btn { width: 40px; height: 40px; }
  .icon-btn svg { width: 22px; height: 22px; }
  .cart-btn__count { min-width: 18px; height: 18px; font-size: 0.62rem; }
  .site-header__menu-toggle { width: 30px; padding: 6px; }

  .product-row > .product-card { flex: 0 0 210px; }
  .category-grid-wide { padding: 0 var(--space-2); }
  .container { padding: 0 var(--space-4); }
}

/* ============================================================
   UTILITIES
   ============================================================ */
.is-locked { overflow: hidden; }

/* Keyboard focus. :focus-visible only, so a mouse click never draws the ring. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--sage-deep);
  outline-offset: 2px;
  border-radius: 2px;
}

/* Skip link — off screen until it takes focus */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  padding: var(--space-3) var(--space-4);
  background: var(--ink);
  color: var(--cream);
  font-family: var(--font-sans);
  font-size: 0.9rem;
  text-decoration: none;
}
.skip-link:focus {
  left: var(--space-4);
  top: var(--space-4);
}


/* Product gallery thumbnails, shown when a product has more than one photo */
.pdp__thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.pdp__thumb {
  width: 68px;
  height: 68px;
  padding: 0;
  border: 1px solid var(--line-soft);
  /* A shade tighter than the frame above it. At 68px square the card corner reads
     proportionally larger than it does on a 280px card, and a thumbnail rounder than
     the photo it controls looks wrong. Still tracks --radius-card. */
  border-radius: calc(var(--radius-card) - 2px);
  overflow: hidden;
  background: var(--cream-deep);
  cursor: pointer;
  transition: border-color var(--card-hover-duration), transform var(--card-hover-duration);
}
.pdp__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pdp__thumb:hover { transform: translateY(-2px); }
.pdp__thumb.is-active { border-color: var(--sage-deep); }

/* ============================================================
   SEARCH RESULTS

   MATCHED BY ID, NOT BY CLASS, AND IT HAS TO STAY THAT WAY.

   WordPress puts `search-results` on the <body> of any search results page — it is one
   of body_class()'s standard output classes. A bare `.search-results` rule therefore
   hits the search page's BODY as well as this panel, and these three declarations are
   ruinous there: the body got max-height 520px and overflow-y auto, so the page's own
   content overflowed a 520px box and, with html carrying overflow-x: clip, the viewport
   had nothing left to scroll. Measured on /?s=kush: main 3438px tall, footer ending at
   4113px, document scrollHeight stuck at 900px — the results below the fold could not
   be reached at all.

   This is the same trap the WooCommerce section of this file already warns about for
   .is-active and .open. #search-results is in THEME-CONTRACT.md §8.1, so the id is as
   reliable as the class and cannot collide with anything WordPress generates.
   ============================================================ */
#search-results {
  max-height: min(60vh, 520px);
  overflow-y: auto;
  padding: var(--space-2) 0 var(--space-4);
}
.search-results__hint,
.search-results__empty,
.search-results__count {
  font-family: var(--font-serif);
  font-style: italic;
  color: var(--muted);
  margin: 0 0 var(--space-2);
  padding: var(--space-2) 0;
}
.search-results__count {
  font-size: 0.85rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-style: normal;
  font-family: var(--font-display);
  border-bottom: 1px solid var(--line-soft);
}
.search-result {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-2);
  border-bottom: 1px solid var(--line-soft);
  transition: background var(--card-hover-duration);
}
.search-result__link {
  flex: 1;
  min-width: 0;
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr) auto;
  gap: var(--space-4);
  align-items: center;
  text-decoration: none;
  color: inherit;
}
.search-result:hover { background: var(--cream); }
.search-result__thumb {
  position: relative;
  overflow: hidden;
  width: 56px;
  height: 56px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 1.3rem;
}
.search-result__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.search-result__text { min-width: 0; }
.search-result__name {
  display: block;
  font-family: var(--font-display);
  font-size: 1.05rem;
  overflow-wrap: anywhere;
}
.search-result__meta {
  display: block;
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.88rem;
  color: var(--muted);
}
.search-result__price {
  font-family: var(--font-display);
  font-size: 0.98rem;
  white-space: nowrap;
}
.search-results__more {
  display: block;
  width: 100%;
  margin-top: var(--space-3);
  padding: var(--space-3);
  background: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-family: var(--font-display);
  font-size: 0.9rem;
  color: var(--ink);
  cursor: pointer;
}
.search-results__more:hover { background: var(--cream); }

@media (max-width: 480px) {
  .search-result__link { grid-template-columns: 44px minmax(0, 1fr); }
  .search-result__thumb { width: 44px; height: 44px; }
  .search-result__price { grid-column: 2; }
}

/* ============================================================
   COMPONENTS ADDED WITH THE DATA LAYER
   ============================================================ */

/* Note on the styled photo placeholder */
.media-placeholder__note {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 28px;
  z-index: 1;
  text-align: center;
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  color: var(--muted);
}
.pdp__label .media-placeholder__note { bottom: 46px; font-size: 0.85rem; }

.product-card__name a { color: inherit; text-decoration: none; }
.product-card__name a:hover { text-decoration: underline; text-underline-offset: 3px; }
.product-card__stock--inline { position: static; }

/* Category initial, shown only when a tile's icon image can't load */
.category-tile__monogram {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 64%;
  height: 64%;
  border-radius: 50%;
  background: var(--cream-deep);
  color: var(--sage-dark);
  font-family: var(--font-display);
  font-size: 2.4rem;
}
.category-tile__monogram[hidden] { display: none; }

/* Attribute filters on category pages */
.cat-filters {
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
}
.cat-filters[hidden] { display: none; }
.filter-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.filter-group__label {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 0.85rem;
  letter-spacing: 0.08em;
  color: var(--amber-deep);
  margin-right: 4px;
}
.filter-group__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.cat-sort[hidden] { display: none; }
.cat-empty__links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin-top: var(--space-4);
}

/* Product detail */
.pdp__cat a { color: inherit; text-decoration: none; }
.pdp__cat a:hover { text-decoration: underline; text-underline-offset: 3px; }
.pdp__price,
.quickview__price {
  font-family: var(--font-display);
  font-size: 1.5rem;
  color: var(--ink);
  margin: 0 0 var(--space-3);
}
.pdp__price .product-card__per { margin-left: var(--space-3); }
.pdp__attrs {
  display: grid;
  /* Flow across, not down. Five attributes stacked one per row is a long scroll for
     five short values; auto-fit turns the same five into two or three columns on a
     desktop and folds back to one below ~260px of track. row-gap stays tight,
     column-gap is wide enough that a value never reads as belonging to the label on
     its right. */
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  row-gap: var(--space-4);
  column-gap: var(--space-6);
  margin: var(--space-6) 0 0;
  padding-top: var(--space-5);
  border-top: 1px solid var(--line-soft);
}
.pdp__attr {
  display: grid;
  /* 130px of label out of a full-width row left plenty for the value; out of a
     ~370px column it does not. The label track now takes what the longest label
     needs and no more, so "Relaxed, Euphoric, Sleepy" has somewhere to go. */
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  gap: var(--space-3);
}
.pdp__attr dt {
  font-family: var(--font-display);
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--amber-deep);
  padding-top: 3px;
}
.pdp__attr dd {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 1rem;
  color: var(--ink-soft);
  overflow-wrap: anywhere;
}
.pdp__sku {
  margin: var(--space-4) 0 0;
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.88rem;
  color: var(--muted);
}

/* Cart line photo, once one is on hand */
.cart-item__media { position: relative; overflow: hidden; }
.cart-item__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Shown in place of product grids when the catalogue can't load */
.catalog-notice {
  grid-column: 1 / -1;
  flex: 1 0 100%;
  text-align: center;
  padding: var(--space-8) var(--space-4);
  border: 1px dashed var(--line);
  border-radius: 18px;
  background: var(--paper);
}
.catalog-notice__title {
  font-family: var(--font-display);
  font-size: 1.3rem;
  color: var(--ink);
  margin: 0 0 var(--space-2);
}
.catalog-notice__sub {
  font-family: var(--font-serif);
  font-style: italic;
  color: var(--muted);
  margin: 0;
}
.catalog-notice__sub a { color: var(--amber-deep); }

@media (max-width: 820px) {
  .pdp__attr { grid-template-columns: minmax(0, 1fr); gap: 2px; }
}

/* Long real values (e.g. the Form "Diamonds/Budder/Distillate/Shatter") contain no
   spaces, so they must be allowed to break or they overflow narrow screens */
.product-card__label-eyebrow,
.product-card__label-name,
.product-card__label-desc,
.product-card__name,
.product-card__desc,
.pdp__label-eyebrow,
.pdp__label-name,
.pdp__name,
.quickview__name,
.quickview__desc,
.cart-item__name,
.cart-item__media-name,
.cart-item__media-cat {
  max-width: 100%;
  overflow-wrap: anywhere;
}
.cat-filters,
.filter-group,
.filter-group__chips {
  min-width: 0;
  max-width: 100%;
}
.filter-chip {
  max-width: 100%;
  white-space: normal;
  overflow-wrap: anywhere;
}
/* A subcategory held open after it empties: still on the row, clearly not
   something you can click. */
.filter-chip--empty {
  opacity: 0.45;
  cursor: default;
  pointer-events: none;
}




/* ============================================================
   BEHAVIOUR + DESIGN ROUND
   ============================================================ */

/* The icon grows inside the tile on hover, the way a card's photo does */
.category-tile__art {
  transition: transform var(--tile-zoom-duration) var(--tile-zoom-ease);
}
.category-tile:hover .category-tile__art,
.category-tile.is-hovered .category-tile__art,
.category-tile.is-pressed .category-tile__art {
  transform: scale(var(--tile-icon-zoom));
}

/* Quick View: the square photo sits centred against the taller info column
   instead of leaving a gap underneath */
.quickview__media { align-self: center; }

/* Search field: no focus box (the bar under it darkens instead), and no second
   clear button from the browser next to our own ✕ */
.search-overlay__form:focus-within { border-bottom-width: 2px; }
.search-overlay__form input:focus,
.search-overlay__form input:focus-visible { outline: none; }
.search-overlay__form input::-webkit-search-cancel-button,
.search-overlay__form input::-webkit-search-decoration { -webkit-appearance: none; appearance: none; }

/* Back arrow on the product page */
.pdp__back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-bottom: var(--space-3);
  padding: 6px 14px 6px 8px;
  background: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-family: var(--font-display);
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
  cursor: pointer;
  transition: background var(--card-hover-duration), border-color var(--card-hover-duration);
}
.pdp__back svg { width: 16px; height: 16px; }
.pdp__back:hover { background: var(--cream); border-color: var(--sage); }

/* Quick add straight from a search result */
.search-result__add {
  flex: none;
  width: 38px;
  height: 38px;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: none;
  color: var(--ink-soft);
  font-family: var(--font-display);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  transition: background var(--card-hover-duration), color var(--card-hover-duration),
              border-color var(--card-hover-duration);
}
.search-result__add:hover {
  background: var(--sage-deep);
  border-color: var(--sage-deep);
  color: var(--cream);
}

/* ============================================================
   THE ROUND BUTTON — one definition for every circular control

   Header icons, the menu toggle, Back to top, the back control, the carousel
   arrows and every panel's close button. They were eight sizes between 36px and
   52px and four different treatments — transparent, bordered, tinted, glass —
   which read as four different design languages on one page.

   Anything that needs a different size still sets its own after this; the hero
   arrows do exactly that, because they sit over a photograph and want the extra
   presence.

   Not applied to .hero-carousel__dot (a dot, not a button), .cart-btn__count (a
   badge) or .category-tile__monogram (decoration) — round, but none of them is
   something you press.
   ============================================================ */
.icon-btn,
.site-header__menu-toggle,
.back-to-top,
.products-carousel__arrow,
.hero-carousel__arrow,
.quickview__close,
.mobile-menu__close,
.cart-drawer__close,
.search-result__add {
  width: var(--btn-round);
  height: var(--btn-round);
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--glass-button);
  -webkit-backdrop-filter: var(--glass-button-blur);
  backdrop-filter: var(--glass-button-blur);
  box-shadow: var(--glass-button-lift);
  border: 1px solid var(--glass-button-edge);
  color: var(--ink-soft);
}
/* The ✕ in a close button had no size of its own and was taking the button's, so
   a 36px icon appeared the moment the button grew to 50px and filled the circle.
   18px is what the cart drawer's close has always used and it reads right. */
.quickview__close svg,
.mobile-menu__close svg,
.cart-drawer__close svg {
  width: 18px;
  height: 18px;
}

.icon-btn:hover,
.site-header__menu-toggle:hover,
.back-to-top:hover,
.products-carousel__arrow:hover,
.hero-carousel__arrow:hover,
.quickview__close:hover,
.mobile-menu__close:hover,
.cart-drawer__close:hover,
.search-result__add:hover {
  background: var(--glass-button-hover);
}

/* The hero arrows, the one control that does not take the standard whole.

   Bigger, because they sit over a photograph and want the presence — and much
   more transparent, because that photograph is the point of the section and a
   0.55 panel sits on top of it like a sticker. At 0.28 the image reads through
   and the blur still separates the chevron from whatever is behind it.

   Below the shared :hover on purpose, or the 0.85 hover would undo the
   transparency the moment a cursor arrived. */
.hero-carousel__arrow {
  width: 52px;
  height: 52px;
  background: rgba(244, 239, 230, 0.28);
}
.hero-carousel__arrow:hover {
  background: rgba(244, 239, 230, 0.5);
}

/* No arrows on a phone, in either orientation. Swipe and the dots are how you move
   the hero there, and a 52px control over a hero that is already short is a lot of
   the picture spent on something a thumb does better.

   It has to be stated HERE, below THE ROUND BUTTON: this hide used to live in the
   (max-width: 820px) block further up, and the standard's `display: inline-flex`
   came later in the file and quietly put the arrows back.

   The comma covers a phone on its side too, which is wider than 820px on a recent
   handset and would otherwise keep them. */
@media (max-width: 820px), (max-height: 500px) {
  .hero-carousel__arrow { display: none; }
}

/* Back to top — translucent, bottom right, out of the way until you scroll */
/* Size, shape and glass come from THE ROUND BUTTON above; what is left here is
   only what makes this one a floating control. */
.back-to-top {
  position: fixed;
  right: var(--space-5);
  bottom: var(--space-5);
  z-index: 90;
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transform: translateY(10px);
  transition: opacity 0.3s, transform 0.3s, visibility 0.3s, background 0.2s;
}
.back-to-top.is-visible { opacity: 1; visibility: visible; transform: none; }
.back-to-top svg { width: 20px; height: 20px; }

.cat-toolbar.is-tucked {
  opacity: 0;
  transform: translateY(-10px);
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .cat-toolbar { transition: none; }
  .dropdown,
  .dropdown__link {
    transition: opacity 0.12s linear;
    transform: none !important;
  }
  .dropdown { transform: translateX(-50%) !important; }
  .has-dropdown:hover .dropdown__link,
  .has-dropdown:focus-within .dropdown__link,
  .has-dropdown.is-open .dropdown__link { transition-delay: 0s; }
}

/* Less air between the category title block and the filters.
   --space-8 (64px) was already a trim from the global .section padding, and still
   more than this page wants: the hero has just ended and the filters are the next
   thing you reach for. --space-5 takes roughly two thirds of it back. The phone and
   landscape values are further down, each a step lower again on the same scale. */
#category-page .section { padding-top: var(--space-5); }

/* Search results: a scrollbar that stays out of the way. By id — see the SEARCH
   RESULTS block above for why a bare .search-results is not safe here. */
#search-results {
  scrollbar-width: thin;
  scrollbar-color: rgba(31, 27, 23, 0.18) transparent;
}
#search-results::-webkit-scrollbar { width: 6px; }
#search-results::-webkit-scrollbar-track { background: transparent; }
#search-results::-webkit-scrollbar-thumb {
  background: rgba(31, 27, 23, 0.16);
  border-radius: 999px;
}
#search-results::-webkit-scrollbar-thumb:hover { background: rgba(31, 27, 23, 0.3); }

/* Cart line: Edit reveals the other weights of the same product */
.cart-item__actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.cart-item__edit {
  background: none;
  border: none;
  padding: 0;
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.85rem;
  color: var(--amber-deep);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.cart-item__edit:hover { color: var(--ink); }
.cart-item__options {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: var(--space-2);
  grid-column: 1 / -1;
}
.cart-item__options[hidden] { display: none; }
.cart-item__option {
  padding: 5px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--paper);
  font-family: var(--font-serif);
  font-size: 0.82rem;
  color: var(--ink-soft);
  cursor: pointer;
  transition: background var(--card-hover-duration), border-color var(--card-hover-duration);
}
.cart-item__option:hover { border-color: var(--sage); }
.cart-item__option.is-active {
  background: var(--sage-deep);
  border-color: var(--sage-deep);
  color: var(--cream);
}

/* Footer credit — set in the system UI font (San Francisco on Apple devices,
   Segoe on Windows), the way Apple sets its own "Designed by Apple in
   California" line: small, regular weight, slightly tight, quiet grey.
   The double class beats .site-footer__bottom p without needing !important. */
.site-footer__bottom .site-footer__credit {
  margin-top: 6px;
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display",
               "Helvetica Neue", "Segoe UI", Helvetica, Arial, sans-serif;
  font-size: 0.76rem;
  font-weight: 400;
  font-style: normal;
  letter-spacing: -0.01em;
  opacity: 0.45;
}


/* ============================================================
   FLOATING LEAVES — page margins
   Built by app.js initParticles(). To switch them off set
   --particles: 0 in the MOTION block above; nothing is rendered
   at all. They are hidden below 1024px, where the content uses
   the full width, and whenever reduced motion is requested.
   ============================================================ */
/* Anchored to the DOCUMENT, not the viewport.
   position: fixed glued the whole layer to the screen, so every leaf travelled down
   the page with you and read as part of the scroll rather than as something drifting
   on its own. Absolute, with the height set to the document by initParticles(), means
   you scroll past them. */
.page-particles {
  /* inset: 0 against a positioned <body>, NOT a height written by JS.

     initParticles() used to measure the page and set this element's height in pixels.
     That measurement happens once, early, and the page is not finished settling — web
     fonts swap, the card reveal runs, images land. On a category page it came out 16px
     tall than the page ended up, and because the layer is a child of <body> those 16px
     were 16px of scrollable nothing under the footer. Every page had its own figure,
     which is why the white bar was a different size on each one, and why the home page
     — measured correctly — never showed it.

     inset: 0 makes the layer exactly as tall as the body, continuously and for free. An
     absolutely positioned child cannot add to its parent's height, so the feedback loop
     that caused this cannot form again whatever the JS does.

     body { position: relative } below is what it resolves against. */
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  overflow: hidden;
}
.page-particles span {
  position: absolute;
  top: var(--y);
  left: var(--x);
  width: var(--size);
  height: var(--size);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath d='M50 5 C60 25 65 50 50 90 C35 50 40 25 50 5 Z' fill='%2387936b'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-size: contain;
  opacity: 0;
  will-change: transform, opacity;
  animation: pageLeaf var(--dur) linear var(--delay) infinite;
}
/* A short local drift, because each leaf now sits at its own place in the page rather
   than crossing the whole screen. --particle-rise is how far it travels. */
@keyframes pageLeaf {
  0%   { transform: translate3d(0, 0, 0) rotate(0deg); opacity: 0; }
  15%  { opacity: var(--particle-opacity); }
  85%  { opacity: var(--particle-opacity); }
  100% { transform: translate3d(var(--drift), calc(-1 * var(--particle-rise, 220px)), 0) rotate(var(--spin)); opacity: 0; }
}
/* Only where there is margin to spare */
@media (max-width: 1024px) {
  .page-particles { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .page-particles { display: none; }
}

/* Without backdrop-filter there is no blur to keep text legible over moving
   content, so those browsers get the solid paper background instead. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-header,
  .dropdown { background: var(--paper); }
  /* Without the blur these two read as washed-out rather than glassy, and they float
     over product photos where a tint alone loses the icon. */
  .back-to-top,
  .page-back__link { background: var(--paper); }
}

/* ============================================================
   WOOCOMMERCE BRIDGE — THE ONLY NEW CSS IN THIS FILE
   ============================================================
   Everything ABOVE this line is styles.css from the static build,
   copied byte for byte: 3164 lines, every comment, every value,
   the whole MOTION token block. Nothing above changed. A diff
   against the static preview is a pure append, which is what keeps
   this file hand-tunable by the client.

   NEW LINES: 3165 to the end of the file.

   Why any of this is needed: WooCommerce wraps our markup in its
   own containers (.woocommerce, ul.products, li.product,
   .woocommerce-notices-wrapper, the breadcrumb) and ships its own
   cart, checkout and my-account templates, which this theme never
   rebuilds. These rules make WooCommerce's markup inherit the
   design instead of fighting it. Borders, type and colour only —
   no layout is rebuilt anywhere below.

   Do not minify this file. cssValue() / cssNumber() in theme.js
   read nine :root MOTION tokens at runtime, and a minifier that
   inlines custom properties silently changes the motion.

   Rules are kept scoped (.filter-chip.is-active, never a bare
   .is-active) because WooCommerce ships its own .is-active and
   .open on tabs and widgets.
   ============================================================ */

/* ------------------------------------------------------------
   1. Loop wrappers — the ones that actually break the layout

   The grid class lands on the <ul> (woocommerce_product_loop_start)
   and the card classes land on li.product ITSELF
   (woocommerce_post_class), because .product-row > .product-card
   (line 1825) and .product-row > .product-card.is-clone (1828) are
   direct-child selectors. A card nested inside the <li> silently
   loses its width, and .product-card.has-img:hover
   .product-card__img--primary (1056) stops matching too.
   ------------------------------------------------------------ */
.woocommerce ul.products,
ul.products {
  list-style: none;
  margin: 0;
  padding: 0;
}
/* WooCommerce's float-grid clearfix, which a CSS grid does not want */
.woocommerce ul.products::before,
.woocommerce ul.products::after,
ul.products::before,
ul.products::after {
  content: none;
  display: none;
}
.woocommerce ul.products li.product,
ul.products li.product {
  list-style: none;
  float: none;
  margin: 0;
  clear: none;
}
.woocommerce ul.products li.product::marker,
ul.products li.product::marker { content: ""; }
/* Width belongs to the grid, or to .product-row > .product-card's flex
   basis. Only reset it where WooCommerce's float grid actually set one. */
.woocommerce ul.products li.product:not(.product-card),
ul.products li.product:not(.product-card) { width: auto; }
/* .first / .last exist for that float grid and mean nothing here. */
.woocommerce ul.products li.product.first,
.woocommerce ul.products li.product.last,
ul.products li.product.first,
ul.products li.product.last {
  clear: none;
  margin-left: 0;
  margin-right: 0;
}
/* The .woocommerce wrapper must not introduce a box of its own. */
.woocommerce,
.woocommerce-page {
  margin: 0;
  padding: 0;
}
/* WooCommerce hides the loop title and rating; ours never render, but a
   plugin can put them back and they have no place in the card design. */
.woocommerce ul.products li.product .woocommerce-loop-product__title,
.woocommerce ul.products li.product .star-rating { display: none; }

/* The hidden attribute has to win here, and on its own it does not.

   theme.js filters a category archive with card.hidden = true and never
   with a display override, because .cat-grid is a grid and a display set
   from JS fights this stylesheet. But [hidden] is only a user-agent rule,
   and .product-card declares display: flex at line 1016 — an author rule,
   which beats it, so a "hidden" card stays on screen and the chips look
   dead. The static build never met this: it re-rendered the whole grid
   rather than hiding cards.

   The file already spells the same rule out everywhere else something is
   toggled by attribute — .search-overlay (619), .quickview (1550),
   .category-tile__monogram (2767), .cat-filters (2775), .cat-sort (2795),
   .cart-item__options (3082). The product card is the one case the port
   introduced, so it is the one rule that has to be added. */
.product-card[hidden],
.woocommerce ul.products li.product[hidden],
ul.products li.product[hidden] { display: none; }

/* ------------------------------------------------------------
   2. Price markup

   get_price_html() nests the amount in
   <span class="woocommerce-Price-amount"><bdi>…</bdi></span>.
   The design sets type on .product-card__price / .pdp__price, so
   these have to inherit rather than introduce their own.
   woocommerce_price_trim_zeros is filtered to true in PHP so a
   whole amount reads $60, as the preview does.
   ------------------------------------------------------------ */
.woocommerce-Price-amount,
.woocommerce-Price-amount bdi,
.woocommerce-Price-currencySymbol {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  text-decoration: none;
}
.product-card__price del,
.pdp__price del,
.quickview__price del {
  color: var(--muted);
  opacity: 1;
  margin-right: var(--space-2);
}
.product-card__price ins,
.pdp__price ins,
.quickview__price ins {
  text-decoration: none;
  background: none;
  color: inherit;
}

/* ------------------------------------------------------------
   3. Notices

   WooCommerce's default is a green bar with a dashicon glyph.
   Cream ground, one hairline, our serif — the same treatment the
   rest of the page gets.
   ------------------------------------------------------------ */
.woocommerce-notices-wrapper:empty { display: none; }
.woocommerce-message,
.woocommerce-info,
.woocommerce-error,
.woocommerce-noreviews {
  background: var(--cream);
  border: 1px solid var(--line);
  border-top-width: 1px;
  border-radius: var(--radius);
  color: var(--ink);
  font-family: var(--font-serif);
  font-size: 1.02rem;
  list-style: none;
  margin: 0 0 var(--space-5);
  padding: var(--space-4) var(--space-5);
}
.woocommerce-error { border-color: var(--amber); }
.woocommerce-message::before,
.woocommerce-info::before,
.woocommerce-error::before,
.woocommerce-message::after,
.woocommerce-info::after,
.woocommerce-error::after {
  content: none;
  display: none;
}
.woocommerce-error li { list-style: none; }
.woocommerce-message .button,
.woocommerce-info .button,
.woocommerce-error .button {
  float: none;
  margin-left: var(--space-4);
  font-family: var(--font-sans);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 10px 18px;
  border: 1px solid var(--ink);
  border-radius: var(--radius);
  background: var(--ink);
  color: var(--paper);
  text-decoration: none;
}

/* ------------------------------------------------------------
   4. Archive furniture

   The result count and the ordering select are removed in PHP
   (remove_action on woocommerce_before_shop_loop) in favour of
   .cat-toolbar. These rules only cover the case where a plugin
   puts them back.
   ------------------------------------------------------------ */
.woocommerce-products-header {
  margin: 0 0 var(--space-5);
  font-family: var(--font-display);
}
.woocommerce-products-header__title {
  margin: 0;
  font-weight: 400;
}
.woocommerce-breadcrumb {
  font-family: var(--font-serif);
  font-size: 0.95rem;
  color: var(--muted);
  margin: 0 0 var(--space-5);
}
.woocommerce-breadcrumb a {
  color: var(--ink-soft);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.woocommerce-breadcrumb a:hover { border-bottom-color: var(--line); }
.woocommerce-result-count {
  font-family: var(--font-serif);
  font-style: italic;
  color: var(--muted);
  margin: 0;
}
.woocommerce-ordering select {
  font-family: var(--font-serif);
  font-size: 1.02rem;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 8px 12px;
}

/* ------------------------------------------------------------
   5. Pagination

   Unused while the archive runs at posts_per_page = -1, but the
   rules have to exist for the day that changes.
   ------------------------------------------------------------ */
nav.woocommerce-pagination {
  margin: var(--space-7) 0 0;
  text-align: center;
}
nav.woocommerce-pagination ul.page-numbers {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  list-style: none;
  margin: 0;
  padding: 0;
  border: none;
}
nav.woocommerce-pagination ul.page-numbers li {
  list-style: none;
  border: none;
  margin: 0;
}
nav.woocommerce-pagination a.page-numbers,
nav.woocommerce-pagination span.page-numbers {
  display: inline-block;
  min-width: 40px;
  padding: 8px 12px;
  font-family: var(--font-display);
  font-size: 0.95rem;
  color: var(--ink-soft);
  background: var(--cream);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  text-decoration: none;
}
nav.woocommerce-pagination a.page-numbers:hover { background: var(--cream-deep); }
nav.woocommerce-pagination span.page-numbers.current {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}

/* ------------------------------------------------------------
   6. Single product — alias WooCommerce's structure onto .pdp__*

   The gallery is NOT bridged. This theme keeps its own
   ab_media_html() + ab_gallery_thumbs_html() and does not add
   wc-product-gallery-zoom / -lightbox / -slider theme support,
   because WooCommerce's gallery renders a grey placeholder image
   and 100 of 186 products have no photo. .flex-control-thumbs
   therefore never renders and has no rules here.
   ------------------------------------------------------------ */
.woocommerce div.product { margin: 0; }
.woocommerce div.product .product_title {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--ink);
}
.woocommerce div.product .woocommerce-product-details__short-description {
  font-family: var(--font-serif);
  color: var(--ink-soft);
}
.woocommerce div.product p.price,
.woocommerce div.product span.price {
  font-family: var(--font-display);
  color: var(--ink);
}
.woocommerce div.product .product_meta {
  font-family: var(--font-serif);
  font-size: 0.95rem;
  color: var(--muted);
}
.woocommerce div.product .woocommerce-tabs { display: none; }

/* ------------------------------------------------------------
   7. The variation form

   WooCommerce's default picker is a <select>. The design is radio
   cards, produced by the
   woocommerce_dropdown_variation_attribute_options_html filter —
   a hook, not a template override. The real <select> stays in the
   DOM inside .tier-options__native so add-to-cart-variation.js can
   find it, and theme.js mirrors a radio click onto it.

   It is VISUALLY HIDDEN, never display:none — some WooCommerce JS
   paths measure the control and a display:none select misbehaves.
   ------------------------------------------------------------ */
.tier-options__native {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.woocommerce div.product form.variations_form { margin: 0; }
.woocommerce div.product form.variations_form table.variations {
  margin: 0;
  border: none;
  width: 100%;
}
.woocommerce div.product form.variations_form table.variations th,
.woocommerce div.product form.variations_form table.variations td {
  border: none;
  padding: 0;
  background: none;
  display: block;
}
.woocommerce div.product form.variations_form table.variations th.label { display: none; }
.woocommerce-variation-price {
  font-family: var(--font-display);
  font-size: 1.35rem;
  color: var(--ink);
  margin: var(--space-4) 0 0;
}
.woocommerce-variation-availability {
  font-family: var(--font-sans);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.woocommerce-variation-description {
  font-family: var(--font-serif);
  color: var(--ink-soft);
}
.woocommerce-variation-add-to-cart {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: 0;
  flex-wrap: wrap;
}

/* Price, stepper and Add to cart on one line.

   WooCommerce puts the selected variation's price in .woocommerce-variation and
   the stepper and button in .woocommerce-variation-add-to-cart — two block
   siblings, so they stacked and the price sat on a line of its own above a wide
   gap. Laying their shared parent out as a row is enough; neither element has to
   be touched or moved, so the variation script still finds everything where it
   expects it.

   It wraps rather than squeezes: at a narrow column the button drops to its own
   line instead of the price being crushed. */
.single_variation_wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
}
/* The price block takes what it needs and no more, so the button sits next to it
   rather than being pushed to the far edge of the column. */
.single_variation_wrap > .woocommerce-variation {
  flex: 0 0 auto;
  margin: 0;
}
/* Both of these spaced the old stacked layout. On one line they are dead height
   above and below the row. */
.single_variation_wrap .woocommerce-variation-price { margin: 0; }
.single_variation_wrap .woocommerce-variation-add-to-cart { margin: 0; }
/* TODO: if :has() support ever matters, theme.js can mirror the checked
   radio onto a .tier-option class instead. Reversible, one line there
   and one rule here. .tier-option:has(input:checked) is at line 2134. */

/* ------------------------------------------------------------
   8. Add to cart and quantity

   woocommerce_loop_add_to_cart_args adds "btn btn--primary" to the
   loop button in PHP, so the existing button rules apply directly.
   These cover the single-product button and the quantity field,
   whose wrapper is given the .quickview__qty class by the one
   quantity-input.php override.
   ------------------------------------------------------------ */
.woocommerce div.product form.cart { margin: 0; }
.woocommerce div.product form.cart .button,
.woocommerce button.button.alt,
.woocommerce a.button.alt,
.woocommerce .single_add_to_cart_button,
.woocommerce #respond input#submit,
.woocommerce a.button,
.woocommerce button.button,
.woocommerce input.button {
  font-family: var(--font-sans);
  font-size: 0.72rem;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 14px 28px;
  border: 1px solid var(--ink);
  border-radius: var(--radius);
  background: var(--ink);
  color: var(--paper);
  cursor: pointer;
  line-height: 1.2;
  text-decoration: none;
  transition: background 0.2s ease, color 0.2s ease;
}
.woocommerce a.button:hover,
.woocommerce button.button:hover,
.woocommerce input.button:hover,
.woocommerce button.button.alt:hover,
.woocommerce a.button.alt:hover,
.woocommerce .single_add_to_cart_button:hover,
.woocommerce #respond input#submit:hover {
  background: var(--paper);
  color: var(--ink);
}
.woocommerce a.button.disabled,
.woocommerce button.button:disabled,
.woocommerce button.button.disabled,
.woocommerce input.button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
/* The "View cart" link WooCommerce appends after an AJAX add. The drawer
   opens instead, so it would be a second, redundant affordance. */
.woocommerce a.added_to_cart { display: none; }
.woocommerce .button.loading { opacity: 0.6; }
.woocommerce .button.loading::after { content: none; }

/* The quantity field. quantity-input.php gives the wrapper both the
   WooCommerce class and ours, so .quickview__qty (line 1693) supplies
   the look and this only clears WooCommerce's own box. */
.woocommerce .quantity,
.woocommerce div.product form.cart .quantity {
  float: none;
  margin: 0;
}
.woocommerce .quantity input.qty {
  width: auto;
  background: none;
  border: none;
  box-shadow: none;
}
.woocommerce .quantity .plus,
.woocommerce .quantity .minus { display: none; }

/* ------------------------------------------------------------
   9. Quick View loading line

   The one class the port adds. The quickview body is fetched, so
   there is a moment with nothing in it.
   ------------------------------------------------------------ */
.quickview__loading {
  grid-column: 1 / -1;
  padding: var(--space-8) var(--space-5);
  text-align: center;
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 1.05rem;
  color: var(--muted);
}

/* ------------------------------------------------------------
   10. Mini-cart fragment

   mini-cart.php emits our own classes directly (a justified
   override — the reason is in that file's header), so the fragment
   lands in #cart-body already skinned. These are the safety net
   for the case where WooCommerce's default markup gets through.
   ------------------------------------------------------------ */
.widget_shopping_cart_content { font-family: var(--font-serif); }
.woocommerce ul.cart_list,
.woocommerce ul.product_list_widget {
  list-style: none;
  margin: 0;
  padding: 0;
}
.woocommerce ul.cart_list li,
.woocommerce ul.product_list_widget li {
  list-style: none;
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--line-soft);
}
.woocommerce ul.cart_list li a,
.woocommerce ul.product_list_widget li a {
  font-family: var(--font-display);
  color: var(--ink);
  text-decoration: none;
}
.woocommerce .widget_shopping_cart .total,
.woocommerce.widget_shopping_cart .total {
  border-top: 1px solid var(--line);
  padding-top: var(--space-3);
  font-family: var(--font-display);
  color: var(--ink);
}

/* ------------------------------------------------------------
   11. Cart, checkout and my-account

   NEVER REBUILT. WooCommerce's own templates render; this is
   borders, type and colour only. No grid, no flex, no positioning,
   nothing that moves a field.
   ------------------------------------------------------------ */
.woocommerce table.shop_table,
.woocommerce-cart-form table.shop_table {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  border-collapse: collapse;
  font-family: var(--font-serif);
  color: var(--ink);
  width: 100%;
  margin: 0 0 var(--space-6);
}
.woocommerce table.shop_table th,
.woocommerce table.shop_table td {
  border-top: 1px solid var(--line-soft);
  padding: var(--space-4);
  color: var(--ink);
}
.woocommerce table.shop_table thead th {
  border-top: none;
  font-family: var(--font-sans);
  font-size: 0.7rem;
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  background: var(--cream);
}
.woocommerce table.shop_table td.product-name a {
  font-family: var(--font-display);
  color: var(--ink);
  text-decoration: none;
}
.woocommerce table.shop_table .product-remove a.remove {
  color: var(--muted);
  font-size: 1.2rem;
  text-decoration: none;
}
.woocommerce table.shop_table .product-remove a.remove:hover {
  color: var(--ink);
  background: none;
}
.woocommerce .cart_totals h2,
.woocommerce .cross-sells h2,
.woocommerce-checkout h3,
.woocommerce-billing-fields h3,
.woocommerce-shipping-fields h3,
.woocommerce-additional-fields h3 {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--ink);
}
.woocommerce .cart-collaterals,
.woocommerce .cart_totals { font-family: var(--font-serif); }
.woocommerce .cart .actions .coupon input.input-text {
  width: auto;
  min-width: 180px;
}
.woocommerce form.checkout_coupon,
.woocommerce form.login,
.woocommerce form.register {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--cream);
  padding: var(--space-5);
}
.woocommerce #order_review,
.woocommerce table.woocommerce-checkout-review-order-table {
  font-family: var(--font-serif);
  color: var(--ink);
}
.woocommerce #payment {
  background: var(--cream);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.woocommerce #payment ul.payment_methods {
  border-bottom: 1px solid var(--line-soft);
  padding: var(--space-5);
  list-style: none;
}
.woocommerce #payment ul.payment_methods li {
  list-style: none;
  font-family: var(--font-serif);
  color: var(--ink);
}
.woocommerce #payment ul.payment_methods li label {
  font-family: var(--font-display);
  font-size: 1.1rem;
  color: var(--ink);
}
/* The Interac e-Transfer instructions live here (step 5). They are several
   lines of text a customer has to read and copy accurately, so this box
   gets real room and the body typeface rather than fine print. */
.woocommerce #payment div.payment_box {
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  color: var(--ink);
  font-family: var(--font-serif);
  font-size: 1.02rem;
  line-height: 1.7;
  padding: var(--space-5);
  margin: var(--space-3) 0 0;
}
.woocommerce #payment div.payment_box::before { content: none; }
.woocommerce #payment div.form-row.place-order { padding: var(--space-5); }
.woocommerce-MyAccount-navigation ul {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.woocommerce-MyAccount-navigation ul li {
  list-style: none;
  border-bottom: 1px solid var(--line-soft);
}
.woocommerce-MyAccount-navigation ul li:last-child { border-bottom: none; }
.woocommerce-MyAccount-navigation ul li a {
  display: block;
  padding: var(--space-3) var(--space-4);
  font-family: var(--font-serif);
  font-size: 1.02rem;
  color: var(--ink-soft);
  text-decoration: none;
}
.woocommerce-MyAccount-navigation ul li.is-active a,
.woocommerce-MyAccount-navigation ul li a:hover {
  background: var(--cream);
  color: var(--ink);
}
.woocommerce-MyAccount-content { font-family: var(--font-serif); }
.woocommerce .woocommerce-order,
.woocommerce .woocommerce-thankyou-order-details {
  font-family: var(--font-serif);
  color: var(--ink);
}
.woocommerce ul.woocommerce-thankyou-order-details,
.woocommerce ul.order_details {
  list-style: none;
  padding: var(--space-5);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--cream);
}
.woocommerce ul.woocommerce-thankyou-order-details li,
.woocommerce ul.order_details li {
  list-style: none;
  border-right-color: var(--line-soft);
  font-family: var(--font-sans);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.woocommerce ul.woocommerce-thankyou-order-details li strong,
.woocommerce ul.order_details li strong {
  font-family: var(--font-display);
  font-size: 1.1rem;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
}

/* Form controls — cart, checkout, account, and anything else WooCommerce
   or WordPress renders. Type, hairline and colour; the layout is theirs. */
.woocommerce form .form-row { padding: var(--space-2) 0; }
.woocommerce form .form-row label,
.woocommerce-page form .form-row label {
  font-family: var(--font-sans);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.woocommerce form .form-row .required { color: var(--amber); }
.woocommerce form .form-row input.input-text,
.woocommerce form .form-row textarea,
.woocommerce form .form-row select,
.woocommerce-page form .form-row input.input-text,
.woocommerce-page form .form-row textarea,
.woocommerce-page form .form-row select,
.woocommerce input.input-text,
.woocommerce textarea,
.woocommerce select {
  font-family: var(--font-serif);
  font-size: 1.02rem;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 12px;
}
.woocommerce form .form-row input.input-text:focus,
.woocommerce form .form-row textarea:focus,
.woocommerce form .form-row select:focus {
  outline: 2px solid var(--sage-deep);
  outline-offset: 1px;
}
.woocommerce form .form-row.woocommerce-invalid input.input-text { border-color: var(--amber); }
.select2-container--default .select2-selection--single {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  height: auto;
  padding: 6px 4px;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
  font-family: var(--font-serif);
  font-size: 1.02rem;
  color: var(--ink);
  line-height: 1.6;
}
.select2-dropdown {
  border-color: var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  font-family: var(--font-serif);
}
.select2-container--default .select2-results__option--highlighted[aria-selected] {
  background: var(--sage-deep);
  color: var(--paper);
}

/* ------------------------------------------------------------
   12. One hero rule

   .hero__actions is emitted on every slide now that the hero is a
   repeater, where the static markup wrapped only slide 1's two
   buttons. A photo-split slide's buttons have to stay left-aligned,
   which .hero__split-text .hero__actions (line 1051) already does
   for the split text block; this covers the inner wrapper.
   ------------------------------------------------------------ */
.hero__inner--photo .hero__actions { justify-content: flex-start; }

/* ------------------------------------------------------------
   13. WordPress core classes

   Verified absent from the static stylesheet, and all required.
   .screen-reader-text above all: WordPress and WooCommerce markup
   leans on it, and without it their labels render visibly in the
   middle of the page.
   ------------------------------------------------------------ */
.screen-reader-text {
  border: 0;
  clip: rect(1px, 1px, 1px, 1px);
  clip-path: inset(50%);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  width: 1px;
  word-wrap: normal !important;
}
.screen-reader-text:focus {
  background-color: var(--paper);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  clip: auto !important;
  clip-path: none;
  color: var(--ink);
  display: block;
  font-family: var(--font-sans);
  font-size: 0.8rem;
  height: auto;
  left: 6px;
  line-height: normal;
  padding: 15px 23px 14px;
  text-decoration: none;
  top: 6px;
  width: auto;
  z-index: 100000;
}
.alignleft {
  float: left;
  margin-right: var(--space-5);
  margin-bottom: var(--space-4);
}
.alignright {
  float: right;
  margin-left: var(--space-5);
  margin-bottom: var(--space-4);
}
.aligncenter {
  display: block;
  margin-left: auto;
  margin-right: auto;
}
.alignwide { max-width: var(--max-w); }
.alignfull {
  max-width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
}
.wp-caption {
  max-width: 100%;
  margin-bottom: var(--space-5);
}
.wp-caption img { display: block; }
.wp-caption-text,
.gallery-caption {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.95rem;
  color: var(--muted);
  margin-top: var(--space-2);
  text-align: center;
}
.sticky { display: block; }
.bypostauthor { display: block; }
.wp-block-image { margin: 0 0 var(--space-5); }
.wp-block-image figcaption {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.95rem;
  color: var(--muted);
  text-align: center;
}
.wp-block-quote {
  border-left: 2px solid var(--line);
  margin: 0 0 var(--space-5);
  padding-left: var(--space-5);
  font-family: var(--font-serif);
  font-style: italic;
  color: var(--ink-soft);
}

/* ------------------------------------------------------------
   14. Reduced motion, for what the port added

   The stylesheet above already silences the hero, the tiles, the
   ticker and the leaves (lines 982, 1306, 3028, 3155). These two
   cover the elements WooCommerce brings with it.
   ------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  .woocommerce a.button,
  .woocommerce button.button,
  .woocommerce input.button,
  .woocommerce .single_add_to_cart_button { transition: none; }
  .woocommerce .blockUI.blockOverlay::before { animation: none; }
}

/* ------------------------------------------------------------
   15. Print

   Paper gets the words and the prices. Every fixed, floating,
   animated or interactive layer goes: the header, the drawer, the
   overlays, the leaves, the ticker and the age gate would each
   print as a block of colour across the page.
   ------------------------------------------------------------ */
@media print {
  .announcement,
  .site-header,
  .site-footer,
  .search-overlay,
  .cart-drawer,
  .quickview,
  .age-gate,
  .mobile-menu,
  .page-particles,
  .back-to-top,
  .products-carousel__arrow,
  .product-card__quick,
  .cat-toolbar,
  .hero-carousel__dot,
  .hero-carousel__arrow,
  .skip-link,
  .woocommerce-notices-wrapper {
    display: none !important;
  }
  html { scroll-behavior: auto; }
  body {
    background: #ffffff;
    color: #000000;
  }
  .product-card,
  .cat-grid,
  .product-grid {
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .product-row {
    overflow: visible;
    display: block;
  }
  a[href]::after { content: ""; }
}

/* ============================================================
   INTERAC E-TRANSFER — step 5
   The notice above the payment section at checkout, and the
   instructions block on the order-received page and in the
   customer's email. Email clients ignore external stylesheets,
   so the email version inherits WooCommerce's own email styles
   and only reads as a plain definition list — that is on
   purpose, not an oversight.
   ============================================================ */

/* The heads-up at checkout, so nobody waits for a card form */
.etransfer-notice {
  margin: 0 0 1.5rem;
  padding: 1.1rem 1.25rem;
  border: 1px solid var(--line);
  border-left: 3px solid var(--sage-deep);
  background: var(--cream);
  border-radius: 2px;
}
.etransfer-notice__title {
  margin: 0 0 0.4rem;
  font-family: var(--font-display, Georgia, serif);
  font-size: 1.05rem;
  color: var(--ink);
}
.etransfer-notice p {
  margin: 0;
  font-size: 0.92rem;
  line-height: 1.6;
  color: var(--ink-soft);
}

/* The instructions themselves */
.etransfer {
  margin: 2rem 0;
  padding: 1.75rem;
  border: 1px solid var(--line);
  background: var(--paper);
  border-radius: 2px;
}
.etransfer__title {
  margin: 0 0 1.25rem;
  font-family: var(--font-display, Georgia, serif);
  font-size: 1.45rem;
  font-weight: 500;
  color: var(--ink);
}
.etransfer__rows {
  margin: 0 0 1.25rem;
  display: grid;
  gap: 0.75rem;
}
.etransfer__row {
  display: grid;
  grid-template-columns: 11rem 1fr;
  gap: 0.75rem;
  align-items: baseline;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--line-soft);
}
.etransfer__row:last-child { border-bottom: 0; padding-bottom: 0; }
.etransfer__row dt {
  margin: 0;
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.etransfer__row dd {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
}

/* The two values a customer retypes into their banking app. A typo in the
   security answer means the shop cannot release the payment, so they are set
   in a monospaced face where 0/O and 1/l are told apart. */
.etransfer__value {
  font-size: 1rem;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.etransfer__value--strong {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 1.1rem;
  letter-spacing: 0.02em;
  color: var(--ink);
}

.etransfer__copy {
  padding: 0.32rem 0.7rem;
  font-family: inherit;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 2px;
  cursor: pointer;
  transition: background var(--t-fast, 0.2s) ease, color var(--t-fast, 0.2s) ease,
              border-color var(--t-fast, 0.2s) ease;
}
.etransfer__copy:hover,
.etransfer__copy:focus-visible {
  background: var(--cream);
  border-color: var(--sage-deep);
  color: var(--ink);
}
.etransfer__copy.is-copied {
  background: var(--sage-deep);
  border-color: var(--sage-deep);
  color: var(--paper);
}

.etransfer__note,
.etransfer__ships {
  margin: 0 0 0.6rem;
  font-size: 0.88rem;
  line-height: 1.6;
  color: var(--ink-soft);
}
.etransfer__ships {
  margin: 1rem 0 0;
  padding-top: 1rem;
  border-top: 1px solid var(--line-soft);
  color: var(--ink);
}

@media (max-width: 560px) {
  .etransfer { padding: 1.25rem; }
  .etransfer__row { grid-template-columns: 1fr; gap: 0.3rem; }
}

/* ============================================================
   PDP LAYOUT BRIDGE — step 7 fix
   WooCommerce composes the single product page as
   div.product > [our .pdp__media] + div.summary + section.related.
   The design's two-column grid is .pdp__layout, which
   inc/woocommerce.php now puts on div.product itself. These rules
   make WooCommerce's own .summary behave as the design's
   .pdp__info, and stop the related row being trapped in one
   column.

   Without this the media column is full width and the image
   swells to fill it, pushing the title, price and buttons out to
   the edges — which is exactly what the first live install did.
   ============================================================ */
.woocommerce div.product.pdp__layout > .summary,
.woocommerce div.product.pdp__layout > .entry-summary {
  padding-top: var(--space-3);
  margin-bottom: 0;
  min-width: 0;              /* a grid child must be allowed to shrink */
}

/* Anything WooCommerce appends after the summary spans the full width — and so does
   the attribute table, which is hooked there deliberately so it can. */
.woocommerce div.product.pdp__layout > .related,
.woocommerce div.product.pdp__layout > .up-sells,
.woocommerce div.product.pdp__layout > .section--related,
.woocommerce div.product.pdp__layout > .woocommerce-tabs,
.woocommerce div.product.pdp__layout > .pdp__attrs,
.woocommerce div.product.pdp__layout > .pdp__crumbs {
  grid-column: 1 / -1;
}

/* The grid's own row gap already separates it from the summary above. */
.woocommerce div.product.pdp__layout > .pdp__attrs { margin-top: 0; }

/* The breadcrumb sits above the grid, not inside a column */
.woocommerce div.product.pdp__layout > .pdp__crumbs {
  grid-row: 1;
}

/* The media column must never exceed its column, whatever the file's size */
.pdp__media { min-width: 0; }
.pdp__main > .pdp__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ============================================================
   LANDSCAPE PHONES — the header on its side

   A phone held sideways has under 400px of viewport, and the header was taking
   220px of it: the announcement strip, then a wordmark row, then a nav row. That
   left the Shop dropdown about 170px to live in and pushed the products off the
   bottom of the screen entirely.

   Keyed on HEIGHT, not on orientation: `orientation: landscape` is also true of
   every desktop, and it is the short viewport that is the actual problem. A
   desktop window dragged down to 500px gets the same treatment, which is right.

   The nav moves up into the empty first column of the header's grid — empty above
   820px, because that is where the hamburger lives and the hamburger is hidden by
   then. Below 820px the nav is already gone and the drawer takes over, so the row
   merge only runs where there are two rows to merge.
   ============================================================ */
@media (max-height: 500px) {
  /* Everything in this block just buys height back. */
  .announcement { padding: 5px 0; }

  .site-header__inner {
    padding: 4px var(--space-4);
    gap: var(--space-4);
  }

  .brand__ornament { display: none; }
  .brand__name { font-size: 1.35rem; }
  .brand__tagline { display: none; }

  /* The filter bar.
     Above 767px the chips keep their desktop size, which on a 390px-tall screen
     is two rows of 36px plus 64px of section padding before you reach the first
     product. Half the padding, shorter chips, and the sort control brought down
     with them so the row does not end up with one tall thing in it. */
  #category-page .cat-hero + .section {
    /* The same proportion again, off the landscape figure. The ID is needed to
       outrank #category-page .section; see the note on that rule. */
    padding-top: var(--space-3);
  }
  .filter-chip {
    padding: 6px 12px;
  }
  .cat-sort-ui__button,
  select.cat-sort {
    height: 30px;
    min-height: 0;
    padding: 3px 10px;
  }

  /* The cart drawer.
     The panel is head / list / foot at auto / 1fr / auto. On a 390px screen the
     head was taking 114px and the foot 204px, which left the list 72px — less
     than one product, so the drawer read as a subtotal with a sliver of cart
     above it. Everything below is trimming those two so the 1fr in the middle
     has something to work with; measured after, the list goes from 72px to
     about 230px, which is three lines instead of most of one. */
  .cart-drawer__head {
    padding: var(--space-3) var(--space-4);
  }
  .cart-drawer__head .eyebrow { display: none; }
  .cart-drawer__title { font-size: 1.15rem; }
  .cart-drawer__close { width: 32px; height: 32px; }

  .cart-drawer__body { padding: var(--space-2) var(--space-4); }

  .cart-drawer__foot {
    padding: var(--space-3) var(--space-4);
  }
  .cart-drawer__row {
    font-size: 0.95rem;
    margin-bottom: var(--space-2);
  }
  .cart-drawer__subtotal { font-size: 1.15rem; }
  .cart-drawer__checkout { padding: 10px; }
  /* Shipping and the age line are repeated at checkout; the list is worth more
     than they are on a screen this short. */
  .cart-drawer__note { display: none; }

  /* Through the token, not width/height: THE ROUND BUTTON sets those from
     --btn-round and sits later in the file, so a literal 40px here was being
     overruled back to 50px and the landscape header grew 10px. */
  :root { --btn-round: 40px; }
}

@media (max-height: 500px) and (min-width: 821px) {
  /* Three rows become two: the nav joins the wordmark's row instead of sitting
     under it behind a rule. */
  .site-header__inner {
    grid-template-rows: auto;
  }
  /* Every cell is pinned to row 1 explicitly. Left to auto-placement the wordmark
     dropped into an implicit second row — which is the exact thing this block is
     here to remove, and it is not worth re-deriving the placement algorithm to
     find out why. Three items, three columns, one row, all stated. */
  .site-header__left,
  .brand,
  .site-header__actions {
    grid-row: 1;
  }
  .primary-nav {
    grid-column: 1;
    grid-row: 1;
    justify-self: start;
    border-top: 0;
    padding-top: 0;
    margin-top: 0;
  }
  /* --space-7 between three items needs 280px, which the first column does not
     have once the wordmark has taken the middle. */
  .primary-nav__list {
    gap: var(--space-4);
    justify-content: flex-start;
  }
}

/* ============================================================
   THE "BACK" PILL — inside the header's left cell
   Aligned by the same grid as the logo, so it can never overlap
   the product grid however narrow the window. Rendered by
   header.php; absent on the front page and on product pages,
   which carry their own "Back to <category>" crumb.

   It starts hidden and fades in once initBackPill() decides the
   filters have been scrolled past — the [hidden] attribute is
   removed first so there is something to transition.
   ============================================================ */
/* Fixed to the viewport and centred on the header's height, rather than sitting in the
   first row's flow. transform holds the centring, so the fade-in slide uses `translate` —
   animating both on transform would make them fight.

   Fixed rather than absolute-inside-the-header because the header has a backdrop-filter,
   which makes it the containing block for any fixed descendant — see the note in
   header.php, where the markup now lives. The header is sticky at top:0 and this control
   only ever appears once you have scrolled, so half of --header-h is reliably the
   header's own centre on screen. trackHeaderHeight() keeps that variable current; the
   64px fallback is the header's height before it first measures. */
.page-back__link {
  position: fixed;
  left: var(--space-5);
  top: calc(var(--header-h, 64px) / 2);
  transform: translateY(-50%);
  z-index: 101;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 11px 5px 8px;
  font-family: var(--font-sans);
  font-size: 0.68rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--ink-soft);
  text-decoration: none;
  /* Same glass as .back-to-top — see the --glass-button tokens. */
  background: var(--glass-button);
  -webkit-backdrop-filter: var(--glass-button-blur);
  backdrop-filter: var(--glass-button-blur);
  box-shadow: var(--glass-button-lift);
  border: 1px solid var(--glass-button-edge);
  border-radius: 999px;
  /* Out of the layout until it is wanted, so the header does not jump. */
  opacity: 0;
  translate: -6px 0;
  pointer-events: none;
  transition: opacity 0.28s ease, translate 0.28s var(--dropdown-ease),
              background 0.18s ease, border-color 0.18s ease, color 0.18s ease;
}
.page-back__link.is-visible {
  opacity: 1;
  translate: 0 0;
  pointer-events: auto;
}
.page-back__link svg {
  width: 13px;
  height: 13px;
  flex: none;
  transition: transform 0.18s ease;
}
.page-back__link:hover,
.page-back__link:focus-visible {
  background: var(--glass-button-hover);
  border-color: var(--sage-deep);
  color: var(--ink);
}
.page-back__link:hover svg { transform: translateX(-2px); }

/* ============================================================
   PHONE, UPRIGHT — a little air in the header

   Narrow AND tall, so a phone on its side is untouched: that one is deliberately
   tight and is handled in the LANDSCAPE PHONES block.

   Two selectors deep on purpose. .site-header__inner also carries .container, and
   `.container { padding: 0 var(--space-4) }` in the (max-width: 480px) block sits
   AFTER `.site-header__inner`'s own padding in the file — same specificity, later
   wins — so on a phone the header had no vertical padding at all and a 44px
   button sat in a 58px band. Adding a class to the chain settles it without
   reordering anything or reaching for !important.
   ============================================================ */
@media (max-width: 767px) and (min-height: 501px) {
  .site-header .site-header__inner {
    padding-block: 6px;
    /* Nearer the edges. .container's 16px is right for body copy and too much for
       a control that wants to sit in the corner. */
    padding-inline: var(--space-2);
  }

  /* A few pixels lower, against the wordmark's optical centre rather than the
     box's. transform rather than margin or top: it moves what you see and
     nothing about the layout, so the header does not grow by three pixels. */
  .site-header__left,
  .site-header__actions {
    transform: translateY(3px);
  }

  /* The wordmark grows into the room the tighter padding freed up.

     Tied to the viewport rather than fixed, because the space it has is the
     screen minus two 44px controls and the padding — about 219px on a 375pt
     phone and only 148px on a 320pt one. A single size that fills the first
     would overflow the second; this one fills whatever is actually there, and
     on the narrowest phones it simply wraps to two lines as it always did.

     Capped at 1.25rem: past that it collides with the buttons on a large
     handset, where vw alone would keep growing. */
  .brand__name {
    font-size: clamp(1rem, 5vw, 1.35rem);
  }

  /* The flourish above the wordmark goes, and the wordmark takes the height it
     was using. At this size it is three faint strokes nobody reads; the name is
     what the space is worth spending on, and dropping it buys back the line the
     bigger type needs so the header does not grow to pay for it. */
  .brand__ornament { display: none; }
}

/* ============================================================
   THE BACK CONTROL, FLOATING

   Bottom left, paired with Back to top on the right: same 24px inset, same 44px
   circle, same glass. Fixed, so it does not scroll away, and it still only appears
   once you are past the filters.

   The comma is the point. This fires on a NARROW screen (a phone upright, where the
   control landed on the hamburger) and on a SHORT one (a phone on its side, where the
   header is now a single row and the control landed on the HOME link instead). Two
   different collisions, one answer, and writing it twice would guarantee the two
   copies drift apart.
   ============================================================ */
@media (max-width: 767px), (max-height: 500px) {
  .page-back__link {
    position: fixed;
    left: var(--space-5);
    bottom: var(--space-5);
    top: auto;
    /* The header-centred version holds itself there with transform: translateY(-50%).
       Fixed to the bottom it must not, or the button sits half a button too high. */
    transform: none;
    z-index: 90;
    width: var(--btn-round);
    height: var(--btn-round);
    min-width: var(--btn-round);
    min-height: var(--btn-round);
    padding: 0;
    display: grid;
    place-items: center;
    justify-content: center;
    border-radius: 50%;
    /* Rises into place instead of sliding in from the left, which is what it did
       against the header. */
    translate: 0 8px;
  }
  .page-back__link.is-visible {
    translate: 0 0;
  }
  /* A circle has no room for "Back to Concentrates" — the label is clipped rather than
     display: none'd, at the foot of this file, so the link keeps an accessible name. */
}


/* ============================================================
   QUANTITY CONTROL WIDTH — live-install fix
   The bridge rule `.woocommerce .quantity input.qty { width: auto }`
   (0,3,1) outranks `.quickview__qty input { width: 50px }` (0,1,1),
   so WooCommerce's own input stretched the control to 254px. In a
   nowrap flex row beside a 176px "Add to cart" that pushed the
   button 96px past the panel and gave the page a horizontal
   scrollbar.

   Matched at (0,4,1) so it wins outright rather than by source
   order, and the row is allowed to wrap as a second line of
   defence at narrow widths.
   ============================================================ */
.woocommerce .quantity.quickview__qty input.qty,
.quickview__qty input.qty,
.quickview__qty input[type="number"] {
  width: 56px;
  min-width: 0;
  flex: none;
  -moz-appearance: textfield;
}
.quickview__qty input.qty::-webkit-outer-spin-button,
.quickview__qty input.qty::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* The control never grows, the button takes what is left, and if there is
   genuinely not enough room the two stack instead of overflowing. */
.quickview__actions {
  flex-wrap: wrap;
  row-gap: var(--space-3);
}
.quickview__actions .quickview__qty,
.quickview__actions .quantity { flex: none; }
.quickview__actions .btn {
  flex: 1 1 auto;
  min-width: 0;
}

/* Nothing inside the modal may widen the page */
.quickview__panel { max-width: min(820px, calc(100vw - 2 * var(--space-5))); }
#quickview-body { min-width: 0; }

/* ============================================================
   PRODUCT GRID SCROLL-REVEAL
   The same fade-and-rise the category tiles use, applied to the
   cards in a category archive and in search results. NOT to
   .product-row, which is the Best Sellers / Our Latest marquee —
   those are cloned and measured for the scroll, and hiding them
   would break it.

   The start state is behind .reveal-ready, which initCardReveal()
   adds. Without JavaScript the class never lands and the cards
   simply render, rather than staying invisible forever.

   `translate` is deliberately separate from `transform`: the card's
   hover lift uses transform, and animating both on one property
   would make the two fight.
   ============================================================ */
.cat-grid.reveal-ready .product-card,
.product-grid.reveal-ready .product-card {
  opacity: 0;
  translate: 0 var(--card-reveal-rise, 26px);
  transition: transform var(--card-hover-duration), box-shadow var(--card-hover-duration),
              border-color var(--card-hover-duration),
              opacity var(--card-reveal-duration, 0.55s) ease var(--reveal-delay, 0ms),
              translate var(--card-reveal-duration, 0.55s) var(--tile-reveal-ease) var(--reveal-delay, 0ms);
}
.cat-grid.reveal-ready .product-card.is-visible,
.product-grid.reveal-ready .product-card.is-visible {
  opacity: 1;
  translate: 0 0;
}

@media (prefers-reduced-motion: reduce) {
  .cat-grid.reveal-ready .product-card,
  .product-grid.reveal-ready .product-card {
    opacity: 1;
    translate: 0 0;
  }
}

/* ============================================================
   QUICK VIEW / PRODUCT PAGE ENTRY ANIMATIONS — REMOVED
   Both opened on a fade. Taken out at the client's request: the
   modal's opacity/transform start state and the product page's
   keyframe were interfering with scroll position on the grid.
   The modal now shows and hides outright, as it did before.
   ============================================================ */

/* ============================================================
   PAGE TRANSITIONS — REMOVED
   A fade on <body> was tried and taken out again. The reason is
   worth keeping: an element with a transform becomes the
   containing block for every position:fixed descendant inside it,
   so animating <body> re-anchored the cart drawer, the age gate
   and the back-to-top button to the document instead of the
   viewport. The drawer stretched to the full page height and its
   checkout button ended up far below the fold, and scroll
   position stopped being restored correctly on navigation.

   If page transitions are ever wanted again, the safe route is
   the View Transitions API, which does not put a transform on an
   ancestor. Do not animate <body>.
   ============================================================ */

/* ============================================================
   CART DRAWER CONTROLS — empty cart, and checkout gating
   ============================================================ */
.cart-drawer__checkout.is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: auto;       /* the click is blocked in JS, not by the cursor */
}
.cart-drawer__empty {
  display: block;
  width: 100%;
  margin-top: var(--space-2);
  padding: 8px 0;
  font-family: var(--font-sans);
  font-size: 0.72rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
  background: none;
  border: 0;
  border-bottom: 1px solid transparent;
  cursor: pointer;
  transition: color 0.18s ease, border-color 0.18s ease;
}
.cart-drawer__empty:hover,
.cart-drawer__empty:focus-visible {
  color: var(--ink);
  border-bottom-color: var(--line);
}
.cart-drawer__empty[disabled] { opacity: 0.5; cursor: default; }

/* ============================================================
   ORDER RECEIVED — thank you, and the way out
   ============================================================ */
.order-thanks {
  text-align: center;
  padding: var(--space-6) 0 var(--space-5);
  border-bottom: 1px solid var(--line-soft);
  margin-bottom: var(--space-6);
}
.order-thanks__ornament {
  display: block;
  width: 60px;
  margin: 0 auto var(--space-3);
  color: var(--amber);
}
.order-thanks__title {
  margin: 0 0 var(--space-2);
  font-family: var(--font-display);
  font-size: clamp(2rem, 5vw, 3rem);
  font-weight: 500;
  color: var(--ink);
}
.order-thanks__copy {
  margin: 0 auto;
  max-width: 52ch;
  font-size: 1rem;
  line-height: 1.7;
  color: var(--ink-soft);
}
/* Each sentence on its own line, and each one allowed to wrap on its own terms if the
   window is narrow enough to need it. */
.order-thanks__line { display: block; }
.nowrap { white-space: nowrap; }
.order-thanks__done {
  margin: var(--space-7) 0 var(--space-5);
  text-align: center;
}
.order-thanks__done .btn { min-width: 200px; }

/* ============================================================
   TOUCH TARGETS — phones
   Measured on a 375px viewport: the filter chips, the header
   controls and the card's quick-view button were all under the
   40px that Apple and Google both ask for, and the chips in
   particular were fiddly enough to mis-tap.

   Sizes are raised with padding and min-height rather than by
   scaling type, so nothing about the design's proportions
   changes on a desktop.
   ============================================================ */
@media (max-width: 767px) {
  /* Filter and strain chips.
     They were the worst tap targets on the site at ~28px tall, so this block first raised
     them to the 44px Apple and Google both ask for. 44px turned out to be too much of the
     screen for a row of five, and the row wrapped. 38px is the compromise: still a
     comfortable thumb target, two thirds of a chip shorter, and it keeps the row on one
     line alongside the abbreviated labels and the tighter type below. Do not take it below
     about 36px — that is where mis-taps start. */
  .filter-chip {
    min-height: 38px;
    padding: 7px 11px;
    font-size: 0.72rem;
    letter-spacing: 0.04em;
    /* 20px, the same number as the desktop chip, so the shape reads the same on both.
       Against a 38px chip that is just over half the height — a pill, where the 6px
       this used to carry read as a square button. */
    border-radius: 20px;
    /* The desktop rule lets a long label break mid-word, which is right when chips wrap
       and wrong when they do not. */
    white-space: nowrap;
    overflow-wrap: normal;
  }

  /* "Sativa-Dominant" becomes "Sativa-Dom" — the swap the markup prints both halves for. */
  .filter-chip__long { display: none; }
  .filter-chip__short { display: inline; }

  /* One line per group. The chips no longer wrap; if a category ever carries more values
     than the screen can hold, the row scrolls sideways rather than growing a second line
     and pushing the grid down. min-width:0 is what allows the row to be narrower than its
     content — without it a flex item refuses to shrink below its contents and the whole
     toolbar overflows the page instead. */
  .filter-group {
    flex-wrap: nowrap;
  }
  .filter-group__chips {
    flex-wrap: nowrap;
    min-width: 0;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .filter-group__chips::-webkit-scrollbar { display: none; }
  /* The label was set to sit beside 44px chips. Against the smaller ones it now reads as
     the biggest thing in the row, so it comes down with them — and every pixel it gives
     back is a pixel of chip that fits before the row has to scroll. */
  .filter-group__label {
    flex: 0 0 auto;
    white-space: nowrap;
    font-size: 0.72rem;
    letter-spacing: 0.04em;
    margin-right: 2px;
  }
  .filter-group,
  .filter-group__chips {
    gap: 6px;
  }

  /* The sort select sits in the same row and should match the chips. It was the
     tallest thing on the toolbar at 44px; 36px lines it up with them.

     The font deliberately stays at 16px or above: iOS Safari zooms the whole page
     when you focus a form control set smaller than that, and a select that yanks the
     layout about on tap is a worse problem than a tall select. Height comes off the
     padding instead. */
  /* select.cat-sort, not .cat-sort: the WooCommerce bridge sets `.woocommerce select`
     (0,1,1) with 10px padding, which outranks a bare class. Matching type + class ties
     the specificity and wins on source order. This is also where the 16.32px font comes
     from, and that one is left alone deliberately. */
  select.cat-sort {
    height: 36px;
    min-height: 0;
    padding: 4px 10px;
  }
  /* The replacement carries the same height. Its font can drop below 16px where the
     native select's could not — there is no form control left to focus, so iOS has
     nothing to zoom for. */
  .cat-sort-ui__button {
    height: 36px;
    padding: 4px 10px;
    font-size: 0.8rem;
  }
  .cat-sort-ui__option { font-size: 0.85rem; padding: 10px 12px; }

  /* The blank band above the filters.
     The generic .section wrapper brings 64px of top padding, which is right for a
     section that opens with a heading and wrong here — the category hero has just
     ended, and the chips are the next thing you want your thumb on. */
  /* #category-page, because line 3087 already trims this same padding with
     `#category-page .section` — an ID selector, which no plain class can outrank. */
  #category-page .cat-hero + .section {
    padding-top: var(--space-3);
  }

  /* The scroll-edge fade.
     mask-image over a fade to transparent, so whatever is behind the toolbar shows
     through — a solid gradient overlay would have to know the background colour and
     would be wrong the moment the toolbar is over anything else. The classes are set
     by initChipScrollFade() in theme.js, which is what keeps the fade on the side
     that actually has more content: a permanent right-hand fade would still be
     fading once you had scrolled to the end, which reads as a rendering fault. */
  .filter-group__chips {
    --fade: 28px;
  }
  .filter-group__chips.has-fade-end {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - var(--fade)), transparent 100%);
    mask-image: linear-gradient(to right, #000 calc(100% - var(--fade)), transparent 100%);
  }
  .filter-group__chips.has-fade-start {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--fade));
    mask-image: linear-gradient(to right, transparent 0, #000 var(--fade));
  }
  .filter-group__chips.has-fade-start.has-fade-end {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--fade),
      #000 calc(100% - var(--fade)), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, #000 var(--fade),
      #000 calc(100% - var(--fade)), transparent 100%);
  }

  /* The back control is sized and placed in its own block, "THE BACK CONTROL,
     FLOATING", which fires on a short screen as well as a narrow one. */

  /* One number moves every round button on a phone, because THE ROUND BUTTON
     reads it. 44px is Apple's minimum tap target and the size these have been. */
  :root { --btn-round: 44px; }

  .site-header__menu-toggle,
  .icon-btn { padding: 0; }

  /* The bars were 100% of a 36px button; in a 44px circle they have to be sized
     themselves or they run into the rim. */
  .site-header__menu-toggle {
    align-items: center;
    justify-content: center;
    gap: 4px;
  }
  .site-header__menu-toggle span { width: 18px; }

  .icon-btn svg { width: 22px; height: 22px; }

  /* Clear of the rim, and no longer overlapping the button beside it. */
  .cart-btn__count {
    top: -2px;
    right: -2px;
    min-width: 18px;
    height: 18px;
    font-size: 0.65rem;
  }

  /* Quick view on a card — 51 of them on a category page */
  .product-card__quick {
    min-width: 44px;
    min-height: 44px;
  }

  /* The − / + around a quantity field */
  .quickview__qty button,
  .cart-item__qty button {
    min-width: 44px;
    min-height: 44px;
  }

  /* Cart line actions were text links at ~18px tall */
  .cart-item__edit,
  .cart-item__remove,
  .cart-item__option {
    min-height: 40px;
    padding-top: 8px;
    padding-bottom: 8px;
  }

  /* The dropdown and mobile menu entries */
  .dropdown__link,
  .mobile-menu__link,
  .mobile-menu__list > li > a {
    min-height: 44px;
    display: flex;
    align-items: center;
  }

  /* The three close/clear controls that were still short */
  .mobile-menu__close,
  .cart-drawer__close {
    min-width: 44px;
    min-height: 44px;
  }
  .cart-drawer__empty { min-height: 44px; }
}

/* The product title inside a card stays small on purpose: theme.js makes the WHOLE card
   tappable through data-href, so the 165x300 card is the target and the title link is a
   convenience on top of it. Raising the link instead would box the heading. */


/* ============================================================
   DISPLAY CUTOUT / SAFE AREA

   header.php asks for viewport-fit=cover. Without it iOS letterboxes a landscape page
   between the sensor housing and the home indicator: grey bars down both sides and a
   layout that visibly does not reach the edges of the phone. With it the page fills the
   display, and the cost is that anything touching an edge can now sit UNDER the housing
   or the indicator. Everything that reaches an edge therefore takes the inset back here.

   max() rather than a plain add, so the padding is the larger of the design value and
   the inset — on a phone with no cutout, and on every desktop browser, these resolve to
   exactly the value they already had and nothing moves. The @supports wrapper keeps the
   whole block away from browsers that would choke on env().
   ============================================================ */
@supports (padding: max(0px, env(safe-area-inset-left))) {
  .container {
    padding-left: max(var(--space-5), env(safe-area-inset-left));
    padding-right: max(var(--space-5), env(safe-area-inset-right));
  }
  @media (max-width: 480px) {
    /* Matches the tighter gutter the 480px block sets. */
    .container {
      padding-left: max(var(--space-4), env(safe-area-inset-left));
      padding-right: max(var(--space-4), env(safe-area-inset-right));
    }
  }

  /* Fixed and absolutely positioned layers are outside .container and take their own. */
  .cart-drawer__panel { padding-right: env(safe-area-inset-right); }
  .quickview,
  .age-gate {
    padding-left: max(var(--space-5), env(safe-area-inset-left));
    padding-right: max(var(--space-5), env(safe-area-inset-right));
  }
  .mobile-menu__panel {
    padding-left: max(var(--space-5), env(safe-area-inset-left));
    padding-right: max(var(--space-5), env(safe-area-inset-right));
  }

  /* The three floating round buttons. The bottom inset is the home indicator. */
  .back-to-top {
    right: max(var(--space-5), env(safe-area-inset-right));
    bottom: max(var(--space-5), env(safe-area-inset-bottom));
  }
  .page-back__link { left: max(var(--space-5), env(safe-area-inset-left)); }
  @media (max-width: 767px), (max-height: 500px) {
    .page-back__link { bottom: max(var(--space-5), env(safe-area-inset-bottom)); }
  }
}

/* ============================================================
   THE FOUR-COLUMN CATEGORY GRID BETWEEN 821px AND 1100px

   .cat-grid is four columns above 820px and two below it, and nothing in between. At
   956px — an iPhone Pro Max on its side, and equally a half-width desktop window — four
   columns resolve to 205px cards. The THC badge is absolutely positioned top-left and
   the stock pill top-right, so at that width a long value ("THC 22% - 26%", 111px) and
   the pill (58px) close to within 14px of each other and read as one jammed strip.

   Three columns at these widths give ~280px cards and about 100px of clear air between
   the two, which is the same breathing room the layout has at full desktop width.
   ============================================================ */
@media (min-width: 821px) and (max-width: 1100px) {
  .cat-grid { grid-template-columns: repeat(3, 1fr); }
}

/* ============================================================
   HERO TEXT: ENTRANCE ONCE, NOT ON EVERY ROTATION

   The staggered heroRise entrance runs off .hero--primary[aria-hidden="false"], and the
   carousel sets that attribute every time slide 1 comes back around. So the animation
   re-ran on every lap: the last element in the stagger starts at 0.68s and takes 0.9s,
   which is the "text takes a second to load each time it changes" — the slide had
   already glided into place and the words were still fading up behind it.

   initHeroCarousel() marks the carousel .is-cycled the first time the slide actually
   changes. The first view of the page keeps the entrance it was designed with; every
   return after that paints the text with the slide.
   ============================================================ */
.hero-carousel.is-cycled .hero--primary[aria-hidden="false"] .eyebrow,
.hero-carousel.is-cycled .hero--primary[aria-hidden="false"] .hero__title-line,
.hero-carousel.is-cycled .hero--primary[aria-hidden="false"] .hero__copy,
.hero-carousel.is-cycled .hero--primary[aria-hidden="false"] .hero__actions,
.hero-carousel.is-cycled .hero--primary[aria-hidden="false"] .hero__ornament {
  animation: none;
}

/* ============================================================
   THE HAMBURGER BELONGS TO THE PHONE ONLY

   Its base rule is `display: none`, turned on at (max-width: 820px) — the same width
   where .primary-nav goes away. THE ROUND BUTTON standard then listed it among the
   circular controls and gave the whole group `display: inline-flex`, which sits later
   in the file and so switched it back on everywhere, desktop included. That is the
   second thing that block quietly re-showed; the hero arrows were the first.

   Both now take the hide back BELOW the standard, where it cannot be undone. 821px is
   the width the nav appears at, so this is exactly "there is already a menu on screen".
   It deliberately has no height clause: a big phone on its side is 956px wide, shows the
   nav, and must not also show the burger.
   ============================================================ */
@media (min-width: 821px) {
  .site-header__menu-toggle { display: none; }
}

/* ============================================================
   NO HERO ENTRANCE ON A PHONE

   heroRise staggers the eyebrow, both title lines, the copy, the buttons and the
   flourish, the last starting 0.68s in and running 0.9s. On a desktop that is a
   considered entrance well below the fold of attention. On a phone the hero IS the
   screen, and measured on the bench the buttons were still at opacity 0 a second and a
   half after load — which is not read as an animation, it is read as a page that has
   not finished loading, or a blink.

   Phones get the text with the slide. Desktop keeps the entrance it was designed with.
   The carousel's own .is-cycled rule still stops the replay on every lap above.
   ============================================================ */
@media (max-width: 767px), (max-height: 500px) {
  .hero--primary[aria-hidden="false"] .eyebrow,
  .hero--primary[aria-hidden="false"] .hero__title-line,
  .hero--primary[aria-hidden="false"] .hero__copy,
  .hero--primary[aria-hidden="false"] .hero__actions,
  .hero--primary[aria-hidden="false"] .hero__ornament {
    animation: none;
  }
}

/* ============================================================
   QUICK VIEW ON A PHONE KEEPS THE X

   The (max-width: 820px) block swapped the corner X for a "Back to products" row above
   the content. Two controls for one action, and the X is the one people reach for in a
   panel this size — it is where every other overlay in the theme puts its close. The
   back row goes; the X stays, in the same corner as the cart drawer's and the mobile
   menu's. Both still carry data-close, so nothing in theme.js changes.
   ============================================================ */
@media (max-width: 820px) {
  .quickview__back { display: none; }
  .quickview__close { display: inline-flex; }
}

/* The X needs a layer of its own. On one column the photo is the first thing in the
   panel and lands directly under the corner the X sits in — with z-index: auto the
   image painted OVER it, so the button was an empty glass disc and a tap in the middle
   of it hit the photo instead of closing. Measured: elementFromPoint at the button's
   centre returned IMG.quickview__img. This never showed on desktop, where two columns
   put the photo on the left and the corner over the text. */
.quickview__close { z-index: 3; }

/* ============================================================
   THE FULL WORDMARK ON A PORTRAIT PHONE

   The flourish and "— Cannabis Co. · Est. 2026 —" were both dropped here: the flourish
   to buy back a line for bigger type, the tagline long before that to save height. The
   brand is a centred column already (ornament / name / tagline), so putting them back is
   a matter of sizing them for the width rather than any new layout.

   The header grows from 65px to about 88px to hold all three. That is the "a little
   bigger if needed" — it is still under a quarter of a 956pt screen, and the sticky
   header keeps the same proportion to the hero.

   The name comes down slightly from the 5vw it was given when it was alone in the
   column, because three stacked lines want a quieter middle one, and the tagline needs
   to fit on ONE line at 320px — hence the vw-tied size and the nowrap.
   ============================================================ */
@media (max-width: 767px) and (min-height: 501px) {
  .site-header .site-header__inner { padding-block: 8px; }

  .brand { gap: 1px; }

  .brand__ornament {
    display: block;
    width: clamp(48px, 16vw, 68px);
    height: 11px;
    margin-bottom: 0;
  }

  .brand__name { font-size: clamp(0.95rem, 4.6vw, 1.25rem); }

  .brand__tagline {
    display: block;
    font-size: clamp(0.52rem, 2.3vw, 0.66rem);
    letter-spacing: 0.04em;
    white-space: nowrap;
  }
}

/* ============================================================
   THE BOTTOM INSET BELONGS TO THE FOOTER

   With viewport-fit=cover the page now reaches under the home indicator. Left alone
   that strip is page background below the last footer content, which reads as blank
   space you can scroll to. Giving it to the footer's own padding makes it footer.

   max(), so on anything without a home indicator this is the 24px it already was.
   ============================================================ */
@supports (padding: max(0px, env(safe-area-inset-bottom))) {
  .site-footer { padding-bottom: max(var(--space-5), env(safe-area-inset-bottom)); }
}

/* ============================================================
   QUICK VIEW ON A PHONE: ONE SCREEN, NO SCROLL

   Measured at 440x956 before this block: 980px of content in an 858px panel (the
   max-height: 90vh cap), so it scrolled by 122px and Add to Cart sat below the fold.

   Two things paid for that. The description is gone, in inc/quickview.php — 66px with
   its margin. The rest is the tier list: 209px as a stacked radio list, about 60px as
   one row of chips. Together that is ~215px, which puts the panel at roughly 765px in
   the 858px it has.

   The radios are still radios. They are hidden rather than replaced, because they are
   what theme.js reads the variation id from and what a keyboard and a screen reader
   navigate — a <select> or a set of buttons would have meant touching addToCart(). Each
   one is still inside its <label>, so the whole chip is the tap target, and
   .tier-option:has(input:checked) already draws the selected state.

   auto-fit with a 68px floor rather than a fixed column count: three weights fit across
   a phone, and a category that ever ships four or five wraps to a second row instead of
   squeezing each chip below a readable width.

   The comma is OR: a phone on its side is 956px wide, so the width test alone misses it,
   and 440px of height is where the panel is tightest of all.
   ============================================================ */
@media (max-width: 820px), (max-height: 500px) {
  .quickview__tiers {
    padding: var(--space-2) var(--space-3);
    margin-bottom: var(--space-3);
  }
  .quickview__tiers-label {
    font-size: 0.72rem;
    letter-spacing: 0.06em;
    margin-bottom: 4px;
  }

  .tier-options {
    grid-template-columns: repeat(auto-fit, minmax(68px, 1fr));
    gap: 6px;
  }

  .tier-option {
    position: relative;
    grid-template-columns: 1fr;
    justify-items: center;
    gap: 0;
    padding: 7px 6px;
    border-radius: 12px;
    text-align: center;
  }

  /* Out of sight, still in the tree: focusable, announced, and the thing JS reads.
     display:none or visibility:hidden would take all three away. */
  .tier-option input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    margin: 0;
    pointer-events: none;
  }

  .tier-option__label {
    font-size: 0.82rem;
    line-height: 1.2;
  }
  .tier-option__price {
    font-size: 0.92rem;
    line-height: 1.25;
  }

  /* The panel's own padding was set for a two-column desktop layout. */
  .quickview__panel { padding: var(--space-5); }
  .quickview__link { margin-top: var(--space-3); }
}

/* ============================================================
   QUICK VIEW IN LANDSCAPE: THE PHOTO IS WHAT DOES NOT FIT

   With the chips above, the text column comes to about 290px and clears the 90vh cap
   (396px on a 440px-tall screen) on its own. The square photo does not: at 338px in a
   two-column panel it is the taller of the two cells, so it, and not the copy, is what
   was pushing Add to Cart below the fold — measured 601px of content in 394px.

   3/2 there instead of 1/1. The image is object-fit: cover already, so it crops rather
   than distorts, and 225px is still the largest single thing on the screen.
   ============================================================ */
@media (max-height: 500px) and (min-width: 821px) {
  .quickview__panel { padding: var(--space-4); }
  .quickview__media { aspect-ratio: 3 / 2; }
  .quickview__body { gap: var(--space-5); }

  /* With the photo down to 249px the text column became the taller cell, and it was
     still 20px over the cap. Measured, the two costly items were the name — 32px type
     wrapping to two lines, 50px on its own — and the 24px of air above the buttons.
     Both come down; nothing else in the column is touched. */
  .quickview__name {
    font-size: 1.45rem;
    margin-bottom: var(--space-2);
  }
  .quickview__cat { margin-bottom: 4px; }
  .quickview__badges { margin-bottom: var(--space-2); }
  .quickview__price { margin-bottom: var(--space-2); }
  .quickview__actions { margin-top: var(--space-3); }
  .quickview__link { margin-top: var(--space-2); }
}

/* ============================================================
   THE BACK CONTROL — ONE SHAPE EVERYWHERE

   It was already a round glass button on a phone. On a desktop it was still the old
   pill, with "Back to Flower" spelled out, tucked against the wordmark. This makes it
   the same control at every width: the circle, the glass, --btn-round like the cart and
   the search and Back to top, floating clear of the header rather than inside it.

   WHERE IT SITS ON A DESKTOP: hard left, 24px in — the mirror of Back to top's 24px on
   the right — and vertically on the row the primary nav occupies. That row runs 93px to
   136px inside a 145px header, so its centre is 30px up from the header's bottom edge.
   Tied to --header-h (which trackHeaderHeight() keeps current) rather than written as a
   number, so it follows the header if its height ever changes.

   The arrow itself goes from 13px to 20px. It is the only thing left in the button once
   the label is gone, and at 13px in a 50px circle it read as a speck.
   ============================================================ */
@media (min-width: 768px) and (min-height: 501px) {
  .page-back__link {
    left: var(--space-5);
    top: calc(var(--header-h, 145px) - 30px);
    transform: translateY(-50%);
    width: var(--btn-round);
    height: var(--btn-round);
    min-width: var(--btn-round);
    min-height: var(--btn-round);
    padding: 0;
    display: grid;
    place-items: center;
    border-radius: 50%;
    /* It rose from below on a phone; here it still arrives from the left, which is the
       direction it points. translate is separate from transform so the two do not
       fight over the vertical centring above. */
    translate: -8px 0;
  }
  .page-back__link.is-visible { translate: 0 0; }
}

/* One size for the glyph at every width. */
.page-back__link svg { width: 20px; height: 20px; }

/* ============================================================
   THE LABEL STAYS READABLE TO A SCREEN READER

   `display: none` took "Back to Flower" out of the accessibility tree along with the
   layout, and the <svg> beside it is aria-hidden — so the circle was a link with no
   accessible name at all, announced as just "link". Clipped instead of removed: gone
   from the page, still the button's name.
   ============================================================ */
.page-back__link span[data-back-label] {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ============================================================
   FIRST-TAP RESPONSE

   touch-action: manipulation switches off double-tap-to-zoom on these controls, and
   with it the ~300ms Safari waits after a tap to find out whether a second one is
   coming. That wait is why the arrow felt like it needed a firm press: the tap was
   registered immediately and the navigation was not.

   Worth having on everything you press, not just the back arrow.
   ============================================================ */
.page-back__link,
.back-to-top,
.icon-btn,
.site-header__menu-toggle,
.btn,
.product-card,
.product-card__quick,
.filter-chip,
.tier-option,
.quickview__close,
.mobile-menu__close,
.cart-drawer__close,
.hero-carousel__arrow,
.products-carousel__arrow,
.cat-sort-ui__button {
  touch-action: manipulation;
}

/* ============================================================
   THE TWO CARD BADGES ON A NARROW PHONE

   Same collision as the 821–1100px one fixed above, at the other end. Two columns on a
   375pt screen give 166px cards, so 146px between the two 10px insets — and the THC
   badge ("THC 22% - 26%", 111px) plus the stock pill (58px) come to 169px. Measured:
   38 of the 41 cards on Flower overlapped, by about 26px, which clipped the THC value
   mid-number.

   There is no room to win back with layout — both are absolutely positioned in opposite
   corners of a fixed-width card — so the type and the padding come down instead. 82px +
   52px leaves 12px of daylight. The badge text itself is untouched: it is the product's
   real THC range and not something to abbreviate.
   ============================================================ */
@media (max-width: 480px) {
  .product-card__badges .badge {
    font-size: 0.54rem;
    letter-spacing: 0.04em;
    padding: 3px 6px;
  }
  .product-card__stock {
    font-size: 0.65rem;
    padding: 2px 7px;
  }
}

/* ============================================================
   SCROLL CHAINING AND THE RUBBER BAND — the empty space under the footer

   The report that pinned it: it happens on a FAST scroll, and it happened with Quick
   View open. Those two together are scroll chaining.

   A flick inside .quickview__panel that reaches the panel's end does not stop there —
   iOS hands the leftover momentum to the scroller behind it, and with nothing left to
   scroll that becomes a rubber-band of the whole document. Released mid-fling, Safari
   can settle with the page dragged up and background showing past the footer. The
   footer has not moved and the document has not grown; you are looking under it. That
   is why it never reproduced on a bench, where scrolls are instant and momentum-free:
   the page height was steady in every measurement because the page height was never
   the problem.

   The nav dropdown already carried `overscroll-behavior: contain` for exactly this,
   with a comment saying the menu "reads as falling apart" without it. Every other panel
   that scrolls was left chaining. They all get it now, and the root gets `none`, which
   stops the document itself from rubber-banding past its own end.

   `none` rather than `contain` on the root: contain would still allow the bounce within
   the document, and the bounce is the thing being seen.
   ============================================================ */
html,
body {
  overscroll-behavior-y: none;
}

.quickview__panel,
.cart-drawer__body,
.mobile-menu__panel,
#search-results {
  overscroll-behavior: contain;
}

/* The horizontal ones. A sideways flick that runs out of carousel or chips hands its
   momentum to the VERTICAL scroller behind it on iOS, which is the same fault sideways. */
.product-row,
.filter-group__chips {
  overscroll-behavior-x: contain;
}

/* ============================================================
   THE BACK BUTTON LINES UP WITH THE GRID

   It sat 24px off the viewport, which read as detached from the page — the first
   product card starts at the container's content edge, not the window's.

   .container is max-width 1280 centred with a 24px gutter, so its content edge is
   (viewport - 1280) / 2 + 24 once the window is wider than the container, and 24 below
   that. max() of the two is exactly that, with no JS and no magic number: 96.5px at
   1440, against the grid's measured 97px.

   Percentages on a fixed element resolve against the viewport, which is what makes the
   first half of this work at all.
   ============================================================ */
@media (min-width: 768px) and (min-height: 501px) {
  .page-back__link {
    left: max(var(--space-5), calc((100% - var(--max-w)) / 2 + var(--space-5)));
  }
}

/* ============================================================
   THE TWO CLASS SETS THAT HAD NO RULES

   Both were carried to handoff as TODOs. Neither is hypothetical: README-DEPLOY tells
   the client that assigning the Footer menu is how they add Policies / Shipping /
   Contact without a template edit, so .site-footer__nav is one admin screen away from
   being on the live site, unstyled — a bulleted, left-aligned list with markers, in a
   footer whose every other element is centred.

   .entry* is the fallback archive in index.php. The static build had no blog, so there
   was no design to port; these four rules give it the page's own type at a readable
   size rather than leaving it to the browser, and stop short of inventing a blog
   design. Reusing .section__title there was the wrong alternative — it sets every entry
   in 3.25rem display type.
   ============================================================ */
.site-footer__menu {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2) var(--space-5);
}
.site-footer__menu li { margin: 0; }
.site-footer__menu a {
  font-family: var(--font-display);
  font-size: 0.78rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--cream);
  opacity: 0.75;
  text-decoration: none;
  transition: opacity 0.2s;
}
.site-footer__menu a:hover,
.site-footer__menu a:focus-visible { opacity: 1; }

.entry {
  max-width: var(--max-w-narrow);
  margin: 0 auto var(--space-7);
  padding-bottom: var(--space-6);
  border-bottom: 1px solid var(--line);
}
.entry:last-child { border-bottom: 0; }
.entry__title {
  font-family: var(--font-display);
  font-size: clamp(1.4rem, 3vw, 1.9rem);
  font-weight: 500;
  line-height: 1.2;
  margin: 0 0 var(--space-2);
}
.entry__title a { color: var(--ink); text-decoration: none; }
.entry__title a:hover { text-decoration: underline; text-underline-offset: 3px; }
.entry__meta {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.88rem;
  color: var(--muted);
  margin: 0 0 var(--space-3);
}
.entry__excerpt {
  font-family: var(--font-serif);
  font-size: 1.02rem;
  line-height: 1.6;
  color: var(--ink-soft);
  margin: 0;
}
