/* ==========================================================================
   Bochic storefront — base + components (design pass, 2026-09-07)
   Tokens: tokens.css. Rationale: execution/pilot/evidence/storefront/design-pass/DESIGN_NOTES.md.
   One rule set, mobile first; every selector here is emitted by src/** or public/assets/js/**.
   ========================================================================== */

/* ---------------------------------------------------------------- reset */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body, h1, h2, h3, h4, p, figure, blockquote, dl, dd, ol, ul { margin: 0; padding: 0; }
ul[class], ol[class] { list-style: none; }
img, picture, video, svg { max-width: 100%; display: block; }
input, button, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }

/* iOS Safari zooms the whole document when a focused field renders text below 16px. Every text
   control starts at 1rem here and is only ever raised by a component rule, never lowered. */
input:not([type="checkbox"]):not([type="radio"]), select, textarea { font-size: 1rem; }
input[type="checkbox"], input[type="radio"] { width: 20px; height: 20px; accent-color: var(--c-ink); }

/* Off-canvas drawers at translateX(100%) still count toward scrollWidth; `clip` suppresses that
   without creating a scroll container, so position:sticky keeps working (not `hidden`). */
html { overflow-x: clip; }

body {
  background: var(--c-paper);
  color: var(--c-ink);
  font-family: var(--font-ui);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: var(--fw-body);
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}

:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-offset); }
:focus:not(:focus-visible) { outline: none; }

.skip-link {
  position: absolute; left: var(--s-4); top: -100px; z-index: var(--z-modal);
  background: var(--c-ink); color: var(--c-ink-invert);
  padding: var(--s-3) var(--s-5); text-decoration: none;
  font-size: var(--fs-label); letter-spacing: var(--tr-label); text-transform: uppercase;
  display: inline-flex; align-items: center; min-height: var(--target-min);
}
.skip-link:focus { top: var(--s-4); }

.visually-hidden {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------------------------------------------------------------- type */
h1, h2, h3, h4, .display {
  font-family: var(--font-display); font-weight: 400; font-optical-sizing: auto;
  text-wrap: balance; overflow-wrap: break-word;
}
.d-xl { font-size: var(--fs-display-xl); line-height: var(--lh-display-xl); letter-spacing: var(--tr-display-xl); font-weight: var(--fw-display-xl); }
.d-l  { font-size: var(--fs-display-l);  line-height: var(--lh-display-l);  letter-spacing: var(--tr-display-l);  font-weight: var(--fw-display-l); }
.d-m  { font-size: var(--fs-display-m);  line-height: var(--lh-display-m);  letter-spacing: var(--tr-display-m);  font-weight: var(--fw-display-m); }
.d-s  { font-size: var(--fs-display-s);  line-height: var(--lh-display-s);  letter-spacing: var(--tr-display-s);  font-weight: var(--fw-display-s); }

.label, .eyebrow {
  font-family: var(--font-ui); font-size: var(--fs-label); line-height: var(--lh-label);
  letter-spacing: var(--tr-label); font-weight: var(--fw-label); text-transform: uppercase;
}
.label-s {
  font-family: var(--font-ui); font-size: var(--fs-label-s); line-height: var(--lh-label-s);
  letter-spacing: var(--tr-label-s); font-weight: var(--fw-label-s); text-transform: uppercase;
}
/* An eyebrow is the small line above a heading: muted, and followed by a fixed gap. */
.eyebrow { color: var(--c-ink-mute); display: block; }
.eyebrow + h1, .eyebrow + h2, .eyebrow + h3 { margin-top: var(--s-3); }
.lede { font-size: var(--fs-body-l); line-height: var(--lh-body-l); color: var(--c-ink-soft); }
.muted { color: var(--c-ink-mute); }
.prose { max-width: var(--max-prose); overflow-wrap: break-word; }
.prose p + p { margin-top: 1em; }
.prose em { font-style: italic; }
.num, .price { font-variant-numeric: tabular-nums lining-nums; }

a { color: inherit; }
.link-u {
  text-decoration: none; border-bottom: 1px solid var(--c-line-strong); padding-bottom: 1px;
  transition: border-color var(--d-fast) var(--e-standard);
}
.link-u:hover { border-color: var(--c-ink); }
/* Standalone small links (section "See all", trust "Policy") keep their quiet size and get an
   invisible 44px hit area. Links inside prose are exempt (WCAG 2.5.8) and are not touched. */
.link-u.label-s, .link-u.label { position: relative; }
.link-u.label-s::after, .link-u.label::after,
.crumbs a::after, .announce a::after, .footer-base a::after, .search-empty a.link-u::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: max(100%, var(--target-min)); height: max(100%, var(--target-min));
  transform: translate(-50%, -50%);
}
.crumbs a, .footer-base a, .search-empty a.link-u { position: relative; }

/* The one gold hairline. */
.rule-gilt { width: 40px; height: 1px; background: var(--c-gilt); border: 0; margin: 0; }

/* ---------------------------------------------------------------- layout */
.wrap, .wrap-wide, .wrap-narrow {
  width: 100%; margin-inline: auto;
  padding-left: max(var(--gutter), env(safe-area-inset-left));
  padding-right: max(var(--gutter), env(safe-area-inset-right));
}
.wrap { max-width: var(--max-content); }
.wrap-wide { max-width: var(--max-page); }
.wrap-narrow { max-width: var(--max-narrow); }
.section { padding-block: var(--section-y); }
.section-sm { padding-block: calc(var(--section-y) * .55); }
.band-sunk { background: var(--c-paper-sunk); }
/* `.band-night` is emitted by markup this pass does not own (the newsletter section, the about
   quote, the landing final band). It is no longer a night band: one surface family, no black. */
.band-night { background: var(--c-paper-sunk); color: var(--c-ink); }
.band-night .muted, .band-night .lede { color: var(--c-ink-soft); }
.stack > * + * { margin-top: var(--s-4); }
.stack-lg > * + * { margin-top: var(--s-6); }
.center { text-align: center; }
.center h1, .center h2, .center .lede, .center .prose { margin-inline: auto; }
.flow-head {
  display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s-4) var(--s-5);
  flex-wrap: wrap; margin-bottom: var(--s-6);
}
.flow-head > div { min-width: 0; }
.flow-head .lede { margin-top: var(--s-3); }
.section-head { margin-bottom: var(--s-6); }
.section-head .lede { margin-top: var(--s-3); }
@media (max-width: 599px) { .flow-head, .section-head { margin-bottom: var(--s-5); } }

/* ---------------------------------------------------------------- media frame
   The one rule for a piece of jewellery: portrait, contained, on sunk paper, hairline. */
/* The ground is WHITE, not sunk paper: the merchant's photography is shot on pure white, so any
   other ground shows the photograph's own rectangle inside the frame. `width: 100%` is load-bearing:
   with an auto width, `max-height` transfers through `aspect-ratio` into a max-WIDTH (CSS sizing
   "transferred size"), which shrank the landscape-phone gallery to 225px. */
.frame, .pcard-media, .tile-media, .gallery-main, .split-media, .hero-media, .lp-card-media, .lp-featured-media {
  position: relative; width: 100%; background: var(--c-paper-raised); border: 1px solid var(--c-line);
  overflow: hidden;
}
/* An image that fails (or never had a URL) leaves a quiet, labelled frame — never a broken icon. */
[data-img-missing] img { visibility: hidden; }
[data-img-missing]::after {
  content: "Photograph to follow"; position: absolute; inset: 0; display: grid; place-items: center;
  font-family: var(--font-ui); font-size: var(--fs-label-s); letter-spacing: var(--tr-label-s); text-transform: uppercase;
  color: var(--c-ink-mute); background: var(--c-paper-sunk); padding: var(--s-4); text-align: center;
}
.frame img, .pcard-media img, .tile-media img, .gallery-main img, .split-media img, .hero-media img,
.lp-card-media img, .lp-featured-media img {
  width: 100%; height: 100%; object-fit: contain; object-position: center;
}

/* ---------------------------------------------------------------- buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  min-height: 48px; padding: var(--s-3) var(--s-6);
  font-family: var(--font-ui); font-size: var(--fs-label); letter-spacing: .12em;
  font-weight: 500; text-transform: uppercase; text-decoration: none; line-height: 1.2;
  border: 1px solid var(--c-ink); border-radius: var(--r-none);
  transition: background var(--d-fast) var(--e-standard), color var(--d-fast) var(--e-standard),
              border-color var(--d-fast) var(--e-standard);
  cursor: pointer; text-align: center; -webkit-tap-highlight-color: transparent;
}
.btn-primary { background: var(--c-ink); color: var(--c-ink-invert); }
.btn-primary:hover { background: #000; border-color: #000; }
.btn-ghost, .btn-secondary { background: transparent; color: var(--c-ink); }
.btn-ghost:hover, .btn-secondary:hover { background: var(--c-ink); color: var(--c-ink-invert); }
.btn-quiet { border-color: var(--c-line-strong); background: transparent; color: var(--c-ink); }
.btn-quiet:hover { border-color: var(--c-ink); }
.btn-block { width: 100%; }
.btn[aria-disabled="true"], .btn:disabled { opacity: .45; cursor: not-allowed; pointer-events: none; }
.btn .ext, .link-ext .ext { font-size: .9em; opacity: .7; }
.hero-cta { display: flex; gap: var(--s-3); flex-wrap: wrap; margin-top: var(--s-5); }
@media (max-width: 479px) { .hero-cta > .btn { flex: 1 1 100%; } }
@media (max-width: 599px) { .btn { padding-inline: var(--s-5); } }

/* ---------------------------------------------------------------- header */
.announce {
  background: var(--c-paper-sunk); color: var(--c-ink-soft); border-bottom: 1px solid var(--c-line);
  font-size: var(--fs-label-s); letter-spacing: .1em; line-height: 1.5; text-transform: uppercase;
  text-align: center; padding: 7px var(--gutter);
}
.announce a { position: relative; color: var(--c-ink); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--c-line-strong); }
@media (max-width: 599px) { .announce { padding: 6px var(--s-4); letter-spacing: .08em; } }

.site-header {
  position: sticky; top: 0; z-index: var(--z-header);
  background: var(--c-paper); border-bottom: 1px solid var(--c-line);
  /* viewport-fit=cover: keep the bar's contents inside the notch / cutout on every edge. */
  padding-top: env(safe-area-inset-top);
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}
.header-bar {
  /* `minmax(max-content, 1fr)`: the two icons never squeeze below 44px at 320 (equal 1fr columns
     did that); with any room to spare both sides are equal and the brandmark sits centred. */
  display: grid; grid-template-columns: minmax(max-content, 1fr) auto minmax(max-content, 1fr); align-items: center; gap: var(--s-3);
  min-height: var(--header-h); max-width: var(--max-page); margin-inline: auto; padding-inline: var(--gutter);
}
.header-left, .header-right { display: flex; align-items: center; gap: var(--s-1); min-width: 0; }
.header-right { justify-content: flex-end; }
.brandmark {
  display: inline-flex; align-items: center; min-height: var(--target-min);
  font-family: var(--font-ui); font-weight: 500; font-size: 1.125rem;
  letter-spacing: .32em; text-transform: uppercase; text-decoration: none; color: var(--c-ink);
  padding-left: .32em; /* optical: balance the trailing letter-space */
  white-space: nowrap;
}
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: var(--target-min); height: var(--target-min); color: var(--c-ink);
  transition: opacity var(--d-fast) var(--e-standard);
}
.icon-btn:hover { opacity: .7; }
.icon-btn svg { width: 20px; height: 20px; }
.cart-count {
  position: absolute; transform: translate(12px, -12px);
  background: var(--c-ink); color: var(--c-ink-invert);
  font-size: 10px; line-height: 1; min-width: 16px; height: 16px;
  display: inline-flex; align-items: center; justify-content: center; border-radius: var(--r-pill);
  padding: 0 4px; font-family: var(--font-ui);
}
.cart-count[hidden] { display: none; }

/* Desktop nav. Ten items plus the brandmark and two icons do not fit below 1200px, so the drawer
   serves 1024–1199 too. At 1200+ the brandmark moves to the leading edge (DOM order is unchanged;
   grid `order` does it), the nav sits centred, icons trail. */
.nav-desktop { display: none; }
@media (min-width: 1200px) {
  .header-bar { grid-template-columns: auto 1fr auto; }
  .brandmark { order: -1; padding-left: 0; }
  .header-left { justify-content: center; }
  .nav-desktop { display: flex; align-items: center; gap: clamp(8px, 1.4vw, 20px); }
  .nav-toggle { display: none; }
}
.nav-item-secondary { display: none; }
@media (min-width: 1400px) { .nav-item-secondary { display: block; } }
.nav-item { position: relative; }
.nav-link {
  display: inline-flex; align-items: center; gap: 4px; min-height: var(--target-min); padding: 0 2px;
  font-size: var(--fs-label-s); letter-spacing: .14em; text-transform: uppercase;
  font-weight: 500; text-decoration: none; white-space: nowrap; color: var(--c-ink-soft);
  border-bottom: 1px solid transparent; transition: color var(--d-fast) var(--e-standard), border-color var(--d-fast) var(--e-standard);
}
.nav-link:hover, .nav-item[data-open="true"] > .nav-link { color: var(--c-ink); border-bottom-color: var(--c-ink); }
/* Active is computed on pathname alone (`shell/nav.ts`), so on /shop/ every category item is
   "active" — a full-strength underline on six items reads as noise. Colour only. */
.nav-item.is-active > .nav-link { color: var(--c-ink); }
.nav-link .chev { width: 8px; height: 8px; transition: transform var(--d-fast) var(--e-standard); }
.nav-item[data-open="true"] .chev { transform: rotate(180deg); }
.nav-panel {
  position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  min-width: 220px; background: var(--c-paper-raised); border: 1px solid var(--c-line);
  box-shadow: var(--sh-card); padding: var(--s-2); display: none; z-index: var(--z-header);
}
.nav-item[data-open="true"] .nav-panel { display: block; }
.nav-panel a {
  display: flex; align-items: center; min-height: var(--target-min); padding: var(--s-2) var(--s-3); text-decoration: none;
  font-size: var(--fs-body-s); color: var(--c-ink-soft);
}
.nav-panel a:hover { background: var(--c-paper-sunk); color: var(--c-ink); }

/* mobile drawer */
.drawer {
  position: fixed; inset: 0 auto 0 0; width: min(88vw, 380px);
  background: var(--c-paper); z-index: var(--z-drawer);
  transform: translateX(-100%); transition: transform var(--d-base) var(--e-standard);
  display: flex; flex-direction: column; overflow-y: auto;
  padding: max(var(--s-4), env(safe-area-inset-top)) max(var(--s-5), env(safe-area-inset-right))
           calc(var(--s-8) + env(safe-area-inset-bottom)) max(var(--s-5), env(safe-area-inset-left));
}
.drawer[data-side="right"] { inset: 0 0 0 auto; transform: translateX(100%); }
.drawer[data-open="true"] { transform: translateX(0); box-shadow: var(--sh-drawer); }
.drawer-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--s-4); min-height: var(--target-min); }
.drawer-head .label-s { color: var(--c-ink-mute); }
.scrim {
  position: fixed; inset: 0; background: rgba(31, 29, 26, .4); z-index: calc(var(--z-drawer) - 1);
  opacity: 0; pointer-events: none; transition: opacity var(--d-base) var(--e-standard);
}
.scrim[data-open="true"] { opacity: 1; pointer-events: auto; }
body[data-locked="true"] { overflow: hidden; }

.mnav { border-top: 1px solid var(--c-line); }
.mnav-row { border-bottom: 1px solid var(--c-line); }
.mnav-row > a, .mnav-row > button {
  display: flex; align-items: center; justify-content: space-between; width: 100%;
  min-height: 56px; padding: var(--s-3) 0; text-decoration: none; text-align: left;
  font-family: var(--font-display); font-size: 1.25rem; letter-spacing: 0; text-transform: none; font-weight: 400;
}
.mnav-sub { padding: 0 0 var(--s-4) var(--s-4); display: none; }
.mnav-row[data-open="true"] .mnav-sub { display: block; }
.mnav-sub a { display: flex; align-items: center; min-height: var(--target-min); padding: var(--s-2) 0; font-size: var(--fs-body); text-decoration: none; color: var(--c-ink-soft); }
.mnav-row .chev { transition: transform var(--d-fast) var(--e-standard); width: 12px; height: 12px; color: var(--c-ink-mute); }
.mnav-row[data-open="true"] .chev { transform: rotate(180deg); }

/* ---------------------------------------------------------------- search */
.search-panel {
  position: fixed; inset: 0 0 auto 0; background: var(--c-paper);
  z-index: var(--z-drawer);
  padding: max(var(--s-5), env(safe-area-inset-top)) env(safe-area-inset-right) var(--s-6) env(safe-area-inset-left);
  border-bottom: 1px solid var(--c-line); transform: translateY(-100%);
  transition: transform var(--d-base) var(--e-standard); max-height: 100vh; overflow-y: auto;
}
.search-panel[data-open="true"] { transform: translateY(0); box-shadow: var(--sh-card); }
.search-field { display: flex; align-items: center; gap: var(--s-3); border-bottom: 1px solid var(--c-ink); padding-bottom: var(--s-3); }
.search-field input {
  flex: 1; min-width: 0; border: 0; background: transparent; font-family: var(--font-display);
  font-size: clamp(1.35rem, 4vw, 2rem); padding: var(--s-2) 0;
}
.search-field input:focus { outline: none; }
.search-field input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.search-field input::-webkit-search-decoration { -webkit-appearance: none; }
.search-results { margin-top: var(--s-5); display: grid; gap: var(--s-2); }
.search-hit {
  display: grid; grid-template-columns: 56px 1fr auto; gap: var(--s-4); align-items: center;
  padding: var(--s-2); text-decoration: none; border: 1px solid transparent;
}
.search-hit:hover, .search-hit:focus-visible, .search-hit[data-active="true"] { background: var(--c-paper-sunk); border-color: var(--c-line); }
.search-hit img { width: 56px; height: 70px; object-fit: contain; background: var(--c-paper-sunk); border: 1px solid var(--c-line); }
.search-hit .pcard-title { font-size: 1rem; }
.search-empty { padding: var(--s-6) 0; color: var(--c-ink-mute); }

/* ---------------------------------------------------------------- product card + grids */
.grid-products {
  display: grid; gap: var(--grid-gap-y) var(--grid-gap);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 768px)  { .grid-products { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* Four across from 1024 (a 1024 viewport gives ~215px cards, a 1440 one ~270px): four related pieces
   on the PDP sit in one row instead of three plus an orphan. */
@media (min-width: 1024px) { .grid-products { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.grid-3 { display: grid; gap: var(--grid-gap-y) var(--grid-gap); grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* Five form tiles sit in ONE row from 900px (auto-fit at a 160px floor); three collection tiles
   likewise — no orphaned second row. */
@media (min-width: 900px) { .grid-3 { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); } }

.pcard { position: relative; display: flex; flex-direction: column; text-decoration: none; min-width: 0; }
.pcard-media { aspect-ratio: var(--ar-product-card); }
/* OWNER (2026-09-08): product cards fill their frame edge to edge — a square or landscape source no
   longer sits letterboxed inside the 4:5 frame beside cropped neighbours (the Trending row read as
   "two photos not aligned"). The piece is centred in every lead photograph, so a cover crop keeps it. */
.pcard-media img { object-fit: cover; }
.pcard-media img { transition: transform var(--d-slow) var(--e-standard); }
.pcard-media img.alt { position: absolute; inset: 0; opacity: 0; }
@media (hover: hover) {
  .pcard:hover .pcard-media img { transform: scale(1.025); }
}
.pcard:hover .pcard-media { border-color: var(--c-line-strong); }
.pcard-body { padding-top: var(--s-3); display: flex; flex-direction: column; gap: var(--s-1); flex: 1; }
.pcard-line { font-size: var(--fs-label-s); letter-spacing: var(--tr-label-s); text-transform: uppercase; color: var(--c-ink-mute); }
.pcard-title {
  font-family: var(--font-display); font-size: 1.0625rem; line-height: 1.3; font-weight: 400;
  color: var(--c-ink); text-wrap: pretty;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.pcard-meta { display: flex; align-items: baseline; gap: var(--s-2) var(--s-3); flex-wrap: wrap; margin-top: auto; padding-top: var(--s-1); }
.pcard-price { font-size: var(--fs-body-s); font-weight: 500; color: var(--c-ink); font-variant-numeric: tabular-nums lining-nums; }
@media (max-width: 359px) {
  .pcard-title { font-size: 0.9375rem; -webkit-line-clamp: 3; }
  .pcard-line { font-size: 0.625rem; }
}

/* Availability: `src/availability/index.ts` decides the words, `src/shell/availability.ts` renders
   them. Stated in words AND colour — never colour alone. `.stock-*` are lane 3's cssClass values. */
.availability, .stock {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-label-s); letter-spacing: .1em; text-transform: uppercase;
}
.availability::before, .stock::before {
  content: ""; width: 6px; height: 6px; border-radius: var(--r-pill); background: currentColor; flex: none;
}
.stock-ready, .stock-in { color: var(--c-success); }
.stock-made { color: var(--c-notice); }
.stock-out { color: var(--c-notice); }
/* UNKNOWN_OR_STALE is a qualification, not a status: no dot, sentence case. */
.stock-unknown { color: var(--c-ink-mute); text-transform: none; letter-spacing: 0; font-size: var(--fs-body-s); }
.stock-unknown::before { display: none; }
.pcard .availability { max-width: 100%; }
.availability-note {
  display: flex; flex-direction: column; gap: 2px;
  margin: 0 0 var(--s-4); font-size: var(--fs-body-s); color: var(--c-ink-soft); max-width: 60ch;
}
.availability-note:empty { display: none; }
.availability-unique { font-size: var(--fs-label-s); letter-spacing: var(--tr-label-s); text-transform: uppercase; color: var(--c-gilt-deep); }
.pcard .availability-note { display: none; }

.badge {
  position: absolute; top: var(--s-2); left: var(--s-2); z-index: var(--z-raised);
  background: var(--c-paper-raised); color: var(--c-ink);
  font-size: var(--fs-label-s); letter-spacing: .1em; text-transform: uppercase;
  padding: 5px var(--s-3); border: 1px solid var(--c-line);
}
.badge-archive { background: var(--c-paper-raised); color: var(--c-ink-soft); }

/* editorial tiles (By form / Collections): frame, then a caption under it — never over the jewel */
.tile { display: flex; flex-direction: column; text-decoration: none; min-width: 0; }
.tile-media { aspect-ratio: var(--ar-editorial); }
.tile-media img { transition: transform var(--d-slow) var(--e-standard); }
@media (hover: hover) { .tile:hover .tile-media img { transform: scale(1.025); } }
.tile:hover .tile-media { border-color: var(--c-line-strong); }
.tile-cap { padding-top: var(--s-3); display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-3); flex-wrap: wrap; }
.tile-cap h3 { font-size: var(--fs-display-s); line-height: var(--lh-display-s); }
.tile-cap .label-s, .tile-cap p { color: var(--c-ink-mute); margin: 0; }
/* Legacy structure (an <img> directly inside `.tile`, the client-built fallback before it was
   updated): give the image its own frame so the caption still sits below. */
.tile > img { aspect-ratio: var(--ar-editorial); object-fit: contain; background: var(--c-paper-sunk); border: 1px solid var(--c-line); width: 100%; height: auto; }

/* ---------------------------------------------------------------- shop */
.shop-head { padding-block: calc(var(--section-y) * .55) var(--s-6); }
.shop-head .lede { margin-top: var(--s-3); }
.crumbs { display: flex; flex-wrap: nowrap; gap: 4px var(--s-2); align-items: center; margin-bottom: var(--s-4); color: var(--c-ink-mute); min-width: 0; }
.crumbs a, .crumbs span[aria-hidden] { flex: none; color: var(--c-ink-soft); }
.crumbs [data-crumb] { flex: 0 1 auto; min-width: 0; color: var(--c-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.shop-layout { display: grid; gap: var(--s-5); }
@media (min-width: 1024px) { .shop-layout { grid-template-columns: 232px minmax(0, 1fr); gap: var(--s-9); align-items: start; } }
/* Filters are a `<details>` disclosure below 1024 (open with no JS; `shop.js` collapses it), and
   a sticky sidebar from 1024 where the summary is hidden. */
.filter-panel { display: block; }
@media (min-width: 1024px) { .filter-panel { position: sticky; top: calc(var(--header-h) + var(--s-5)); } }
.filter-disclosure { border: 1px solid var(--c-line-strong); background: var(--c-paper-raised); }
.filter-disclosure > summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  min-height: 48px; padding: var(--s-2) var(--s-4); cursor: pointer;
  font-size: var(--fs-label); letter-spacing: var(--tr-label); text-transform: uppercase; font-weight: 500;
  list-style: none;
}
.filter-disclosure > summary::-webkit-details-marker { display: none; }
.filter-disclosure > summary::after { content: "+"; font-size: 1.15rem; line-height: 1; color: var(--c-ink-mute); }
.filter-disclosure[open] > summary { border-bottom: 1px solid var(--c-line); }
.filter-disclosure[open] > summary::after { content: "\2212"; }
.filter-disclosure > .filter-body { padding: var(--s-4); }
@media (min-width: 1024px) {
  .filter-disclosure { border: 0; background: none; }
  .filter-disclosure > summary { display: none; }
  .filter-disclosure > .filter-body { padding: 0; }
}
.filter-group + .filter-group { margin-top: var(--s-5); border-top: 1px solid var(--c-line); padding-top: var(--s-5); }
.filter-group h3 { font-family: var(--font-ui); font-size: var(--fs-label-s); letter-spacing: var(--tr-label-s); text-transform: uppercase; font-weight: 500; margin-bottom: var(--s-2); color: var(--c-ink-mute); }
.filter-opt { display: flex; align-items: center; gap: var(--s-3); min-height: var(--target-min); cursor: pointer; font-size: var(--fs-body-s); text-decoration: none; color: var(--c-ink-soft); }
.filter-opt:hover { color: var(--c-ink); }
.filter-opt[aria-current="true"] { color: var(--c-ink); font-weight: 500; }
.filter-opt[aria-current="true"] > span:first-child::before { content: "\2713\00a0"; color: var(--c-gilt-deep); }
.filter-opt .n { margin-left: auto; color: var(--c-ink-mute); font-size: var(--fs-body-s); font-variant-numeric: tabular-nums; }
.filter-apply { margin-top: var(--s-5); }
@media (min-width: 1024px) { .filter-apply { display: none; } }
.js .filter-apply { display: none; }
.shop-toolbar { display: flex; align-items: center; gap: var(--s-3) var(--s-4); flex-wrap: wrap; justify-content: space-between; margin-bottom: var(--s-4); padding-bottom: var(--s-3); border-bottom: 1px solid var(--c-line); }
.shop-sort { display: flex; align-items: center; gap: var(--s-3); }
.shop-sort label { color: var(--c-ink-mute); }
.chips { display: flex; gap: var(--s-2); flex-wrap: wrap; margin-bottom: var(--s-5); }
.chip {
  display: inline-flex; align-items: center; gap: var(--s-2); min-height: var(--target-min);
  padding: 4px var(--s-4); border: 1px solid var(--c-line-strong); background: transparent;
  font-size: var(--fs-label-s); letter-spacing: .08em; text-transform: uppercase; color: var(--c-ink);
  text-decoration: none; transition: border-color var(--d-fast) var(--e-standard);
}
.chip:hover { border-color: var(--c-ink); }
.chip[aria-selected="true"] { border-color: var(--c-ink); background: var(--c-ink); color: var(--c-ink-invert); }
.chip .x { font-size: 15px; line-height: 1; color: var(--c-ink-mute); }
.chip[aria-selected="true"] .x { color: inherit; }
select.sortby {
  min-height: var(--target-min); border: 1px solid var(--c-line-strong); background: var(--c-paper-raised);
  padding: 0 var(--s-7) 0 var(--s-4); max-width: 100%; appearance: none; cursor: pointer;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'><path d='m2 4 4 4 4-4' fill='none' stroke='%234F4942' stroke-width='1.4'/></svg>");
  background-repeat: no-repeat; background-position: right var(--s-4) center; background-size: 12px;
}
.filter-open-btn { display: inline-flex; }
@media (min-width: 1024px) { .filter-open-btn { display: none; } }
.load-more { margin-top: var(--s-7); text-align: center; }

/* Empty / zero states: a heading, one sentence, a way out. Never a dashed box. */
.grid-products > .empty-state, .grid-3 > .empty-state { grid-column: 1 / -1; }
.empty-state { padding: var(--s-8) var(--s-4); text-align: center; border-top: 1px solid var(--c-line); border-bottom: 1px solid var(--c-line); }
.empty-state h1, .empty-state h2 { font-family: var(--font-display); font-size: var(--fs-display-m); line-height: var(--lh-display-m); margin-bottom: var(--s-3); }
.empty-state .lede { margin-inline: auto; max-width: 46ch; }
.empty-state .empty-actions { display: flex; gap: var(--s-3); flex-wrap: wrap; justify-content: center; margin-top: var(--s-5); }
.empty-state > .btn { margin-top: var(--s-5); }
p.empty-state { border: 0; color: var(--c-ink-mute); padding: var(--s-6) 0; }

/* ---------------------------------------------------------------- product detail */
.pdp-crumbs { padding-block: var(--s-4) 0; }
.pdp-section { padding-block: var(--s-4) var(--section-y); }
.pdp { display: grid; gap: var(--s-6); }
/* Phone-width PDP: the implicit `auto` column sizes to the widest max-content child, and a piece with a
   dozen photographs has a thumbnail strip wider than the viewport — the WHOLE column then grows past the
   screen (measured on staging 2026-09-07: 764px in a 390px viewport, the piece half off-screen). One
   explicit `minmax(0, 1fr)` column and `min-width: 0` on the strip keep every child at the viewport. */
.pdp { grid-template-columns: minmax(0, 1fr); }
.pdp > * { min-width: 0; }
.gallery-thumbs { min-width: 0; max-width: 100%; }
@media (min-width: 900px) {
  .pdp { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: clamp(32px, 6vw, 96px); align-items: start; }
  .pdp-section { padding-top: var(--s-6); }
}
/* Gallery: contained, portrait, capped at 52vh on a phone so title/price/action land inside the
   first screen-and-a-bit (measured in the previous pass; kept). */
.gallery-main { aspect-ratio: var(--ar-product-hero); max-height: 46vh; touch-action: pan-y; }
@media (min-width: 900px) { .gallery-main { max-height: none; } }
.gallery-main img { padding: 4%; }
.gallery-nav, .gallery-zoom {
  position: absolute; z-index: var(--z-raised);
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--target-min); height: var(--target-min);
  background: rgba(250, 248, 244, .9); color: var(--c-ink);
  border: 1px solid var(--c-line); border-radius: var(--r-pill); line-height: 1;
  transition: background var(--d-fast) var(--e-standard), border-color var(--d-fast) var(--e-standard);
}
.gallery-nav:hover, .gallery-zoom:hover { background: var(--c-paper-raised); border-color: var(--c-line-strong); }
.gallery-nav[hidden], .gallery-zoom[hidden] { display: none; }
.gallery-nav { top: 50%; transform: translateY(-50%); font-size: 1.5rem; padding-bottom: 3px; font-family: var(--font-display); }
.gallery-prev { left: var(--s-3); }
.gallery-next { right: var(--s-3); }
.gallery-zoom { right: var(--s-3); bottom: var(--s-3); font-size: 1rem; }
.gallery-counter {
  position: absolute; left: var(--s-3); bottom: var(--s-3); z-index: var(--z-raised); margin: 0;
  background: rgba(250, 248, 244, .9); color: var(--c-ink-soft); border: 1px solid var(--c-line);
  font-size: var(--fs-label-s); letter-spacing: .1em;
  padding: 6px var(--s-3); border-radius: var(--r-pill); font-variant-numeric: tabular-nums;
}
.gallery-thumbs { display: flex; gap: var(--s-2); margin-top: var(--s-3); overflow-x: auto; padding: 2px 2px 6px; scrollbar-width: thin; scroll-snap-type: x proximity; }
.gallery-thumbs[hidden] { display: none; }
.gallery-thumbs button {
  flex: none; width: 56px; height: 70px; min-width: var(--target-min); min-height: var(--target-min);
  border: 1px solid var(--c-line); background: var(--c-paper-sunk); padding: 4px; scroll-snap-align: start;
  transition: border-color var(--d-fast) var(--e-standard);
}
@media (min-width: 900px) { .gallery-thumbs button { width: 64px; height: 80px; } }
.gallery-thumbs button:hover { border-color: var(--c-line-strong); }
.gallery-thumbs button[aria-current="true"] { border-color: var(--c-ink); }
.gallery-thumbs img { width: 100%; height: 100%; object-fit: contain; }

/* Enlarge overlay: a paper sheet, the piece contained, controls in the corners. */
.pdp-lightbox { position: fixed; inset: 0; z-index: var(--z-modal); }
.pdp-lightbox[hidden] { display: none; }
.pdp-lb-backdrop { position: absolute; inset: 0; background: rgba(250, 248, 244, .97); }
.pdp-lb-dialog {
  position: relative; height: 100%; display: flex; align-items: center; justify-content: center;
  padding: max(var(--s-8), env(safe-area-inset-top)) max(var(--s-8), var(--gutter)) calc(var(--s-8) + env(safe-area-inset-bottom));
}
.pdp-lb-imgwrap { max-width: min(92vw, 960px); max-height: 82vh; }
.pdp-lb-img { display: block; max-width: 100%; max-height: 82vh; width: auto; height: auto; margin: 0 auto; object-fit: contain; }
.pdp-lb-close, .pdp-lb-nav {
  position: absolute; display: inline-flex; align-items: center; justify-content: center;
  width: 48px; height: 48px; color: var(--c-ink); background: var(--c-paper-raised);
  border: 1px solid var(--c-line); border-radius: var(--r-pill); line-height: 1;
}
.pdp-lb-close:hover, .pdp-lb-nav:hover { border-color: var(--c-ink); }
.pdp-lb-close { top: max(var(--s-3), env(safe-area-inset-top)); right: var(--s-3); font-size: 1rem; }
.pdp-lb-nav { top: 50%; transform: translateY(-50%); font-size: 1.6rem; padding-bottom: 4px; font-family: var(--font-display); }
.pdp-lb-prev { left: var(--s-3); }
.pdp-lb-next { right: var(--s-3); }
.pdp-lb-counter {
  position: absolute; left: 50%; bottom: max(var(--s-4), env(safe-area-inset-bottom)); transform: translateX(-50%);
  margin: 0; font-size: var(--fs-label-s); letter-spacing: .1em; color: var(--c-ink-mute); font-variant-numeric: tabular-nums;
}
.pdp-lightbox :focus-visible { outline: var(--focus-ring); outline-offset: 2px; }

@media (min-width: 900px) { .pdp-info { position: sticky; top: calc(var(--header-h) + var(--s-5)); } }
.pdp-info .eyebrow { margin-bottom: var(--s-3); }
.pdp-title { margin: 0 0 var(--s-3); }
.pdp-subtitle { color: var(--c-ink-mute); margin: 0 0 var(--s-3); }
.pdp-price-row { display: flex; align-items: baseline; gap: var(--s-2) var(--s-4); flex-wrap: wrap; margin-bottom: var(--s-3); }
.pdp-price { font-family: var(--font-display); font-size: var(--fs-display-s); line-height: 1.2; font-variant-numeric: tabular-nums lining-nums; }
.pdp-summary { margin-bottom: var(--s-5); max-width: 62ch; }
.pdp-cta { margin-bottom: var(--s-6); }
.pdp-cta .form-note { margin-top: var(--s-3); }
.pdp-info .lede, .pdp-info .prose, .acc-panel { max-width: 66ch; }
.spec-list { border-top: 1px solid var(--c-line); }
.spec-row { display: flex; gap: var(--s-4); justify-content: space-between; flex-wrap: wrap; padding: var(--s-3) 0; border-bottom: 1px solid var(--c-line); font-size: var(--fs-body-s); }
.spec-row dt { color: var(--c-ink-mute); text-transform: uppercase; letter-spacing: .1em; font-size: var(--fs-label-s); padding-top: 3px; flex: none; }
.spec-row dd { text-align: right; color: var(--c-ink); overflow-wrap: anywhere; margin-left: auto; }
.pdp-accordions { margin-top: var(--s-6); }
.acc { border-bottom: 1px solid var(--c-line); }
.acc:first-child { border-top: 1px solid var(--c-line); }
.acc-btn {
  display: flex; width: 100%; align-items: center; justify-content: space-between; gap: var(--s-4);
  min-height: 56px; text-align: left; font-size: var(--fs-label); letter-spacing: var(--tr-label); text-transform: uppercase; font-weight: 500;
}
.acc-panel { padding-bottom: var(--s-5); font-size: var(--fs-body-s); color: var(--c-ink-soft); line-height: 1.7; }
.acc-panel[hidden] { display: none; }
.acc-btn .chev { transition: transform var(--d-fast) var(--e-standard); width: 12px; height: 12px; color: var(--c-ink-mute); flex: none; }
.acc[data-open="true"] .chev { transform: rotate(180deg); }
/* `<details>` disclosures (PDP + landing FAQ, 2026-09-07): the same `.acc` look, but the row opens
   with no JavaScript. `list-style: none` + the WebKit pseudo-element remove the native triangle so
   the existing chevron is the only marker; `cursor: pointer` and the 56-px `.acc-btn` min-height
   keep the whole summary a real 44-px+ target. `display: flex` on a `<summary>` is what removes the
   marker box in Firefox. */
details.acc > summary.acc-btn { cursor: pointer; list-style: none; padding-block: var(--s-3); }
details.acc > summary.acc-btn::-webkit-details-marker { display: none; }
details.acc > summary.acc-btn::marker { content: ""; }
details.acc[open] > summary.acc-btn .chev { transform: rotate(180deg); }
details.acc > summary.acc-btn:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-offset); }
/* The description's own "Read more", inside the house note. Quiet: it is a second-level control. */
.pdp-more { margin-top: var(--s-3); }
.pdp-more > summary.pdp-more__btn {
  cursor: pointer; list-style: none; display: inline-flex; align-items: center;
  min-height: var(--target-min); color: var(--c-ink); font-size: var(--fs-body-s);
}
.pdp-more > summary.pdp-more__btn::-webkit-details-marker { display: none; }
.pdp-more > summary.pdp-more__btn::marker { content: ""; }
.pdp-more[open] > summary.pdp-more__btn { color: var(--c-ink-mute); }
.pdp-more__body { padding-top: var(--s-2); }
/* Merchant copy through the projection: sometimes no separator between label and value
   ("SapphireTotal Weight") — an upstream content issue; typography keeps it readable. */
.acc-panel .prose { overflow-wrap: anywhere; }
.acc-panel .prose p { margin: 0 0 var(--s-3); }
.acc-panel .prose p:last-child { margin-bottom: 0; }
.acc-panel .prose strong, .acc-panel .prose b { font-weight: 500; color: var(--c-ink); }
.acc-panel .prose ul, .acc-panel .prose ol { margin: 0 0 var(--s-3); padding-left: var(--s-5); }
.acc-panel .prose .muted { font-size: var(--fs-body-s); }
.acc-panel .link-u { color: var(--c-ink); }

/* ---------------------------------------------------------------- hero / editorial */
/* Split hero: copy on paper, the piece in a frame. No scrim, no treated photograph. */
.hero { background: var(--c-paper); border-bottom: 1px solid var(--c-line); }
.hero-grid { display: grid; gap: var(--s-5); padding-block: var(--s-4) var(--s-7); align-items: center; }
/* Phone: the piece first (product-led), then the copy. Desktop restores copy-left / piece-right. */
.hero-media { aspect-ratio: var(--ar-product-hero); max-height: 44vh; order: -1; }
.hero-media img { padding: 3%; }
.hero-copy { min-width: 0; }
.hero-copy .eyebrow { margin-bottom: var(--s-3); }
.hero-copy h1 { max-width: 14ch; }
.hero-copy .lede { margin-top: var(--s-4); max-width: 44ch; }
@media (min-width: 768px) {
  .hero-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(32px, 6vw, 96px); padding-block: clamp(40px, 6vw, 80px); }
  .hero-media { max-height: min(70vh, 640px); justify-self: end; max-width: 520px; order: 0; }
  .hero-copy .lede { margin-top: var(--s-5); }
}
/* Lifestyle hero (owner decision 2026-09-07): a campaign photograph instead of a framed piece. The
   photograph is COVER-cropped around a CMS focal point (`--hero-focal`), never padded, never scrimmed;
   copy stays on paper beside/below it so nothing is written over the image. Phone: a 4:5 crop, tall
   enough to keep face and hands; desktop: the photo's own 3:2, the copy column beside it. */
.hero-media--lifestyle { aspect-ratio: 4 / 5; max-height: 62vh; border-radius: var(--r-frame, 4px); overflow: hidden; background: var(--c-paper-sunk); }
.hero-media--lifestyle img { padding: 0; object-fit: cover; object-position: var(--hero-focal, 50% 40%); width: 100%; height: 100%; }
/* Desktop fine-tune (2026-09-07): the hero takes the wide page measure so the photograph is the
   size of a real campaign frame, not a thumbnail beside a column of copy; 3:2 beside a narrow
   column at 768, 4:3 (taller, still cover-cropped around the focal point) from 1024 where the
   column is wide enough for the two buttons to sit on one line. Vertical rhythm scales with the
   viewport and the next chapter's heading always peeks above the fold. */
@media (min-width: 768px) {
  .hero--lifestyle .hero-grid { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); align-items: center; gap: clamp(32px, 5vw, 80px); padding-block: clamp(40px, 5vw, 72px); }
  /* Tablet portrait: the copy column is narrow and tall, so the photograph keeps the phone's 4:5
     crop beside it (3:2 here was a 380 × 253 thumbnail next to a stack of copy). */
  .hero-media--lifestyle { aspect-ratio: 4 / 5; max-height: min(64vh, 620px); max-width: none; justify-self: stretch; }
}
@media (min-width: 1024px) {
  .hero--lifestyle .hero-grid { max-width: var(--max-page); grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); padding-block: clamp(48px, 5vw, 80px); }
  .hero--lifestyle .hero-copy { padding-left: clamp(0px, 2vw, 32px); }
  /* 14ch (was 12ch): at the 64-px cap "Wearing Freedom" is 460 px — 12ch of Fraunces broke it onto a third line. */
  .hero--lifestyle .hero-copy h1 { max-width: 14ch; }
  .hero--lifestyle .hero-copy .lede { max-width: 40ch; }
  .hero--lifestyle .hero-cta { margin-top: var(--s-6); }
  .hero--lifestyle .hero-cta > .btn { flex: 0 0 auto; }
  .hero-media--lifestyle { aspect-ratio: 4 / 3; max-height: min(74vh, 720px); }
}
@media (min-width: 1440px) {
  .hero--lifestyle .hero-grid { grid-template-columns: minmax(0, 6fr) minmax(0, 8fr); }
}
/* Legacy hero (a `.hero-media` absolutely positioned behind `.hero-inner`) is gone; keep
   `.hero-inner` as a plain block so any page still using it renders sanely. */
.hero-inner { position: relative; padding-block: var(--s-6); }

.split { display: grid; gap: var(--s-6); align-items: center; }
@media (min-width: 900px) { .split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(32px, 6vw, 96px); } }
.split > div { min-width: 0; }
.split .eyebrow { margin-bottom: var(--s-3); }
.split h2 { margin-bottom: var(--s-4); }
.split .rule-gilt { margin-bottom: var(--s-5); }
.split .hero-cta { margin-top: var(--s-6); }
/* `height: auto` is load-bearing: the <img> carries width/height attributes, and aspect-ratio is
   ignored whenever both dimensions are definite. */
.split-media { aspect-ratio: var(--ar-editorial); max-height: 600px; }
.split-media img { height: 100%; padding: 3%; }
@media (max-width: 899px) { .split-media { max-height: 56vh; } }

/* trust row */
.trust-row { display: grid; gap: var(--s-6) var(--s-5); grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 900px) { .trust-row { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.trust-item { display: flex; flex-direction: column; gap: 6px; }
.trust-item h3 { font-family: var(--font-ui); color: var(--c-ink); margin-top: var(--s-3); }
.trust-item p { color: var(--c-ink-soft); font-size: var(--fs-body-s); }
.trust-item .link-u { margin-top: var(--s-2); align-self: flex-start; color: var(--c-ink-mute); }

/* newsletter (banded section, markup in shell/newsletter.ts) */
.nl-form { display: flex; gap: var(--s-2); flex-wrap: wrap; max-width: 520px; margin-inline: auto; }
.nl-form input[type="email"] {
  flex: 1 1 220px; min-width: 0; min-height: 48px; padding: 0 var(--s-4);
  border: 1px solid var(--c-line-strong); background: var(--c-paper-raised); color: var(--c-ink);
}
.nl-form input[type="email"]:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
.form-note { font-size: var(--fs-body-s); margin-top: var(--s-3); color: var(--c-ink-soft); }
.form-note:empty { display: none; }
.form-note[data-state="error"] { color: var(--c-notice); }
.form-note[data-state="ok"] { color: var(--c-success); }
.form-consent-note { font-size: var(--fs-body-s); color: var(--c-ink-soft); }
.band-night .form-signup-disclosure, .band-night .form-signup-note, .band-night .form-consent-note { max-width: 62ch; margin-inline: auto; }
/* `shell/newsletter.ts` (not this pass's file) paints its eyebrow gold INLINE (`style="color:
   var(--c-gilt)"`): 1.95:1 on sunk paper, measured. `!important` is the only thing that outranks
   an inline declaration; remove it once that markup drops the inline colour. */
[data-shell="newsletter"] .eyebrow, [data-shell="newsletter"] .label-s { color: var(--c-ink-mute) !important; }
[data-shell="newsletter"] h2 { margin: var(--s-3) 0 var(--s-3); }
[data-shell="newsletter"] .lede { margin-bottom: var(--s-5); }

/* ---------------------------------------------------------------- footer */
.site-footer { background: var(--c-paper); color: var(--c-ink); border-top: 1px solid var(--c-line); padding-block: var(--s-8) var(--s-6); }
.footer-grid { display: grid; gap: var(--s-6) var(--s-5); grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 768px) { .footer-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.footer-col h3 { font-family: var(--font-ui); font-size: var(--fs-label-s); letter-spacing: var(--tr-label-s); text-transform: uppercase; font-weight: 500; margin-bottom: var(--s-3); color: var(--c-ink-mute); }
.footer-col a { display: flex; align-items: center; min-height: var(--target-min); padding: 4px 0; font-size: var(--fs-body-s); text-decoration: none; color: var(--c-ink-soft); }
.footer-col a:hover { color: var(--c-ink); }
.footer-base { margin-top: var(--s-7); padding-top: var(--s-5); border-top: 1px solid var(--c-line); display: flex; gap: var(--s-4); flex-wrap: wrap; justify-content: space-between; font-size: var(--fs-body-s); color: var(--c-ink-mute); }
.footer-base a { display: inline-flex; align-items: center; color: var(--c-ink-soft); }
.proto-note { background: var(--c-paper-sunk); border-top: 1px solid var(--c-line); padding: var(--s-4) 0; font-size: var(--fs-body-s); color: var(--c-ink-soft); }
.proto-note p { max-width: none; }
@media (max-width: 599px) { .site-footer { padding-block: var(--s-7) var(--s-5); } }

/* ---------------------------------------------------------------- cart drawer */
.cart-line { display: grid; grid-template-columns: 64px 1fr auto; gap: var(--s-3); padding: var(--s-3) 0; border-bottom: 1px solid var(--c-line); }
.cart-line img { width: 64px; height: 80px; object-fit: contain; background: var(--c-paper-sunk); border: 1px solid var(--c-line); }
.qty { display: inline-flex; align-items: center; border: 1px solid var(--c-line-strong); }
.qty button { width: var(--target-min); height: var(--target-min); }
.qty span { min-width: 28px; text-align: center; font-size: var(--fs-body-s); font-variant-numeric: tabular-nums; }
.cart-foot { margin-top: auto; padding-top: var(--s-5); border-top: 1px solid var(--c-line); }

/* ---------------------------------------------------------------- rendered ad copy (creative gallery) */
.copy-rendered { max-width: 74ch; font-size: var(--fs-body-s); line-height: 1.68; color: var(--c-ink-soft); }
.copy-rendered h2 { font-family: var(--font-display); font-size: 1.5rem; line-height: 1.2; color: var(--c-ink); margin: var(--s-7) 0 var(--s-3); }
.copy-rendered h3 { font-family: var(--font-ui); font-size: var(--fs-label-s); letter-spacing: var(--tr-label-s); text-transform: uppercase; color: var(--c-ink); margin: var(--s-6) 0 var(--s-3); }
.copy-rendered h4 { font-family: var(--font-ui); font-size: var(--fs-body-s); font-weight: 500; color: var(--c-ink); margin: var(--s-5) 0 var(--s-2); }
.copy-rendered > *:first-child { margin-top: 0; }
.copy-rendered p { margin: 0 0 var(--s-4); }
.copy-rendered strong { color: var(--c-ink); font-weight: 500; }
.copy-rendered hr { border: 0; border-top: 1px solid var(--c-line); margin: var(--s-6) 0; }
.copy-rendered ul { margin: 0 0 var(--s-4); padding-left: var(--s-5); list-style: disc; }
.copy-rendered li { margin-bottom: 4px; }
.copy-rendered blockquote { margin: 0 0 var(--s-4); padding: var(--s-4) var(--s-5); background: var(--c-paper-sunk); border-left: 2px solid var(--c-gilt); color: var(--c-ink); }
.copy-rendered code { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: .92em; background: var(--c-paper-sunk); padding: 1px 5px; border-radius: var(--r-sm); }
.copy-rendered pre { background: var(--c-paper-sunk); border: 1px solid var(--c-line); padding: var(--s-4); overflow-x: auto; margin: 0 0 var(--s-4); }
.copy-rendered pre code { background: none; padding: 0; word-break: break-all; white-space: pre-wrap; }
.copy-rendered .table-scroll { overflow-x: auto; margin: 0 0 var(--s-5); -webkit-overflow-scrolling: touch; }
.copy-rendered table { border-collapse: collapse; width: 100%; min-width: 480px; font-size: var(--fs-label-s); letter-spacing: 0; text-transform: none; }
.copy-rendered th, .copy-rendered td { text-align: left; padding: var(--s-2) var(--s-3); border-bottom: 1px solid var(--c-line); vertical-align: top; }
.copy-rendered th { color: var(--c-ink); font-weight: 500; white-space: nowrap; }

/* ---------------------------------------------------------------- debug inspector */
.tracking-inspector {
  position: fixed; right: 12px; bottom: 12px; z-index: var(--z-toast);
  width: min(92vw, 330px); max-height: 62vh; overflow: auto;
  background: #14130F; color: #E8E3D8; font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 11px; line-height: 1.5; border: 1px solid #3A352E; box-shadow: var(--sh-lift);
}
.tracking-inspector header { display: flex; justify-content: space-between; align-items: center; padding: 8px 10px; border-bottom: 1px solid #3A352E; position: sticky; top: 0; background: #14130F; }
.tracking-inspector header button { color: #E8E3D8; font-size: 16px; width: 24px; height: 24px; }
.tracking-inspector .ti-status { padding: 8px 10px; border-bottom: 1px solid #3A352E; color: #B5AD9F; }
.tracking-inspector .ti-status p { margin: 0 0 4px; }
.tracking-inspector .ti-status code { color: var(--c-gilt); word-break: break-all; }
.tracking-inspector .ti-list { padding: 6px 10px 10px; margin: 0; }
.tracking-inspector .ti-list li { padding: 2px 0; border-bottom: 1px solid #241F1A; }
.tracking-inspector .ti-list b { color: var(--c-gilt); font-weight: 500; }
.tracking-inspector .ti-list span { color: #8E877B; }

/* ---------------------------------------------------------------- contact page */
.contact-layout { display: grid; gap: var(--s-8); }
@media (min-width: 900px) { .contact-layout { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: var(--s-9); align-items: start; } }
.contact-layout h2 { margin-bottom: var(--s-5); }
.field { display: flex; flex-direction: column; gap: 6px; margin-top: var(--s-5); }
.field:first-child { margin-top: 0; }
.field label { font-size: var(--fs-label-s); letter-spacing: var(--tr-label-s); text-transform: uppercase; font-weight: 500; color: var(--c-ink-soft); }
.field input, .field select, .field textarea {
  min-height: 48px; border: 1px solid var(--c-line-strong); background: var(--c-paper-raised);
  padding: var(--s-3) var(--s-4); width: 100%; color: var(--c-ink);
  transition: border-color var(--d-fast) var(--e-standard);
}
.field input:hover, .field select:hover, .field textarea:hover { border-color: var(--c-ink-mute); }
.field textarea { min-height: 140px; resize: vertical; }
.field select { appearance: none; padding-right: var(--s-7); background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'><path d='m2 4 4 4 4-4' fill='none' stroke='%234F4942' stroke-width='1.4'/></svg>"); background-repeat: no-repeat; background-position: right var(--s-4) center; background-size: 12px; }
.field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible { outline: var(--focus-ring); outline-offset: 2px; border-color: var(--c-ink); }
.field[data-invalid="true"] input, .field[data-invalid="true"] select, .field[data-invalid="true"] textarea { border-color: var(--c-notice); }
.field-err { font-size: var(--fs-body-s); color: var(--c-notice); min-height: 1em; }
.field-err:empty { display: none; }
.field input[type="checkbox"] { min-height: 0; width: 20px; height: 20px; flex: none; border: 0; padding: 0; background: none; }
.field .check-row, .field label:has(> input[type="checkbox"]) {
  display: flex; align-items: center; gap: var(--s-3); min-height: var(--target-min);
  text-transform: none; letter-spacing: 0; font-weight: 400; font-size: var(--fs-body); color: var(--c-ink);
}
.field .form-consent-note { color: var(--c-ink-soft); }
.contact-submit { margin-top: var(--s-6); }
.contact-note { border: 1px solid var(--c-line-strong); background: var(--c-paper-raised); padding: var(--s-5); margin-top: var(--s-5); }
.contact-note[data-state="error"] { border-color: var(--c-notice); }
.contact-note[hidden] { display: none; }
.contact-note p + p { margin-top: var(--s-3); }
.reason-list { display: flex; flex-direction: column; gap: var(--s-4); }
.reason-list dt { font-size: var(--fs-label-s); letter-spacing: var(--tr-label-s); text-transform: uppercase; font-weight: 500; }
.reason-list dd { color: var(--c-ink-soft); font-size: var(--fs-body-s); margin: 4px 0 0; max-width: 52ch; }
.addr-note { font-size: var(--fs-body-s); color: var(--c-ink-mute); margin-top: var(--s-6); }

/* ---------------------------------------------------------------- about page */
.stat-strip { display: flex; flex-wrap: wrap; gap: var(--s-5) var(--s-7); margin: var(--s-6) 0 var(--s-6); padding-top: var(--s-5); border-top: 1px solid var(--c-line); }
.stat-strip div { display: flex; flex-direction: column; gap: 4px; }
.stat-strip strong { font-family: var(--font-display); font-size: var(--fs-display-s); font-weight: 400; font-variant-numeric: tabular-nums; }
.stat-strip .label-s { color: var(--c-ink-mute); }
.line-rows { display: flex; flex-direction: column; margin-top: var(--s-5); }
.line-row {
  display: grid; grid-template-columns: 88px 1fr auto; gap: var(--s-5); align-items: center;
  padding: var(--s-5) 0; border-top: 1px solid var(--c-line); text-decoration: none; color: inherit;
}
.line-rows a.line-row:hover h3 { text-decoration: underline; text-underline-offset: 4px; text-decoration-color: var(--c-line-strong); }
.line-row:last-child { border-bottom: 1px solid var(--c-line); }
.line-row img { width: 88px; height: 110px; object-fit: contain; background: var(--c-paper-sunk); border: 1px solid var(--c-line); flex: none; }
.line-row .n { white-space: nowrap; text-align: right; color: var(--c-ink-mute); }
.line-row p { color: var(--c-ink-soft); font-size: var(--fs-body-s); margin-top: 4px; max-width: 56ch; }
.gem-rows { margin-top: var(--s-5); display: flex; flex-direction: column; gap: var(--s-3); max-width: 46ch; }
.gem-row { display: grid; grid-template-columns: 116px 1fr 40px; gap: var(--s-4); align-items: center; }
.gem-row .gem-label { font-size: var(--fs-body-s); }
.gem-track { height: 6px; background: var(--c-paper-deep); position: relative; }
.gem-fill { position: absolute; inset: 0 auto 0 0; background: var(--c-gilt-deep); width: 0; }
.gem-row .n { text-align: right; color: var(--c-ink-mute); font-size: var(--fs-body-s); font-variant-numeric: tabular-nums; }
blockquote.about-quote {
  font-family: var(--font-display); font-size: clamp(1.375rem, 1.1rem + 1.4vw, 2rem); line-height: 1.3;
  font-weight: 300; font-style: normal; margin: 0; text-wrap: balance;
}
.about-attribution { margin-top: var(--s-4); }
@media (max-width: 560px) {
  .line-row { grid-template-columns: 64px 1fr; gap: var(--s-4); }
  .line-row img { width: 64px; height: 80px; }
  .line-row .n { grid-column: 2; justify-self: start; margin-top: 2px; }
  .gem-row { grid-template-columns: 92px 1fr 36px; }
}

/* ---------------------------------------------------------------- campaigns page */
.campaign-badge {
  display: inline-flex; align-items: center; gap: 8px; margin-bottom: var(--s-4);
  font-size: var(--fs-label-s); letter-spacing: var(--tr-label-s); text-transform: uppercase;
  color: var(--c-ink); background: var(--c-paper-sunk); border: 1px solid var(--c-line); padding: 6px var(--s-3); font-weight: 500;
}
.campaign-badge::before { content: ""; width: 6px; height: 6px; border-radius: var(--r-pill); background: var(--c-notice); }
.outbound-grid { display: grid; gap: var(--s-4); grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); margin-top: var(--s-6); }
.outbound-card {
  display: flex; flex-direction: column; gap: var(--s-3); padding: var(--s-5);
  border: 1px solid var(--c-line); background: var(--c-paper-raised); text-decoration: none; color: inherit;
  transition: border-color var(--d-fast) var(--e-standard);
}
.outbound-card:hover { border-color: var(--c-ink); }
.outbound-card .oc-top { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s-3); }
.outbound-card .ext { font-size: 1.1em; opacity: .6; }
.outbound-card p { color: var(--c-ink-soft); font-size: var(--fs-body-s); }
.outbound-card .label-s.tag { color: var(--c-ink-mute); }

/* ---------------------------------------------------------------- landing page (lp-*) */
.lp-hero { display: grid; background: var(--c-paper); border-bottom: 1px solid var(--c-line); }
@media (min-width: 900px) { .lp-hero { grid-template-columns: 1fr 1fr; align-items: center; } }
/* `width: 100%` is load-bearing here exactly as on `.frame`: without it `max-height` transfers through the
   aspect ratio into a max-WIDTH and the phone hero shrank to 276px, left-aligned (staging, 2026-09-07). */
.lp-hero-media { position: relative; width: 100%; background: var(--c-paper-sunk); order: -1; aspect-ratio: 4 / 5; max-height: 52vh; border-bottom: 1px solid var(--c-line); overflow: hidden; }
@media (min-width: 900px) { .lp-hero-media { order: 2; max-height: 640px; border-bottom: 0; border-left: 1px solid var(--c-line); } }
.lp-hero-media img { width: 100%; height: 100%; object-fit: contain; padding: 4%; }
.lp-hero-copy { padding: var(--s-6) var(--gutter) var(--s-7); display: flex; flex-direction: column; justify-content: center; }
@media (min-width: 900px) { .lp-hero-copy { padding: var(--s-9) clamp(32px, 5vw, 80px); } }
.lp-hero-copy .lede { color: var(--c-ink-soft); margin-top: var(--s-4); }
.lp-price-line { margin-top: var(--s-4); font-size: var(--fs-body); color: var(--c-ink-soft); }
.lp-price-line strong { color: var(--c-ink); font-weight: 500; font-variant-numeric: tabular-nums; }
.lp-hero-copy .hero-cta { margin-top: var(--s-5); }
.lp-trust-cue { margin-top: var(--s-4); display: flex; align-items: center; gap: var(--s-2); font-size: var(--fs-body-s); color: var(--c-ink-mute); }
.lp-trust-cue svg { width: 14px; height: 14px; flex: none; color: var(--c-gilt-deep); }
.lp-hero-media .badge { position: absolute; top: var(--s-4); left: var(--s-4); }
.lp-variant-tag {
  position: fixed; left: var(--s-3); bottom: var(--s-3); z-index: var(--z-sticky);
  background: var(--c-night); color: var(--c-ink-invert); font-family: ui-monospace, Menlo, monospace;
  font-size: 10px; letter-spacing: .04em; padding: 5px 9px; border-radius: var(--r-sm); opacity: .88;
}
.lp-facts { display: grid; gap: var(--s-6); }
@media (min-width: 900px) { .lp-facts { grid-template-columns: 1fr 1fr; gap: var(--s-8); align-items: center; } }
.lp-macro-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-3); }
.lp-macro-grid img { width: 100%; height: auto; aspect-ratio: 1 / 1; max-height: 340px; object-fit: contain; background: var(--c-paper-sunk); border: 1px solid var(--c-line); }
.lp-featured { display: grid; gap: 0; background: var(--c-paper-raised); border: 1px solid var(--c-line); }
@media (min-width: 760px) { .lp-featured { grid-template-columns: 1fr 1fr; align-items: stretch; } }
.lp-featured-media { border: 0; border-bottom: 1px solid var(--c-line); aspect-ratio: var(--ar-product-card); }
@media (min-width: 760px) { .lp-featured-media { aspect-ratio: auto; border-bottom: 0; border-right: 1px solid var(--c-line); } }
.lp-featured-media img { max-height: 520px; padding: 4%; }
.lp-featured-body { padding: var(--s-6); display: flex; flex-direction: column; }
.lp-fact-line { font-size: var(--fs-body-s); color: var(--c-ink-mute); margin-top: 2px; }
.lp-edit-row {
  display: flex; gap: var(--s-4); overflow-x: auto; scroll-snap-type: x mandatory;
  padding-bottom: var(--s-3); margin: 0 calc(var(--gutter) * -1); padding-inline: var(--gutter);
  -webkit-overflow-scrolling: touch; scrollbar-width: thin;
}
.lp-card { flex: 0 0 min(72vw, 240px); scroll-snap-align: start; display: flex; flex-direction: column; }
@media (min-width: 640px) {
  .lp-edit-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); overflow: visible; margin: 0; padding: 0; gap: var(--grid-gap-y) var(--grid-gap); }
  .lp-card { flex: none; }
}
.lp-card-media { aspect-ratio: var(--ar-product-card); }
.lp-card-body { padding-top: var(--s-3); display: flex; flex-direction: column; gap: 4px; flex: 1; }
.lp-card-title { font-family: var(--font-display); font-size: 1.0625rem; line-height: 1.3; }
.lp-card .btn { margin-top: var(--s-3); }
.lp-steps { list-style: none; counter-reset: lp-step; display: grid; gap: var(--s-6); }
@media (min-width: 760px) { .lp-steps { grid-template-columns: repeat(3, 1fr); } }
.lp-steps li { counter-increment: lp-step; position: relative; padding-left: var(--s-8); }
.lp-steps li::before {
  content: counter(lp-step); position: absolute; left: 0; top: -2px;
  font-family: var(--font-display); font-size: 1.6rem; font-weight: 300; color: var(--c-gilt-deep);
}
.lp-final { position: relative; background: var(--c-paper-sunk); color: var(--c-ink); overflow: hidden; border-top: 1px solid var(--c-line); }
.lp-final-media { display: none; }
.lp-final { min-height: 0; display: flex; align-items: center; }
.lp-final-inner { position: relative; z-index: var(--z-raised); text-align: center; padding-block: var(--s-9); }
.lp-final .lede { color: var(--c-ink-soft); }
.lp-sticky {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-sticky);
  display: none; align-items: center; gap: var(--s-3);
  background: var(--c-paper-raised); border-top: 1px solid var(--c-line);
  padding: var(--s-3) var(--gutter); padding-bottom: calc(var(--s-3) + env(safe-area-inset-bottom));
  box-shadow: var(--sh-lift);
  transform: translateY(100%); transition: transform var(--d-base) var(--e-standard);
}
.lp-sticky[data-shown="true"] { transform: translateY(0); }
.lp-sticky-info { flex: 1; min-width: 0; }
.lp-sticky-info .label-s { color: var(--c-ink-mute); display: block; }
.lp-sticky-info strong { font-family: var(--font-display); font-size: 1rem; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 400; }
@media (max-width: 899px) { .lp-sticky { display: flex; } }
@media (prefers-reduced-motion: reduce) { .lp-sticky { transition: none; } }

/* ---------------------------------------------------------------- cookie consent notice
   Markup and logic belong to `src/forms/pe-script.ts` / `src/shell/consent.ts`; this block owns
   only placement and appearance, written against the stable `data-consent-*` hooks and the
   `.consent-banner*` / `.consent-purpose*` / `.btn-consent*` class contract.

   2026-09-07 fine-tune (lane A). The notice measured 140 px at 390 (16.6 % of the viewport) and
   173 px at 1440 (19.2 %): a painted heading, a four-line paragraph, a second clarifying paragraph
   and three 44 px buttons, all stacked. That is the owner report of "huge on mobile AND on web".
   It is now one calm line — a single sentence beside its controls on desktop, and two lines above
   one row of three controls on a phone.

   Nothing about the DECISION changed, and nothing here may change it: same three purposes, same
   four buttons in the same order, Accept and Reject still the identical control at the identical
   size and contrast, no pre-ticked box, no dimming, no corner toast, still the bottom edge at every
   width. Two things moved, both of them appearance only:
     - the heading is visually hidden at every width (it is still the region's accessible name);
     - the separation sentence (`.consent-banner__note`) is shown with the "Choose cookies" panel it
       explains. It stays in the DOM, in the same place, always readable to assistive tech that
       ignores `display`, and the selector is a plain sibling combinator rather than `:has()` so a
       browser without `:has()` support still reveals it. */
[data-consent-banner] {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-toast);
  background: var(--c-paper); color: var(--c-ink);
  border-top: 1px solid var(--c-line-strong); font-size: 13px;
  max-height: min(88vh, 640px); overflow-y: auto; overscroll-behavior: contain;
  padding: 10px max(var(--gutter), env(safe-area-inset-left))
           calc(10px + env(safe-area-inset-bottom)) max(var(--gutter), env(safe-area-inset-right));
}
/* An author `display` rule beats the UA `[hidden]` rule regardless of specificity — the banner,
   its purpose panel and the "Save choices" button all need the explicit override. */
[data-consent-banner][hidden] { display: none !important; }
[data-consent-banner] [data-consent-manage-panel][hidden] { display: none !important; }
[data-consent-banner] button[hidden] { display: none !important; }

[data-consent-banner] .consent-banner__inner {
  max-width: var(--max-content); margin-inline: auto;
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px var(--s-6);
}
[data-consent-banner] .consent-banner__title {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
[data-consent-banner] p { margin: 0; line-height: 1.35; }
[data-consent-banner] .consent-banner__body { flex: 1 1 100%; min-width: 0; max-width: 84ch; color: var(--c-ink); }
[data-consent-banner] .consent-banner__actions { display: flex; align-items: center; flex: 1 1 100%; gap: 6px; }
/* The panel and the separation sentence are the only parts allowed to be tall, and only once
   "Choose cookies" is open; both take a full row of their own. NO `order` anywhere in this block:
   the panel sits before the buttons in the DOM, so reordering it visually would put the tab order
   (checkboxes, then Accept/Reject) above the reading order (buttons, then checkboxes). The panel
   therefore opens BETWEEN the sentence and the buttons, exactly where the markup puts it. */
[data-consent-banner] .consent-banner__note {
  flex: 1 0 100%; display: none; color: var(--c-ink-mute);
  font-size: var(--fs-label-s); letter-spacing: 0; text-transform: none; line-height: 1.5;
}
[data-consent-banner] [data-consent-manage-panel]:not([hidden]) ~ .consent-banner__note { display: block; }
[data-consent-banner] [data-consent-manage-panel]:not([hidden]) ~ .consent-banner__actions { margin-top: var(--s-2); }

/* NO DARK PATTERN: Accept and Reject are the same control at the same size, weight, padding and
   contrast. Only "Choose cookies" is quieter, because it opens a panel rather than recording a
   decision; "Save choices" is the single action inside that panel. */
[data-consent-banner] .consent-banner__actions button,
[data-consent-banner] button {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 40px; height: 40px; flex: 0 1 auto; padding: 0 12px;
  border: 1px solid var(--c-ink); background: none; border-radius: var(--r-none);
  font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  font-weight: 500; color: var(--c-ink); white-space: nowrap; cursor: pointer;
  transition: background var(--d-fast) var(--e-standard), color var(--d-fast) var(--e-standard);
}
[data-consent-banner] button:hover { background: var(--c-ink); color: var(--c-ink-invert); }
[data-consent-banner] button[data-consent-accept],
[data-consent-banner] button[data-consent-reject],
[data-consent-banner] button[data-consent-accept-btn],
[data-consent-banner] button[data-consent-reject-btn] {
  flex: 1 1 0; min-width: 0; background: none; color: var(--c-ink); border-color: var(--c-ink); font-weight: 500;
}
[data-consent-banner] button[data-consent-accept]:hover,
[data-consent-banner] button[data-consent-reject]:hover { background: var(--c-ink); color: var(--c-ink-invert); }
[data-consent-banner] button[data-consent-save] { background: var(--c-ink); color: var(--c-ink-invert); }
[data-consent-banner] .btn-consent--secondary,
[data-consent-banner] button[data-consent-manage] { border-color: var(--c-line-strong); color: var(--c-ink-soft); }

/* Phones: "Choose cookies" reads as a quiet text link so Accept and Reject keep equal, full
   targets on one row; it is still a 40 px control, not a smaller one. */
@media (max-width: 599px) {
  [data-consent-banner] { padding-top: 8px; padding-bottom: calc(8px + env(safe-area-inset-bottom)); }
  [data-consent-banner] p { line-height: 1.3; }
  [data-consent-banner] button[data-consent-manage] {
    flex: 0 1 auto; border-color: transparent; padding: 0 4px; letter-spacing: .04em;
    text-decoration: underline; text-underline-offset: 3px;
  }
  [data-consent-banner] button[data-consent-manage]:hover { background: none; color: var(--c-ink); }
  /* "Save choices" only exists while the panel is open, and a fourth control does not fit the row
     on a phone — it takes a full-width line of its own rather than running off the right edge. */
  [data-consent-banner] .consent-banner__actions { flex-wrap: wrap; }
  [data-consent-banner] button[data-consent-save] { flex: 1 0 100%; }
  [data-consent-banner] .consent-banner__purposes { max-height: 60vh; overflow-y: auto; overscroll-behavior: contain; }
}
/* 320-359 px: three controls still fit one row only without their letter-spacing. */
@media (max-width: 359px) {
  [data-consent-banner] .consent-banner__actions { gap: 4px; }
  [data-consent-banner] .consent-banner__actions button { padding: 0 6px; letter-spacing: 0; }
  [data-consent-banner] button[data-consent-manage] { padding: 0 2px; letter-spacing: 0; }
}
/* Tablets: the sentence takes its own row, the controls sit under it at their natural width —
   Accept and Reject stretched to 340 px on an 899-px viewport when they shared the row's leftover. */
@media (min-width: 600px) {
  [data-consent-banner] button[data-consent-accept],
  [data-consent-banner] button[data-consent-reject] { flex: 0 1 auto; }
}
/* Desktop: one row — sentence left, controls right, 36 px controls, 12 px of air above and below. */
@media (min-width: 900px) {
  [data-consent-banner] { padding-top: 12px; padding-bottom: calc(12px + env(safe-area-inset-bottom)); }
  [data-consent-banner] .consent-banner__inner { gap: 8px var(--s-6); }
  [data-consent-banner] .consent-banner__body { flex: 1 1 200px; line-height: 1.4; }
  [data-consent-banner] .consent-banner__actions { flex: 0 0 auto; gap: 8px; margin-left: auto; }
  /* Once the panel is open the controls have their own row under it: left-aligned with everything
     else rather than stranded against the right edge. */
  [data-consent-banner] [data-consent-manage-panel]:not([hidden]) ~ .consent-banner__actions { margin-left: 0; }
  [data-consent-banner] .consent-banner__actions button,
  [data-consent-banner] button { min-height: 36px; height: 36px; padding: 0 14px; letter-spacing: .08em; }
  [data-consent-banner] button[data-consent-accept],
  [data-consent-banner] button[data-consent-reject] { flex: 0 0 auto; }
}
[data-consent-banner] .consent-banner__purposes {
  flex: 1 0 100%;
  list-style: none; margin: var(--s-3) 0 0; padding: var(--s-3) 0 0;
  border-top: 1px solid var(--c-line); display: flex; flex-direction: column; gap: var(--s-3);
}
[data-consent-banner] .consent-purpose__label { display: flex; align-items: center; gap: var(--s-3); min-height: var(--target-min); font-size: var(--fs-body); font-weight: 500; cursor: pointer; }
[data-consent-banner] .consent-purpose--fixed .consent-purpose__label { cursor: default; justify-content: flex-start; min-height: 0; }
[data-consent-banner] .consent-purpose__state { margin-left: auto; color: var(--c-ink-mute); font-size: var(--fs-label-s); letter-spacing: var(--tr-label-s); text-transform: uppercase; }
[data-consent-banner] .consent-purpose__desc { color: var(--c-ink-soft); font-size: var(--fs-body-s); margin: 0 0 0 32px; line-height: 1.45; }
[data-consent-banner] .consent-purpose--fixed .consent-purpose__desc { margin-left: 0; }
[data-consent-banner] .consent-purpose__input { width: 20px; height: 20px; flex: none; accent-color: var(--c-ink); }
[data-consent-banner] [data-consent-manage-panel] label { display: flex; align-items: center; gap: var(--s-2); min-height: var(--target-min); text-transform: none; letter-spacing: normal; font-size: var(--fs-body); }
[data-consent-banner] :focus-visible { outline: var(--focus-ring); outline-offset: 2px; }

/* ---------------------------------------------------------------- email signup + inquiry
   Markup from `shell/newsletter.ts` and `forms/actions/inquiry.ts`. One ask per placement; the
   disclosure is part of the control and gets body-size type at body contrast. */
.signup-inline {
  border: 1px solid var(--c-line); background: var(--c-paper-raised);
  padding: var(--s-5); margin-top: var(--s-8); max-width: var(--max-narrow);
}
@media (min-width: 720px) { .signup-inline { padding: var(--s-6); } }
.signup-inline__title { font-family: var(--font-display); font-size: var(--fs-display-s); line-height: var(--lh-display-s); font-weight: 400; margin: 0; }
.signup-inline__lede { color: var(--c-ink-soft); margin-top: var(--s-2); max-width: 54ch; }
.signup-inline__incentive { margin-top: var(--s-3); font-size: var(--fs-label-s); letter-spacing: var(--tr-label-s); text-transform: uppercase; color: var(--c-gilt-deep); font-weight: 500; }
.signup-inline__form, .inquiry-form { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-4); align-items: stretch; }
.signup-inline__field, .inquiry-form__field {
  flex: 1 1 240px; min-width: 0; min-height: 48px; padding: 0 var(--s-4);
  border: 1px solid var(--c-line-strong); background: var(--c-paper-raised); color: var(--c-ink);
}
.signup-inline__field:focus-visible, .inquiry-form__field:focus-visible { outline: var(--focus-ring); outline-offset: 2px; border-color: var(--c-ink); }
.signup-inline__submit, .inquiry-form__submit { flex: 0 0 auto; }
@media (max-width: 479px) {
  .signup-inline__field, .inquiry-form__field, .nl-form input[type="email"] { flex-basis: 100%; }
  .signup-inline__submit, .inquiry-form__submit, .nl-form .btn { width: 100%; }
}
.signup-inline__disclosure, .signup-inline__note, .inquiry-form__note, .form-signup-disclosure, .form-signup-note {
  margin-top: var(--s-3); font-size: var(--fs-body-s); line-height: 1.55; color: var(--c-ink-soft); max-width: 62ch;
}
.signup-inline__note, .form-signup-note { color: var(--c-ink-mute); }
.inquiry-form__note { flex-basis: 100%; margin-top: var(--s-2); }
.inquiry-form { max-width: var(--max-narrow); }
.band-night .signup-inline { background: var(--c-paper-raised); }

/* ---------------------------------------------------------------- page hooks (replace former inline styles) */
.shop-body { padding-bottom: var(--section-y); }
.shop-results { min-width: 0; }
.hero-cta--center { justify-content: center; margin-top: var(--s-6); }
.contact-title { max-width: 18ch; margin-bottom: var(--s-4); }
.split h1 { margin-bottom: var(--s-4); }
.center h2 + .about-quote { margin-top: var(--s-5); }
.field .check-row { align-items: flex-start; }
.field .check-row input[type="checkbox"] { margin-top: 2px; }
/* JS-rebuilt gallery path (product.js renderMain): the whole frame is the enlarge control */
.gallery-zoom-btn { display: block; width: 100%; height: 100%; padding: 0; cursor: zoom-in; }
.gallery-zoom-hint {
  position: absolute; right: var(--s-3); bottom: var(--s-3); width: var(--target-min); height: var(--target-min);
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(250, 248, 244, .9); border: 1px solid var(--c-line); border-radius: var(--r-pill);
}

/* ---------------------------------------------------------------- utility pages (server 404, preferences) */
.error-page { padding-block: var(--section-y); }
.error-page .hero-cta { margin-top: var(--s-6); }

/* ---------------------------------------------------------------- responsive rhythm */
@media (max-width: 599px) {
  .section { padding-block: clamp(40px, 8vw, 64px); }
  .section-sm { padding-block: clamp(24px, 6vw, 40px); }
}
/* landscape phones (844x390): the whole height budget is 390px */
@media (max-height: 460px) and (orientation: landscape) {
  .section { padding-block: clamp(28px, 9vh, 56px); }
  .hero-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s-6); padding-block: var(--s-5); }
  .hero-media { max-height: 72vh; }
  .gallery-main { max-height: 72vh; }
  [data-consent-banner] { max-height: 62vh; }
}

/* long unbroken strings (a merchant title, a gemstone list, a URL) wrap rather than push the page */
.pcard-title, .spec-row dd, .prose, .lede, .empty-state, .crumbs { overflow-wrap: break-word; }

/* visible focus on media-only controls */
.pcard:focus-visible, .tile:focus-visible, .gallery-thumbs button:focus-visible, .search-hit:focus-visible,
.line-row:focus-visible, .outbound-card:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-offset); }

@media print {
  .site-header, .announce, .drawer, .scrim, [data-consent-banner], .site-footer, .proto-note, .gallery-nav, .gallery-zoom { display: none !important; }
}

/* ==========================================================================
   Design fine-tune (2026-09-07): desktop rhythm, the funnel chapters, editorial photography.
   Rationale: execution/pilot/evidence/storefront/fine-tune/DESIGN_NOTES.md. Mobile rules above are
   untouched except where a rule below is explicitly scoped to a min-width breakpoint.
   ========================================================================== */

/* ---------------------------------------------------------------- editorial photograph frames
   A merchant campaign photograph inside an editorial frame: cover-cropped around `--focal`, no
   padding, no white ground (the photographs are not shot on white). The frame keeps its 4:5. */
.media--photo { background: var(--c-paper-sunk); }
.media--photo img { object-fit: cover; object-position: var(--focal, 50% 40%); padding: 0; width: 100%; height: 100%; }
.split-media.media--photo { max-height: none; }
@media (max-width: 899px) { .split-media.media--photo { aspect-ratio: 4 / 5; max-height: 62vh; } }
@media (min-width: 900px) {
  /* Beside a column of copy the photograph reads better a touch shorter than the product frames. */
  .split-media.media--photo { aspect-ratio: 5 / 6; }
}

/* ---------------------------------------------------------------- proof row right after the hero */
.hero + .section-sm { border-bottom: 1px solid var(--c-line); }
@media (min-width: 900px) {
  .hero + .section-sm .trust-row { gap: var(--s-6) var(--s-7); }
}

/* ---------------------------------------------------------------- curated row (desktop)
   The discovery grid is one horizontal, snap-scrolling row from 768: four pieces in view on a
   desktop, three on a tablet, arrows and keyboard from home.js, plain scrolling without it. On a
   phone nothing changes — the two-up grid the owner accepted stays. */
.flow-head__aside { display: flex; align-items: center; gap: var(--s-4); }
.row-controls { display: flex; gap: var(--s-2); }
.row-controls[hidden] { display: none; }
.row-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--target-min); height: var(--target-min); border-radius: var(--r-pill);
  border: 1px solid var(--c-line-strong); color: var(--c-ink); background: var(--c-paper-raised);
  transition: border-color var(--d-fast) var(--e-standard), opacity var(--d-fast) var(--e-standard);
}
.row-btn svg { width: 14px; height: 14px; }
.row-btn:hover { border-color: var(--c-ink); }
.row-btn:disabled { opacity: .35; cursor: default; }
.row-scroll { outline-offset: 6px; }
@media (min-width: 768px) {
  .row-scroll {
    overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory;
    scrollbar-width: none; -ms-overflow-style: none;
    margin-inline: calc(var(--gutter) * -1); padding-inline: var(--gutter); padding-bottom: var(--s-2);
    scroll-padding-inline: var(--gutter);
  }
  .row-scroll::-webkit-scrollbar { display: none; }
  .grid-products.curated-row { display: flex; gap: var(--grid-gap); }
  .grid-products.curated-row > .pcard { flex: 0 0 calc((100% - 2 * var(--grid-gap)) / 3); scroll-snap-align: start; }
  .grid-products.curated-row > .empty-state { flex: 1 1 100%; }
}
@media (min-width: 1024px) {
  .grid-products.curated-row > .pcard { flex-basis: calc((100% - 3 * var(--grid-gap)) / 4); }
}
@media (min-width: 1440px) {
  .grid-products.curated-row > .pcard { flex-basis: calc((100% - 3.5 * var(--grid-gap)) / 4.5); }
}
/* The same row on the landing page's edit (desktop). */
@media (min-width: 640px) {
  .lp-edit-row.row-scroll { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; margin-inline: calc(var(--gutter) * -1); padding: 0 var(--gutter) var(--s-2); gap: var(--grid-gap); }
  .lp-edit-row.row-scroll::-webkit-scrollbar { display: none; }
  .lp-edit-row.row-scroll > .lp-card { flex: 0 0 calc((100% - 2 * var(--grid-gap)) / 3); scroll-snap-align: start; }
}
@media (min-width: 1024px) { .lp-edit-row.row-scroll > .lp-card { flex-basis: calc((100% - 3 * var(--grid-gap)) / 4); } }

/* ---------------------------------------------------------------- the email chapter
   One ask, as a chapter: the inline aside centred in a sunk band (home, landing), or quiet inside
   a column (PDP, below the specifications). `[data-offer-slot]` is empty until the offers lane
   renders into it; it takes no space while empty. */
.chapter-email__inner { display: flex; flex-direction: column; align-items: center; }
.chapter-email__eyebrow { margin-bottom: var(--s-4); }
.chapter-email .signup-inline { margin-top: 0; width: 100%; max-width: 640px; }
.chapter-email .signup-inline__lede { max-width: none; }
.signup-inline__offer:empty { display: none; }
/* The reveal is a card with its own frame (see "the welcome-offer reveal" block at the end of this
   file); the slot only spaces it, so the card's border is not doubled by a rule above it. */
.signup-inline__offer { margin-top: var(--s-5); }
.chapter-email--quiet { margin-top: var(--s-7); }
.chapter-email--quiet .signup-inline { margin-top: 0; padding: var(--s-5); max-width: none; background: var(--c-paper); }
.chapter-email--quiet .signup-inline__title { font-size: 1.125rem; }
.chapter-email--quiet .signup-inline__lede { font-size: var(--fs-body-s); }
.chapter-email--quiet .signup-inline__note, .chapter-email--quiet .signup-inline__disclosure { font-size: 0.8125rem; }
@media (min-width: 720px) { .chapter-email--quiet .signup-inline { padding: var(--s-5); } }
/* Two sunk bands in a row read as one: give the chapter paper when it follows a sunk band. The home
   page wraps the early offer chapter in `.offer-anchor` (the dialog's scroll target), so the rule
   must also see through that wrapper — without it Trending and the offer sat as two sunk bands with
   doubled padding and no edge between them (owner, 2026-09-08). */
.band-sunk + .chapter-email,
.band-sunk + .offer-anchor > .chapter-email { background: var(--c-paper); border-top: 1px solid var(--c-line); }
/* …and close the seam: the sunk band above already carries its own bottom padding. */
.band-sunk + .offer-anchor > .chapter-email { padding-top: calc(var(--section-y) * .55); }
.offer-anchor { display: block; margin: 0; }

/* ---------------------------------------------------------------- FAQ chapter */
.chapter-faq .eyebrow + h2 { margin-bottom: var(--s-6); }
.faq .acc:first-child { border-top: 1px solid var(--c-line); }
.faq .acc-btn { font-family: var(--font-display); font-size: 1.1875rem; line-height: 1.3; letter-spacing: 0; text-transform: none; font-weight: 400; min-height: 60px; padding: var(--s-3) 0; }
.faq .acc-panel { font-size: var(--fs-body); color: var(--c-ink-soft); padding-bottom: var(--s-5); max-width: var(--max-prose); }
.faq .acc-panel p + p { margin-top: var(--s-2); }
.faq .acc-btn:hover .chev { color: var(--c-ink); }

/* ---------------------------------------------------------------- final chapter */
.chapter-final { border-top: 1px solid var(--c-line); }
.chapter-final h2 { max-width: 20ch; }
.chapter-final .hero-cta { margin-top: var(--s-6); }

/* ---------------------------------------------------------------- landing templates: more pieces */
.lt-more { border-top: 1px solid var(--c-line); }
.lt-card-title.link-u { display: inline-flex; align-items: center; min-height: var(--target-min); }

/* ---------------------------------------------------------------- measure: never past 70ch */
.wrap-narrow > p, .wrap-narrow .lede, .lt-claims, .lt-claim, .lt-hero-body .lede, .lp-hero-copy .lede, .center .lede { max-width: var(--max-prose); }
.lt-claims li, .lt-references li, [data-lt-section="collection"] .lede, .line-row > div, .contact-layout p, .addr-note, .reason-list dd { max-width: var(--max-prose); }
.chapter-final .lede, .center.wrap-narrow .lede { margin-inline: auto; }

/* ---------------------------------------------------------------- desktop rhythm */
@media (min-width: 1024px) {
  .section-head .lede, .flow-head .lede { max-width: 56ch; }
  .split > div:not(.split-media) { padding-inline: clamp(0px, 1.5vw, 24px); }
  .site-footer { padding-block: var(--s-9) var(--s-7); }
  .footer-grid { gap: var(--s-6) var(--s-8); }
  .shop-head { padding-block: var(--s-8) var(--s-7); }
  .contact-layout { gap: clamp(48px, 8vw, 128px); }
  .outbound-grid { gap: var(--s-5); grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (min-width: 1440px) {
  .section { padding-block: clamp(80px, 6.5vw, 112px); }
  .section-sm { padding-block: var(--s-8); }
}
/* Hover states on a pointer device: quiet underlines, never colour. */
@media (hover: hover) {
  .tile:hover .tile-cap h3, .pcard:hover .pcard-title { text-decoration: underline; text-underline-offset: 4px; text-decoration-color: var(--c-line-strong); }
  .footer-col a:hover { text-decoration: underline; text-underline-offset: 4px; text-decoration-color: var(--c-line-strong); }
}

/* ---------------------------------------------------------------- landing templates (lt-*)
   `src/pages/landing-templates.ts` never had rules of its own: the hero image rendered at its full
   1400px, the proof blocks and claims ran as bare lines. Same components as the rest of the site. */
.lt-hero { display: grid; gap: var(--s-6); align-items: center; }
.lt-hero-body { min-width: 0; }
.lt-hero-body h1 { margin-top: var(--s-3); max-width: 16ch; }
.lt-hero-body .lede { margin-top: var(--s-4); }
.lt-hero-media { aspect-ratio: var(--ar-product-hero); max-height: 52vh; order: -1; }
.lt-hero-media img { padding: 4%; }
@media (min-width: 900px) {
  .lt-hero { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(32px, 6vw, 96px); }
  .lt-hero-media { order: 0; max-height: 600px; max-width: 520px; justify-self: end; }
}
.lt-proof-grid { display: grid; gap: var(--s-5) var(--s-6); grid-template-columns: repeat(2, minmax(0, 1fr)); border-top: 1px solid var(--c-line); padding-top: var(--s-5); }
@media (min-width: 768px) { .lt-proof-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.lt-proof-value { font-family: var(--font-display); font-size: var(--fs-display-s); line-height: var(--lh-display-s); margin-top: var(--s-2); }
.lt-proof-detail { font-size: var(--fs-body-s); margin-top: var(--s-1); max-width: 30ch; }
[data-lt-section="body"] .lede:last-child { margin-bottom: 0; }
.lt-cta { max-width: 420px; }
.lt-cta-price { margin-top: var(--s-3); }
.lt-claims { display: grid; gap: var(--s-4); }
.lt-claim { border-top: 1px solid var(--c-line); padding-top: var(--s-4); }
.lt-claim-statement { font-weight: 500; }
.lt-claim-detail, .lt-claim-attribution, .lt-claim-source { font-size: var(--fs-body-s); margin-top: var(--s-1); }
.lt-claim-source { overflow-wrap: anywhere; }
[data-lt-section="trust"] > p { margin-top: var(--s-5); }
.lt-card-grid { display: grid; gap: var(--grid-gap-y) var(--grid-gap); grid-template-columns: repeat(1, minmax(0, 1fr)); }
@media (min-width: 640px) { .lt-card-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .lt-card-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.lt-card { display: flex; flex-direction: column; gap: var(--s-2); padding: var(--s-5); border: 1px solid var(--c-line); background: var(--c-paper-raised); }
.lt-card-title { font-family: var(--font-display); font-size: 1.125rem; line-height: 1.3; color: var(--c-ink); }
.lt-card-note { font-size: var(--fs-body-s); }
.lt-card .lt-cta { margin-top: auto; padding-top: var(--s-3); }
.lt-references { display: grid; gap: var(--s-3); }
.lt-references li { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-3); align-items: baseline; font-size: var(--fs-body); }
.lt-references li a { min-height: var(--target-min); display: inline-flex; align-items: center; }
.lt-concierge-form { max-width: var(--max-narrow); }

/* ==== fine-tune: home begin ==== */
/* Design fine-tune 2 (2026-09-07), lane C: the desktop home page as one path — proof inside the
   hero, one repeated ask, chapters that open the same way — with the poolside photograph showing
   the face and the headline in full on every desktop window, including a 1440×800 laptop with the
   consent notice up. Every rule below is scoped to a desktop breakpoint except the two that hide
   desktop-only markup on phones, so the phone page is unchanged. */

/* REMOVED 2026-09-07 (funnel wave): `.trust-row--inline`, the hairline strip of service titles
   ("30-day returns" among them) that sat directly under "Explore the collection", together with the
   `.hero + .section-sm { display: none }` rule that hid the real band because of it. The owner asked
   for the strip to go. The verified service facts now render ONCE, restrained, beside the pieces
   (`.chapter-service`), with their bodies and their policy links — which the strip never carried.
   Do not reintroduce a claims strip inside the hero. */

/* THE SPLIT HERO on a desktop window (funnel wave: balance, wrapping, button spacing).
   Before: a 5fr/7fr grid gave a 1280 window a 455-px copy column, in which a 60-px "Wearing Freedom"
   ran onto a third line and the two buttons could not share a row. The copy column is now the equal
   half of the pair up to 1440 and the larger share of a 7/8 split above it, and the headline size is
   pinned per band so the wrap is chosen rather than discovered. The photograph keeps its cover crop
   and its focal point (`schemas/hero-media.ts`, 46% 30% — face, hands, jewellery). */
@media (min-width: 1024px) {
  .hero--lifestyle .hero-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .hero-media--lifestyle { max-height: min(70vh, 640px); }
  /* 52 px at 1024–1279: a 445-px column breaks "The Art of / Wearing Freedom" in two, which is the
     wrap the headline is written for. 12ch stops it reaching for a third line. */
  .hero--lifestyle .hero-copy h1 { font-size: min(var(--fs-display-xl), 3.25rem); max-width: 12ch; }
  /* Button spacing: a wider column gap than the row gap, so a pair that wraps still reads as a pair
     and a pair that does not is never crowded. */
  .hero--lifestyle .hero-cta { gap: var(--s-3) var(--s-4); margin-top: var(--s-6); }
}
@media (min-width: 1280px) {
  .hero--lifestyle .hero-copy h1 { font-size: min(var(--fs-display-xl), 3.625rem); max-width: 13ch; }
}
@media (min-width: 1440px) {
  /* The wide page: 7/8 (was 6/8) is ~50 px more measure — enough for both buttons on one line at
     1440 — while the photograph stays a campaign frame rather than a column-height thumbnail. */
  .hero--lifestyle .hero-grid { grid-template-columns: minmax(0, 7fr) minmax(0, 8fr); padding-block: 40px; }
  .hero--lifestyle .hero-copy h1 { font-size: min(var(--fs-display-xl), 4rem); max-width: 14ch; }
  .hero--lifestyle .hero-cta { gap: var(--s-4) var(--s-5); }
}
/* A short desktop window (a 1440×800 or 1366×768 laptop): the copy column must clear a bottom
   consent notice, so the grid tightens rather than letting the buttons sink under it. */
@media (min-width: 1024px) and (max-height: 820px) {
  .hero--lifestyle .hero-grid { padding-block: var(--s-5) var(--s-6); align-items: start; }
  .hero-media--lifestyle { max-height: min(66vh, 560px); }
  .hero--lifestyle .hero-copy { padding-top: var(--s-4); }
  .hero--lifestyle .hero-copy .lede { margin-top: var(--s-4); }
  .hero--lifestyle .hero-cta { margin-top: var(--s-5); }
}

/* PHONES: a deliberate hero crop, so the first screen carries the photograph AND both actions.
   The 4:5 frame at 62vh put the buttons ~520 px below a 390-px phone's fold; 42vh is a considered
   landscape-ish crop of the same photograph around the same focal point (46% 30%, which is where
   the sunglasses and the hands are), and it leaves room for the headline, the lede, the shopping
   button and the offer button above 844 px. 320 keeps a floor so the crop never becomes a band. */
@media (max-width: 899px) {
  .hero-media--lifestyle { max-height: max(30vh, 190px); }
  .hero--lifestyle .hero-grid { gap: var(--s-4); padding-block: var(--s-3) var(--s-6); }
  .hero--lifestyle .hero-copy .lede { margin-top: var(--s-3); }
  .hero--lifestyle .hero-cta { margin-top: var(--s-4); }
}
/* Below 480 the two hero buttons stack full-width (108 px of stack), so the crop has to give up a
   little more room: BOTH actions must clear the consent notice on first paint, not just the black
   one. 26vh is 219 px of photograph on a 390 x 844 phone — a deliberate 1.6:1 crop around the same
   focal point, with the sunglasses and the hands still in frame. */
@media (max-width: 479px) {
  .hero-media--lifestyle { max-height: max(26vh, 180px); }
  .hero--lifestyle .hero-grid { gap: var(--s-3); }
  .hero--lifestyle .hero-cta { margin-top: var(--s-3); }
}
/* MEASURED, not guessed (`test/visual/funnel-shots.mjs`, 390 x 844): with a 38vh crop the shopping
   button's top sat at 758 and the consent notice's at 749 — the notice covered the page's one
   action on first paint. At 30vh the button pair is at 690-738, clear of the notice, and the
   photograph is still 253 px of a 844-px screen. At 320 the copy alone is taller than the room a
   112-px (three-button) notice leaves, so the headline and lede step down one size there too. */
@media (max-width: 359px) {
  .hero--lifestyle .hero-copy h1 { font-size: 2.125rem; }
  .hero--lifestyle .hero-copy .lede { font-size: var(--fs-body-s); }
  .hero-media--lifestyle { max-height: max(25vh, 170px); }
}

/* ---------------------------------------------------------------- the funnel's own chapters */

/* Offer placements. This funnel owns the POSITION; `shell/offer.ts` owns what renders there and
   `offer.css` carries the component's own styles. Only the two POSITIONAL rules the funnel is
   responsible for live here — the announcement inside the hero copy, and the second chapter's band. */
/* (a) The hero-adjacent announcement sits under the two hero actions, left-aligned on the copy's
   own measure rather than centred across a strip. Higher specificity than `offer.css`'s own rule,
   so the component is unchanged everywhere else it appears. */
.hero-copy .offer-announce { justify-content: flex-start; margin-top: var(--s-4); max-width: 44ch; }
/* (c) Two chapters, one component: the second must not read as a repeat of the first, so its band is
   paper rather than sunk ground. `offer.css` already quietens the aside itself
   (`.offer-inline--story`); its hairline would double the band edge here, so it stands down. */
.chapter-email:has(.offer-inline--story) {
  background: var(--c-paper); border-top: 1px solid var(--c-line); border-bottom: 1px solid var(--c-line);
}
/* (2026-09-08) The old hairline compensation `.offer-inline--story { border-top:0; padding-top:0 }` is gone:
   the card now carries one ink frame with even padding (offer.css), and stripping its top edge is what
   made it look cut off under Trending. */
/* The hero's second button jumps here; keep the landing clear of the sticky header. */
#welcome-offer { scroll-margin-top: calc(var(--header-h) + var(--s-5)); }

/* The featured piece. A restrained square — 520 px at most on a desktop, inside the owner's
   480–600 range — and always a PRODUCT frame: `home.ts` renders nothing at all rather than
   substituting a campaign photograph for the piece the section names. */
@media (min-width: 900px) {
  .chapter-featured .split:has(> .split-media:first-child) { grid-template-columns: minmax(0, 520px) minmax(0, 1fr); }
  .chapter-featured .split-media { max-width: 520px; max-height: 520px; }
}
.featured-meta { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; margin-top: var(--s-5); }
.featured-price { font-family: var(--font-ui); font-size: var(--fs-body-l); font-weight: 500; color: var(--c-ink); font-variant-numeric: tabular-nums lining-nums; }
.featured-title { margin-top: var(--s-2); font-size: var(--fs-body-s); color: var(--c-ink-mute); max-width: 46ch; }

/* Client care, restrained: this is where the hero strip went — beside the pieces, with a visible
   heading, each claim's body and its policy link. */
.chapter-service { border-top: 1px solid var(--c-line); }
.chapter-service .eyebrow { margin-bottom: var(--s-2); }
.chapter-service h2 { margin-bottom: var(--s-6); }
.chapter-service .trust-item p { font-size: 0.8125rem; }
/* A policy link is a real destination, so it gets a real target (WCAG 2.5.8): the 66 x 15 hit area
   the audit measured is not one. `align-self: flex-start` keeps the underline where it was. */
.chapter-service .trust-item .link-u { display: inline-flex; align-items: center; min-height: var(--target-min); }

/* "Named for where they take you": every line with a piece gets a tile on desktop (up to five);
   phones keep the accepted three — the fourth and fifth are marked by the renderer. */
@media (max-width: 899px) {
  .tile--desktop-only { display: none; }
}

/* One ask: the curated row's aside is the black button on desktop and the CMS text link on phones
   (both in the markup; one shows per breakpoint). The arrows are in the markup from the start and
   only exist where the row scrolls (768+). */
.flow-head__cta { display: none; }
.row-controls { display: none; }
@media (min-width: 768px) {
  .row-controls { display: flex; }
}
@media (min-width: 1024px) {
  .flow-head__cta { display: inline-flex; }
  .flow-head__link { display: none; }
  /* The campaign band and the house band each keep exactly one door on desktop. */
  .split .hero-cta > .btn + .btn { display: none; }
}

/* Chapter grammar: every chapter opens eyebrow → H2 → lede at the same x. The final chapter reads as
   the end — more air above, one line of lede, buttons centred. The lede is a desktop addition;
   phones keep the accepted final chapter unchanged. */
@media (max-width: 1023px) {
  .chapter-final .lede { display: none; }
}
@media (min-width: 1024px) {
  .chapter-final { padding-top: calc(var(--section-y) + 32px); }
  .chapter-final .lede { margin-top: var(--s-4); }
  .chapter-final .hero-cta { margin-top: var(--s-7); }
}
@media (min-width: 1440px) {
  .chapter-final { padding-top: calc(clamp(80px, 6.5vw, 112px) + 32px); }
}
/* ==== fine-tune: home end ==== */
/* ==== fine-tune: tiles begin ==== */
/* Owner item 3, desktop only ("the padding between the pieces and the photographs is wrong").
   Every product frame was a fixed 4:5 box with `object-fit: contain`. The merchant's fixture
   photographs are square (199 of 200) and the live catalogue is mostly 3:4 portrait, so a square
   inside a 4:5 frame left a measured 20-21 % white band above and below the jewel — up to five
   times the side margin — and neighbouring pieces read at visibly different scales.

   The fix is one attribute and a few rules. `data-ratio` (`LANES.md` frame contract) is emitted
   server-side on every frame from the photograph's own width/height, AND on the grid itself from
   the shape most of that grid's photographs have. The GRID wins where it declares one: a row whose
   frames were individually shaped would go ragged the moment one piece was square among portraits
   (the live catalogue is 40 portrait / 5 square / 1 landscape / 2 unphotographed), and a caption
   sitting 20 % higher than its neighbour's reads as a bug. The minority shape keeps `contain`
   inside the row's frame — the old look for a handful of tiles, never a clipped piece.

   Phones are untouched: the owner accepted the mobile grid as it is. */
@media (min-width: 1024px) {
  .pcard-media { aspect-ratio: var(--frame-ar, var(--ar-product-card)); }
  /* OWNER (2026-09-08, late evening): the home page's own Trending row was the one grid still
     missing this ruling — `pages/home.ts`'s `renderDiscovery` now stamps the majority `data-ratio`
     on `.curated-row` exactly as the shop grid and the PDP's related row already do, so a square
     photograph among Trending's portraits no longer sits in its own shorter box. A row with no
     ruling at all (a one-off home/landing band) still falls back to each card's own shape here. */
  .pcard-media[data-ratio="square"] { --frame-ar: 1; }
  /* The grid ruled: every frame in it agrees, including the ones with no photograph at all. */
  .grid-products[data-ratio] .pcard-media { --frame-ar: var(--grid-frame-ar); }
  .grid-products[data-ratio="square"] { --grid-frame-ar: 1; }
  .grid-products[data-ratio="portrait"],
  .grid-products[data-ratio="landscape"] { --grid-frame-ar: var(--ar-product-card); }

  /* A portrait photograph fills its portrait frame instead of letterboxing it: the crop is 6 % of
     height for a 3:4 and eats the photograph's own white margin, not the piece. Biasing the
     position up keeps a pendant's drop in frame. */
  .pcard-media[data-ratio="portrait"] img { object-fit: cover; object-position: 50% 45%; }
  /* ...but never inside a frame of a different shape — cropping 25 % of a piece to fit a square
     is worse than the white band this pass is removing. */
  .grid-products[data-ratio="square"] .pcard-media[data-ratio="portrait"] img { object-fit: contain; }
  /* Landscape is never covered: a cuff photographed wide has nothing to spare at its edges, so it
     stays contained (the default) inside whatever frame its row uses. */
  /* One inner margin, so the hairline never touches a jewel and the hover scale stays inside the
     box — but only on a frame whose shape is known. A frame with no dimensions from the catalogue
     keeps exactly the look it had (the contract's "no attribute, no change" rule); padding it
     would only add to the letterbox it already has. */
  .pcard-media[data-ratio] img,
  .grid-products[data-ratio] .pcard-media img { padding: 5%; }
  /* Square frames are ~20 % shorter, so the old row gap started reading as a gutter. */
  .grid-products { row-gap: calc(var(--grid-gap-y) - 8px); }
  /* Shop grid: the filter rail takes 232px of a 1200px wrap and the remaining 776px column does
     NOT grow past 1440, so four cards in it are 176px wide — a thumbnail, not a luxury grid.
     Three across is 243px at every desktop width. The PDP's related row has the whole wrap to
     itself and stays four across. */
  .grid-products[data-grid] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* The PDP gallery follows the same rule from the 900px two-column breakpoint. */
@media (min-width: 900px) {
  .gallery-main[data-ratio="square"] { aspect-ratio: 1; }
}
/* ==== fine-tune: tiles end ==== */
/* ==== fine-tune: email begin ==== */
/* The email chapter as the funnel's reward moment, and the welcome code as the reward.
   Owner item 10 ("I want to see how the coupon will look like — there is nothing currently").
   Before: the client-built reveal reached the page with NO styling at all — `[data-offer-slot]` is
   empty on a first render, so `shell/newsletter.ts`'s `<style data-offer-reveal-style>` was never
   emitted on the page where the code is actually earned, and the coupon rendered as body text
   (`fine-tune-2/email/before-home-revealed-w{390,1440}.png`). The design therefore lives HERE, in the
   always-present stylesheet, under two classes so it also wins over that inline block on the surfaces
   which do carry it (the no-JS `?nl=ok` render). Same card either way. */

/* --- the ask: one field, one line of promise ------------------------------------------------- */
@media (min-width: 1024px) {
  .chapter-email .signup-inline { max-width: 560px; }
}
.chapter-email .signup-inline__lede { max-width: 60ch; }
/* Disclosure and separation note are two paragraphs in the DOM (they are two different promises and
   `aria-describedby` points at the first); on screen they read as one quiet line under the field. */
.chapter-email .signup-inline__form { margin-bottom: var(--s-4); }
.chapter-email .signup-inline__disclosure,
.chapter-email .signup-inline__note {
  display: inline; margin-top: 0; font-size: 0.8125rem; line-height: 1.55; color: var(--c-ink-mute);
}
.chapter-email .form-note { margin-top: var(--s-4); }
.chapter-email .form-note:empty { margin-top: 0; }

/* --- the reward: the welcome code ------------------------------------------------------------- */
.signup-inline__offer .offer-reveal {
  margin: 0; padding: var(--s-5); text-align: left;
  background: var(--c-paper); border: 1px solid var(--c-line);
  border-top: 2px solid var(--c-gilt);
  animation: gp-offer-in var(--d-base) var(--e-enter) both;
}
.signup-inline__offer .offer-reveal__lede {
  margin: 0; font-family: var(--font-ui); font-size: var(--fs-label-s); line-height: var(--lh-label-s);
  font-weight: var(--fw-label-s); letter-spacing: var(--tr-label-s); text-transform: uppercase;
  color: var(--c-gilt-deep);
}
.signup-inline__offer .offer-reveal__code {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--s-3) var(--s-4); margin: var(--s-3) 0 0;
}
.signup-inline__offer .offer-reveal__value {
  font-family: var(--font-display); font-size: clamp(1.75rem, 1.45rem + 1vw, 2rem); line-height: 1.1;
  font-weight: 400; letter-spacing: 0.12em; color: var(--c-ink); overflow-wrap: anywhere;
  -webkit-user-select: all; user-select: all;
}
.signup-inline__offer .offer-reveal__copy {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--target-min); padding: 0 var(--s-4);
  font-family: var(--font-ui); font-size: var(--fs-label); line-height: 1.2; font-weight: 500;
  letter-spacing: 0.12em; text-transform: uppercase; cursor: pointer;
  border: 1px solid var(--c-ink); background: transparent; color: var(--c-ink);
  transition: background var(--d-fast) var(--e-standard), color var(--d-fast) var(--e-standard);
}
.signup-inline__offer .offer-reveal__copy:hover { background: var(--c-ink); color: var(--c-ink-invert); }
/* The button ships `hidden` and is unhidden only where `navigator.clipboard` exists: a laid-out
   `display` must never outrank that, or a no-JS page shows a button that cannot do anything. */
.signup-inline__offer .offer-reveal__copy[hidden] { display: none; }
.signup-inline__offer .offer-reveal__copy:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
.signup-inline__offer .offer-reveal__terms {
  margin: var(--s-4) 0 0; font-size: 0.8125rem; line-height: 1.55; max-width: 46ch;
  color: var(--c-ink-mute);
}
@keyframes gp-offer-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .signup-inline__offer .offer-reveal { animation: none; } }

/* --- the quiet variant (PDP, below the specifications): the same card, one step down ---------- */
.chapter-email--quiet .signup-inline__offer { margin-top: var(--s-4); }
.chapter-email--quiet .signup-inline__offer .offer-reveal {
  padding: var(--s-4); background: var(--c-paper-raised);
}
.chapter-email--quiet .signup-inline__offer .offer-reveal__value { font-size: 1.625rem; }
/* ==== fine-tune: email end ==== */
/* ==== fine-tune: landing begin ====
   Lane D (2026-09-07): landing page, landing templates, campaigns page, and the CSS half of the home
   line tiles + campaign band. Rationale: execution/pilot/evidence/storefront/fine-tune-2/landing/.
   Mobile (< 900) is untouched except the curated template's new photographs and the sold-out ask. */

/* ---------------------------------------------------------------- tiles (line tiles, form tiles, campaign collections)
   Column count follows the tile count instead of `auto-fit`, which grew two tiles to 540 px each on
   the live catalogue ("two huge pictures"). Never wider than 300 px, left-aligned under the section
   head, square from 900 px with a 6% inner margin so the hairline never touches a jewel. A 3:4
   photograph (the live catalogue's common case) is cover-cropped around its upper-middle. */
@media (min-width: 900px) {
  .grid-3 { grid-template-columns: repeat(3, minmax(0, 300px)); justify-content: start; }
  .grid-3:has(> :nth-child(4)) { grid-template-columns: repeat(4, minmax(0, 300px)); }
  .grid-3:has(> :nth-child(5)) { grid-template-columns: repeat(5, minmax(0, 300px)); }
  .grid-3 > .tile { max-width: 300px; }
  .tile-media { aspect-ratio: 1 / 1; }
  .tile-media img { padding: 6%; }
  .tile-media[data-ratio="portrait"] img { object-fit: cover; object-position: 50% 42%; }
  .tile-cap { padding-top: var(--s-3); }
  .tile-cap h3 { font-size: 1.125rem; }
}

/* ---------------------------------------------------------------- split bands (campaign band, story, construction, campaigns lead)
   The frame beside a column of copy is a 1:1 at most 460 px (it was a 509 × 600 4:5 letterboxing a
   square cut-out); the column template stops it growing at 1920. Real photographs (`.media--photo`)
   keep their 5:6 cover crop inside the same column. */
@media (min-width: 900px) {
  .split:has(> .split-media:first-child) { grid-template-columns: minmax(0, 460px) minmax(0, 1fr); }
  .split:has(> .split-media:last-child) { grid-template-columns: minmax(0, 1fr) minmax(0, 460px); }
  .split-media { aspect-ratio: 1 / 1; max-width: 460px; max-height: 460px; width: 100%; }
  .split-media img { padding: 6%; }
  .split-media[data-ratio="portrait"] img { object-fit: cover; object-position: 50% 42%; }
  .split-media.media--photo { aspect-ratio: 5 / 6; max-height: none; }
  .split-media.media--photo img { padding: 0; }
}

/* ---------------------------------------------------------------- landing page (lp-*), desktop
   Hero: a framed 1:1 at most 600 px beside the copy (the 1000w candidate is ≥ 1× on a retina screen
   at every width; the old half-viewport panel was 0.52× at 1920). Proof row under the buttons from
   1024 px replaces the separate trust band. */
.lp-proof-row { display: none; }
@media (min-width: 900px) {
  .lp-hero { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: center; padding-inline: max(var(--gutter), calc((100% - var(--max-page)) / 2)); }
  .lp-hero-media { order: 2; aspect-ratio: 1 / 1; width: 100%; max-width: 600px; max-height: 600px; justify-self: center; border: 1px solid var(--c-line); background: var(--c-paper-raised); margin-block: clamp(32px, 4vw, 64px); }
  .lp-hero-media img { padding: 6%; }
  .lp-hero-media[data-ratio="portrait"] img { object-fit: cover; object-position: 50% 42%; }
  .lp-hero-copy { padding-inline: 0 clamp(32px, 5vw, 80px); }
}
@media (min-width: 1024px) {
  .lp-trust-band, .lp-hero-copy .lp-trust-cue { display: none; }
  .lp-proof-row {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-4);
    list-style: none; margin: var(--s-6) 0 0; padding: var(--s-4) 0 0; border-top: 1px solid var(--c-line);
    font-family: var(--font-ui); font-size: var(--fs-label-s); letter-spacing: var(--tr-label-s); text-transform: uppercase; color: var(--c-ink-mute);
  }
  .lp-proof-row li { display: inline-flex; align-items: center; gap: var(--s-2); }
  .lp-proof-row li + li::before { content: "·"; color: var(--c-line-strong); margin-right: var(--s-2); }
  .lp-proof-row svg { width: 14px; height: 14px; flex: none; color: var(--c-gilt-deep); }
}
/* Facts: two DIFFERENT macro frames (or none — `.lp-facts--solo` centres the spec list alone). */
.lp-macro-frame { position: relative; width: 100%; aspect-ratio: 1 / 1; background: var(--c-paper-raised); border: 1px solid var(--c-line); overflow: hidden; }
.lp-macro-frame img { width: 100%; height: 100%; object-fit: contain; padding: 6%; display: block; }
.lp-macro-frame[data-ratio="portrait"] img { object-fit: cover; object-position: 50% 42%; }
@media (min-width: 900px) {
  .lp-macro-grid { max-width: 584px; }
  .lp-facts--solo { grid-template-columns: minmax(0, 1fr); }
  .lp-facts--solo > div { max-width: var(--max-narrow); }
}
/* The Orient edit: the featured card's frame is a 1:1 at most 440 px; the five cards sit in one row
   from 1024 px on the 1200 px measure and take their photograph's own ratio. */
@media (min-width: 760px) {
  .lp-featured { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
  .lp-featured-media { display: grid; place-items: center; padding: var(--s-4); }
  .lp-featured-media img { width: 100%; height: auto; aspect-ratio: 1 / 1; max-width: 440px; max-height: 440px; padding: 4%; }
  .lp-featured-media[data-ratio="portrait"] img { object-fit: cover; object-position: 50% 42%; }
  .lp-featured-body { padding: var(--s-6) var(--s-7); justify-content: center; }
}
@media (min-width: 1024px) {
  .lp-edit-row { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  .lp-edit-row.row-scroll > .lp-card { flex-basis: calc((100% - 4 * var(--grid-gap)) / 5); }
  .lp-card-media[data-ratio="square"] { aspect-ratio: 1 / 1; }
  .lp-card-media img { padding: 5%; }
  .lp-card-media[data-ratio="portrait"] img { object-fit: cover; object-position: 50% 42%; }
}
@media (min-width: 1024px) and (max-width: 1199px) {
  .lp-card .btn { padding-inline: var(--s-4); }
}
/* Landing rhythm: a paid-traffic page asks one thing at a time, so its chapters sit closer than the
   home page's (page ≤ 6,500 px at 1440; it was 7,752). Phones are untouched. */
@media (min-width: 1024px) {
  [data-page-type="landing"] .section { padding-block: clamp(44px, 3.6vw, 64px); }
  [data-page-type="landing"] .lp-final .lp-final-inner { padding-block: var(--s-7); }
}

/* ---------------------------------------------------------------- landing templates (lt-*): card photographs, sold-out ask */
.lt-card { padding: 0; }
.lt-card-media { display: block; aspect-ratio: var(--ar-product-card); border: 0; border-bottom: 1px solid var(--c-line); }
.lt-card-media img { padding: 5%; transition: transform var(--d-slow) var(--e-standard); }
.lt-card-body { display: flex; flex-direction: column; gap: var(--s-2); padding: var(--s-5); flex: 1; }
.lt-card .lt-cta { margin-top: auto; padding-top: var(--s-3); }
.lt-cta--sold .lt-cta-price { color: var(--c-ink-soft); }
@media (hover: hover) { .lt-card:hover .lt-card-media img { transform: scale(1.025); } }
/* Phones: photograph left, copy right — six full-width 4:5 frames made the page 8,300 px tall. */
@media (max-width: 639px) {
  .lt-card { flex-direction: row; align-items: stretch; }
  .lt-card-media { flex: 0 0 38%; aspect-ratio: auto; min-height: 150px; border-bottom: 0; border-right: 1px solid var(--c-line); }
  .lt-card-body { padding: var(--s-4); min-width: 0; }
  .lt-card .lt-cta { padding-top: var(--s-2); }
}
@media (min-width: 900px) {
  .lt-card-media { aspect-ratio: 1 / 1; }
  .lt-card-media img { padding: 6%; }
  .lt-card-media[data-ratio="portrait"] img { object-fit: cover; object-position: 50% 42%; }
}
/* ==== fine-tune: landing end ==== */

/* ==== production takeover (lane C, 2026-09-07): house pages, policies, lookbook ==== */
/* The house tiles (Campaigns / Films / Collections) share `.tile`; the grid only fixes the
   three-up shape above phones so the row never becomes a 4/1 orphan. */
.house-tiles { margin-top: var(--s-6); }
@media (min-width: 900px) { .house-tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.house-tiles .tile-media[data-ratio="landscape"] { aspect-ratio: 3 / 2; }
.house-tiles .tile-media[data-ratio="square"] { aspect-ratio: 1 / 1; }
.house-tiles .tile-media img, .house-gallery img, .film-poster img { object-fit: cover; }
.house-foot { margin-top: var(--s-6); font-size: var(--fs-body-s); max-width: var(--max-prose); }
.house-pager { display: flex; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; }

/* Campaign galleries: two columns on phones, three from tablets. Each frame takes the photograph
   ratio it was measured at (`data-ratio`): a landscape frame is 3:2, a portrait one 2:3. */
.house-gallery { display: grid; gap: var(--grid-gap); grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 900px) { .house-gallery { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.house-gallery__item { margin: 0; min-width: 0; }
.house-gallery .frame { aspect-ratio: 2 / 3; }
.house-gallery .frame[data-ratio="landscape"] { aspect-ratio: 3 / 2; }
.house-gallery .frame[data-ratio="square"] { aspect-ratio: 1 / 1; }
.house-gallery .frame img { transition: transform var(--d-slow) var(--e-standard); }
@media (hover: hover) { .house-gallery__item:hover .frame img { transform: scale(1.02); } }

/* Films: poster + Play. Nothing third-party loads until the button is pressed; the iframe then
   fills the same frame. The play control is a 64px target with a visible focus ring. */
.film-third-party { margin-top: var(--s-4); font-size: var(--fs-body-s); max-width: var(--max-prose); }
.film-grid { display: grid; gap: var(--s-7) var(--grid-gap); grid-template-columns: minmax(0, 1fr); margin-top: var(--s-6); }
@media (min-width: 900px) { .film-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.film { min-width: 0; }
.film-frame { position: relative; }
.film-poster { position: relative; width: 100%; aspect-ratio: 16 / 9; background: var(--c-ink); border: 1px solid var(--c-line); overflow: hidden; }
.film-poster img { width: 100%; height: 100%; }
.film-play {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  display: inline-flex; flex-direction: column; align-items: center; gap: var(--s-1);
  min-width: 96px; min-height: 64px; padding: var(--s-3) var(--s-4);
  background: rgba(250, 248, 244, .92); color: var(--c-ink); border: 1px solid var(--c-ink);
  font-family: var(--font-ui); font-size: var(--fs-label); letter-spacing: .12em; text-transform: uppercase; font-weight: 500;
  cursor: pointer;
}
.film-play:hover { background: var(--c-paper); }
.film-play__icon { width: 0; height: 0; border-left: 14px solid currentColor; border-top: 9px solid transparent; border-bottom: 9px solid transparent; margin-left: 3px; }
.film-play__note { font-size: 10px; letter-spacing: .08em; text-transform: none; color: var(--c-ink-mute); }
.film[data-film-playing] .film-play, .film[data-film-playing] .film-poster { display: none; }
.consent-reopen { color: inherit; }
.film-player { display: block; width: 100%; aspect-ratio: 16 / 9; border: 0; background: var(--c-ink); }
.film-fail { margin-top: var(--s-3); color: var(--c-ink-soft); font-size: var(--fs-body-s); }
.film-meta { padding-top: var(--s-3); }
.film-meta .label-s { margin-top: var(--s-1); }
.film-links { margin-top: var(--s-2); display: flex; gap: var(--s-4); flex-wrap: wrap; }
@media (prefers-reduced-motion: reduce) {
  .house-gallery .frame img, .house-tiles .tile-media img, .tile-media img { transition: none; }
  .house-gallery__item:hover .frame img, .tile:hover .tile-media img { transform: none; }
}

/* Client-care policies: a short document with its own in-section nav. */
.policy-layout { display: grid; gap: var(--s-6); }
@media (min-width: 900px) { .policy-layout { grid-template-columns: 220px minmax(0, 1fr); gap: clamp(32px, 6vw, 96px); align-items: start; } }
.policy-nav ul { list-style: none; margin: 0; padding: 0; display: flex; gap: var(--s-2) var(--s-4); flex-wrap: wrap; }
@media (min-width: 900px) { .policy-nav { position: sticky; top: var(--s-6); } .policy-nav ul { flex-direction: column; } }
.policy-nav a, .policy-nav span { font-family: var(--font-ui); font-size: var(--fs-label); letter-spacing: .12em; text-transform: uppercase; text-decoration: none; color: var(--c-ink-mute); display: inline-flex; align-items: center; padding: var(--s-2) 0; min-height: 44px; }
.policy-nav a:hover { color: var(--c-ink); }
.policy-nav [aria-current="page"] { color: var(--c-ink); border-bottom: 1px solid var(--c-gilt); }
.policy { max-width: 70ch; }
.policy .rule-gilt { margin-block: var(--s-5) var(--s-6); }
.policy-section + .policy-section { margin-top: var(--s-6); }
.policy-section h2 { margin-bottom: var(--s-3); }
.policy-section .prose { color: var(--c-ink-soft); line-height: 1.7; }
.policy-section .prose a { color: var(--c-ink); }
.policy-reviewed { margin-top: var(--s-7); font-size: var(--fs-body-s); }

/* About: the house story (founding / the Bochic woman / the designer) as a two-column chapter. */
.house-story { display: grid; gap: var(--s-6); margin-top: var(--s-6); }
@media (min-width: 900px) { .house-story { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(32px, 6vw, 96px); } }
.house-story h3 { margin-bottom: var(--s-3); }
.house-story .prose { color: var(--c-ink-soft); }
.house-story .label-s { display: block; margin-top: var(--s-3); }

/* Lookbook (web version). The print stylesheet makes each `.lb-page` a page of the PDF. */
.lb-page { padding-block: var(--section-y); border-top: 1px solid var(--c-line); }
.lb-page:first-child { border-top: 0; }
.lb-cover .hero-media { aspect-ratio: 3 / 2; }
.lb-cover-copy { padding-top: var(--s-6); }
.lb-grid { display: grid; gap: var(--grid-gap-y) var(--grid-gap); grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: var(--s-6); }
@media (min-width: 900px) { .lb-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.lb-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 900px) { .lb-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.lb-piece { min-width: 0; }
.lb-piece .pcard-media { aspect-ratio: 1 / 1; }
.lb-piece .pcard-media img { object-fit: contain; padding: 6%; }
.lb-piece h3 { margin-top: var(--s-3); font-size: var(--fs-body); line-height: 1.4; font-family: var(--font-display); font-weight: 400; }
.lb-piece .lb-price { color: var(--c-ink-soft); font-size: var(--fs-body-s); margin-top: var(--s-1); }
.lb-qr { display: flex; align-items: center; gap: var(--s-3); margin-top: var(--s-3); }
.lb-qr img { width: 56px; height: 56px; border: 1px solid var(--c-line); background: #fff; flex: none; }
.lb-qr span { font-size: var(--fs-body-s); color: var(--c-ink-mute); word-break: break-all; }
.lb-note { margin-top: var(--s-6); font-size: var(--fs-body-s); color: var(--c-ink-mute); max-width: var(--max-prose); }
@media print {
  [data-page-type="lookbook"] .site-header, [data-page-type="lookbook"] .announce, [data-page-type="lookbook"] .site-footer,
  [data-page-type="lookbook"] [data-consent-banner], [data-page-type="lookbook"] .drawer, [data-page-type="lookbook"] .scrim { display: none !important; }
  .lb-page { break-after: page; page-break-after: always; border-top: 0; padding-block: 0; }
  .lb-page:last-child { break-after: auto; page-break-after: auto; }
  .lb-grid, .lb-grid--4 { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
  .film-play { display: none; }
  .lb-page a { text-decoration: none; color: inherit; }
  .film-frame, .lb-piece, .tile, .house-gallery__item { break-inside: avoid; }
}
/* Campaign pager + archive links: 44px targets (the label-s link pattern is 15px tall on its own). */
.house-pager a, .house-pager span { display: inline-flex; align-items: center; min-height: 44px; }
/* Lookbook grids: a frame takes the photograph's own ratio (the campaign covers are landscape). */
.lb-grid .tile-media[data-ratio="landscape"] { aspect-ratio: 3 / 2; }
.lb-grid .tile-media[data-ratio="square"] { aspect-ratio: 1 / 1; }
.lb-grid .tile-media img { object-fit: cover; }

/* ------------------------------------------ the consent notice vs. the page's own primary action
   Lane C proposal P9, applied 2026-09-07 (lane F3). The responsive audit
   (`execution/pilot/evidence/storefront/takeover/shots/audit.json`) flagged
   `primaryActionCoveredByBanner` on `/product/` at 390 and 430: the notice is `position: fixed` at
   the bottom edge, so on a phone it sat on top of the PDP's hand-off button on first paint.

   Two rules, and deliberately no third:

   1. `--consent-notice-h` is the height the notice actually occupies (measured on the built page,
      not guessed: 95 px at 390/430 and 88 px from 600 up, both with the one-line copy). It is the
      one number the rules below share, so a change to the notice's own padding or button height is
      a single edit here rather than three.

   2. While the notice is OPEN, the document reserves that height at its end, and anything the
      browser scrolls into view stops short of it. That is what makes the buy button — and the
      footer's own "Cookie settings" control, which is the one thing a visitor who already accepted
      needs — reachable and fully clickable rather than half under a fixed bar. `:has()` carries the
      reservation, and its absence degrades to exactly today's behaviour rather than to a permanent
      gap: a browser without `:has()` support keeps the overlap it has now and loses nothing.

   What is deliberately NOT here: any rule that moves the CTA up the page, shortens the gallery, or
   pads content until the button falls below the fold. Those would clear the audit flag by changing
   where the button is rather than by making it usable, and the flag is a proxy for "can the visitor
   press this", not the thing itself. Measured after this change at 390 and 430 (the shots and their
   JSON are in `execution/pilot/release/f3-shots/`): the button is fully clear of the notice's band,
   and the page can be scrolled until it is. */
:root { --consent-notice-h: 88px; }
@media (max-width: 599px) { :root { --consent-notice-h: 95px; } }

html { scroll-padding-bottom: calc(var(--consent-notice-h) + var(--s-3)); }
body:has([data-consent-banner]:not([hidden])) { padding-bottom: var(--consent-notice-h); }

/* The PDP's buy column and the footer's withdrawal control are the two things that must never be
   half-covered; both get the notice's height as scroll margin so `scrollIntoView`, an in-page
   anchor and the keyboard's own focus scroll all stop above it. */
.pdp-cta, .pdp-cta .btn, .consent-reopen { scroll-margin-bottom: calc(var(--consent-notice-h) + var(--s-4)); }

/* The withdrawal control itself (consent-email PROPOSALS §6): a plain text button in the footer's
   own colour and size, underlined so it reads as an action, with a 24 px minimum target — it sits
   inside a line of legal text, where a 44 px block would break the line box. */
.consent-reopen {
  display: inline-flex; align-items: center; min-height: 24px;
  padding: 0; border: 0; background: none; cursor: pointer;
  font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit;
  text-decoration: underline; text-underline-offset: 3px;
}
.consent-reopen:hover { color: var(--c-ink); }

/* The per-line quantity limit under the price (`pages/product.ts` `renderPurchaseLimitNote`): the
   same quiet register as the dispatch sentence it follows, never a warning. */
.availability-limit { margin-top: var(--s-1); color: var(--c-ink-mute); font-size: var(--fs-body-s); }

/* ------------------------------------------------------------------------------------------------
   PDP purchase controls + description (owner instruction 2026-09-08, `pages/product.ts`)
   ------------------------------------------------------------------------------------------------ */
/* The made-to-order notice sits beside the action now, above the form. */
.pdp-cta > .availability-note { margin-bottom: var(--s-3); }
/* The bounded quantity control (only rendered when the allocation allows more than one). */
.qty-field { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-3); }
.qty-field label { font-size: var(--fs-label-s); letter-spacing: var(--tr-label-s); text-transform: uppercase; font-weight: 500; color: var(--c-ink-soft); }
.qty-field input {
  width: 5.5rem; min-height: 48px; border: 1px solid var(--c-line-strong); background: var(--c-paper-raised);
  padding: var(--s-3) var(--s-4); color: var(--c-ink); font-variant-numeric: tabular-nums;
}
.qty-field input:focus-visible { outline: var(--focus-ring); outline-offset: 2px; border-color: var(--c-ink); }
/* The photographed size, a fact in the same quiet register as the "Made to your size" line. */
.ring-size-fact { margin-top: var(--s-1); color: var(--c-ink-mute); font-size: var(--fs-body-s); }
/* The description: a preview paragraph, then a <details> holding the full source copy. The preview
   is hidden while the full copy is open (`:has()` needs no script; `data-expanded` is the mirror
   product.js keeps for older engines), so no sentence is read twice. */
.pdp-desc { margin: 0 0 var(--s-6); max-width: 66ch; }
.pdp-desc__preview { margin: 0; color: var(--c-ink-soft); line-height: 1.7; }
.pdp-desc:has(> .pdp-desc__more[open]) > .pdp-desc__preview,
.pdp-desc[data-expanded="true"] > .pdp-desc__preview { display: none; }
.pdp-desc__more > summary.pdp-desc__toggle {
  cursor: pointer; list-style: none; display: inline-flex; align-items: center;
  min-height: var(--target-min); margin-top: var(--s-1); color: var(--c-ink);
  font-size: var(--fs-body-s); text-decoration: underline; text-underline-offset: 3px;
}
.pdp-desc__more > summary.pdp-desc__toggle::-webkit-details-marker { display: none; }
.pdp-desc__more > summary.pdp-desc__toggle::marker { content: ""; }
.pdp-desc__more > summary.pdp-desc__toggle:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-offset); }
.pdp-desc__more > summary .pdp-desc__label-less { display: none; }
.pdp-desc__more[open] > summary .pdp-desc__label-more { display: none; }
.pdp-desc__more[open] > summary .pdp-desc__label-less { display: inline; }
.pdp-desc__full { color: var(--c-ink-soft); line-height: 1.7; overflow-wrap: anywhere; }
.pdp-desc__more[open] > .pdp-desc__full { padding-top: var(--s-2); }
.pdp-desc__full p { margin: 0 0 var(--s-3); }
.pdp-desc__full p:last-child { margin-bottom: 0; }
.pdp-desc__full strong, .pdp-desc__full b { font-weight: 500; color: var(--c-ink); }
.pdp-desc__full ul, .pdp-desc__full ol { margin: 0 0 var(--s-3); padding-left: var(--s-5); }
.pdp-desc__less { display: inline-flex; align-items: center; min-height: var(--target-min); margin-top: var(--s-2); color: var(--c-ink); font-size: var(--fs-body-s); }
.pdp-desc__less[hidden] { display: none; }
/* The featured piece's photograph is a link (home, D7). */
a.split-media--link { display: block; color: inherit; }
a.split-media--link:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-offset); }

/* ------------------------------------------------------------------------------------------------
   Ring sizing on the PDP (lane Q, 2026-09-07 — `execution/pilot/google/PROPOSALS-L.md` §3)

   Two elements, both small on purpose. 63 of the 75 cohort records are rings and every one imported
   as a single variant with no size option, so the page has to ask for a size in prose and in one
   field rather than in a variant selector that does not exist. `SIZE_FIT` is the most common return
   reason on the merchant's 1stDibs orders; this is the cheapest place to stop that.

   The line reads in the same quiet register as the dispatch sentence and the per-line limit it
   follows — it is information, not a warning, and it must not compete with the price.
   ------------------------------------------------------------------------------------------------ */
.ring-size-note { margin-top: var(--s-1); color: var(--c-ink-mute); font-size: var(--fs-body-s); }

/* The optional size field inside the buy form. It borrows `.field`'s input treatment rather than
   redefining one, so it matches every other input on the site, and it sits ABOVE the button with a
   full-width target: a 48 px control is thumb-sized, and a buyer who ignores it loses nothing. */
.checkout-note-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: var(--s-3); }
.checkout-note-field label {
  font-size: var(--fs-label-s); letter-spacing: var(--tr-label-s); text-transform: uppercase;
  font-weight: 500; color: var(--c-ink-soft);
}
.checkout-note-field input {
  min-height: 48px; width: 100%; border: 1px solid var(--c-line-strong);
  background: var(--c-paper-raised); padding: var(--s-3) var(--s-4); color: var(--c-ink);
  transition: border-color var(--d-fast) var(--e-standard);
}
.checkout-note-field input:hover { border-color: var(--c-ink-mute); }
.checkout-note-field input:focus-visible { outline: var(--focus-ring); outline-offset: 2px; border-color: var(--c-ink); }
.checkout-note-field .form-note { margin-top: 0; }

/* ================================================================================================
   PURCHASE BLOCK — the PDP's buy controls (owner instruction 2026-09-08)
   ------------------------------------------------------------------------------------------------
   What it replaced: one full-width black button under a bare text input, with the size field, the
   quantity and the production sentence each floating at a different rhythm. The owner called the
   whole flow "terrible" on both phone and desktop, and the diagnosis was spacing rather than
   colour — nothing on the block declared a relationship between the choices and the actions.

   So the block is now three bands with one rhythm: the CHOICES (size, quantity) in a field group,
   the two ACTIONS as equal-weight full-width buttons, and one quiet line of small print. The two
   actions are deliberately the same size: "Buy now" and "Add to cart" are both the real thing a
   visitor came to do, and making one of them a text link is what made the flow read as broken.
   ================================================================================================ */
.purchase-form { display: block; margin: 0; }

.purchase-fields { display: grid; gap: var(--s-4); margin-bottom: var(--s-4); }
/* Side by side once there is room for two real targets; stacked below that, never squeezed. */
@media (min-width: 560px) {
  .purchase-fields:has(> * + *) { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
.purchase-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.purchase-field > label {
  font-size: var(--fs-label-s); letter-spacing: var(--tr-label-s); text-transform: uppercase;
  font-weight: 500; color: var(--c-ink-soft);
}

/* One control treatment for every select in a purchase context — PDP and cart alike. 16px is a
   floor, not a preference: iOS Safari zooms the page when a focused control is smaller, and it does
   not zoom back out. The chevron is a background SVG so the control keeps a square, site-native
   frame instead of the platform's rounded one. */
.ring-size-select,
.cart-qty-select,
.cart-size-select {
  -webkit-appearance: none; -moz-appearance: none; appearance: none;
  width: 100%; min-height: 48px; padding: var(--s-3) var(--s-7) var(--s-3) var(--s-4);
  font-family: var(--font-ui); font-size: var(--fs-body); color: var(--c-ink);
  background-color: var(--c-paper-raised);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' fill='none' stroke='%234F4942' stroke-width='1.4'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--s-4) center; background-size: 12px 8px;
  border: 1px solid var(--c-line-strong); border-radius: var(--r-none);
  transition: border-color var(--d-fast) var(--e-standard);
}
.ring-size-select:hover, .cart-qty-select:hover, .cart-size-select:hover { border-color: var(--c-ink-mute); }
.ring-size-select:focus-visible, .cart-qty-select:focus-visible, .cart-size-select:focus-visible {
  outline: var(--focus-ring); outline-offset: 2px; border-color: var(--c-ink);
}
.ring-size-select[aria-invalid="true"] { border-color: var(--c-notice); }
/* A size nobody has chosen yet reads as a prompt, not as a value. */
.ring-size-select:invalid, .cart-size-select:invalid { color: var(--c-ink-mute); }

.ring-size-field .ring-size-fact,
.ring-size-field .form-note { margin-top: 2px; font-size: var(--fs-body-s); }
.ring-size-field .ring-size-fact { color: var(--c-ink-mute); }
.form-note--error { color: var(--c-notice); }

.purchase-actions { display: grid; gap: var(--s-2); }
.purchase-actions .btn { width: 100%; }
/* The outlined action carries the same weight as the filled one — a hairline on paper reads lighter
   than ink, so it takes the strong border rather than the line colour. */
.purchase-actions .btn-ghost { border-color: var(--c-ink); }

.pdp-cta .purchase-form + .pdp-reassurance { margin-top: var(--s-3); }
.pdp-reassurance { display: flex; flex-wrap: wrap; gap: 6px; align-items: baseline; }
.pdp-ask-sep { color: var(--c-line-strong); }
.pdp-cta .availability-note { margin-bottom: var(--s-3); }
.purchase-form .form-note[data-made-to-order] { margin-top: var(--s-3); }

/* ================================================================================================
   THE SELECTION PAGE (/cart/)
   ------------------------------------------------------------------------------------------------
   Every class the page emits was unstyled until 2026-09-08, so it rendered as a stack of default
   browser boxes — which is what "the whole add-to-cart flow looks terrible" was describing at the
   other end. It now uses the shop grid's own vocabulary: a portrait frame at the site's product
   ratio, the title in the display face, one column of money on the right, and the controls in the
   same field treatment the PDP uses. Mobile first — one column, full-width controls, no horizontal
   scroll at any width.
   ================================================================================================ */
.cart-layout { display: grid; gap: var(--s-7); align-items: start; }
@media (min-width: 900px) {
  .cart-layout { grid-template-columns: minmax(0, 1fr) 320px; gap: var(--s-8); }
}
.cart-heading { margin: 0 0 var(--s-5); }
.cart-notice {
  margin: 0 0 var(--s-5); padding: var(--s-3) var(--s-4);
  background: var(--c-paper-sunk); border-left: 2px solid var(--c-gilt);
  font-size: var(--fs-body-s); color: var(--c-ink-soft);
}
.cart-notice[data-cart-notice="ring_size_required"],
.cart-notice[data-cart-notice="ring_size_invalid"],
.cart-notice[data-cart-notice="invalid"] { border-left-color: var(--c-notice); color: var(--c-ink); }

.cart-lines { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--c-line); }

/* Scoped under `.cart-lines` so the cart DRAWER's own `.cart-line` rule further up keeps its 64px
   layout — two different surfaces, one class name, and the page is the more specific of the two. */
/* Phone: photograph on the left, title and price stacked beside it, controls full width underneath —
   the price lands where a buyer looks for it, immediately under the name of the piece.
   Desktop: the controls move up beside the title and the money becomes one right-hand column, so the
   eye reads a single column of figures straight down the page. */
.cart-lines .cart-line {
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  grid-template-areas: "media body" "media money" "ctrl ctrl";
  align-content: start;
  column-gap: var(--s-4); row-gap: var(--s-2);
  padding: var(--s-5) 0; border-bottom: 1px solid var(--c-line);
}
.cart-line-controls { grid-area: ctrl; min-width: 0; margin-top: var(--s-4); }
@media (min-width: 600px) {
  .cart-lines .cart-line {
    grid-template-columns: 120px minmax(0, 1fr) auto;
    grid-template-areas: "media body money" "media ctrl money";
    grid-template-rows: auto 1fr;
    column-gap: var(--s-5);
  }
  .cart-line-controls { margin-top: 0; }
}
.cart-lines .cart-line-media {
  grid-area: media; display: block; aspect-ratio: var(--ar-product-card);
  background: var(--c-paper-sunk); border: 1px solid var(--c-line); overflow: hidden;
  width: auto; height: auto;
}
.cart-lines .cart-line-media img { width: 100%; height: 100%; object-fit: contain; display: block; border: 0; }
.cart-line-body { grid-area: body; min-width: 0; }
.cart-line-money { grid-area: money; text-align: left; align-self: start; }
@media (min-width: 600px) { .cart-line-money { text-align: right; min-width: 8ch; } }

.cart-line-title {
  font-family: var(--font-display); font-size: var(--fs-display-s); line-height: var(--lh-display-s);
  font-weight: 400; margin: 0 0 var(--s-3);
}
.cart-line-title a { text-decoration: none; color: var(--c-ink); }
.cart-line-title a:hover { text-decoration: underline; text-underline-offset: 3px; }
.cart-line-price { margin: 0; font-variant-numeric: tabular-nums lining-nums; }
.cart-line-total {
  margin: 2px 0 0; font-size: var(--fs-body-s); color: var(--c-ink-mute);
  font-variant-numeric: tabular-nums lining-nums;
}

.cart-line-form { display: grid; gap: var(--s-3); align-items: end; }
@media (min-width: 480px) {
  .cart-line-form { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
  .cart-line-form .cart-line-actions { grid-column: 1 / -1; }
}
.cart-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.cart-field > label {
  font-size: var(--fs-label-s); letter-spacing: var(--tr-label-s); text-transform: uppercase;
  font-weight: 500; color: var(--c-ink-soft);
}
.cart-line-actions { display: flex; flex-wrap: wrap; gap: var(--s-4); align-items: center; }
.cart-line-actions .btn { min-height: var(--target-min); padding-inline: var(--s-5); }
/* "Remove" is destructive and secondary: a real 44px target, but never a button-shaped one competing
   with Checkout for attention. */
.cart-remove {
  display: inline-flex; align-items: center; min-height: var(--target-min);
  padding: 0; background: none; border: 0; cursor: pointer;
  font-family: var(--font-ui); font-size: var(--fs-body-s); color: var(--c-ink-mute);
  text-decoration: underline; text-underline-offset: 3px;
}
.cart-remove:hover { color: var(--c-ink); }
.cart-remove:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }

.cart-line-problem { margin: 0 0 var(--s-3); font-size: var(--fs-body-s); color: var(--c-notice); }

.cart-summary {
  background: var(--c-paper-sunk); border: 1px solid var(--c-line);
  padding: var(--s-5); display: grid; gap: var(--s-3);
}
@media (min-width: 900px) { .cart-summary { position: sticky; top: calc(var(--header-h) + var(--s-4)); } }
.cart-summary-title { margin: 0; color: var(--c-ink-mute); }
.cart-subtotal {
  display: flex; justify-content: space-between; align-items: baseline; gap: var(--s-3);
  margin: 0; padding-bottom: var(--s-3); border-bottom: 1px solid var(--c-line);
  font-family: var(--font-display); font-size: var(--fs-display-s); line-height: var(--lh-display-s);
}
.cart-subtotal strong { font-weight: 400; font-variant-numeric: tabular-nums lining-nums; }
.cart-units { font-family: var(--font-ui); font-size: var(--fs-body-s); color: var(--c-ink-mute); }
.cart-tax-note, .cart-dispatch-note, .cart-reserve-note { margin: 0; font-size: var(--fs-body-s); }
.cart-offer {
  margin: 0; padding: var(--s-3); background: var(--c-paper-raised);
  border: 1px solid var(--c-line); font-size: var(--fs-body-s); color: var(--c-ink-soft);
}
.cart-offer strong { color: var(--c-ink); letter-spacing: .04em; }
.cart-checkout-form { margin: 0; }
.cart-continue { margin: var(--s-5) 0 0; }
.cart-empty .lede { margin-bottom: var(--s-5); }

/* ==== owner pass 2026-09-08 (evening): the home page's two house doors ==== */
/* The home page shows Campaigns and Films only, and they must be the SAME SIZE: each tile kept its
   photograph's own aspect ratio before, so a portrait frame drew taller than a landscape one beside
   it (the owner's "the ring image was bigger than the campaign/film tiles"). One 4:5 frame each,
   cover-cropped, two-up from 640 and stacked below it. */
.house-tiles--pair { display: grid; gap: var(--grid-gap-y) var(--grid-gap); grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* `[data-ratio]` is repeated so this ties with `.house-tiles .tile-media[data-ratio="landscape"]`
   above and wins on order — without it, a landscape cover keeps its own 3:2 and the row is uneven
   again. */
.house-tiles--pair .tile-media,
.house-tiles--pair .tile-media[data-ratio] { aspect-ratio: 4 / 5; }
.house-tiles--pair .tile-media img { width: 100%; height: 100%; object-fit: cover; }
/* OWNER (2026-09-08, late evening): on desktop the pair used to be two 360 px portrait frames fed a 320 px
   landscape source — small, soft and left-aligned. Now: two landscape frames across the full row, sharp. */
@media (min-width: 900px) {
  .house-tiles--pair { grid-template-columns: repeat(2, minmax(0, 1fr)); justify-content: stretch; }
  .house-tiles--pair .tile-media, .house-tiles--pair .tile-media[data-ratio] { aspect-ratio: 3 / 2; }
}

/* ==== owner pass 2026-09-08 (evening): the hero's three controls on one grid ==== */
/* The two buttons and the welcome-offer line share one row and one left edge. `.offer-announce` is
   otherwise a page-width strip with its own centring, padding, sunk ground and bottom rule — correct
   at the top of a page, wrong under two left-aligned buttons, which is exactly what the owner saw. */
.hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); margin-top: var(--s-5); }
.hero-actions .hero-cta { margin-top: 0; }
.hero-actions .offer-announce {
  flex: 1 1 100%; justify-content: flex-start; gap: var(--s-2); padding: 0;
  background: none; border-bottom: 0;
}
@media (min-width: 900px) { .hero-actions .offer-announce { flex: 0 1 auto; } }
.hero--lifestyle .hero-actions { margin-top: var(--s-6); }

/* ==== owner pass 2026-09-08 (evening): the PDP's offer block balances the columns ==== */
/* `.pdp-offer` is the third child of the `.pdp` grid. Below 900 the grid is one column and it simply
   comes last, after the accordions. From 900 it is pinned to column 1 — the empty space under the
   gallery — instead of trailing a right-hand column that is already much longer. */
.pdp-offer { min-width: 0; }
@media (min-width: 900px) {
  /* Two explicit rows, and the information column spans BOTH: without that, row 1 is as tall as the
     longer of the two columns and the offer block starts below everything, leaving the same hole it
     was moved out of. With it, the block sits directly under the last thumbnail. */
  .pdp { grid-template-rows: auto 1fr; }
  .pdp > .gallery { grid-column: 1; grid-row: 1; }
  .pdp-info { grid-column: 2; grid-row: 1 / span 2; }
  .pdp-offer { grid-column: 1; grid-row: 2; align-self: start; }
}
