/* LUMEN B — THE CRAFT LAYER · "The Consultation"
 * Tatcha / Aesop / ILIA lineage: calm, centred, unboxed, generous air.
 * Serif display, italic-serif labels. The opposite of theme A's flush-left
 * hairline register. Where A draws a line, B leaves space.
 *
 * OWNERSHIP CONTRACT (trap 7 — "one owner per selector; never define a
 * selector in two stylesheets"). Every section re-emits lumen.css, so link
 * order between lumen.css and this file is NON-deterministic. Therefore:
 *
 *   - lumen.css already OWNS the color/type tokens, and the bare selectors
 *     .b-threshold, .b-altar, .b-line, .b-step, .b-cta, and .b-quote's
 *     descendants. This file NEVER redefines any of them.
 *   - Where the craft chrome must touch one of those components, it does so
 *     through a DISTINCT selector (a :has() form, a compound scope, or a
 *     co-class) whose specificity is strictly higher — so the outcome is
 *     decided by specificity, not by which <link> won the race.
 *   - Motion tokens (--ease, --dur-*) and engine tokens (spinner/skeleton)
 *     are NOT set by lumen.css, so this file owns them outright.
 *
 * CONTRAST AUDIT (WCAG 2.2, sRGB relative luminance; tokens live in lumen.css
 * and are NOT redefined here, so these are the values that actually render):
 *   --b-muted #6B5F57 on --b-ground #F6F2E9 → 5.53:1  ✓ AA body text (≥4.5)
 *   --b-accent #832B2B on --b-ground #F6F2E9 → 8.01:1  ✓ AA text (≥4.5)
 *   --b-line-strong #908B84 on --b-ground   → 3.03:1  ✓ AA UI/control border (≥3)
 *   --b-line #E4DCD0 → DECORATIVE hairline only, exempt from 1.4.11.
 * No token fails; none changed.
 */

:root {
  /* Motion — theme B is slow and ceremonial (easeOutExpo). Overrides the
     lumen-motion.css defaults; those tokens are unowned there, so this is not
     a redefinition of a component selector. */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-card: 400ms;
  --dur-reveal: 700ms;
  --dur-control: 200ms;

  /* Shared-engine surfaces (loading/skeleton) — unset by lumen.css. */
  --spinner-color: var(--b-on-accent);
  --skeleton-base: #ECE6DA;
  --skeleton-sheen: #F8F4EC;

  /* Chrome geometry. */
  --b-notice-h: 2.5rem;
  --b-bar-h: 6.5rem;
  --b-bar-h-condensed: 4rem;
}

/* ============================================================ header / chrome
 * A sticky element is confined to its CONTAINING BLOCK, and Shopify nests each
 * section in .shopify-section inside #header-group (trap 4). Sticking the inner
 * box gives it a travel range of exactly its own height — none. #header-group
 * is the first ancestor tall enough to travel, so IT sticks, pulled up by the
 * notice height so the announcement scrolls out while the bar pins. The engine
 * still toggles .is-condensed on [data-lumen-header] for the shrink; we only
 * neutralise that element's own stickiness so it can't double-pin inside the
 * already-sticky group. */
#header-group { position: sticky; top: calc(-1 * var(--b-notice-h)); z-index: 50; }
/* The notice is optional. Without this, blanking it leaves the group pinned 40px
   too high and the wordmark is clipped above the viewport forever. */
#header-group:not(:has(.b-notice)) { top: 0; }
#header-group [data-lumen-header] { position: static; }
.b-bar-wrap { position: static; background: var(--b-ground); }

/* The announcement line. A single centred serif line — NOT a marquee; theme A
   owns the marquee. Decorative hairline underneath (exempt from 1.4.11). */
.b-notice {
  min-height: var(--b-notice-h);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.5rem 1.25rem;
  border-bottom: 1px solid var(--b-line);
  background: var(--b-ground);
  color: var(--b-accent);
  font-family: var(--b-label);
  font-style: italic;
  font-size: 0.9375rem;
  text-align: center;
}

.b-bar {
  background: var(--b-ground);
  border-bottom: 1px solid var(--b-line);
  color: var(--b-ink);
}
.b-bar__inner {
  display: grid;
  /* Three columns keep the wordmark optically centred while the actions hold
     the right rail; the nav spans the full width on its own row below. */
  grid-template-columns: 1fr auto 1fr;
  grid-template-rows: auto auto;
  align-items: center;
  column-gap: 1rem;
  row-gap: 0.75rem;
  max-width: 62rem;
  margin-inline: auto;
  min-height: var(--b-bar-h);
  padding: 1rem clamp(1.25rem, 5vw, 2.5rem);
  /* NO transition on min-height. #header-group is sticky and therefore in flow,
     so tweening its height reflows every section below it on every frame. The
     height steps once; the wordmark carries the motion, on a composited
     transform. (Theme A had the same defect.) */
}
.b-bar.is-condensed .b-bar__inner { min-height: var(--b-bar-h-condensed); }

.b-bar__mark {
  grid-column: 2;
  grid-row: 1;
  justify-self: center;
  font-family: var(--b-display);
  font-weight: 400;
  font-size: clamp(1.5rem, 3vw, 2rem);
  letter-spacing: 0.02em;
  color: var(--b-ink);
  text-decoration: none;
  transform-origin: center bottom;
  /* transform, not font-size: font-size re-lays-out the text on every frame. */
  transition: transform var(--dur-card) var(--ease);
}
.b-bar.is-condensed .b-bar__mark { transform: scale(0.78); }

.b-bar__nav {
  grid-column: 1 / -1;
  grid-row: 2;
  justify-self: center;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  /* <summary> and <a> have different intrinsic boxes; without this the cabinet
     trigger drops a few pixels below the baseline of its sibling links. */
  align-items: center;
  gap: clamp(1rem, 3vw, 2.25rem);
  font-family: var(--b-label);
  font-style: italic;
  font-size: 1rem;
}
.b-bar__nav a { color: var(--b-ink); text-decoration: none; padding-block: 0.5rem; }
/* Condensed collapses to the wordmark + actions; the nav row folds away. */
.b-bar.is-condensed .b-bar__nav { display: none; }

.b-bar__acts {
  grid-column: 3;
  grid-row: 1;
  justify-self: end;
  display: flex;
  align-items: center;
  gap: 0.25rem;
}
.b-bar__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  min-width: 44px;
  min-height: 44px;
  padding-inline: 0.5rem;
  background: none;
  border: 0;
  color: var(--b-ink);
  font: inherit;
  font-size: 0.9375rem;
  text-decoration: none;
  cursor: pointer;
}
.b-bar__count {
  font-variant-numeric: tabular-nums;
  min-width: 1.375rem;
  height: 1.375rem;
  padding-inline: 0.375rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: var(--b-accent);
  color: var(--b-on-accent);
  font-size: 0.75rem;
}
.b-bar__mark:focus-visible,
.b-bar__nav a:focus-visible,
.b-bar__btn:focus-visible {
  outline: 2px solid var(--b-accent);
  outline-offset: 3px;
  border-radius: var(--b-radius);
}

/* ================================================================== cabinet
 * Mega-nav on <details name="cabinet"> — an exclusive accordion (opening one
 * closes its siblings; Baseline Sept 2025). Unboxed: the trigger is a serif
 * word, the panel is a centred grid of 4:5 portrait tiles. */
.b-cabinet { display: inline-flex; align-items: center; }
.b-cabinet__trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-height: 44px;
  padding-inline: 0.25rem;
  list-style: none;
  cursor: pointer;
  font-family: var(--b-label);
  font-style: italic;
  font-size: 1rem;
  color: var(--b-ink);
}
.b-cabinet__trigger::-webkit-details-marker { display: none; }
.b-cabinet__trigger:focus-visible { outline: 2px solid var(--b-accent); outline-offset: 3px; border-radius: var(--b-radius); }

.b-cabinet__panel {
  padding: clamp(2rem, 5vw, 3.5rem) clamp(1.25rem, 5vw, 2.5rem);
  background: var(--b-surface);
  border-top: 1px solid var(--b-line);
}
.b-cabinet__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: clamp(1rem, 3vw, 2rem);
  max-width: 62rem;
  margin-inline: auto;
}
.b-cabinet__tile {
  position: relative;              /* contains the absolutely-placed picture */
  display: block;
  aspect-ratio: 4 / 5;
  border-radius: var(--b-radius);
  overflow: hidden;
  color: var(--b-on-accent);
  text-decoration: none;
  isolation: isolate;
}
/* Take the picture out of flow AT the <picture>, not the <img>. A grid/flow
   container blockifies inline <picture> into an item and stretches implicit
   rows; display:contents would promote <source> into an item too (trap 5). */
.b-cabinet__tile > picture { position: absolute; inset: 0; z-index: 0; }
.b-cabinet__tile img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* Caption sits over the photo, so it needs a scrim: white on a pale image is
   ~1.3:1 (trap 8). z-index:0 (not -1): among negatively-stacked children paint
   order is DOM order, so a -1 scrim declared before the image paints beneath it
   (trap 6). At 0 it sits above the media and below the caption. */
.b-cabinet__tile::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background: linear-gradient(to top, rgb(24 18 16 / 0.72) 0%, rgb(24 18 16 / 0.12) 55%, rgb(24 18 16 / 0) 100%);
  pointer-events: none;
}
.b-cabinet__cap {
  position: absolute;
  z-index: 1;
  inset-inline: 0;
  bottom: 0;
  padding: 1rem 1.25rem;
  font-family: var(--b-display);
  font-size: 1.125rem;
}

/* ================================================================ threshold
 * Full-bleed 100svh muted video with one centred serif line. lumen.css owns
 * the bare .b-threshold (a padded, centred text band) and MUST NOT be
 * redefined. The :has() guard makes the full-bleed treatment apply ONLY to a
 * threshold that carries a poster — a DISTINCT, higher-specificity selector
 * that beats lumen.css deterministically without touching the bare class. */
.b-threshold:has(.b-threshold__poster) {
  position: relative;             /* containing block for the media + scrim */
  padding: 0;                     /* overrides lumen.css's vertical band */
  min-height: 100svh;
  display: grid;
  place-items: center;
  overflow: clip;
  isolation: isolate;
}
.b-threshold__poster,
.b-threshold__video {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The poster is the real paint and the LCP; the video is an enhancement.
   preload="none" + display:none under reduced motion means it is never even
   fetched — a bandwidth win as well as a motion one (trap: never fetched). */
.b-threshold__poster { display: block; }
@media (prefers-reduced-motion: reduce) {
  .b-threshold__video { display: none; }
}
/* Scrim for the line over moving pale media (trap 8). Scoped to the video
   variant so lumen.css's text .b-threshold is untouched. z-index:1 over media. */
.b-threshold:has(.b-threshold__poster)::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: radial-gradient(120% 90% at 50% 55%, rgb(20 15 13 / 0.62) 0%, rgb(20 15 13 / 0.30) 45%, rgb(20 15 13 / 0.12) 100%);
  pointer-events: none;
}
.b-threshold__copy {
  position: relative;
  z-index: 2;
  /* NOT `24ch`. The `ch` unit resolves against THIS box's font-size (body, ~16px),
     not the 3.75rem display line inside it — 24ch was ~190px wide, which
     guillotined the headline into "proble / m to be solved". Size the measure in
     rem and let the line box do the wrapping. */
  max-width: min(90vw, 46rem);
  margin-inline: auto;
  padding: 2rem;
  text-align: center;
  color: var(--b-on-accent);
}
/* The threshold CTA is a .b-btn, not a .b-cta — an earlier rule targeted the
   wrong class and never applied, so the button sat tight against the headline.
   Generous, responsive breathing room below the display line. */
.b-threshold__copy .b-btn { margin-top: clamp(2.5rem, 6vw, 4rem); }
.b-threshold__line {
  font-family: var(--b-display);
  font-weight: 400;
  font-size: clamp(2rem, 5vw, 3.75rem);
  line-height: 1.15;
  letter-spacing: -0.005em;
  margin: 0;
  text-wrap: balance;
  /* Horizon's base.css sets `overflow-wrap: anywhere` on display text. Display
     type may overflow; it may never break inside a word. */
  overflow-wrap: normal;
  word-break: keep-all;
  hyphens: none;
}

/* ==================================================================== split
 * 50/50 near-square panels. The media side alternates via a BLOCK SETTING
 * (a --flip co-class), placed with grid columns — never CSS `order` or
 * flex-reverse, which desync visual and DOM order (WCAG 1.3.2 / 2.4.3, trap 12). */
.b-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: center;
  max-width: 62rem;
  margin-inline: auto;
  padding: var(--b-pad) clamp(1.25rem, 5vw, 2.5rem);
  text-align: left;
}
/* BOTH items are pinned to row 1. Grid auto-placement is "sparse": once an item
   is explicitly placed in column 2, the cursor has passed column 1 of that row,
   so a following item asking for column 1 drops to row 2. That is exactly what
   happened to the FLIPPED panel — media right, body stranded beneath it. Only
   the flipped variant broke, which is why the first panel looked correct. */
.b-split__media {
  position: relative;             /* contains the picture (trap 5) */
  grid-column: 1;
  grid-row: 1;
  aspect-ratio: 1 / 1;
  border-radius: var(--b-radius);
  overflow: hidden;
}
.b-split__media > picture { position: absolute; inset: 0; }
.b-split__media img { display: block; width: 100%; height: 100%; object-fit: cover; }
.b-split__body { grid-column: 2; grid-row: 1; max-width: 34ch; }
/* Flip: the media takes column 2, body column 1. DOM order is unchanged; only
   grid placement moves, so assistive tech and keyboard order stay coherent. */
.b-split--flip .b-split__media { grid-column: 2; grid-row: 1; }
.b-split--flip .b-split__body { grid-column: 1; grid-row: 1; }
@media (max-width: 46rem) {
  .b-split { grid-template-columns: 1fr; text-align: center; }
  /* Stacked: hand the rows back to auto placement. Media leads, always. */
  .b-split__media,
  .b-split--flip .b-split__media { grid-column: 1; grid-row: auto; }
  .b-split__body,
  .b-split--flip .b-split__body { grid-column: 1; grid-row: auto; margin-inline: auto; }
}

/* =================================================================== scroll
 * Sticky media column; chapters scroll past it. This file styles ONLY the
 * layout/positioning of [data-frame]/[data-chapter]; lumen-motion.css already
 * owns their opacity cross-fade, so we never touch opacity here (trap 7). */
.b-scroll {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(2rem, 6vw, 5rem);
  max-width: 62rem;
  margin-inline: auto;
  padding: var(--b-pad) clamp(1.25rem, 5vw, 2.5rem);
}
.b-scroll__media {
  position: sticky;
  top: calc(var(--b-bar-h-condensed) + 2rem);
  align-self: start;
  aspect-ratio: 4 / 5;
  border-radius: var(--b-radius);
  overflow: hidden;
  isolation: isolate;
}
/* Frames stack in the sticky media box; motion.css cross-fades them by opacity. */
.b-scroll__frame { position: absolute; inset: 0; }
.b-scroll__frame img { display: block; width: 100%; height: 100%; object-fit: cover; }
.b-scroll__chapters { display: grid; }
.b-scroll__chapter {
  min-height: 70svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  max-width: 34ch;
  text-align: left;
}
@media (max-width: 46rem) {
  .b-scroll { grid-template-columns: 1fr; }
  .b-scroll__media { position: relative; top: auto; }
  .b-scroll__chapter { min-height: 0; padding-block: 2.5rem; }
}

/* ===================================================================== read
 * Journal, three-up, 16:9 thumbnails. A quiet grid — no rules between items. */
.b-read {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: clamp(1.5rem, 4vw, 2.5rem);
  max-width: 62rem;
  margin-inline: auto;
  /* Self-pads like .b-split and .b-scroll; lumen.css's section-band list does
     not include .b-read, so without this the journal butts its neighbours. */
  padding: var(--b-pad) clamp(1.25rem, 5vw, 2.5rem);
  text-align: left;
}
.b-read__item { display: block; color: inherit; text-decoration: none; }
.b-read__thumb {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--b-radius);
  margin-bottom: 1rem;
}
.b-read__meta { font-family: var(--b-label); font-style: italic; color: var(--b-accent); font-size: 0.875rem; margin: 0 0 0.375rem; }
.b-read__title { font-family: var(--b-display); font-weight: 400; font-size: 1.25rem; line-height: 1.3; margin: 0; }
.b-read__item:focus-visible { outline: 2px solid var(--b-accent); outline-offset: 4px; border-radius: var(--b-radius); }

/* ==================================================================== quote
 * One pull-quote over a 16:9 photo. lumen.css styles .b-quote's descendants
 * (blockquote/figcaption) for a DIFFERENT "In their words" component, so the
 * craft pull-quote is disambiguated by :has(.b-quote__text) — a distinct,
 * higher-specificity selector that never contaminates lumen.css's quote. */
.b-quote:has(.b-quote__text) {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  isolation: isolate;
  color: var(--b-on-accent);
  text-align: center;
}
.b-quote__media { position: absolute; inset: 0; z-index: 0; }
.b-quote__media img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* Scrim: white over a photo measures ~1.3:1 (trap 8). z-index:1 over media. */
.b-quote:has(.b-quote__text)::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(rgb(20 15 13 / 0.34), rgb(20 15 13 / 0.58));
  pointer-events: none;
}
.b-quote__text {
  position: relative;
  z-index: 2;
  font-family: var(--b-display);
  font-weight: 400;
  font-size: clamp(1.375rem, 3.2vw, 2.25rem);
  line-height: 1.4;
  max-width: 24ch;
  margin: 0 auto 1rem;
  padding-inline: 1.5rem;
}
.b-quote__attrib {
  position: relative;
  z-index: 2;
  font-family: var(--b-label);
  font-style: italic;
  color: rgb(251 248 241 / 0.85);
}

/* ================================================================ mega-footer
 * Serif footer with an oversized centred wordmark. */
.b-foot {
  background: var(--b-ground);
  border-top: 1px solid var(--b-line);
  padding-top: var(--b-pad);
  text-align: center;
}
.b-foot__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: clamp(1.5rem, 4vw, 3rem);
  max-width: 52rem;
  margin-inline: auto;
  padding-inline: clamp(1.25rem, 5vw, 2.5rem);
  text-align: left;
}
.b-foot__col h2 { font-family: var(--b-label); font-style: italic; font-size: 0.9375rem; color: var(--b-accent); margin: 0 0 1rem; }
.b-foot__col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.625rem; }
.b-foot__col a { color: var(--b-ink); text-decoration: none; font-size: 0.9375rem; }
.b-foot__col a:focus-visible { outline: 2px solid var(--b-accent); outline-offset: 3px; border-radius: var(--b-radius); }
.b-foot__fine { color: var(--b-muted); font-size: 0.8125rem; padding: 2rem clamp(1.25rem, 5vw, 2.5rem) 0; }

/* The one indulgence: the wordmark at the scale of a doorway. overflow:hidden
   clips the descenders the huge line-height would otherwise spill. */
.b-foot__word {
  display: block;
  margin-top: var(--b-pad);
  font-family: var(--b-display);
  font-weight: 400;
  font-size: clamp(3.5rem, 20vw, 15rem);
  line-height: 0.82;
  letter-spacing: -0.01em;
  color: var(--b-ink);
  overflow: hidden;
  padding-bottom: 0.06em;
}

.b-sign { display: flex; gap: 0.5rem; max-width: 24rem; margin: 2.5rem auto 0; }
.b-sign input {
  flex: 1;
  min-height: 48px;
  padding-inline: 1rem;
  border: 1px solid var(--b-line-strong);   /* interactive → strong (≥3:1) */
  border-radius: var(--b-radius);
  background: var(--b-surface);
  color: var(--b-ink);
  font: inherit;
}
.b-sign input:focus-visible { outline: 2px solid var(--b-accent); outline-offset: 2px; }
.b-sign__ok { color: var(--b-accent); font-family: var(--b-label); font-style: italic; margin: 0.75rem auto 0; }
.b-sign__err { color: #A3231F; font-size: 0.9375rem; margin: 0.75rem auto 0; }

/* ================================================================== buttons
 * lumen.css owns .b-cta; .b-btn is the craft-layer button, distinct and free. */
.b-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding-inline: 1.75rem;
  border-radius: var(--b-radius);
  border: 1px solid transparent;
  font: inherit;
  font-size: 0.9375rem;
  text-decoration: none;
  cursor: pointer;
  /* Only composite properties animate; never `transition: all` (trap 13). */
  transition: background-color var(--dur-control) var(--ease),
    color var(--dur-control) var(--ease),
    border-color var(--dur-control) var(--ease);
}
.b-btn--fill { background: var(--b-ink); color: var(--b-ground); border-color: var(--b-ink); }
.b-btn--line { background: none; color: var(--b-ink); border-color: var(--b-line-strong); }
@media (hover: hover) {
  .b-btn--fill:hover { background: var(--b-accent); border-color: var(--b-accent); }
  .b-btn--line:hover { background: var(--b-ink); color: var(--b-ground); border-color: var(--b-ink); }
}
.b-btn:focus-visible { outline: 2px solid var(--b-accent); outline-offset: 3px; }
.b-btn[disabled] { background: var(--b-line); color: var(--b-muted); border-color: var(--b-line); cursor: default; }

/* ================================================================== drawer
 * The cart drawer must live in sections/header-group.json — the engine
 * re-renders it via /?section_id=<id>, which only returns sections the
 * requested page renders. lumen-motion.css owns the overlay + slide; here we
 * dress the panel internals only.
 *
 * The panel itself does not scroll; the line list inside it does — otherwise
 * the sticky head and the checkout foot scroll away with the items. We beat
 * motion.css's [data-lumen-drawer-panel]{overflow-y:auto} with a co-class
 * compound (specificity 0,2,0 > 0,1,0), so order can't decide it. */
[data-lumen-drawer-panel].b-drawer__panel {
  overflow: hidden;
  background: var(--b-ground);
  color: var(--b-ink);
}
.b-drawer__top { padding: 1.5rem clamp(1.25rem, 5vw, 2rem); border-bottom: 1px solid var(--b-line); }
.b-drawer__title { font-family: var(--b-display); font-weight: 400; font-size: 1.375rem; margin: 0; text-align: center; }
.b-drawer__x {
  position: absolute;
  top: 1rem;
  right: 1rem;
  min-width: 44px;
  min-height: 44px;
  background: none;
  border: 0;
  color: var(--b-ink);
  font: inherit;
  font-size: 1.25rem;
  cursor: pointer;
}
.b-drawer__x:focus-visible { outline: 2px solid var(--b-accent); outline-offset: 2px; border-radius: var(--b-radius); }

/* Free-shipping meter. motion.css owns .ship__track/.ship__fill geometry
   (scaleX via --ship-progress); we only colour them, scoped so we don't
   redefine the shared selectors. .b-empty is craft-owned and free. */
.b-ship { padding: 1rem clamp(1.25rem, 5vw, 2rem); font-size: 0.8125rem; color: var(--b-muted); text-align: center; }
.b-ship .ship__track { background: var(--b-line); margin-top: 0.625rem; border-radius: 2px; }
.b-ship .ship__fill { background: var(--b-accent); }
.b-ship b { color: var(--b-accent); font-variant-numeric: tabular-nums; }

.b-drawer__lines { list-style: none; margin: 0; padding: 0 clamp(1.25rem, 5vw, 2rem); flex: 1; overflow-y: auto; }
/* lumen.css owns bare .b-line (a 1fr/auto/auto tray row). The drawer line is a
   DISTINCT compound (0,2,0) that re-templates the grid for a thumbnail + a
   stepper + a remove control, and adds the row hairline. Specificity, not
   order, decides — safe against the re-emitted lumen.css. */
.b-drawer__panel .b-line {
  grid-template-columns: 4rem 1fr auto;
  gap: 1rem;
  align-items: start;
  padding-block: 1.25rem;
  border-bottom: 1px solid var(--b-line);
}
.b-line__thumb { width: 100%; aspect-ratio: 4 / 5; object-fit: cover; border-radius: var(--b-radius); background: var(--b-surface); }
.b-line__rm {
  background: none;
  border: 0;
  padding: 0;
  margin-top: 0.5rem;
  color: var(--b-muted);
  font: inherit;
  font-size: 0.8125rem;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.b-line__rm:focus-visible { outline: 2px solid var(--b-accent); outline-offset: 2px; border-radius: var(--b-radius); }

/* Quantity stepper. lumen.css owns bare .b-step (a consultation row); the
   drawer stepper is the DISTINCT compound .b-drawer__panel .b-step, an
   inline-flex control bounded by --b-line-strong (interactive ≥3:1). The +/-
   keys and the <output> are the engine hooks [data-qty-step]/[data-qty-input]. */
.b-drawer__panel .b-step {
  display: inline-flex;
  align-items: center;
  /* lumen.css's bare .b-step is a consultation ROW (padding: 1rem 0; gap: 1rem;
     border-bottom). A compound selector overrides only what it names, so the
     padding and gap leak in and the quantity control renders loose. Reset them. */
  padding: 0;
  gap: 0;
  border: 1px solid var(--b-line-strong);
  border-bottom: 1px solid var(--b-line-strong);
  border-radius: var(--b-radius);
  text-align: center;
}
.b-drawer__panel .b-step [data-qty-step] {
  min-width: 44px;
  min-height: 44px;
  background: none;
  border: 0;
  color: var(--b-ink);
  font: inherit;
  cursor: pointer;
}
.b-drawer__panel .b-step [data-qty-step]:focus-visible { outline: 2px solid var(--b-accent); outline-offset: -2px; }
.b-drawer__panel .b-step [data-qty-input] {
  min-width: 2.25rem;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.b-drawer__foot { padding: 1.5rem clamp(1.25rem, 5vw, 2rem); border-top: 1px solid var(--b-line); background: var(--b-ground); }
.b-drawer__sum { display: flex; justify-content: space-between; align-items: baseline; font-variant-numeric: tabular-nums; margin-bottom: 1rem; }
.b-drawer__sum b { font-family: var(--b-display); font-weight: 400; font-size: 1.25rem; }
.b-drawer__foot .b-btn { width: 100%; }

.b-empty { padding: 4rem clamp(1.25rem, 5vw, 2rem); text-align: center; color: var(--b-muted); }
.b-empty p { margin: 0 0 1.75rem; }

/* ==================================================================== PDP
 * The altar: a centred announcement over an environmental backdrop with a
 * scrim; no gallery above the fold. lumen.css owns bare .b-altar and its
 * .b-altar__* sub-elements (dark ink on ground). Over a photographic backdrop
 * that ink is invisible and the ground background would hide the photo, so the
 * craft PDP wraps the altar in .b-pdp (craft-owned) and re-dresses the altar
 * ONLY within that scope via distinct compounds (0,2,0) — never redefining the
 * bare selectors.
 *
 * REMINDER (trap 1, enforced in the section's Liquid, not here): a bespoke PDP
 * MUST emit  <script type="application/ld+json">{{ product | structured_data }}
 * </script>  — Horizon only prints ProductGroup from its own main-product. */
.b-pdp {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 100svh;
  overflow: clip;
  isolation: isolate;
  text-align: center;
}
/* If the backdrop image is missing, the altar's light copy still needs a dark
   ground under it — the scrim alone would composite over the page colour. */
.b-pdp__backdrop { position: absolute; inset: 0; z-index: 0; background: var(--b-ink); }
.b-pdp__backdrop img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* Scrim over the backdrop so the near-white altar copy clears AA.
 *
 * The floor is COMPUTED, not eyeballed. Worst case is a merchant choosing a pale
 * backdrop. Compositing rgb(18 13 11) at alpha a over #FFF yields 255 - 240a per
 * channel; #FBF8F1 text needs that at or below ~112 to reach 4.5:1, i.e.
 * a >= 0.60. The previous 0.42 measured 2.67:1 — it failed body copy on any pale
 * photograph, while the comment above it claimed the opposite.
 *
 * Measured against a pure-white backdrop: top 0.62 -> 5.13:1, bottom 0.78 ->
 * 9.36:1. Over the intended mid-dark photography both are far higher. A section
 * cannot rely on merchant discipline, so the scrim carries the guarantee. */
.b-pdp::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(rgb(18 13 11 / 0.62) 0%, rgb(18 13 11 / 0.78) 100%);
  pointer-events: none;
}

/* Non-text contrast (WCAG 1.4.11) over the dark altar. lumen.css dresses the
   size pills for a LIGHT ground: the selected pill is dark ink filled with dark
   ink, so over the scrim it loses both its boundary and its selected state.
   Re-dress both states for the dark ground, scoped inside .b-pdp only. */
.b-pdp .b-size { border-color: rgb(251 248 241 / 0.55); color: var(--b-on-accent); }
.b-pdp .b-size.is-current {
  background: var(--b-on-accent);
  color: var(--b-ink);
  border-color: var(--b-on-accent);
}
/* The altar rides above the scrim. Kill lumen.css's opaque ground background
   so the backdrop shows through; lift the copy to light for legibility. */
.b-pdp .b-altar {
  position: relative;
  z-index: 2;
  background: transparent;
  color: var(--b-on-accent);
  padding-inline: clamp(1.25rem, 5vw, 2.5rem);
}
.b-pdp .b-altar__type { color: rgb(251 248 241 / 0.82); }
.b-pdp .b-altar__title { color: var(--b-on-accent); }
.b-pdp .b-altar__place { color: rgb(251 248 241 / 0.9); }
.b-pdp .b-altar__price { color: var(--b-on-accent); }

/* The environmental gallery lives BELOW the fold, unboxed and centred. */
.b-pdp__scroll-cue {
  position: absolute;
  z-index: 2;
  bottom: 1.75rem;
  left: 50%;
  translate: -50% 0;
  color: rgb(251 248 241 / 0.85);
  font-family: var(--b-label);
  font-style: italic;
  font-size: 0.875rem;
}


/* Drawer-wide focus ring. The drawer overlay lives outside every section scope
   that lumen.css hangs its :focus-visible rules on, so its checkout button,
   line-item links and empty-state CTA would otherwise fall through to the UA
   default (WCAG 2.4.7). One rule on the drawer attribute covers every control
   inside it. Found by theme C's adversarial review; applied to all three. */
[data-lumen-drawer] :is(a, button):focus-visible {
  outline: 2px solid var(--b-accent);
  outline-offset: 2px;
}
