/* ==========================================================================
   LIMITEDLUX STOREFRONT — design system
   Owned by the design lane. This is NOT tailwind.css (ops console).
   Budget: <= 14 KB gzipped, enforced by tests/storefrontDesignShell.test.js.
   Mobile-first: every base rule is the 375px rule. Media queries only scale UP.

   ART DIRECTION (see bakeoff/phase1/DESIGN-DIRECTION.md):
   Didone display voice + neutral system body. Warm-biased neutrals, gold as
   punctuation only, square corners, one rhythm scale, motion only where it
   carries meaning. Premium here is subtraction and rhythm.
   ========================================================================== */

/* --- 0. BRAND FACE ------------------------------------------------------- */
/* Bodoni Moda (SIL OFL 1.1, src/public/fonts/OFL.txt). Self-hosted, subset to
   179 Latin glyphs, 19,684 B woff2, preloaded in partials/head.ejs.
   The `opsz` axis is kept LIVE and driven by `font-optical-sizing: auto`: the
   17px wordmark gets sturdy hairlines, the 44px hero gets razor ones. That is
   the whole reason a variable file is worth 7 KB over a static instance.
   `swap` because the LCP element on the homepage is the hero photograph, not
   text — the face may arrive late, it may never block the page. The system
   serif chain stays behind it so a glyph missing from the subset (a product
   title from a 4,219-product catalogue) degrades instead of tofu. */
@font-face {
  font-family: "Bodoni Moda LL";
  src: url("/fonts/bodoni-moda-latin-var.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* --- 1. TOKENS ----------------------------------------------------------- */
:root {
  /* colour — ink.
     The neutrals are HUE-BIASED toward the gold (~38deg) at low chroma. A pure
     achromatic grey on a warm cream ground is the single loudest "defaulted,
     not chosen" signal in the whole palette; every one of these is AA-checked
     against paper, cream AND surface. */
  --ll-ink: #121212;
  --ll-ink-deep: #191713; /* large ink FIELDS — footer, announce, scrims */
  --ll-ink-2: #3b3630; /* 11.18:1 on cream */
  --ll-muted: #6f665b; /* 5.27:1 on cream — AA at 14px */
  --ll-paper: #ffffff;
  --ll-surface: #f6f4ee;
  --ll-cream: #faf7f0;
  --ll-line: #e6e2db;
  --ll-line-2: #d5cfc4;
  --ll-hair: rgba(59, 54, 48, 0.09); /* the quietest rule in the system */

  /* colour — gold, used as punctuation only */
  --ll-gold: #b1985c; /* rules + ornament ONLY (2.6:1 — never text) */
  --ll-gold-lift: #c5a15b; /* gold ON ink (7.35:1) */
  --ll-gold-deep: #8a6a2c; /* the ONLY gold allowed to carry text (4.7:1) */
  --ll-gold-hair: rgba(177, 152, 92, 0.42);

  /* TEXT ON PHOTOGRAPHY. Any caption set over an image MUST sit on this scrim,
     not on a lighter one: the category tiles' own gradient tops out at .72 and
     an automated sweep cannot see through it, so the only guarantee is a floor
     dark enough that white passes AA against the SCRIM ALONE (14.9:1 at .82).
     Measured on the shipped tiles: "Sunglasses" over bright sand was marginal. */
  --ll-scrim: linear-gradient(
    to top,
    rgba(20, 18, 15, 0.82) 0%,
    rgba(20, 18, 15, 0.55) 45%,
    rgba(20, 18, 15, 0) 100%
  );

  /* colour — state */
  --ll-danger: #9b2226;
  --ll-sold: #9a9186;

  /* colour — roles (swap here, nowhere else) */
  --ll-btn-bg: var(--ll-ink);
  --ll-btn-fg: var(--ll-paper);
  /* THE FOCUS RING IS TWO TONES, AND THAT IS THE WHOLE POINT.
     A one-colour ring can only ever be correct on ONE kind of ground, and this
     storefront has three: cream/paper, a sunlit sand PHOTOGRAPH (the hero) and
     the ink footer. The ring was `--ll-gold-deep`, chosen because it is 5.03:1
     on paper — and measured on the running site with a pixel diff of the same
     region focused and unfocused it came out at 2.41:1 on the mobile hero CTA
     and 2.04:1 on desktop (the secondary CTA: 2.78 / 1.77). WCAG 1.4.11 wants
     3:1. No sweep in this repo could see it: they enumerate TEXT NODES, and a
     focus ring is not a text node.
     ⛔ DO NOT collapse these back to one colour, and do not "fix" a failing
     ground by patching `outline-color` in a context block — that is exactly how
     it broke: the mobile CTAs were moved off cream (where gold passed) onto the
     photograph and the ring was left gold. The two tones contrast 14:1 with
     EACH OTHER, so whatever the ground, one of them clears 3:1 against it:
        deep espresso vs sand 7.1:1 / vs cream 14.2:1 / vs ink 1.2:1  (fails)
        halo          vs ink 16.9:1 / vs cream 1.05:1 / vs sand 2.0:1 (fails)
     — never both. `tests/storefrontFocusRing.test.js` measures this the only
     way it can be measured: real pixels, focused minus unfocused. */
  --ll-focus: #2f2410; /* dark tone — outline */
  --ll-focus-halo: #fbf8f1; /* light tone — the 2px core between element and outline */

  /* type families */
  --ll-font-display: "Bodoni Moda LL", "Iowan Old Style", Charter,
    "Palatino Linotype", Palatino, "Book Antiqua", Georgia, "Noto Serif",
    "Times New Roman", serif;
  --ll-font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, "Noto Sans", sans-serif;

  /* type scale (mobile). Widened at the top: the old ramp put the hero only
     1.6x above a section head, which is why the page had no hierarchy drama.
     A Didone can carry the extra size without shouting. */
  --ll-fs-3xl: 2.5rem; /* 40 (was 34) */
  --ll-fs-2xl: 1.75rem; /* 28 (was 26) */
  --ll-fs-xl: 1.3125rem; /* 21 */
  --ll-fs-lg: 1.125rem; /* 18 */
  --ll-fs-md: 1rem; /* 16 — iOS no-zoom floor for inputs */
  --ll-fs-sm: 0.875rem; /* 14 */
  --ll-fs-xs: 0.75rem; /* 12 */
  --ll-fs-2xs: 0.6875rem; /* 11 */

  --ll-lh-display: 1.04; /* Didone at 40px+ wants almost no leading */
  --ll-lh-tight: 1.14;
  --ll-lh-snug: 1.28;
  --ll-lh-base: 1.55;
  --ll-track-display: -0.012em;
  --ll-track-display-lg: -0.022em; /* >=34px: close the Didone up */
  --ll-track-body: 0.005em;
  --ll-track-eyebrow: 0.18em;
  --ll-track-caps: 0.13em; /* buttons, nav, small caps runs */
  --ll-w-display: 400;
  --ll-w-body: 400;
  --ll-w-med: 500;
  --ll-w-bold: 600;

  /* spacing — 4px base */
  --ll-s1: 0.25rem;
  --ll-s2: 0.5rem;
  --ll-s3: 0.75rem;
  --ll-s4: 1rem;
  --ll-s5: 1.5rem;
  --ll-s6: 2rem;
  --ll-s7: 3rem;
  --ll-s8: 4rem;
  --ll-s9: 6rem;

  /* layout */
  --ll-page-max: 1200px;
  --ll-measure: 62ch; /* prose never runs wider than this */
  --ll-gutter: 1.25rem;
  --ll-header-h: 3.5rem;
  --ll-grid-gap-x: 0.75rem;
  --ll-grid-gap-y: 2.25rem;
  --ll-tap: 44px;

  /* RHYTHM — one scale, three roles. The old system had a single
     --ll-section-y, so a full-bleed band (which brings its own edges) and a
     text section got identical padding; that is why the homepage read
     "hairline, then void". Lanes: use --ll-band-y INSIDE a full-bleed band,
     --ll-section-y between sections, --ll-head-gap under a section head. */
  --ll-section-y: 3.25rem;
  --ll-section-y-lg: 4.5rem;
  --ll-band-y: 2rem;
  --ll-head-gap: 1.75rem;

  /* shape — the storefront commits to square. Radius is reserved for the two
     places a curve carries meaning: the cart count and the WhatsApp button. */
  --ll-r-sm: 0;
  --ll-r-md: 0;
  --ll-r-lg: 0;
  --ll-r-pill: 999px;

  /* elevation — ONE shadow, warm, two-layer. A soft grey drop shadow is the
     most generic signal on the web; this reads as paper lifted off paper. */
  --ll-shadow: 0 1px 2px rgba(25, 23, 19, 0.06),
    0 10px 30px rgba(25, 23, 19, 0.14);

  /* motion — restrained */
  --ll-dur-fast: 120ms;
  --ll-dur: 220ms;
  --ll-dur-slow: 460ms;
  --ll-ease: cubic-bezier(0.2, 0.6, 0.2, 1);
  --ll-ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  --ll-fab-lift: 0px; /* a page with a sticky bar raises the FAB by setting this */
  /* The PDP lightbox is the ONLY thing allowed above the drawer: it is a modal
     inspection surface and the sticky Add-to-bag, the header and the WhatsApp
     FAB all have to be under it, not merely behind it. */
  --ll-z-lightbox: 80;
  --ll-z-drawer: 60;
  --ll-z-header: 40;
  --ll-z-fab: 30;
}

@media (min-width: 750px) {
  :root {
    --ll-gutter: 2rem;
    --ll-section-y: 4.5rem;
    --ll-section-y-lg: 6.5rem;
    --ll-band-y: 2.5rem;
    --ll-head-gap: 2.25rem;
    --ll-grid-gap-x: 1.25rem;
    --ll-grid-gap-y: 3rem;
    --ll-fs-3xl: 3.25rem;
    --ll-fs-2xl: 2.125rem;
    --ll-fs-xl: 1.4375rem;
  }
}
@media (min-width: 990px) {
  :root {
    --ll-gutter: 2.5rem;
    --ll-section-y: 6rem;
    --ll-section-y-lg: 8rem;
    --ll-band-y: 3rem;
    --ll-head-gap: 2.75rem;
    --ll-header-h: 4.5rem;
    --ll-fs-3xl: 3.75rem;
    --ll-fs-2xl: 2.375rem;
    --ll-fs-xl: 1.5625rem;
    --ll-fs-lg: 1.1875rem;
  }
}

/* --- 2. BASE ------------------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
/* ⛔ SYSTEM-WIDE, AND IT IS LOAD-BEARING. `[hidden]{display:none}` lives in the
   UA sheet at specificity 0,1,0 — ANY author rule that sets `display` on a
   class beats it. That is how `.ll-chip{display:inline-flex}` painted a false
   "In stock" on 48,568 of 56,971 variants. product.ejs patched it locally for
   `.ll-pdp`; this makes the whole class of bug impossible everywhere, for every
   surface lane, forever. `until-found` is excluded so find-in-page still works. */
[hidden]:not([hidden="until-found"]) {
  display: none !important;
}
body.ll {
  margin: 0;
  background: var(--ll-paper);
  color: var(--ll-ink-2);
  font-family: var(--ll-font-body);
  font-size: var(--ll-fs-md);
  font-weight: var(--ll-w-body);
  line-height: var(--ll-lh-base);
  letter-spacing: var(--ll-track-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img,
svg,
video {
  display: block;
  max-width: 100%;
}
img {
  height: auto;
}
a {
  color: inherit;
  text-decoration: none;
}
button {
  font: inherit;
  color: inherit;
}
h1,
h2,
h3,
h4 {
  margin: 0;
  font-family: var(--ll-font-display);
  font-optical-sizing: auto;
  font-weight: var(--ll-w-display);
  color: var(--ll-ink);
  line-height: var(--ll-lh-snug);
  letter-spacing: var(--ll-track-display);
}
p {
  margin: 0;
}
/* NOT `ul.ll-plain`. At 0,1,1 it outranked `.ll-trust`'s own padding-block and
   silently zeroed it — which is the entire reason the trust strip has been
   shipping jammed against a hairline under the hero. Keep this at 0,1,0 and
   ABOVE the components, so a component always wins on source order. */
.ll-plain {
  list-style: none;
  margin: 0;
  padding: 0;
}
::selection {
  background: rgba(177, 152, 92, 0.3);
  color: var(--ll-ink);
}

/* --- 3. TYPE UTILITIES --------------------------------------------------- */
/* Display type is set as display type: tighter tracking, almost no leading,
   and balanced rag. Titles are the brand's voice — they get the attention. */
.ll-display {
  font-size: var(--ll-fs-3xl);
  line-height: var(--ll-lh-display);
  letter-spacing: var(--ll-track-display-lg);
  text-wrap: balance;
}
.ll-h1 {
  font-size: var(--ll-fs-2xl);
  line-height: var(--ll-lh-tight);
  letter-spacing: var(--ll-track-display-lg);
  text-wrap: balance;
}
.ll-h2 {
  font-size: var(--ll-fs-xl);
  text-wrap: balance;
}
.ll-h3 {
  font-size: var(--ll-fs-lg);
}
.ll-eyebrow {
  font-family: var(--ll-font-body);
  font-size: var(--ll-fs-2xs);
  font-weight: var(--ll-w-med);
  letter-spacing: var(--ll-track-eyebrow);
  text-transform: uppercase;
  color: var(--ll-gold-deep);
  line-height: 1.2;
}
.ll-muted {
  color: var(--ll-muted);
}
.ll-small {
  font-size: var(--ll-fs-sm);
}
.ll-prose {
  max-width: var(--ll-measure);
  text-wrap: pretty;
}
.ll-prose a {
  text-decoration: underline;
  text-underline-offset: 0.2em;
  text-decoration-color: var(--ll-gold-hair);
  text-decoration-thickness: 1px;
  transition: text-decoration-color var(--ll-dur) var(--ll-ease);
}
.ll-prose a:hover {
  text-decoration-color: var(--ll-gold-deep);
}

/* --- 4. LAYOUT ----------------------------------------------------------- */
.ll-page {
  width: 100%;
  max-width: var(--ll-page-max);
  margin-inline: auto;
  padding-inline: var(--ll-gutter);
}
.ll-section {
  padding-block: var(--ll-section-y);
}
/* A section that opens a page, or closes one onto the footer, gets the long
   measure. Rhythm is what a boutique has and a template does not. */
.ll-section--lead {
  padding-block: var(--ll-section-y-lg) var(--ll-section-y);
}
.ll-section--close {
  padding-block: var(--ll-section-y) var(--ll-section-y-lg);
}
.ll-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--ll-s5);
  margin-bottom: var(--ll-head-gap);
}
.ll-rule {
  border: 0;
  border-top: 1px solid var(--ll-line);
  margin: 0;
}
.ll-rule--hair {
  border-top-color: var(--ll-hair);
}
.ll-main {
  min-height: 50vh;
}
.ll-scroll-x {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* product grid — 2 up on mobile, exactly as the theme that earned */
.ll-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--ll-grid-gap-x);
  row-gap: var(--ll-grid-gap-y);
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 750px) {
  .ll-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
@media (min-width: 990px) {
  .ll-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* --- 5. A11Y ------------------------------------------------------------- */
.ll-vh {
  position: absolute !important;
  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;
}
/* TWO CONCENTRIC RINGS, drawn by two different properties because CSS has no
   two-colour outline. The `box-shadow` fills the 2px gap `outline-offset`
   leaves, so the order out from the element is: halo (0-2px), outline (2-4px).
   ⛔ THE HALO IS BEST-EFFORT AND THE OUTLINE IS NOT. A component that sets its
   own `box-shadow` later in the cascade (`.ll-fab`, `.llc-sort[aria-current]`,
   the PDP's selected swatch and size buttons) wins the property and the halo
   does not paint on it. That is deliberate and it is safe: every one of those
   sits on cream or paper, where the OUTLINE alone measures 12-14:1. The halo's
   real job is the ink footer, whose links carry no box-shadow of their own.
   Restating each component's shadow here to keep the halo would silently drop
   whatever state that shadow expresses — the PDP swatch shadow IS the
   "selected" indication, and losing it on the focused swatch is a worse defect
   than the one this rule fixes. Verified by sweeping the footer and the PDP. */
:focus-visible {
  outline: 2px solid var(--ll-focus);
  outline-offset: 2px;
  box-shadow: 0 0 0 2px var(--ll-focus-halo);
}
:focus:not(:focus-visible) {
  outline: none;
}
.ll-skip:focus {
  position: fixed;
  top: 12px;
  left: 12px;
  z-index: 999;
  width: auto;
  height: auto;
  margin: 0;
  padding: 12px 18px;
  clip: auto;
  clip-path: none;
  overflow: visible;
  background: var(--ll-ink);
  color: var(--ll-paper);
  border: 1px solid var(--ll-gold);
  font-size: var(--ll-fs-xs);
  letter-spacing: var(--ll-track-eyebrow);
  text-transform: uppercase;
}

/* --- 6. HEADER ----------------------------------------------------------- */
.ll-announce {
  background: var(--ll-ink-deep);
  color: rgba(255, 255, 255, 0.92);
  font-size: var(--ll-fs-2xs);
  letter-spacing: var(--ll-track-caps);
  text-transform: uppercase;
  text-align: center;
  padding: 0.625rem var(--ll-gutter);
  /* a hairline, not a stripe: full-strength gold across 390px read as a rule
     of its own and competed with the wordmark directly beneath it */
  border-bottom: 1px solid var(--ll-gold-hair);
}
.ll-announce b {
  color: var(--ll-gold-lift);
  font-weight: var(--ll-w-med);
}

.ll-header {
  position: sticky;
  top: 0;
  z-index: var(--ll-z-header);
  background: var(--ll-paper);
  border-bottom: 1px solid var(--ll-line);
}
.ll-header__bar {
  display: grid;
  /* symmetric outer columns so the wordmark is optically centred, not
     shoved off-centre by the two action icons on the right */
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--ll-s2);
  min-height: var(--ll-header-h);
}
/* `1fr auto 1fr` alone does NOT centre the wordmark: a `1fr` track floors at
   its item's min-content, and the menu is one 44px button against two, so the
   outer tracks based at 44 and 88 and free space split equally ON TOP of that.
   Measured at 375px: the mark sat 17px left of the bar's optical centre. Give
   both outer items the SAME floor and the tracks are genuinely symmetric. */
.ll-header__menu,
.ll-header__actions {
  min-width: calc(var(--ll-tap) * 2);
}
.ll-header__menu {
  justify-self: start;
}
.ll-header__brand {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: var(--ll-tap);
}
.ll-wordmark {
  font-family: var(--ll-font-display);
  font-optical-sizing: auto;
  font-size: 1rem;
  /* the tracking IS the logotype. Bodoni's caps are narrower than the old
     system serif's, so the mark carries more air at a smaller size and still
     leaves the 375px header symmetric. */
  letter-spacing: 0.24em;
  /* letter-spacing puts its full quantum AFTER the last glyph, so the ink sits
     half a quantum left of the box centre. Indent by HALF, not by all of it. */
  text-indent: 0.12em;
  text-transform: uppercase;
  color: var(--ll-ink);
  line-height: 1;
  white-space: nowrap;
}
.ll-wordmark em {
  font-style: normal;
  color: var(--ll-gold-deep);
}
.ll-header__logo {
  max-height: 2rem;
  width: auto;
}
.ll-header__actions {
  display: flex;
  align-items: center;
  justify-self: end;
}
@media (min-width: 430px) {
  .ll-wordmark {
    font-size: 1.125rem;
    letter-spacing: 0.3em;
    text-indent: 0.15em;
  }
}
@media (min-width: 990px) {
  .ll-wordmark {
    font-size: 1.5rem;
    letter-spacing: 0.34em;
    text-indent: 0.17em;
  }
}

.ll-iconbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--ll-tap);
  min-height: var(--ll-tap);
  padding: 0;
  background: none;
  border: 0;
  color: var(--ll-ink);
  cursor: pointer;
  position: relative;
  border-radius: var(--ll-r-md);
}
.ll-iconbtn svg {
  width: 21px;
  height: 21px;
  stroke: currentColor;
  stroke-width: 1.35;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.ll-cart-count {
  position: absolute;
  top: 6px;
  right: 5px;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  border-radius: var(--ll-r-pill);
  /* the ornament gold is 2.79:1 against white — a count has to be readable */
  background: var(--ll-gold-deep);
  color: #fff;
  font-size: 10px;
  font-weight: var(--ll-w-bold);
  line-height: 17px;
  text-align: center;
  letter-spacing: 0;
}

/* desktop inline nav — additive to the drawer, see lane report */
.ll-nav {
  display: none;
}
@media (min-width: 990px) {
  .ll-nav {
    display: block;
    border-top: 1px solid var(--ll-line);
  }
  .ll-nav__list {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    gap: var(--ll-s6);
    list-style: none;
    margin: 0;
    padding: 0;
  }
  .ll-nav__link {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: var(--ll-tap);
    min-width: var(--ll-tap);
    font-size: var(--ll-fs-xs);
    letter-spacing: var(--ll-track-eyebrow);
    text-transform: uppercase;
    color: var(--ll-ink);
    position: relative;
  }
  .ll-nav__link::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 10px;
    height: 1px;
    background: var(--ll-gold);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--ll-dur-slow) var(--ll-ease-out);
  }
  .ll-nav__link:hover::after,
  .ll-nav__link[aria-current="page"]::after {
    transform: scaleX(1);
  }
}

/* --- 7. DRAWER ----------------------------------------------------------- */
.ll-drawer {
  position: fixed;
  inset: 0;
  z-index: var(--ll-z-drawer);
  visibility: hidden;
}
.ll-drawer[open],
.ll-drawer.is-open {
  visibility: visible;
}
.ll-drawer__scrim {
  position: absolute;
  inset: 0;
  background: rgba(18, 18, 18, 0.45);
  opacity: 0;
  transition: opacity var(--ll-dur) var(--ll-ease);
  border: 0;
  padding: 0;
  width: 100%;
  cursor: pointer;
}
.ll-drawer__panel {
  position: absolute;
  inset-block: 0;
  left: 0;
  width: min(86%, 380px);
  background: var(--ll-paper);
  display: flex;
  flex-direction: column;
  transform: translateX(-100%);
  transition: transform var(--ll-dur-slow) var(--ll-ease);
  overflow-y: auto;
}
.ll-drawer.is-open .ll-drawer__scrim {
  opacity: 1;
}
.ll-drawer.is-open .ll-drawer__panel {
  transform: none;
}
.ll-drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: var(--ll-header-h);
  padding-inline: var(--ll-s4) var(--ll-s2);
  border-bottom: 1px solid var(--ll-line);
}
.ll-drawer__list {
  list-style: none;
  margin: 0;
  padding: var(--ll-s2) 0;
}
.ll-drawer__link {
  display: flex;
  align-items: center;
  min-height: 52px;
  padding: 0 var(--ll-s5);
  font-size: var(--ll-fs-md);
  color: var(--ll-ink);
  border-bottom: 1px solid var(--ll-line);
}
.ll-drawer__foot {
  margin-top: auto;
  padding: var(--ll-s5);
  border-top: 1px solid var(--ll-line);
}

/* --- 8. BUTTONS ---------------------------------------------------------- */
.ll-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--ll-s2);
  min-height: var(--ll-tap);
  min-width: var(--ll-tap);
  padding: 0.8125rem 1.75rem;
  border: 1px solid transparent;
  border-radius: var(--ll-r-md);
  background: var(--ll-btn-bg);
  color: var(--ll-btn-fg);
  font-family: var(--ll-font-body);
  font-size: var(--ll-fs-sm);
  font-weight: var(--ll-w-med);
  letter-spacing: var(--ll-track-caps);
  text-transform: uppercase;
  text-align: center;
  cursor: pointer;
  transition: background var(--ll-dur) var(--ll-ease),
    color var(--ll-dur) var(--ll-ease),
    border-color var(--ll-dur) var(--ll-ease),
    box-shadow var(--ll-dur) var(--ll-ease);
}
/* The primary button does not change colour on hover — it gains a gold inner
   rule. Nothing moves, nothing flashes; it reads as an object being noticed. */
.ll-btn:hover {
  background: var(--ll-ink-deep);
  box-shadow: inset 0 0 0 1px var(--ll-gold-hair);
}
.ll-btn--full {
  width: 100%;
}
.ll-btn--lg {
  min-height: 52px;
  font-size: var(--ll-fs-md);
}
.ll-btn--secondary {
  background: transparent;
  color: var(--ll-ink);
  border-color: var(--ll-ink);
}
.ll-btn--secondary:hover {
  background: var(--ll-ink);
  color: var(--ll-paper);
  box-shadow: none;
}
.ll-btn--ghost {
  background: transparent;
  color: var(--ll-ink);
  border-color: var(--ll-line-2);
}
.ll-btn--ghost:hover {
  background: var(--ll-surface);
  border-color: var(--ll-gold);
  box-shadow: none;
}
.ll-btn[disabled],
.ll-btn[aria-disabled="true"] {
  background: var(--ll-surface);
  color: var(--ll-muted);
  border-color: var(--ll-line);
  cursor: not-allowed;
}
.ll-link {
  display: inline-flex;
  align-items: center;
  min-height: var(--ll-tap);
  min-width: var(--ll-tap);
  font-size: var(--ll-fs-xs);
  letter-spacing: var(--ll-track-eyebrow);
  text-transform: uppercase;
  color: var(--ll-ink);
  border-bottom: 1px solid var(--ll-gold-hair);
  padding-bottom: 2px;
  transition: border-color var(--ll-dur) var(--ll-ease);
}
.ll-link:hover {
  border-bottom-color: var(--ll-gold-deep);
}

/* --- 9. FORM CONTROLS ---------------------------------------------------- */
.ll-field {
  display: block;
  margin-bottom: var(--ll-s4);
}
.ll-label {
  display: block;
  margin-bottom: var(--ll-s2);
  font-size: var(--ll-fs-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ll-muted);
}
.ll-input,
.ll-select,
.ll-textarea {
  display: block;
  width: 100%;
  min-height: var(--ll-tap);
  padding: 0.6875rem 0.875rem;
  border: 1px solid var(--ll-line-2);
  border-radius: var(--ll-r-md);
  background: var(--ll-paper);
  color: var(--ll-ink);
  font-family: var(--ll-font-body);
  font-size: var(--ll-fs-md); /* 16px — below this iOS Safari zooms the page */
  line-height: 1.4;
  transition: border-color var(--ll-dur) var(--ll-ease);
  appearance: none;
}
.ll-textarea {
  min-height: 7rem;
  resize: vertical;
}
.ll-input::placeholder,
.ll-textarea::placeholder {
  color: var(--ll-muted);
}
.ll-input:hover,
.ll-select:hover,
.ll-textarea:hover {
  border-color: var(--ll-ink-2);
}
.ll-input:focus,
.ll-select:focus,
.ll-textarea:focus {
  border-color: var(--ll-gold);
}
.ll-select {
  background-image: linear-gradient(45deg, transparent 50%, var(--ll-ink) 50%),
    linear-gradient(135deg, var(--ll-ink) 50%, transparent 50%);
  background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 2.25rem;
}
.ll-help {
  margin-top: var(--ll-s2);
  font-size: var(--ll-fs-xs);
  color: var(--ll-muted);
}
.ll-error {
  color: var(--ll-danger);
}
.ll-input[aria-invalid="true"] {
  border-color: var(--ll-danger);
}

/* search entry */
.ll-search {
  display: flex;
  align-items: stretch;
  gap: var(--ll-s2);
}
.ll-search .ll-input {
  flex: 1 1 auto;
  min-width: 0;
}
.ll-searchbar {
  border-top: 1px solid var(--ll-line);
  padding-block: var(--ll-s3);
}
.ll-searchbar[hidden] {
  display: none;
}

/* size / variant swatch. The theme that earned used a 40px pill; a square
   with a hairline is the boutique convention and it reads as a considered
   control rather than a tag. 44px tap target is unchanged. */
.ll-swatch {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 3rem;
  min-height: var(--ll-tap);
  padding: 0 0.875rem;
  border: 1px solid var(--ll-line-2);
  border-radius: var(--ll-r-md);
  background: var(--ll-paper);
  color: var(--ll-ink);
  font-size: var(--ll-fs-sm);
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: border-color var(--ll-dur) var(--ll-ease),
    background var(--ll-dur) var(--ll-ease),
    color var(--ll-dur) var(--ll-ease);
}
.ll-swatch:hover {
  border-color: var(--ll-ink);
}
.ll-swatch[aria-pressed="true"],
.ll-swatch[aria-checked="true"] {
  border-color: var(--ll-ink);
  background: var(--ll-ink);
  color: var(--ll-paper);
}
.ll-swatch--sold {
  color: var(--ll-muted);
  border-color: var(--ll-line);
  background-image: linear-gradient(
    to top left,
    transparent calc(50% - 1px),
    var(--ll-line-2) 50%,
    transparent calc(50% + 1px)
  );
  cursor: not-allowed;
}

/* --- 10. PRICE, BADGES, STOCK -------------------------------------------- */
.ll-price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--ll-s2);
  font-family: var(--ll-font-body);
}
.ll-price__now {
  font-size: 1.0625rem;
  font-weight: var(--ll-w-med);
  color: var(--ll-ink);
  letter-spacing: 0.01em;
}
.ll-price--sale .ll-price__now {
  font-weight: var(--ll-w-bold);
}
/* explicit: a reset must never be able to silently drop the strikethrough */
.ll-price__was {
  font-size: var(--ll-fs-sm);
  color: var(--ll-muted);
  text-decoration-line: line-through;
  text-decoration-thickness: 1px;
}
.ll-price--pdp .ll-price__now {
  font-size: 1.375rem;
}
.ll-price--pdp .ll-price__was {
  font-size: var(--ll-fs-md);
}

/* Badges sit ON photography, so a badge is a PLATE, never an outline. The
   shipped `--last` chip was `background: transparent` with a 1px gold border
   and gold-deep text: over a busy product photo it was simply unreadable
   (see the Tod's card in the audit shots). One plate, three inks — a discount
   is a fact stated quietly, not a sticker. */
.ll-chip {
  display: inline-flex;
  align-items: center;
  padding: 5px 9px;
  border: 0;
  border-radius: var(--ll-r-sm);
  background: rgba(250, 247, 240, 0.95);
  color: var(--ll-ink);
  font-size: var(--ll-fs-2xs);
  font-weight: var(--ll-w-med);
  letter-spacing: var(--ll-track-caps);
  text-transform: uppercase;
  line-height: 1;
  white-space: nowrap;
}
.ll-chip--save {
  color: var(--ll-gold-deep);
}
.ll-chip--last {
  color: var(--ll-ink);
}
.ll-chip--sold {
  background: var(--ll-ink-deep);
  color: var(--ll-cream);
}

.ll-stock {
  display: flex;
  align-items: center;
  gap: var(--ll-s2);
  font-size: var(--ll-fs-sm);
  color: var(--ll-ink-2);
}
.ll-stock__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ll-gold);
  flex: none;
}

/* --- 11. PRODUCT CARD ---------------------------------------------------- */
.ll-card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.ll-card__media {
  position: relative;
  display: block;
  aspect-ratio: 1 / 1; /* square, as the collection template that earned */
  overflow: hidden;
  background: var(--ll-surface);
}
.ll-card__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--ll-dur-slow) var(--ll-ease-out);
}
/* hover-gated: on a touch device `:hover` sticks after a tap, so the shipped
   rule left cards zoomed at random on the phones that are 100% of the traffic */
@media (hover: hover) and (pointer: fine) {
  .ll-card:hover .ll-card__img {
    transform: scale(1.035);
  }
}
.ll-card__badges {
  position: absolute;
  left: var(--ll-s3);
  bottom: var(--ll-s3);
  /* column-reverse, not wrap: two badges on a 175px card wrapped into a ragged
     block whose second row landed wherever the photo happened to be busiest */
  display: flex;
  flex-direction: column-reverse;
  align-items: flex-start;
  gap: 3px;
  max-width: calc(100% - var(--ll-s6));
}
.ll-card__body {
  padding-top: var(--ll-s4);
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.ll-card__title {
  font-family: var(--ll-font-body);
  font-size: var(--ll-fs-sm);
  font-weight: var(--ll-w-med);
  letter-spacing: 0.01em;
  line-height: 1.35;
  color: var(--ll-ink);
  margin: 0;
  /* reserve two lines so prices sit on one baseline across a grid row —
     a ragged price row is the single loudest "template" tell */
  min-height: 2.7em;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  /* this is an <h3>: `balance` from the utility classes must NOT reach it or a
     clamped 2-line title re-rags every time the grid reflows */
  text-wrap: wrap;
}
.ll-card__link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}
.ll-card__title span {
  background-image: linear-gradient(var(--ll-gold), var(--ll-gold));
  background-size: 0% 1px;
  background-position: 0 100%;
  background-repeat: no-repeat;
  transition: background-size var(--ll-dur) var(--ll-ease);
}
@media (hover: hover) and (pointer: fine) {
  .ll-card:hover .ll-card__title span {
    background-size: 100% 1px;
  }
}
/* Split deliberately. `opacity` on the media box also faded the "Sold out"
   plate nested inside it — the one label on the card that has to stay crisp,
   and the reason sold-out badges read as muddy grey. So the FADE goes on the
   photograph alone; the DESATURATION stays on the media box (it is what
   `storefrontDesignShell` reads for "renders greyed", and a near-neutral
   cream/ink plate is unchanged by grayscale). */
.ll-card--sold .ll-card__media {
  filter: grayscale(0.55);
}
.ll-card--sold .ll-card__img {
  opacity: 0.5;
}
.ll-card--sold .ll-card__title,
.ll-card--sold .ll-price__now {
  color: var(--ll-muted);
}

/* --- 12. PAGINATION ------------------------------------------------------ */
/* recorded defect: pagination was computed but never rendered — 24 of 1,358
   products reachable. This component exists so the collection lane cannot
   ship without it, and storefrontDesignShell.test.js asserts the links. */
.ll-pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--ll-s2);
  padding-block: var(--ll-s6);
  list-style: none;
  margin: 0;
}
.ll-pagination__item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--ll-tap);
  min-height: var(--ll-tap);
  padding: 0 0.5rem;
  border: 1px solid transparent;
  border-radius: var(--ll-r-md);
  font-size: var(--ll-fs-sm);
  color: var(--ll-ink);
}
.ll-pagination__item:hover {
  border-color: var(--ll-line-2);
}
.ll-pagination__item[aria-current="page"] {
  background: var(--ll-ink);
  border-color: var(--ll-ink);
  color: var(--ll-paper);
  font-weight: var(--ll-w-med);
}
.ll-pagination__gap {
  display: inline-flex;
  align-items: center;
  min-height: var(--ll-tap);
  padding: 0 var(--ll-s1);
  color: var(--ll-muted);
}

/* --- 13. FOOTER ---------------------------------------------------------- */
/* Was #f7f5f2 under a #f6f4ee section under white: three near-identical light
   tones stacked with no intent, which is what "unconsidered" looks like. An
   ink close FRAMES the page — the same device every luxury house uses — and it
   costs nothing. Every colour below is re-checked against #191713. */
.ll-footer {
  /* no margin: the ink edge IS the separator. A gap here re-introduced the
     white band between two near-identical off-whites that started all this. */
  margin-top: 0;
  background: var(--ll-ink-deep);
  color: rgba(250, 247, 240, 0.72);
  padding-block: var(--ll-section-y-lg) var(--ll-s7);
}
.ll-footer__cols {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--ll-s6);
}
@media (min-width: 750px) {
  .ll-footer__cols {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--ll-s7) var(--ll-s6);
  }
}
@media (min-width: 990px) {
  .ll-footer__cols {
    grid-template-columns: 1.4fr 1fr 1fr 1.2fr;
  }
}
.ll-footer__h {
  font-family: var(--ll-font-body);
  font-size: var(--ll-fs-2xs);
  font-weight: var(--ll-w-med);
  letter-spacing: var(--ll-track-eyebrow);
  text-transform: uppercase;
  color: var(--ll-gold-lift); /* 7.35:1 on --ll-ink-deep */
  margin-bottom: var(--ll-s3);
}
.ll-footer__list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.ll-footer__link {
  display: flex;
  align-items: center;
  min-height: var(--ll-tap);
  min-width: var(--ll-tap);
  font-size: var(--ll-fs-sm);
  color: rgba(250, 247, 240, 0.78); /* 10.2:1 */
  transition: color var(--ll-dur) var(--ll-ease);
}
.ll-footer__link:hover {
  color: var(--ll-cream);
}
.ll-footer__note {
  font-size: var(--ll-fs-sm);
  color: rgba(250, 247, 240, 0.72); /* 8.9:1 */
  max-width: 42ch;
}
.ll-footer__note strong {
  color: var(--ll-cream);
  font-weight: var(--ll-w-med);
}
.ll-footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--ll-s3);
  margin-top: var(--ll-s7);
  padding-top: var(--ll-s5);
  border-top: 1px solid rgba(250, 247, 240, 0.16);
  font-size: var(--ll-fs-xs);
  color: rgba(250, 247, 240, 0.6); /* 6.5:1 */
}
/* the shared `.ll-link` is ink-on-paper by default — on the ink footer that is
   invisible. Re-cast it rather than asking the footer lane to remember. */
.ll-footer .ll-link {
  color: var(--ll-cream);
  border-bottom-color: rgba(197, 161, 91, 0.55);
}
.ll-footer .ll-link:hover {
  border-bottom-color: var(--ll-gold-lift);
}
.ll-footer__bottom .ll-footer__link {
  min-height: var(--ll-tap);
  font-size: var(--ll-fs-xs);
  color: inherit;
}
.ll-footer__bottom .ll-footer__link:hover {
  color: var(--ll-cream);
}
/* ⛔ THE FOOTER USED TO OVERRIDE `outline-color` TO GOLD-LIFT HERE, and that
   override is GONE ON PURPOSE. It was the only reason a dark-ground focus ring
   was visible anywhere on this site, which meant the footer was the one place
   the UNIVERSAL ring was never actually exercised — so when the hero CTAs moved
   onto a photograph nothing failed until a human looked. The two-tone ring's
   halo carries the footer on its own (measured 16.6:1 against the ink ground,
   against 7.35:1 for the old gold-lift patch), and deleting the patch is what
   makes `tests/storefrontFocusRing.test.js` a real gate on a dark ground rather
   than a test of a special case. Do not reintroduce a per-context colour. */
.ll-footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ll-s4);
  list-style: none;
  margin: 0;
  padding: 0;
}

/* --- 14. TRUST STRIP ----------------------------------------------------- */
/* Highest-value copy on the site for a ~100% COD audience. ONE beat: the band
   pads by the same step it gaps by, so the edge rules never sit closer to the
   ink than the rows sit to each other (they used to: 8px vs 32px). No internal
   rules — three ruled rows read as a data table, not a boutique. Spacing is
   PINNED AS COMPUTED PX by storefrontTrustStrip.test.js: `ul.ll-plain` (0,1,1)
   silently zeroed this padding once already. Selector text proves nothing. */
.ll-trust {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--ll-s5);
  margin: 0;
  padding-block: var(--ll-s5);
  border-block: 1px solid var(--ll-line);
  list-style: none;
}
.ll-trust__item {
  display: flex;
  align-items: flex-start;
  gap: var(--ll-s4);
}
@media (min-width: 750px) {
  .ll-trust {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--ll-s7);
    padding-block: var(--ll-band-y);
  }
}
.ll-trust__item svg {
  width: 26px;
  height: 26px;
  flex: none;
  margin-top: 1px;
  stroke: var(--ll-gold);
  stroke-width: 1.25;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.ll-trust__t {
  display: block;
  font-size: var(--ll-fs-sm);
  font-weight: var(--ll-w-med);
  letter-spacing: 0.01em;
  color: var(--ll-ink);
}
.ll-trust__b {
  display: block;
  margin-top: 1px;
  font-size: var(--ll-fs-xs);
  line-height: 1.45;
  color: var(--ll-muted);
}

/* --- 15. WHATSAPP FAB ---------------------------------------------------- */
.ll-fab {
  position: fixed;
  right: var(--ll-gutter);
  /* --ll-fab-lift lets a page with a sticky action bar (the PDP) raise the
     button instead of letting it sit on top of "Add to bag" */
  bottom: calc(20px + var(--ll-fab-lift) + env(safe-area-inset-bottom, 0px));
  z-index: var(--ll-z-fab);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--ll-ink-deep);
  color: var(--ll-gold-lift);
  box-shadow: var(--ll-shadow), inset 0 0 0 1px var(--ll-gold-hair);
  transition: box-shadow var(--ll-dur) var(--ll-ease);
}
.ll-fab:hover {
  box-shadow: var(--ll-shadow), inset 0 0 0 1px var(--ll-gold-lift);
}
/* ⛔ THE FAB HAS TO RESTATE THE HALO, and it is the element that needs it most.
   `.ll-fab` sets `box-shadow` for its elevation and is declared after the global
   `:focus-visible`, so it WINS the property and the halo silently does not
   paint. Measured with the halo suppressed, on /cart at both widths: 1.18:1 —
   WORSE than the gold ring it replaced (3.56:1), because this button is
   `position: fixed` over ARBITRARY content and on a short page it comes to rest
   over the ink footer, where a dark ring is invisible. It is the one control on
   the site with no fixed ground at all, so it is the one that most needs both
   tones. Halo FIRST in the list so it paints on top of the blurred elevation.
   This was found by measuring, not by reading: the assumption in the note above
   the global rule — "everything that clobbers the halo sits on cream" — was
   REFUTED here. Anything that sets `box-shadow` on a focusable must be checked
   the same way. */
.ll-fab:focus-visible {
  box-shadow: 0 0 0 2px var(--ll-focus-halo), var(--ll-shadow),
    inset 0 0 0 1px var(--ll-gold-hair);
}
.ll-fab svg {
  width: 26px;
  height: 26px;
  fill: currentColor;
}

/* --- 16. MOTION SAFETY --------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .ll-card:hover .ll-card__img {
    transform: none !important;
  }
}

/* --- 17. PDP LIGHTBOX ----------------------------------------------------
   Get the customer CLOSER at the largest derivative there is (1440).
   ⛔ BUILT BY pdp.js; nothing here is server-rendered. The full rationale for
   every rule below lives in the LIGHTBOX section of pdp.js — kept there
   because JS has 17 KB of budget headroom and this sheet has none. */
.ll-lb {
  position: fixed;
  inset: 0;
  z-index: var(--ll-z-lightbox);
  background: var(--ll-ink-deep);
  display: flex;
  flex-direction: column;
  opacity: 0;
  transition: opacity var(--ll-dur) var(--ll-ease-out);
  --ll-lb-dy: 0px; /* driven by the drag-to-dismiss gesture; 0 at rest */
}
/* ⛔ No `.ll-lb[hidden]` rule: §2.7's global [hidden] !important already
   beats this display:flex, and restating it is how that defence rots. */
.ll-lb.is-open {
  opacity: 1;
}
/* the field fades WITH the drag, so the gesture reports its own progress */
.ll-lb.is-dragging,
.ll-lb.is-dragging .ll-lb__track {
  transition: none;
}

/* chrome: a word, not a white X in a circle, beside the photo not on it */
.ll-lb__bar {
  position: relative;
  /* 3, NOT 2 — load-bearing. See lbBuild() in pdp.js. */
  z-index: 3;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ll-s4);
  padding: calc(var(--ll-s3) + env(safe-area-inset-top, 0px)) var(--ll-gutter)
    var(--ll-s3);
  border-bottom: 1px solid var(--ll-gold-hair); /* the only ornament here */
}
.ll-lb__count {
  margin: 0;
  font-size: var(--ll-fs-2xs);
  letter-spacing: 0.06em;
  /* tabular so the bar cannot twitch as the customer swipes */
  font-variant-numeric: tabular-nums;
  color: rgba(250, 247, 240, 0.55);
}
/* ⛔ --ll-gold-lift is the ONLY gold that may carry text (7.35:1 on ink) */
.ll-lb__count b {
  font-weight: inherit;
  color: var(--ll-gold-lift);
}
.ll-lb__close,
.ll-lb__edge {
  appearance: none;
  border: 0;
  background: none;
  padding: 0;
}
.ll-lb__close {
  color: var(--ll-cream);
  font: inherit;
  font-size: var(--ll-fs-2xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  min-height: 44px; /* 44px target; the ink of the word stays small and quiet */
  padding-inline: var(--ll-s2);
  margin-right: calc(var(--ll-s2) * -1);
}
/* ⛔ MUST override the global ring: --ll-focus #2f2410 is a DARK tone for the
   cream ground and is invisible (~1.1:1) on this ink field. */
.ll-lb__close:focus-visible,
.ll-lb__edge:focus-visible {
  outline: 2px solid var(--ll-gold-lift);
  outline-offset: 2px;
  box-shadow: none;
}

/* ONE structure for both platforms: a scroll-snap track, i.e. what the PDP
   gallery already IS, so the phone gesture carries over unchanged. */
.ll-lb__track {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  /* ⛔ `pinch-zoom` IS LOAD-BEARING. Native pinch is the zoom on iOS (72% of
     traffic); `pan-x` alone or `none` kills it and no JS can give it back. */
  touch-action: pan-x pinch-zoom;
  transform: translate3d(0, var(--ll-lb-dy), 0);
}
.ll-lb__track::-webkit-scrollbar {
  display: none;
}
.ll-lb__cell {
  flex: 0 0 100%;
  scroll-snap-align: center;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: var(--ll-s2);
}
.ll-lb__img {
  display: block;
  max-width: 100%;
  max-height: 100%;
  /* settles rather than pops; killed under reduced motion below */
  transition: transform var(--ll-dur-slow) var(--ll-ease-out);
}
.ll-lb:not(.is-open) .ll-lb__img {
  transform: scale(0.985);
}

/* INSPECT — the point of the feature. Fitted, a 1440-wide asset shows at
   ~680px in a 1440x900 window. A second click sets it to natural width,
   LARGER THAN THE VIEWPORT, and the pointer position pans it. */
.ll-lb__img--zoom {
  max-width: none;
  max-height: none;
  width: var(--ll-lb-zw, auto);
  height: auto;
  transition: none;
  will-change: transform;
}
@media (hover: hover) and (pointer: fine) {
  /* ⛔ gated on the class pdp.js sets once the overlay is WIRED: a zoom-in
     cursor over a dead click is the defect this feature replaces */
  .ll-lb-ready .ll-gal__img,
  .ll-lb__cell.is-zoomable {
    cursor: zoom-in;
  }
  .ll-lb__cell.is-zoomed {
    cursor: zoom-out;
  }
}

/* 45 photographs and no dot row: 45 dots is not a control. Edge zones with a
   hairline chevron, invisible until hovered, plus the arrow keys. */
.ll-lb__edge {
  position: absolute;
  inset-block: 0;
  width: min(14%, 120px);
  z-index: 2;
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--ll-dur) var(--ll-ease);
}
/* Edge zones are a WIDE-DESKTOP affordance only — see lbBuild() in pdp.js. */
@media (max-width: 1099px) {
  .ll-lb__edge {
    display: none;
  }
}
.ll-lb__edge--prev {
  left: 0;
}
.ll-lb__edge--next {
  right: 0;
}
.ll-lb__edge::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 13px;
  height: 13px;
  border-left: 1px solid var(--ll-cream);
  border-bottom: 1px solid var(--ll-cream);
}
.ll-lb__edge--prev::after {
  left: 34%;
  transform: translateY(-50%) rotate(45deg);
}
.ll-lb__edge--next::after {
  right: 34%;
  transform: translateY(-50%) rotate(-135deg);
}
@media (hover: hover) and (pointer: fine) {
  .ll-lb__edge:hover,
  .ll-lb__edge:focus-visible {
    opacity: 0.85;
  }
}

/* The sticky Add-to-bag and the WhatsApp FAB are position:fixed and would
   otherwise sit inside the inspection surface, merely painted underneath it. */
body.ll-lb-open .ll-pdp-sticky,
body.ll-lb-open .ll-fab {
  display: none;
}

/* ⛔ The global motion block zeroes transition DURATIONS, which removes NEITHER
   motion here: snap-scroll on arrow-key nav, and the settle-in scale. */
@media (prefers-reduced-motion: reduce) {
  .ll-lb__track {
    scroll-behavior: auto !important;
  }
  .ll-lb__img,
  .ll-lb:not(.is-open) .ll-lb__img {
    transform: none !important;
    transition: none !important;
  }
}
