/* ══════════════════════════════════════════════════════════════════════════
   NOVARA DIAMONDS — foundation
   Tokens live in layout/theme.liquid's {% style %} block (merchant-editable).
   This file is the reset, the type ramp, and the primitives every section
   composes from. Section-specific CSS belongs in that section's
   {% stylesheet %} block, never here.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── fonts ─────────────────────────────────────────────────────────────────
   Both families are VARIABLE fonts, so one file covers the whole weight
   range — declaring 300..700 here is what lets a single 37KB download serve
   the wordmark at 500 and body copy at 400. Two subsets each (latin,
   latin-ext); unicode-range means a Latin-only page fetches just the first. */
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal; font-weight: 300 700; font-display: swap;
  src: url('cormorant-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal; font-weight: 300 700; font-display: swap;
  src: url('cormorant-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: italic; font-weight: 300 700; font-display: swap;
  src: url('cormorant-italic-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: italic; font-weight: 300 700; font-display: swap;
  src: url('cormorant-italic-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Karla';
  font-style: normal; font-weight: 200 800; font-display: swap;
  src: url('karla-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Karla';
  font-style: normal; font-weight: 200 800; font-display: swap;
  src: url('karla-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ── reset ─────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* The chrome is sticky and the footer must sit at the bottom of a short
     page (404, empty cart) rather than floating mid-screen. */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  overflow-x: hidden;
}
body > main { flex: 1 0 auto; }

img, svg, video, canvas { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; transition: color .18s ease, border-color .18s ease; }
a:hover { color: var(--color-accent); }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; padding: 0; cursor: pointer; }
table { border-collapse: collapse; width: 100%; }

/* Focus is visible ONLY for keyboard users — a gold ring on every mouse click
   would fight the design, but removing it outright fails WCAG 2.4.7. */
:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 3px; }
:focus:not(:focus-visible) { outline: none; }

/* ── typography ────────────────────────────────────────────────────────────
   Every heading is Cormorant at weight 400 — the display face carries the
   luxury register, so bolding it would read as shouting. Sizes are clamp()ed
   so a heading is fluid between phone and 4K without a single media query. */
h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading);
  font-weight: 400;
  line-height: 1.1;
  letter-spacing: 0;
  margin: 0 0 .5em;
  text-wrap: balance;
}
h1 { font-size: var(--text-h1); line-height: 1.06; }
h2 { font-size: var(--text-h2); }
h3 { font-size: var(--text-h3); }
h4 { font-size: var(--text-h4); }
p  { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }
strong, b { font-weight: 600; }
small { font-size: var(--text-sm); }

.skip-to-content {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  padding: .75rem 1rem; background: var(--color-text); color: var(--color-bg);
}
.skip-to-content:focus { left: 0; }
.visually-hidden {
  position: absolute !important; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; padding: 0; margin: -1px;
}

/* ── layout ────────────────────────────────────────────────────────────────
   --gutter is a clamp, so horizontal padding grows with the viewport instead
   of stepping at breakpoints. .page-width additionally caps the measure on
   very large screens; full-bleed bands use .band and keep only the gutter. */
.page-width {
  width: 100%;
  max-width: var(--page-width);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
/* The design source pads the SECTION by the gutter and lets the inner box be a
   pure pass-through — so `getComputedStyle(section).padding` really is
   `clamp(40px,5vw,72px) clamp(20px,5vw,56px)` and the first tile starts exactly
   at the section's padding edge instead of a gutter inside a centred 1400px
   box. Sections that follow the source use this modifier and own the
   padding-inline themselves.

   UNCAPPED, and that is the decision — taken 2026-08-10, from the user, in
   response to seeing the capped build side by side with the design source and
   asking for the source. The brief is a replica of it, and the source is
   gutter-only: at 1920 a band is 1808px wide with its first tile at x=56, at
   2560 it is 2448px.

   An earlier revision of this comment asserted the opposite — that the cap was
   the user's explicit choice. It was not; it was written by a concurrent
   session that was reverting this rule, and the claim did not survive checking
   with the user. Treat a comment claiming user intent as a claim to verify, not
   as settled fact, this one included.

   Capping at --page-width parks each band in a centred 1400px column: the first
   tile lands at x=260 on a 1920 screen and x=580 on a 2560 one, which is the
   "width issue" that was reported three times. Capping the header to match
   makes the page self-consistent but still not the source. .header__inner is
   uncapped for the same reason. .footer--full and .reviews below DO keep a cap
   because the source caps those two bands itself.

   Verified against the source at 1440/1920/2560: header x, heading x,
   first-tile x and tile width all identical. */
.page-width--flush { padding-inline: 0; max-width: none; margin-inline: 0; }

/* These two bands ARE capped in the design source — checked against the source
   file, not inferred: it carries `max-width: 1200px; margin: 0 auto` on the
   reviews grid, the footer grid and the footer's bottom bar, and `1100px` on
   the ink band. The product and category grids carry no cap at all, which is
   why they stay on the gutter above.
   The number is the source's 1200, NOT --page-width: this is replicating a
   specific layout, so it does not move when a merchant changes the page-width
   setting — that setting governs .page-width bands, which these are not.
   Their backgrounds sit on the SECTION, so capping the inner box narrows the
   content without breaking the full-bleed band behind it. */
.footer--full,
.reviews { max-width: 1200px; margin-inline: auto; }

/* The header pads ITSELF rather than sitting inside a padded section, and the
   source gives it no cap — so it gets none here either. With the bands above
   uncapped, the gutter alone lines the wordmark up with the first tile of every
   band at x=56, at every width. */
.header__inner { max-width: none; margin-inline: 0; }

.band { padding-inline: var(--gutter); }
.section { padding-block: var(--section-spacing); }
.section--tight { padding-block: calc(var(--section-spacing) * .6); }
.section--flush { padding-block: 0; }
.section--surface { background: var(--color-surface); }
.section--ink { background: var(--color-text); color: var(--color-cream); }
.section--ink h1, .section--ink h2, .section--ink h3 { color: var(--color-cream); }
.section--bordered { border-top: 1px solid var(--color-border); }

/* ── the eyebrow ───────────────────────────────────────────────────────────
   Gold, tracked-out, tiny caps. Appears above nearly every heading and is the
   single most identifiable piece of the brand's typography. */
.eyebrow {
  display: block;
  font-size: var(--text-xs);
  letter-spacing: .28em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--color-accent);
  margin: 0 0 var(--space-3);
}
.section--ink .eyebrow { color: var(--color-accent-light); }

/* ── section header ────────────────────────────────────────────────────────
   'split' is the dominant pattern on this site: heading hard left, a gold
   underlined link hard right, baselines aligned. It wraps to two rows on a
   phone rather than letting the link crush the heading. */
/* --section-header-mb is the per-section hook: the design source sets 28px on
   the product/category bands and 24px on the gallery, and neither is a step on
   the spacing ramp. Sections that do not set it keep the theme default. */
.section-header { margin-bottom: var(--section-header-mb, var(--space-6)); }
.section-header__title { margin: 0; }
.section-header__text { color: var(--color-text-muted); margin-top: var(--space-3); max-width: 62ch; }
.section-header--center { text-align: center; }
.section-header--center .section-header__text { margin-inline: auto; }
.section-header--split {
  display: flex; flex-wrap: wrap; gap: var(--space-4);
  align-items: baseline; justify-content: space-between;
}

.link-more {
  position: relative;
  font-size: var(--text-sm);
  letter-spacing: .1em;
  color: var(--color-accent);
  border-bottom: 1px solid var(--color-accent);
  padding-bottom: 1px;
  white-space: nowrap;
}
/* The rule is a 1px hairline under a 13px word, so the visible box is ~23px
   tall. Growing it to 44 would break the baseline alignment with the heading
   it sits opposite, so the tap target is expanded invisibly instead. */
.link-more::before { content: ""; position: absolute; inset: -11px -8px; }
.link-more:hover { color: var(--color-text); border-color: var(--color-text); }
.link-more__arrow { display: inline-block; transition: transform .2s ease; }
.link-more:hover .link-more__arrow { transform: translateX(.25em); }

/* ── buttons ───────────────────────────────────────────────────────────────
   Three variants, and the hover on all of them resolves to gold — that single
   rule is what makes the whole site feel like one object. Square by default
   (--button-radius is 0): the design's only rounded things are the bag count,
   the metal swatches and the filter pills, each of which opts in explicitly. */
.button {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  padding: var(--space-4) var(--space-7);
  font-size: var(--text-sm);
  letter-spacing: .14em;
  font-weight: 600;
  text-transform: var(--button-transform);
  border-radius: var(--button-radius);
  border: 1px solid transparent;
  background: var(--button-bg);
  color: var(--button-text);
  cursor: pointer;
  text-align: center;
  transition: background-color .2s ease, color .2s ease, border-color .2s ease;
  /* 44px minimum touch target — WCAG 2.5.8, and the difference between a
     tappable and a frustrating button on a phone. */
  min-height: 44px;
}
.button:hover { background: var(--color-accent); color: var(--color-bg); border-color: var(--color-accent); }
.button:disabled, .button[disabled] { opacity: .45; cursor: not-allowed; }
.button:disabled:hover { background: var(--button-bg); color: var(--button-text); }

.button--outline {
  background: transparent; color: var(--color-text);
  border-color: var(--color-text);
  padding-inline: var(--space-6);
}
.button--outline:hover { background: transparent; color: var(--color-accent); border-color: var(--color-accent); }

/* On the dark band the outline button inverts: gold border and gold text,
   filling to solid gold with ink text on hover. */
.button--gold {
  background: transparent; color: var(--color-accent-light);
  border-color: var(--color-accent-light);
}
.button--gold:hover { background: var(--color-accent-light); color: var(--color-text); border-color: var(--color-accent-light); }

.button--full { width: 100%; }
.button--large { padding: var(--space-5) var(--space-7); font-size: var(--text-base); }

/* ── media frame ───────────────────────────────────────────────────────────
   Every image sits in one of these. The diagonal hatch is the brand's
   placeholder: an image that is missing, slow, or transparent leaves a
   deliberate woven panel instead of a white hole. */
.frame {
  /* grid + place-items matches the design source's media box. Every child here
     (img, svg, badge) is absolutely positioned, so this is inert for them and
     only matters for a future in-flow placeholder glyph — but it is what the
     source computes to, so it is what we compute to. */
  position: relative; display: grid; place-items: center; overflow: hidden;
  aspect-ratio: var(--frame-ratio, 1 / 1);
  /* No explicit 0 on the first stop: the source omits it, and a stop list that
     starts at 0 serialises differently even though it paints the same. */
  background:
    repeating-linear-gradient(45deg,
      var(--color-hatch-a), var(--color-hatch-a) 12px,
      var(--color-hatch-b) 12px, var(--color-hatch-b) 24px);
}
.frame > img, .frame > svg, .frame > video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: var(--frame-fit, cover);
}
.frame--square    { --frame-ratio: 1 / 1; }
.frame--portrait  { --frame-ratio: 4 / 5; }
.frame--tall      { --frame-ratio: 3 / 4; }
.frame--landscape { --frame-ratio: 4 / 3; }
.frame--wide      { --frame-ratio: 16 / 9; }
.frame--fill      { aspect-ratio: auto; height: 100%; min-height: inherit; }
.frame--coarse    { background-size: 40px 40px; }
.placeholder-svg  { width: 100%; height: 100%; background: transparent; }

/* ── badge ─────────────────────────────────────────────────────────────────
   Pinned to the top-left of a card's image. Ink for BESTSELLER (and sold out),
   gold for NEW — the two states the design source actually ships.
   text-transform is ours, not the source's: the source hardcodes the string in
   caps, but this one can also carry merchant-typed metafield text, and a pill
   that reads "Limited edition" in sentence case is a different component. The
   rendered glyphs are identical either way. */
.badge {
  position: absolute; top: var(--space-3); left: var(--space-3); z-index: 1;
  background: var(--color-text); color: var(--color-cream);
  font-size: var(--text-2xs); letter-spacing: .12em; font-weight: 400;
  padding: 5px 9px; text-transform: uppercase;
}
.badge--gold { background: var(--color-accent); color: var(--color-bg); }
.badge--sale { background: var(--color-sale); color: var(--color-badge-text); }

/* ── product card ──────────────────────────────────────────────────────────
   Shared by the bestsellers rail, the collection grid and 'complete the look',
   so all three stay identical — which is the point of having one card. */
.card {
  display: flex; flex-direction: column;
  /* 10px in the source. Expressed off --space-2 so the density select still
     rescales it — the theme allows no literal pixel gap. */
  gap: calc(var(--space-2) * 1.25);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  transition: border-color .2s ease, transform .2s ease;
  height: 100%;
}
.card:hover { border-color: var(--color-accent); color: inherit; }
.card:hover .card__title { color: var(--color-accent); }
.card__media { flex: none; }
.card__media img { transition: transform .6s cubic-bezier(.2,.6,.2,1); }
.card:hover .card__media img { transform: scale(1.04); }
.card__body {
  padding: var(--space-1) var(--space-4) var(--space-4);
  display: flex; flex-direction: column; gap: var(--space-1);
  flex: 1 0 auto;
}
/* 15 / 12.5 / 14px are the source's card sizes and none of them is a step on
   the --text-* ramp. They are still multiplied by --body-scale so the
   merchant's type-size setting reaches the card like it reaches everything
   else. */
.card__title {
  font-family: var(--font-body);
  font-size: calc(15px * var(--body-scale));
  font-weight: 600; margin: 0; line-height: 1.35;
  transition: color .18s ease;
  /* Real catalogue titles run to 100+ characters and wrapped to four lines,
     which made every card in the rail a different shape. Two lines is the
     source's silhouette and gives the whole rail one baseline grid. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}
.card__meta {
  font-size: calc(12.5px * var(--body-scale));
  color: var(--color-text-muted); line-height: 1.45;
  /* Same reason as the title: a five-option variant spec must not become the
     one card in the row that is two lines tall. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 1;
  line-clamp: 1;
  overflow: hidden;
}
.card__foot  {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: var(--space-3); margin-top: var(--space-1);
  font-size: calc(14px * var(--body-scale));
}
.card__rating { color: var(--color-accent); font-size: var(--text-xs); white-space: nowrap; }
/* The source's card price is a <strong>, which browsers render at 700. This
   theme's `strong` is 600 site-wide, so the weight is restored here rather than
   changing every bold word on the site. */
.card__foot .price__current { font-weight: 700; }
.card__quick { padding: 0 var(--space-4) var(--space-4); }

/* ── price ─────────────────────────────────────────────────────────────── */
.price { display: flex; gap: .5rem; align-items: baseline; flex-wrap: wrap; }
.price__current { font-weight: 600; }
.price__compare { text-decoration: line-through; color: var(--color-text-muted); font-weight: 400; font-size: .9em; }
.price--on-sale .price__current { color: var(--color-sale); }

/* ── star rating ───────────────────────────────────────────────────────── */
.stars { color: var(--color-accent); letter-spacing: .14em; font-size: var(--text-sm); }

/* ── grids ─────────────────────────────────────────────────────────────────
   auto-fill + minmax reflows with no breakpoints at all: the same declaration
   yields 2 columns on a phone, 3 on a tablet and 5 on a 4K monitor. min() with
   100% is what stops a 250px minimum from overflowing a 320px screen. */
.grid { display: grid; gap: var(--grid-gap); }
.grid--products { grid-template-columns: repeat(auto-fill, minmax(min(var(--card-min, 250px), 100%), 1fr)); }
.grid--auto     { grid-template-columns: repeat(auto-fit,  minmax(min(var(--card-min, 240px), 100%), 1fr)); }
/* There was a `@media (max-width: 519px)` here pinning 2 columns, on the view
   that one column of huge squares makes a 12-piece collection feel like an
   endless scroll. It was removed: the design source is a flat
   minmax(250px, 1fr) with no breakpoint, which gives 1-up on a 390px phone, and
   cards-per-row was called out explicitly as part of the replica. It also beat
   --card-min from the section, so the two rules disagreed about the same grid.
   Restore it only alongside a matching change to the source's own track. */

/* ── horizontal rail ───────────────────────────────────────────────────────
   The bestsellers carousel. Scroll-snap plus a gutter-width scroll-padding so
   a snapped card lines up with the section's left edge rather than the
   viewport's. Scrollbar hidden; the partial next card is the affordance. */
.rail {
  display: grid; grid-auto-flow: column;
  grid-auto-columns: minmax(var(--rail-min, 240px), var(--rail-max, 300px));
  gap: var(--grid-gap);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scroll-padding-left: var(--gutter);
  padding-bottom: var(--space-2);
  scrollbar-width: none;
  -ms-overflow-style: none;
  /* Bleed the rail through the section gutter so cards scroll off the screen
     edge instead of stopping short — the detail that makes it read as a rail
     and not a clipped grid. */
  margin-inline: calc(var(--gutter) * -1);
  padding-inline: var(--gutter);
}
.rail::-webkit-scrollbar { display: none; }
.rail > * { scroll-snap-align: start; }

/* ── forms ─────────────────────────────────────────────────────────────── */
.field { display: grid; gap: .4rem; margin-bottom: var(--space-4); }
.field label { font-size: var(--text-sm); color: var(--color-text-muted); }
.input, .field input, .field select, .field textarea {
  width: 100%;
  padding: var(--space-3) var(--space-4);
  min-height: 44px;
  background: var(--color-bg);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: var(--button-radius);
  font-size: var(--text-base);
}
.input:focus, .field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--color-accent);
}
.select {
  padding: var(--space-3) var(--space-4);
  min-height: 44px;
  border: 1px solid var(--color-border);
  background: var(--color-bg); color: var(--color-text);
  border-radius: var(--button-radius);
  font-size: var(--text-sm);
  cursor: pointer;
}
.select:focus { outline: none; border-color: var(--color-accent); }
.form__error   { color: var(--color-sale); font-size: var(--text-sm); }
.form__success { color: var(--color-accent); font-size: var(--text-sm); }

/* ── option chips (carat, size, metal, facet pills) ───────────────────────
   One chip class serves the PDP's carat/size selectors and the collection's
   filter pills; --chip-radius is the only thing that differs. 44px min in both
   axes so every one of them is a legal touch target. */
.chip {
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px;
  min-width: 44px; min-height: 44px;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--chip-radius, 0);
  background: transparent; color: var(--color-text);
  font-size: var(--text-sm); font-weight: 600;
  line-height: 1.2; white-space: nowrap;
  transition: background-color .18s ease, border-color .18s ease, color .18s ease;
}
.chip:hover { border-color: var(--color-accent); color: var(--color-accent); }
.chip[aria-pressed="true"], .chip.is-active, .chip--active {
  background: var(--color-text); border-color: var(--color-text); color: var(--color-cream);
}
.chip[aria-pressed="true"]:hover, .chip.is-active:hover { background: var(--color-accent); border-color: var(--color-accent); color: var(--color-bg); }
.chip__sub { font-weight: 400; font-size: var(--text-xs); opacity: .75; }
.chip--pill { --chip-radius: 999px; padding-inline: var(--space-5); }

/* ── swatch ────────────────────────────────────────────────────────────────
   Metal circles. The selection ring is an outline with an offset, not a
   border, so picking a metal does not resize the dot and shift the row. */
.swatch {
  width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid var(--color-swatch-edge);
  outline: 2px solid transparent; outline-offset: 3px;
  cursor: pointer; padding: 0;
  transition: outline-color .18s ease;
}
.swatch:hover { outline-color: var(--color-border); }
.swatch[aria-pressed="true"], .swatch.is-active { outline-color: var(--color-text); }
.swatch--yellow { background: linear-gradient(135deg, #E8C87E, #C9A25C); }
.swatch--white  { background: linear-gradient(135deg, #EDEDEA, #C9C9C4); }
.swatch--rose   { background: linear-gradient(135deg, #E8B49A, #CE9276); }
/* The swatch itself is 34px but the tap target must be 44 — a transparent
   pseudo-element grows the hit area without moving the dot. */
.swatch::after { content: ""; position: absolute; inset: -5px; }
.swatch { position: relative; }

/* ── trust strip / assurance row ───────────────────────────────────────── */
.assurance {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-3);
  border: 1px solid var(--color-border);
  padding: var(--space-4);
  text-align: center;
  font-size: var(--text-xs); color: var(--color-text-muted); line-height: 1.45;
}
.assurance strong { display: block; color: var(--color-text); margin-bottom: 2px; font-size: var(--text-sm); }
@media (max-width: 399px) { .assurance { grid-template-columns: 1fr; text-align: start; } }

/* ── accordion (details/summary) ───────────────────────────────────────────
   Native <details> so it works with zero JS and stays keyboard-accessible.
   The +/− is a ::after on the marker row; the default triangle is removed. */
.accordion { border-top: 1px solid var(--color-border); }
.accordion__item { border-bottom: 1px solid var(--color-border); }
.accordion__summary {
  display: flex; justify-content: space-between; align-items: center; gap: var(--space-4);
  padding: var(--space-4) 2px;
  font-size: var(--text-md); font-weight: 600; letter-spacing: .06em;
  cursor: pointer; list-style: none; min-height: 44px;
}
.accordion__summary::-webkit-details-marker { display: none; }
.accordion__summary::after {
  content: "+"; color: var(--color-accent); font-size: 1.15em; line-height: 1;
  transition: transform .2s ease; flex: none;
}
.accordion__item[open] > .accordion__summary::after { content: "\2212"; }
.accordion__summary:hover { color: var(--color-accent); }
/* max-width, because an accordion is not always in a narrow column. On the PDP
   it sits in a ~520px buying column and this never bites; dropped into a
   full-width page body it was setting answers 1288px wide — 177 characters a
   line. The cap only engages where the container is already too wide. */
.accordion__body { padding: 0 2px var(--space-4); font-size: var(--text-md); line-height: 1.7; color: var(--color-text-muted); max-width: 68ch; }
.accordion__body > :last-child { margin-bottom: 0; }

/* ── breadcrumbs ───────────────────────────────────────────────────────── */
.breadcrumbs { font-size: var(--text-xs); color: var(--color-text-muted); letter-spacing: .06em; }
.breadcrumbs ol { list-style: none; display: flex; flex-wrap: wrap; gap: .35rem; margin: 0; padding: 0; }
.breadcrumbs li + li::before { content: "/"; margin-right: .35rem; opacity: .7; }
.breadcrumbs a:hover { color: var(--color-accent); }

/* ── pagination ────────────────────────────────────────────────────────── */
.pagination { display: flex; flex-wrap: wrap; gap: .4rem; justify-content: center; padding-block: var(--space-7); list-style: none; margin: 0; }
/* DIRECT children of the <li>, not descendants. The arrows are
   <a><span aria-hidden>→</span></a>, so a descendant selector gave the inner
   span its own 44px box and border: the next control rendered 62x44 with a
   bordered 44x44 box nested inside it, while every number was a clean 44x44.
   It read as an empty outlined rectangle with a small arrow floating in it. */
.pagination li > a, .pagination li > span {
  display: inline-flex; min-width: 44px; height: 44px; padding-inline: .5rem;
  align-items: center; justify-content: center;
  border: 1px solid var(--color-border); font-size: var(--text-sm);
}
.pagination a:hover { border-color: var(--color-accent); color: var(--color-accent); }
.pagination [aria-current="page"] { background: var(--color-text); border-color: var(--color-text); color: var(--color-cream); }

/* The "…" between page windows is a SEPARATOR, not a control, and it was
   inheriting the full 44px bordered box — so a row of pages read as
   [1] [2] [box] [12], with the gap indistinguishable from a page you could
   click. Only the direct child of the <li> is targeted: the arrows are
   <a><span aria-hidden>←</span></a>, and a bare [aria-hidden] selector would
   strip their boxes too. */
.pagination li > span[aria-hidden] {
  border-color: transparent;
  min-width: var(--space-6);
  padding-inline: 0;
  color: var(--color-text-muted);
}

/* ── quote / review card ───────────────────────────────────────────────── */
.quote {
  margin: 0; background: var(--color-bg); border: 1px solid var(--color-border);
  padding: var(--space-6); display: flex; flex-direction: column; gap: var(--space-4);
  height: 100%;
}
.quote__text { margin: 0; font-size: var(--text-md); line-height: 1.65; }
.quote__text--display { font-family: var(--font-heading); font-size: var(--text-lg); font-style: italic; line-height: 1.5; }
.quote__cite { font-size: var(--text-xs); color: var(--color-text-muted); font-style: normal; }

/* ── prose (richtext output) ───────────────────────────────────────────── */
.prose { max-width: var(--prose-measure, 62ch); }
.prose > :first-child { margin-top: 0; }
.prose > :last-child { margin-bottom: 0; }
.prose a { text-decoration: underline; text-underline-offset: 2px; }
.prose ul, .prose ol { padding-inline-start: var(--space-5); margin-block: 0 1em; }
.prose--center { margin-inline: auto; }

/* ── utilities ─────────────────────────────────────────────────────────── */
.muted       { color: var(--color-text-muted); }
.text-left   { text-align: start; }
.text-center { text-align: center; }
.text-right  { text-align: end; }
.stack       { display: flex; flex-direction: column; }
.stack > * + * { margin-top: var(--stack-space, var(--space-5)); }
.stack--2 { --stack-space: var(--space-2); } .stack--3 { --stack-space: var(--space-3); }
.stack--4 { --stack-space: var(--space-4); } .stack--5 { --stack-space: var(--space-5); }
.stack--6 { --stack-space: var(--space-6); } .stack--7 { --stack-space: var(--space-7); }
.cluster { display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: center; }
.cluster--between { justify-content: space-between; }
.cluster--center  { justify-content: center; }
.cluster--loose   { gap: var(--space-5) var(--space-6); }
.empty-state { text-align: center; padding-block: var(--space-8); color: var(--color-text-muted); }
.spinner { width: 1.1em; height: 1.1em; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; display: inline-block; animation: spin .7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ══════════════════════════════════════════════════════════════════════════
   SHARED COMPOSITION PRIMITIVES
   The secondary pages (blog, article, account, 404, page) and the decorative
   sections (bento, stats, marquee, faq, feature-grid, slideshow, image-banner)
   compose from these. They each ship their own component CSS in a
   {% stylesheet %} block; what follows is only the vocabulary they share.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── surface ───────────────────────────────────────────────────────────── */
.surface {
  background: var(--color-surface);
  border-radius: var(--button-radius);
  padding: var(--surface-pad, var(--space-6));
}
.surface--outline { background: transparent; border: 1px solid var(--color-border); }
.surface--accent  { background: var(--color-text); color: var(--color-cream); }
.surface--accent h1, .surface--accent h2, .surface--accent h3 { color: var(--color-cream); }
.surface--flush   { --surface-pad: 0; }

/* ── media (alias of .frame for sections that predate it) ──────────────── */
.media {
  position: relative; display: grid; place-items: center; overflow: hidden;
  aspect-ratio: var(--media-ratio, 1 / 1);
  background:
    repeating-linear-gradient(45deg,
      var(--color-hatch-a), var(--color-hatch-a) 12px,
      var(--color-hatch-b) 12px, var(--color-hatch-b) 24px);
}
.media > img, .media > svg, .media > video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: var(--media-fit, cover);
}
.media--square { --media-ratio: 1 / 1; }   .media--portrait  { --media-ratio: 4 / 5; }
.media--tall   { --media-ratio: 3 / 4; }   .media--landscape { --media-ratio: 4 / 3; }
.media--wide   { --media-ratio: 16 / 9; }  .media--ultrawide { --media-ratio: 21 / 9; }
.media--contain { --media-fit: contain; }
.media--fill { aspect-ratio: auto; height: 100%; }
.media--pan  > img { animation: ui-pan  22s ease-in-out infinite alternate; }
.media--zoom > img { animation: ui-zoom 22s ease-in-out infinite alternate; }
@keyframes ui-pan  { from { transform: scale(1.12) translateX(-3%); } to { transform: scale(1.12) translateX(3%); } }
@keyframes ui-zoom { from { transform: scale(1); } to { transform: scale(1.12); } }

/* ── overlay — content placed over media, in nine positions ────────────── */
.overlay {
  position: absolute; inset: 0; display: grid;
  padding: var(--overlay-pad, var(--space-5));
  place-content: center; text-align: center;
}
.overlay--top-left     { place-content: start start;  text-align: start; }
.overlay--top-center   { place-content: start center; }
.overlay--top-right    { place-content: start end;    text-align: end; }
.overlay--center-left  { place-content: center start; text-align: start; }
.overlay--center       { place-content: center; }
.overlay--center-right { place-content: center end;   text-align: end; }
.overlay--bottom-left  { place-content: end start;    text-align: start; }
.overlay--bottom-center{ place-content: end center; }
.overlay--bottom-right { place-content: end end;      text-align: end; }

/* The scrim paints at z-index 0 and content at 1 — NOT z-index:-1, which would
   drop it behind the parent .media and hide it under the image it darkens. */
.tone-light { color: #fff; }
.tone-dark  { color: var(--color-text); }
.tone-light::before, .tone-dark::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background: var(--tone-scrim, linear-gradient(to top, rgba(33,29,24,.6), rgba(33,29,24,.05)));
}
.tone-dark::before { --tone-scrim: linear-gradient(to top, rgba(251,249,246,.7), rgba(251,249,246,.1)); }
.tone-light > *, .tone-dark > * { position: relative; z-index: 1; }
.tone-light .eyebrow { color: var(--color-accent-light); }

/* ── extra grid shapes ─────────────────────────────────────────────────── */
.grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* Fixed column counts, mobile-first. The tablet step is a literal 2 rather
   than min(var(--columns),2): a math function IS accepted in an integer
   position by current browsers, but there is no reason to spend that
   compatibility budget when the tablet answer is always two. */
.grid--fixed { grid-template-columns: 1fr; }
@media (min-width: 750px) { .grid--fixed { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 990px) { .grid--fixed { grid-template-columns: repeat(var(--columns, 4), minmax(0, 1fr)); } }

/* Bento spans, capped by min() so a 4-wide tile in a 3-column grid degrades
   instead of overflowing the row. */
.span-1 { grid-column: span 1; }
.span-2 { grid-column: span min(2, var(--columns, 4)); }
.span-3 { grid-column: span min(3, var(--columns, 4)); }
.span-4 { grid-column: span min(4, var(--columns, 4)); }
.row-1 { grid-row: span 1; } .row-2 { grid-row: span 2; }
.row-3 { grid-row: span 3; } .row-4 { grid-row: span 4; }
/* A 2×2 bento tile on a phone is just a tall box — collapse every span. */
@media (max-width: 749px) {
  .span-2, .span-3, .span-4 { grid-column: span 1; }
  .row-2, .row-3, .row-4 { grid-row: span 1; }
}

/* ── banner (image-banner / slideshow) ─────────────────────────────────── */
.banner { position: relative; display: grid; }
.banner__media img { width: 100%; object-fit: cover; }
.banner--small  .banner__media { min-height: clamp(200px, 34vw, 260px); }
.banner--medium .banner__media { min-height: clamp(280px, 46vw, 420px); }
.banner--large  .banner__media { min-height: clamp(360px, 60vw, 580px); }
.banner__content {
  padding: var(--space-6) var(--gutter);
  display: grid; gap: var(--space-3); justify-items: center; text-align: center;
  width: 100%; max-width: var(--page-width); margin-inline: auto;
}
.banner__content--left  { justify-items: start; text-align: start; }
.banner__content--right { justify-items: end;   text-align: end; }
@media (min-width: 750px) {
  .banner--overlay::before {
    content: ""; position: absolute; inset: 0; z-index: 1;
    background: linear-gradient(transparent, rgba(33,29,24,.4));
  }
  .banner--overlay .banner__content {
    position: absolute; inset: 0; z-index: 2; align-content: center; color: #fff;
  }
}

/* ── slideshow ─────────────────────────────────────────────────────────── */
.slideshow { position: relative; }
.slideshow__track { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; }
.slideshow__track::-webkit-scrollbar { display: none; }
.slideshow__slide { flex: 0 0 100%; scroll-snap-align: start; min-width: 0; }
.slideshow__dots { display: flex; gap: .5rem; justify-content: center; padding-top: var(--space-3); }
.slideshow__dot {
  width: 9px; height: 9px; border-radius: 50%; padding: 0; cursor: pointer;
  background: var(--color-border); position: relative;
}
/* 9px dot, 44px tap target. */
.slideshow__dot::after { content: ""; position: absolute; inset: -17px; }
.slideshow__dot[aria-current="true"] { background: var(--color-text); }

/* ── rich text / rte output ────────────────────────────────────────────── */
.rte > :first-child { margin-top: 0; }
.rte > :last-child { margin-bottom: 0; }
.rte a { text-decoration: underline; text-underline-offset: 2px; }
/* …but a button inside rich text is still a button. `.rte a` (0,1,1) outranks
   `.button` (0,1,0), so a CTA authored inside a page body rendered underlined —
   visible on any page whose content links out with .button or .link-more, and
   not fixable from the content side. .link-more draws its own border-bottom, so
   the underline was doubling the rule there. */
.rte .button, .rte .link-more, .prose .button, .prose .link-more { text-decoration: none; }
.rte img { margin-block: var(--space-4); }
/* …but NOT one inside a .frame. A framed image is `position: absolute; inset: 0`,
   and margins still apply to an inset-positioned box: the 16px pushed every
   image 16px down and 16px past the bottom of its frame, so the hatch showed
   as a strip above each photo and the bottom of the picture was clipped.
   Visible on every framed image in a page body — four on the About page. */
.rte .frame img, .prose .frame img { margin-block: 0; }
.rte blockquote {
  margin: var(--space-5) 0; padding-left: var(--space-5);
  border-left: 2px solid var(--color-accent);
  font-family: var(--font-heading); font-size: var(--text-lg); font-style: italic;
}

/* ── form status ───────────────────────────────────────────────────────── */
.form-status { padding: var(--space-3) var(--space-4); border: 1px solid currentColor; margin-bottom: var(--space-4); font-size: var(--text-sm); }
.form-status--error   { color: var(--color-sale); }
.form-status--success { color: var(--color-accent); }

/* ── legacy aliases ────────────────────────────────────────────────────────
   Older call sites used these names. Mapping them here is cheaper and safer
   than editing a dozen secondary templates, and keeps one visual system. */
.button--secondary { background: transparent; color: var(--color-text); border-color: var(--color-text); padding-inline: var(--space-6); }
.button--secondary:hover { background: transparent; color: var(--color-accent); border-color: var(--color-accent); }
.section__header { text-align: center; margin-bottom: var(--space-6); }
.section__header p { color: var(--color-text-muted); margin: 0; }
.card__media--square   { aspect-ratio: 1 / 1; }
.card__media--portrait { aspect-ratio: 4 / 5; }
.card__vendor { font-size: var(--text-xs); color: var(--color-text-muted); }
.card__info { padding-top: var(--space-3); display: grid; gap: .2rem; }
.card__link { display: block; }
.link-arrow { display: inline-flex; align-items: center; gap: var(--space-2); }
.link-arrow::after { content: "\2192"; transition: transform .2s ease; }
.link-arrow:hover::after { transform: translateX(.2em); }
.pad-4 { padding: var(--space-4); } .pad-5 { padding: var(--space-5); }
.pad-6 { padding: var(--space-6); } .pad-7 { padding: var(--space-7); }
.gap-2 { gap: var(--space-2); } .gap-3 { gap: var(--space-3); }
.gap-4 { gap: var(--space-4); } .gap-5 { gap: var(--space-5); }
.gap-6 { gap: var(--space-6); } .gap-7 { gap: var(--space-7); }
.block-image { min-height: var(--space-8); background: var(--color-surface); }
.block-image img { width: 100%; height: auto; }
.badge--numeral { width: 2.25rem; height: 2.25rem; padding: 0; border-radius: 50%; font-size: .95rem; position: static; }
.badge--outline { background: transparent; color: inherit; border: 1px solid currentColor; position: static; }

/* ── motion opt-out ────────────────────────────────────────────────────────
   One global rule. No component ships its own prefers-reduced-motion block. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}
